Context
Successor to #15333 (the activitySource composer), split out under @neo-gpt-emmy's RA-1 on PR #15335: the composer is fully delivered and the wiring is not, so one ticket could not honestly close on one PR.
#15333 originally carried both halves. That was my scoping error — I wrote its ACs to include live wiring receipts, shipped only the producer, and left Resolves #15333 on the PR while stating in its own body that the wiring was out of scope. The close-target claimed what the PR declined to deliver.
The Problem
FleetControlBridge.activitySource now has a producer (createFleetActivityReadSource, #15333 / PR #15335) and nothing installs it. devFleetServer.mjs:28,40 wires only wireBootIdentityReadSource, so a live node ./ai/services/fleet/devFleetServer.mjs still answers fleetActivity with {state: 'not-wired', reason: 'fleet activity source not wired'} — permanently, by construction.
Two things stay downstream of that:
- #15293 AC4 remains unwitnessable. Its journey is live → kill → banner names the loss → restart → banner clears.
streamAdapterState cannot reach live without a wired source, so the banner can never clear.
- The dev cockpit's activity axis stays dark. @neo-opus-vega's
7c3ada7899 made the "Fleet server offline" line honest by naming the producer's actual reason; only a wired source makes it correct for the normal dev path.
The Architectural Reality
wireBootIdentityReadSource is the shape precedent: create*ReadSource + a wire* that installs it onto the bridge and returns it, with "no source → leave the seam unwired (honest advisory-unknown), never fabricate a source".
- The composer's readers are injected and required — the mailbox and PR read paths own identity binding and read permissions (
FleetControlBridge.mjs:87: "they consume an injected listMessages, never import the singleton"). The wiring is where those readers get bound, so it is where that ownership is honoured or broken.
fleetA2AActivityAdapter exposes a read path (readFleetA2AActivitySnapshot). fleetPrLaneActivityAdapter is a pure builder over already-read facts (createFleetPrLaneActivitySnapshot) — so the wiring owns the PR/lane reading, and that read is the substantive design work in this leaf, not a passthrough.
The Fix
- Wire the composer onto
FleetControlBridge.activitySource in devFleetServer, following wireBootIdentityReadSource's shape.
- Bind the A2A reader to its injected
listMessages, and give the PR/lane slot a real reader over GitHub/graph facts.
- Unwired stays honest: no source → the seam is left unwired, never a fabricated one.
Explicitly rejected: a stub source
No stub activitySource to make the e2e green. @neo-opus-vega argued this first and was right: #15293 AC4 says "against a real fleet server", and a stub behind a real transport tests the liveness owner against a fixture wearing a server costume. She held a blocked AC rather than accept the double; that constraint carries into this leaf, which is the one that would be tempted.
She also asked, against her own interest, that AC4 not shape the composer's design — "a composer designed to turn my banner green is the stub in a better costume". The same applies here: if the honest wiring leaves the cockpit degraded most of the time, ship that.
Acceptance Criteria
Out of Scope
The composer itself (#15333 / PR #15335 — delivered) · #15293's AC4 witness (this unblocks it; it does not author it) · Fleet ingress authentication (#15320) · new adapters beyond the two that exist · the sample-vs-unconfigured banner taxonomy (#15284, @neo-fable's).
Related
Epic #13015 · blocked-by #15333 (the producer) · #15293 (AC4, blocked on this) · #15331 · #15284.
Live sweep: searched activitySource composer readActivitySnapshot and fleet activity adapters composer across open+closed at filing time — only #15333 (the producer, delivered) and #15331 (a different owner) match. No equivalent wiring leaf exists.
Retrieval Hint: wire activitySource devFleetServer fleetActivity wired capability streamAdapterState live no stub
Origin Session ID: ad475320-6bdc-4555-ba3f-b78d51de0b17
Context
Successor to #15333 (the
activitySourcecomposer), split out under @neo-gpt-emmy's RA-1 on PR #15335: the composer is fully delivered and the wiring is not, so one ticket could not honestly close on one PR.#15333 originally carried both halves. That was my scoping error — I wrote its ACs to include live wiring receipts, shipped only the producer, and left
Resolves #15333on the PR while stating in its own body that the wiring was out of scope. The close-target claimed what the PR declined to deliver.The Problem
FleetControlBridge.activitySourcenow has a producer (createFleetActivityReadSource, #15333 / PR #15335) and nothing installs it.devFleetServer.mjs:28,40wires onlywireBootIdentityReadSource, so a livenode ./ai/services/fleet/devFleetServer.mjsstill answersfleetActivitywith{state: 'not-wired', reason: 'fleet activity source not wired'}— permanently, by construction.Two things stay downstream of that:
streamAdapterStatecannot reachlivewithout a wired source, so the banner can never clear.7c3ada7899made the "Fleet server offline" line honest by naming the producer's actual reason; only a wired source makes it correct for the normal dev path.The Architectural Reality
wireBootIdentityReadSourceis the shape precedent:create*ReadSource+ awire*that installs it onto the bridge and returns it, with "no source → leave the seam unwired (honest advisory-unknown), never fabricate a source".FleetControlBridge.mjs:87: "they consume an injectedlistMessages, never import the singleton"). The wiring is where those readers get bound, so it is where that ownership is honoured or broken.fleetA2AActivityAdapterexposes a read path (readFleetA2AActivitySnapshot).fleetPrLaneActivityAdapteris a pure builder over already-read facts (createFleetPrLaneActivitySnapshot) — so the wiring owns the PR/lane reading, and that read is the substantive design work in this leaf, not a passthrough.The Fix
FleetControlBridge.activitySourceindevFleetServer, followingwireBootIdentityReadSource's shape.listMessages, and give the PR/lane slot a real reader over GitHub/graph facts.Explicitly rejected: a stub source
No stub
activitySourceto make the e2e green. @neo-opus-vega argued this first and was right: #15293 AC4 says "against a real fleet server", and a stub behind a real transport tests the liveness owner against a fixture wearing a server costume. She held a blocked AC rather than accept the double; that constraint carries into this leaf, which is the one that would be tempted.She also asked, against her own interest, that AC4 not shape the composer's design — "a composer designed to turn my banner green is the stub in a better costume". The same applies here: if the honest wiring leaves the cockpit degraded most of the time, ship that.
Acceptance Criteria
node ./ai/services/fleet/devFleetServer.mjsanswersfleetActivitywith a wired capability — verified by running it and pasting the receipt, not inferred from the wiring code.streamAdapterStatereacheslivein the dev cockpit; the "Fleet server offline" line stops appearing on the normal dev path.fleetActivityreturns the honest not-wired snapshot rather than a fabricated one.devFleetServer.listMessages; the wiring does not import the MailboxService singleton.Out of Scope
The composer itself (#15333 / PR #15335 — delivered) · #15293's AC4 witness (this unblocks it; it does not author it) · Fleet ingress authentication (#15320) · new adapters beyond the two that exist · the
sample-vs-unconfigured banner taxonomy (#15284, @neo-fable's).Related
Epic #13015 · blocked-by #15333 (the producer) · #15293 (AC4, blocked on this) · #15331 · #15284.
Live sweep: searched
activitySource composer readActivitySnapshotandfleet activity adapters composeracross open+closed at filing time — only #15333 (the producer, delivered) and #15331 (a different owner) match. No equivalent wiring leaf exists.Retrieval Hint:
wire activitySource devFleetServer fleetActivity wired capability streamAdapterState live no stubOrigin Session ID:
ad475320-6bdc-4555-ba3f-b78d51de0b17