Skip to content

feat: mirror the fleet board contract, and model SessionInfo.role (P5.0) - #38

Open
saucam wants to merge 2 commits into
mainfrom
feat/conductor-p5-fleet-contract
Open

feat: mirror the fleet board contract, and model SessionInfo.role (P5.0)#38
saucam wants to merge 2 commits into
mainfrom
feat/conductor-p5-fleet-contract

Conversation

@saucam

@saucam saucam commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Companion to highflame-ai/codeoid#294.

The prerequisite

The daemon has carried role on SessionInfo since the conductor shipped. This crate never modelled it — 22 fields, no role — so the TUI could not so much as badge a conductor. conductor-frontends-design.md §10 flagged exactly this: "The Rust SessionInfo is missing the role field entirely; add it first."

What lands

  • SessionRole + SessionInfo.role
  • ClientMessage::FleetSubscribe / FleetUnsubscribe
  • DaemonMessage::FleetSnapshotResult / FleetUpdate
  • FleetSnapshot, FleetTask, FleetEvent, FleetUsage, FleetDelta

Forward-compat is load-bearing here

Every new enum — SessionRole, FleetTaskKind/Shape/Status — carries #[serde(other)] Unknown. This is a client talking to a daemon that may be newer than it. A role or status it has never heard of must degrade to an unrendered node, never fail deserialization of the whole SessionInfo and blank the session list. There's a test for precisely that.

On the tests

The wire tests parse JSON captured from the running TS daemon, not hand-written fixtures. This crate is a hand-maintained mirror of a TS contract and the failure mode is silent field drop on one side — a fixture written from the same assumption as the code would prove nothing. If the daemon's projection changes shape, these stop deserializing.

TUI

Accepts the two new frames and logs them at debug. It never sends fleet.subscribe, so receiving one means a stray broadcast rather than a client bug, and it must not be reported as a forward-compat drop. The board UI is P5.2.

Verification

cargo build, cargo test (7 suites, 10 protocol tests incl. 4 new), cargo fmt --check, cargo clippy — all clean. Six SessionInfo test fixtures updated for the new field; the compiler's exhaustiveness checking caught all of them plus two match sites in codeoid-client.

saucam and others added 2 commits July 27, 2026 09:10
Lockstep half of the daemon's goal-blackboard slice 1. CONTRIBUTING requires
this crate to move with the daemon's packages/protocol.

- `reads` / `writes` on CollaborationRole: the artifact kinds a role may read
  and write on the goal blackboard (`spec`, `research`, `adr`, `task-list`,
  `diff`, `findings`, or `extra/<key>`).

`None` means "use the daemon's default profile for this role name", which is
NOT the same as an empty list ("read/write nothing") — the daemon distinguishes
them, so these stay Option<Vec<String>> rather than defaulting to an empty vec
here. Collapsing the two would silently strip every default profile.

Kept as plain strings, not an enum: the artifact vocabulary has a fixed core
plus an `extra/<key>` escape hatch, so an enum here would reject a valid kind a
newer daemon accepts.

The wire_format review role now carries populated lists rather than None, so
the recursive camelCase walker actually visits them and a future rename of
either field fails the wire test.

cargo build + clippy + test green: 354 tests; clippy warnings unchanged
(7 before, 7 after on codeoid-protocol, verified by stashing).
The daemon has carried `role` on SessionInfo since the conductor shipped; this
crate never modelled it, so the TUI could not so much as badge a conductor. That
was the stated prerequisite for the fleet views
(codeoid docs/conductor-frontends-design.md §10-§11). Adds SessionRole and
mirrors the new read+subscribe surface:

  ClientMessage::FleetSubscribe / FleetUnsubscribe
  DaemonMessage::FleetSnapshotResult / FleetUpdate
  FleetSnapshot, FleetTask, FleetEvent, FleetUsage, FleetDelta

Every new enum — SessionRole, FleetTaskKind/Shape/Status — carries
#[serde(other)] Unknown. This is a client talking to a daemon that may be NEWER
than it: a role or status it has never heard of must degrade to an unrendered
node, never fail deserialization of the whole SessionInfo and blank the session
list.

The wire tests parse JSON CAPTURED from the running TS daemon rather than
hand-written fixtures. This crate is a hand-maintained mirror of a TS contract
and the failure mode is silent field drop on one side, so a fixture written from
the same assumption as the code proves nothing.

The TUI accepts the two new frames and logs them: it never sends
fleet.subscribe, so receiving one means a stray broadcast rather than a client
bug, and it must not be reported as a forward-compat drop. The board UI is P5.2.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants