Skip to content

Wire protocol has no version negotiation; mixed fleets break state sync during rolling upgrades #31

Description

@Mearman

The entity revision fields (#29) and deliveryQueues in SerialisedState (#30) changed the wire format, and the protocol carries no version handshake — both PRs document a "single build" contract. The operational consequence is sharper than a footnote: bridges across harnesses update at different times (npm cache timing, the Claude plugin marketplace, pi extension installs), so a restart onto a new build while other peers still run the old one produces a mixed-fleet window, and in that window:

  • a new-build peer applying an old snapshot throws (applyStateSync hits Object.entries(undefined) on the missing deliveryQueues field), and
  • an old-build peer receiving a new snapshot either drops it in message validation or partially applies it, silently.

So state sync degrades in both directions until every peer has restarted onto the same build.

Fix directions, likely in combination:

  1. Version the handshake. Exchange a protocol version in the introduce / peer-list exchange (src/core/wire-protocol.ts, transports' connectToCoordinator); mismatched peers get a loud, explicit refusal instead of corrupting or dropping state. This also gives the single-build contract a enforcement point.
  2. Make wire evolution tolerant where cheap. A missing optional collection on the wire can default to empty at the parse boundary (a zod .default() on the wire schema) while the internal type stays required — standard schema evolution, distinct from a compatibility shim in domain logic. deliveryQueues is the obvious candidate; the entity version field could similarly default to 1 for old senders, at the cost of treating pre-version records as fresh.

Found while recording the boundaries of #29 and #30; nothing is broken for a converged fleet today.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions