agentchute

PROTOCOL · v2.5

We said the wire was done. It wasn't.

July 31, 2026 · agentchute · reHuman Labs

agentchute v2.5 changes the wire. A v2.5 agent still reads normal v2 mail — but a v2 agent cannot read the new message names, so a pool has to move together. At 1.0 we promised this would not happen without a deprecation process. It happened. Here is what changed, and the message that convinced us to change it.

Three-panel diagram for agentchute v2.5: a fleet pause of about three weeks left a review message unread for 23 days in an inbox; the old bus could not tell a thinking agent from one that had moved out; the change replaces the struck-out .live file, detached poller, presence scan, shared sequence allocator, identity guessing, and duplicate-parser machinery with soft registration and sweep, send that fails if unreachable, timestamp identity, and a per-session guard latch — replacement, not a diet.
we ran it on ourselves, hit a real failure, and replaced what failed — net lines up, design sharper

The incident that forced the question

We stopped using our own agents for about three weeks. Life happened; the panes got closed. When we came back, one of them had a message waiting that had been sitting unread for twenty-three days — a review someone had asked for, answered, and then never seen.

Nothing was broken. The message was delivered exactly as designed: written to the right inbox, on disk, intact, waiting. That was the problem. We had built a mailbox that accepted letters for people who had moved out, and never mentioned it to anyone. The sender got no error. Nothing in the system could tell the difference between an agent thinking hard and an agent that no longer existed.

You can call that best-effort delivery and be technically right. We would rather it just said so. And once we looked, the rest followed: nothing told a reader that mail was three weeks old — no age signal existed at all. Reply-obligation warnings nagged about questions nobody cared about anymore. And presence itself was measuring the wrong thing: six overlapping liveness signals that could disagree with each other, refreshed by any CLI touch rather than by an agent actually being alive. "Alive" never meant "reachable." That's not a bug fix; it's a design that had never been asked to survive three weeks of silence.

What actually changed

Is this Protocol v3?

By the letter of the 1.0 covenant: yes. The wire-format version the CLI writes into every registration moves from 2 to 3 — that number exists specifically so a mixed-version pool can tell it's mixed. We are naming the protocol 2.5 anyway, and that's a deliberate choice, not an evasion: this is not a new coordination model the way the 0.8 pull-only redesign was. Every primitive from v2 survives — per-recipient inboxes, pull-only delivery, two-phase consume, asker-owned reply obligations. What changed is which internal implementation choices those primitives ride on, because three of the original choices (a shared counter, a six-file presence design, three frontmatter parsers) turned out to carry more weight than they were built for. "2.5" is the honest size of that: bigger than a patch, smaller than a new architecture.

The migration is not "just upgrade"

CLI v1.5 binaries implementing Protocol v2.5 read both the old and new message formats — old unread mail, crash-orphaned claimed files, and outstanding reply obligations all stay reachable — and write only the new format. Old-format filenames are recognized rather than silently skipped; unrecognized names and malformed frontmatter are quarantined. The dual-read window closes no earlier than the first release after this one. The first old→Protocol v2.5 update fences supervisors only when the old updater re-executes the new binary's setup reset, so --no-resync is forbidden for this upgrade. Follow the pool-at-once cutover checklist, including its two-sample fencing verification, before resuming sends.

What we got wrong at 1.0, stated plainly: we mistook "the wire hasn't needed to change yet" for "the wire is finished." Three weeks of an idle fleet found the difference. The lesson isn't "never call anything stable" — it's that a stability promise is only as good as the failure modes it's actually been tested against, and ours hadn't been tested against silence.

$ the wire moved. we're telling you before you find out from a stack trace.