HN user

evelant

82 karma
Posts0
Comments40
View on HN
No posts found.

I guess I fail to see why this is such a problem. Yes it would be nice if the wire format were standardized or had a standard schema description, but is writing a parser that handles several formats actually a difficult problem? Modern models could probably whip up a "libToolCallParser" with bindings for all popular languages in an afternoon. Could probably also have an automated workflow for adding any new ones with minimal fuss. An annoyance, yes, but it does not seem like a really "hard" problem. It seems more of a social problem that open source hasn't coalesced around a library that handles it easily yet or am I missing something?

AFAIK that's not really true, at least of modern vapes. Their function is not to "make fire", it's to heat a metal coil to a specific temperature at which propylene glycol and vegetable glycerin will aerosolize which is far lower than ignition temperature. Most modern vapes also use controllers with a feedback loop that pulse power through the coil hundreds of times per second to maintain the ideal temperature and desired power throughput. That being said there are definitely crappy and diy devices that unsafely dump huge current through devices but AFAIK modern devices generally don't do this because it's a bad user experience (burnt taste, too hot, ruined cotton absorber, etc) -- regulating the power is what users want so it's what devices do.

IMO it does not. At least to me the meaning and value of something is in the creative human design behind it, not the tools used to build it. I don’t think AI changes much there. It’s a (very powerful) tool but still IMO the value lies with the creativity and skill of the operator.

I made the vscode integration for this. I feel bad that I haven’t contributed much since, it’s a really cool project. IMO it’s important to try to innovate in the foundational tools of our craft (editors, languages, tooling, OS, etc) which Ki does.

Yeah, it's unfortunate but it's not really react-native/facebook's fault. Apple doesn't allow any sort of JIT to run on iOS outside of their builtin webkit js engine. That means that AFAIK there's no way to run wasm at reasonable speed on iOS, which means react-native can't really support wasm.

Romulus was pretty good actually. If you want great newer aliens universe play the game Alien: Isolation. It’s the best piece of media in the aliens universe since Aliens. It’s an amazing experience and blows all of the later films/shows out of the water in regards to keeping the original “vibe” of the setting.

Alien: Isolation truly is an under appreciated masterpiece. One of the best video games ever made IMO. Aesthetic, sound design (put on headphones and watch the reactor purge scene or the spacewalk near the end it’s phenomenal sound design), emotional design, storytelling, it captures the setting in a way I don’t think anything has done since the first two films.

My prolog anecdote: ~2001 my brother and I writing an A* pathfinder in prolog to navigate a bot around the world of Asheron's Call (still the greatest MMORPG of all time!). A formative experience in what can be done with code. Others had written a plugin system (called Decal) in C for the game and a parser library for the game's terrain file format. We took that data and used prolog to write an A* pathfinder that could navigate the world, avoiding un-walkable terrain and even using the portals to shortcut between locations. Good times.

Personally I’ve been using Brave browser on desktop and iOS. It has some of the best adblocking on mobile. Also use adguard pro which provides dns level filtering.

It certainly doesn’t feel good to have turned out being correct after warning that this is where we were headed way back in the dubyah years. This has always been the plan, it hasn’t been hidden, corporate media has just succeeded in sanewashing it for decades. Abdication of journalistic responsibility in the name of profits has allowed construction of alternate realities for so many people that these atrocities are now possible with few noticing.

I’ve been experimenting with this, it’s a very interesting problem space!

https://github.com/evelant/synchrotron

Idea is to sync business logic calls instead of state. Let business logic resolve all conflicts client side. Logical clocks give consistent ordering. RLS gives permissions and access control. No dedicated conflict resolution logic necessary but still guarantees semantic consistency and maximally preserves user intentions. That’s the idea at least, requires more thought and hacking.

Cool stuff! This looks quite similar to https://traycer.ai/

I think there's probably a lot of value to be gained in tooling for coding agents that codify and enhance the describe -> explore -> plan -> refine -> implement -> verify cycle. With most popular tools (cursor, claude code, roo, augment, windsurf, etc) you have to do this workflow "manually" usually by having the model write out .md files, it isn't super smooth.

It would be up to application logic. This prototype essentially offers the same behavior you would get with a traditional backend API except it works offline. The results would be the same as if clients made those calls to a backend api, that is, up to application logic. My idea was that it's essentially impossible to have generic "conflict resolution" that follows arbitrary business rules so I made the business rules _be_ the conflict resolution. For any given situation the answer to "how would it handle a then b then c" is "the same as any normal backend api, per regular business logic, except it works offline".

It does not pick an arbitrary order for operations. They happen in total (known at the time, eventually converging) order across all clients thanks to hybrid logical clocks. If events arrive that happened before events a client already has locally it will roll back to that point in time and replay all of the actions forward in total ordering.

As for the specific scenario, if a client sets a task as COMPLETE and another sets it as CANCELLED before seeing the COMPLETE from the other client here's what would happen.

Client1: { id: 1, action: completeTask, taskId: 123, clock: ...}

Client1: SYNC -> No newer events, accepted by server

Client2: { id: 2, action: cancelTask, taskId: 123, clock: ...}

Client2: SYNC -> Newer events detected.

Client2: Fetch latest events

Client2: action id: 1 is older than most recent local action, reconcile

Client2: rollback to action just before id: 1 per total logical clock ordering

Client2: Replay action { id: 1, action: completeTask, taskId: 123, clock: ...}

