Found by the independent review of #293, and it is not specific to that PR — it is a hazard for every wire-protocol change we will ever ship.
The order
DaemonBootstrap.swift:53
private static let helpers = ["graphcoded", "zmx", "graphcode"]
The daemon is installed first and the CLI last. A throw anywhere between them leaves a new graphcoded running against an old graphcode — and the staleness timer respawns the new daemon regardless, so the mismatch persists rather than resolving itself.
Why that direction is the dangerous one
The review measured both directions against #293:
| Pairing |
Behaviour |
| Old CLI → new daemon |
❌ silent — prints "the room is empty", "no unread posts", no status line |
| New CLI → old daemon |
✅ loud — errorOccurred, the client says so |
Worse than a wrong answer: with #293's change, the old CLI's mail inbox still advanced the cursor while receiving nothing, which would have made 182 posts permanently unreachable even after upgrading. #293 is fixing that half by landing the delivered-cursor change alongside it.
But the general shape stays: our install order maximises exposure to the one direction that fails quietly.
Proposal
Install the CLI before the daemon — ["graphcode", "zmx", "graphcoded"] — so a partial install leaves the pairing that fails loudly. A user who sees an error opens an issue; a user who sees an empty board assumes the board is empty.
Worth confirming there is no ordering dependency the other way (nothing in the CLI install should require a running current daemon) before changing it.
Two related hardenings worth considering separately:
- Have the daemon carry a protocol version and refuse, loudly, a client too old to understand its snapshot shape — rather than serving it a well-formed reply the client reads as emptiness.
- Never advance a read cursor on a reply the client could not parse. The general rule the review arrived at: a cursor should only ever move through data actually delivered and understood.
Related: #288, #293
🤖 Generated with Claude Code
https://claude.ai/code/session_01BP43ags4cn8fq2ZZdv85J9
Found by the independent review of #293, and it is not specific to that PR — it is a hazard for every wire-protocol change we will ever ship.
The order
DaemonBootstrap.swift:53The daemon is installed first and the CLI last. A throw anywhere between them leaves a new
graphcodedrunning against an oldgraphcode— and the staleness timer respawns the new daemon regardless, so the mismatch persists rather than resolving itself.Why that direction is the dangerous one
The review measured both directions against #293:
errorOccurred, the client says soWorse than a wrong answer: with #293's change, the old CLI's
mail inboxstill advanced the cursor while receiving nothing, which would have made 182 posts permanently unreachable even after upgrading. #293 is fixing that half by landing the delivered-cursor change alongside it.But the general shape stays: our install order maximises exposure to the one direction that fails quietly.
Proposal
Install the CLI before the daemon —
["graphcode", "zmx", "graphcoded"]— so a partial install leaves the pairing that fails loudly. A user who sees an error opens an issue; a user who sees an empty board assumes the board is empty.Worth confirming there is no ordering dependency the other way (nothing in the CLI install should require a running current daemon) before changing it.
Two related hardenings worth considering separately:
Related: #288, #293
🤖 Generated with Claude Code
https://claude.ai/code/session_01BP43ags4cn8fq2ZZdv85J9