Write-up
Commit messages are events. Obligations are state.
Two machines, one repo, and two coding agents that will never meet. Every tool we have moves knowledge between them. None of them move obligation, and that is the thing that actually goes missing.
01The Friday problem
Here is the setup. A desktop that builds the web app and Android. A laptop that builds iOS. One repo, one backend, one set of credentials. A coding agent on each machine, and they are never running at the same time, so they have never met and never will.
Tuesday, on the desktop, you fix a bug where uploaded images come out rotated. It turns out to be an orientation flag dropped during a resize, in shared code, so the fix is already live on iOS the moment the laptop pulls. But the laptop still has to rebuild its native bundle and confirm it on a real device, because "it should work" is not the same as "it works." You write that in the commit message.
Friday, on the laptop. You pull. Thirty commits come down. Your note is somewhere in the middle of them and nobody reads it, because nobody reads thirty commit messages. The verification never happens.
Three weeks later a user reports rotated images on iPhone, and you spend an afternoon rediscovering a bug you already fixed and never confirmed.
The commit message was not wrong. It was the wrong shape of thing.
02The distinction
An event fires once and then it is history. A commit message, a chat handoff, a session note, a message in a channel, a standup. Its entire job is to be read at the moment it arrives. Miss that window and it is functionally deleted, because nobody scrolls back.
State is different. State stays true until something changes it. An open ticket is state. An unticked checkbox is state. A failing test is state. You do not have to be present when state is created, because it will still be there when you arrive.
Work owed between two machines is state. It does not expire because a week went by. It expires when somebody does the work.
Every tool we have for cross-machine coordination transmits events. Almost none of them transmit state.
Go through them. Commit messages are events. Chat handoffs between agents are events, and most of them additionally require both agents to be alive at the same moment, which two machines in two rooms usually are not. Session notes are events. The whole growing category of agent memory products syncs knowledge, which is closer to the mark, but knowledge is not obligation. Knowing that a bug was fixed on the other machine is a fact. Owing a verification is a debt.
Knowledge transfer is solved several times over. Obligation transfer is not solved at all, and it is the one where the failure is silent.
03What an obligation actually looks like
Nothing exotic. An entry with one line per machine, and a checkbox on each line.
- [ ] Uploaded images come out rotated on iOS
Found: win-desktop, 2026-08-13
Cause: shared, orientation flag dropped in the resize step
Windows: [x] fixed in the shared resize helper
Mac: [ ] rebuild and confirm on a real device
The rule that makes it work is the boring one: that entry is put in front of the agent at the start of every session on the machine whose line is unticked. Not once. Not for a week. Not until it scrolls away. Until the box is ticked.
It is not a notification. A notification is an event about state, which is why notifications get dismissed and the underlying thing stays undone. This is the state itself, read fresh every time.
Nobody has to remember to check, either, which matters more than it sounds. A session-start hook reads the file and hands it to the agent before the human has typed anything. Remembering to run a status command is exactly the habit that fails three weeks in.
04One codebase, three kinds of bug
This is the second idea, and it is the one I have not seen written down anywhere.
A hybrid app is one codebase wearing native clothes. Most of what you fix is shared application code, which means the fix is already live on the other platform the instant it pulls. Some of it genuinely has to be written twice. A handoff that does not say which kind it is can be worse than no handoff, because the other machine will either redo work that is already done or skip work that is not.
- shared. Application code, shared types, backend, security rules. The other machine owes a verification, not a fix. Rebuild, retest, tick.
- native-parallel. Permissions, plugins, push, deep links, icons. The same fix has to be written again in the other native project.
- platform-only. WebView quirks, signing, image metadata handling, per-platform build config. The other machine cannot even reproduce it.
Get it wrong in one direction and you burn a session on a duplicate fix that may land on top of the original. Get it wrong in the other and the bug ships on one platform while everyone believes it is closed.
There is a useful corollary. A machine that cannot build a platform can still write a fix for it and hand it over pre-shaped. That turns the other side's job from "diagnose, fix, test" into "build, test," which is a much smaller ask of somebody who has not opened that project in two weeks.
05Your repo lies to you about what your phone is running
Third idea, and it is the most expensive one, because it wastes hours before you even suspect it is happening.
A hybrid app does not run your source. It runs a compiled copy of your source, frozen into the native project at build time. Pull a fix, skip the rebuild, and the device keeps executing the old bundle while every file on your screen looks correct.
There is no error. There is no warning. The code you are reading is fixed, the app you are testing is not, and nothing in the room will tell you. So you debug the fix. You add logging that never prints, because the build that would contain it does not exist yet. I have watched an hour go to this, and the hour is not the worst part. The worst part is that you end up distrusting a correct fix.
The saving grace is that it is a completely checkable fact. Compare the newest modification time inside the synced native bundle against the newest in the shared source. If the source is newer, the device is behind, and you say so out loud before anyone touches anything.
STALE BUILD: the synced bundle is OLDER than the shared source. Shared code was modified 3040 minutes after the last sync. The device is running a COPY, so it is executing old code while every file on screen looks correct. Do not debug until this clears.
One design note worth stealing. Use modification times, not commit hashes. A pull restamps the files it changes, so mtimes catch both "the other machine pushed a fix I have not rebuilt" and "I edited this myself five minutes ago and have not rebuilt." Hashes only catch the first, and the second is the one that happens every day.
06The whole backend is a file
An append-only log committed inside the repo, plus a function that folds it into current state. That is the entire architecture, and it is a couple of hundred lines.
- Append-only, because that is what makes it mergeable. Two machines writing at once produce two new lines, never a conflicting edit to the same line, so git resolves it every time without being asked. A shared document that both agents edit in place is a conflict factory. This is the load-bearing decision and everything else follows from it.
- Git is the transport. No server, no sync protocol, no conflict resolution to write. The repo already travels between the machines and it does that job better than anything I would build.
- No model calls. Timestamps, file parsing, git. The intelligence is the agent already sitting in the terminal, so there is no API key, no metered cost, and no question about which subscription pays for it.
- Nothing is uploaded. No server, no account, no dashboard, no telemetry. It works offline and inside a company that would never approve a new vendor. That is a one-way door: add a database and you can never make the claim again.
Files rather than APIs, which has a pleasant side effect. Any agent that can read a file participates. Two different agents from two different vendors interoperate with no special handling at all, because neither of them knows the other exists. They are both just reading a file in a repo.
07The part where I talk myself out of it
Somebody already built the stale-build half of this. It was published in 2020 as
capacitor-build-safety, and it did the check better than my version does,
using committed hash evidence rather than modification times, which catches
uncommitted edits too. It was abandoned in late 2023. One star. Roughly fifty
downloads a month.
That is worse news than a competitor with ten thousand stars. A competitor tells you there is a market and you have to go win it. This tells you somebody built the right thing, correctly, and nobody came. The pain is real, but it is not sharp enough to send anyone searching for a tool. People just remember to run the build, and mostly that works.
So this is not a product, and pretending otherwise would waste weeks. Everything that makes the design good, the absence of a server and the absence of inference, also leaves nothing to charge for. And the addressable market is people who have two machines and multiple platforms and coding agents and the willingness to install something. That is a small room.
What is left is the idea, and the idea costs nothing to give away.
08Three questions worth asking about your own setup
The tool matters less than these. You can answer all three with a couple of hundred lines of shell and one session-start hook, on whatever stack you already have.
- When your agent hands work to another machine, is it sending an event or state? If it can scroll away unread, it is an event, and eventually it will be missed.
- When it reports a cross-platform bug, does it say whether the other side owes a fix or a verification? If not, one of those two failures is already in your backlog and you do not know which.
- Does anything you own know whether the app you are testing was built from the code you are reading? If not, some fraction of your debugging hours are being spent on code that is not running.
Where this came from. A two-machine project with a Windows box on Android and a MacBook on iOS. Both halves run in production there. The obligation check has survived several week-long gaps, and the stale-build check caught a bundle that was four days behind the source on a morning when everything on screen looked correct.
There is nothing to install. The reference implementation is one script per machine plus the state-file format they parse, published on GitHub as something to copy and adapt rather than something to depend on. Roughly two hundred lines each, MIT, no dependencies.