Client2: Replay action { id: 2, action: cancelTask, taskId: 123, clock: ...} <-- This is running exactly the same application logic as the first cancelTask. It can do whatever you want per app semantics. In this case we'll no-op since transition from completed -> cancelled is not valid.

Client2: SYNC -> no newer actions in remote, accepted

Client1: SYNC -> newer actions in remote, none local, fetch newer actions, apply action { id: 2, action: cancelTask, ...}

At this point client1, client2, and the central DB all have the same consistent state. The task is COMPLETE. Data is consistent and application semantics are preserved.

There's a little more to it than that to handle corner cases and prevent data growth, but that's the gist of it. More details in the repo.

The great thing is that state is reconciled by actually running your business logic functions -- that means that your app always ends up in a valid state. It ends up in the same state it would have ended up in if the app was entirely online and centralized with traditional API calls. Same outcome but works totally offline.

Does that clarify the idea?

You could argue that this would be confusing for Client2 since they set the task to cancelled but it ended up as complete. This isn't any different than a traditional backend api where two users take incompatible actions. The solution is the same, if necessary show an indicator in the UI that some action was not applied as expected because it was no longer valid.

edit: I think I should improve the readme with a written out example like this since it's a bit hard to explain the advantages of this system (or I'm just not thinking of a better way)

You're right, it's not the same as conflict/merge semantics, but you probably could implement those semantics on top of it. My idea was more about being able to merge offline states for arbitrary data without user intervention while also ensuring that application invariants / semantics are preserved. Preserving app semantics while as much as possible preserving user intentions.

For a text document a normal CRDT is perfect. They're very good for that specific case. What I tried to solve is eventual consistency that _also_ preserves application semantics. For example a task tracker:

* first update sets task cancelled_at and cancellation_reason

* second update wants the task to be in progress, so sets started_at

CRDT's operate only at the column/field level. In this situation you'd have a task with cancelled_at, cancellation_reason, status in progress, and started_at. That makes no sense semantically, a task can't both be cancelled and in progress. CRDTs do nothing to solve this. My solution is aimed at exactly this kind of thing. Since it replicates _intentions_ instead of just data it would work like this:

action1: setCancelled(reason) action2: setInProgress

When reconciling total order of actions using logical clocks the app logic for setCancelled runs first then setInProgress runs second on every client once they see these actions. The app logic dictates what should happen, which depends on the application. You could have it discard action2. You could also have it remove the cancellation status and set in_progress. It depends on the needs of the application but the application invariants / semantics are preserved and user intentions are preserved maximally in a way that plain CRDTs cannot do.

The pattern I came up with is similar to event sourcing but with some CRDT and offline-first concepts mixed in. By using logical clocks and a client side postgres (pglite) it doesn't have to keep the entire event history for all time and the server side doesn't have to process actions/events at all beyond storing them. The clients do the resolution of state, not the server. Clients can operate offline as long as they like and the system still arrives at a consistent state. AFAIK this is different than most event sourcing patterns.

At least in my thinking/prototyping on the problem so far I think this solution offers some unique properties. It lets clients operate offline as long as they like. It delegates the heavy lifting of resolving state from actions/events to clients, requiring minimal server logic. It prevents unbounded growth of action logs by doing a sort of "rebase" for clients beyond a cutoff. It seems to me like it maximally preserves intentions without requiring specific conflict resolution logic. IMO worth exploring further.

I prototyped exactly such a framework! It's designed to solve exactly the problem you mentioned. It’s a super interesting problem. https://github.com/evelant/synchrotron

The gist is:

* Replicating intentions (actions, immutable function call definitions that advance state) instead of just replicating state.

* Hybrid logical clocks for total ordering.

* Some client side db magic to make action functions deterministic.

This ensures application semantics are always preserved with no special conflict resolution considerations while still having strong eventual consistency. Check out the readme for more info. I haven’t gotten to take it much further beyond an experiment but the approach seems promising.

The more years I've been developing software (and it's been awhile) the more I come to find that the answer is almost always "it depends". The definition of "simple" depends on the case, the requirements, and the level of uncertainty about the future. The definition of "complex" depends on the case, the requirements, and the level of uncertainty about the future.

I've run into a lot of situations where doing the "simplest thing that could possibly work" ultimately led to mountains of pain and failure. I've also run into situations where things were over-engineered to account for situations that never came to be resulting in similar pain.

It boils down to "it depends" -- a careful analysis of the requirements, tradeoffs, future possibilities, and a mountain of judgement based on experience.

For sure, err on the side of "simple" when there's uncertainty but don't be dogmatic about it. Apply simple principles like loose coupling, pure functions, minimal state, avoid shared state, pick boring tools, and so on to ensure that "the simplest thing that could possibly work" doesn't become "the simplest thing that used to work but is now utter hell in the face of the unexpected". It all depends.

Have you considered expanding this idea to more than just ebikes?

For example at least here in the US lithium ion tool batteries come with lots of different connector shapes just to lock users into a brand and are not repairable. Manufacturers charge a lot for the batteries, often more than the tools, and if (when) they go bad they just have to be thrown out. Manufacturers even occasionally "upgrade" their battery designs so that they're not backwards compatible to force purchase of new batteries for newer tools. A universal repairable tool battery would likely be a big hit!

Another use case might be USB power packs. These are all sealed and not repairable. They go bad after awhile and the whole thing is waste when it's most likely just a single cell failing.

Sins of a solar empire 2. AI War 2. There haven’t been any really “big” ones like StarCraft but some very good smaller ones like those two.