ADR 0003 · Couple to caliban only through its NDJSON wire format
- Status: accepted (amended by 0011)
- Date: 2026-06-05
- Source:
docs/superpowers/specs/2026-06-05-prospero-framework-design.md§3, §4
Context
Prospero needs to talk to caliband: send control requests (List, Spawn, Attach,
Kill, …) and read per-agent stream-json frames. Caliban implements this in Rust crates
(e.g. caliban-supervisor) that Prospero could depend on directly to reuse the request,
reply, and SpawnSpec types.
Depending on caliban's crates would tie Prospero to caliban's internal Rust API, version cadence, and transitive dependencies — coupling far wider than the bytes actually exchanged on the socket.
Decision
The caliband wire format is the only contract. Prospero owns a thin NDJSON client
in prospero-core (CalibandClient) with its own mirrored serde types
(CtlRequest / CtlReply / AgentRecord / SpawnSpec) and newline-delimited framing over
tokio::net::UnixStream. Prospero does not depend on caliban-supervisor or any other
caliban crate.
Consequences
- Positive: Prospero and caliban evolve independently; the only thing that must stay compatible is the bytes on the wire, which is also the surface real deployments depend on. Because the coupling is just a socket protocol, the entire control plane can be tested against a fake that speaks the same protocol — see 0007. Unknown/forward-compatible frames are tolerated by the normalizer (skip-and-log), so caliban can add frame types without breaking Prospero.
- Negative: the wire types are mirrored, so a protocol change in caliban requires a corresponding edit in Prospero's client — an intentional, explicit seam rather than a silent transitive break, but a seam someone must remember to keep in sync.
- Revisit if: the wire protocol churns fast enough that hand-mirroring the types becomes a recurring source of drift — a generated client or a shared schema crate might then earn its coupling cost.
Amendment (2026-09-19, ADR 0011)
Two refinements, neither of which changes the rule that prospero couples to caliban through a wire format rather than its crates:
- There is now a second drive protocol. ACP (an open, third-party protocol) may carry an agent's data path instead of the NDJSON attach stream, chosen per spawn. Control — spawn, list, kill, drain — stays on caliband's NDJSON wire.
- The "revisit if" clause above has fired. The hand-mirrored control/NDJSON types are
replaced by caliban's published
caliban-contractcrate (#239): serde-only, no daemon internals, so it is not the wide coupling this ADR rejected.