Summary
Add a fleet-mode First Officer so a single spacedock claude session can drive multiple commissions (workflows) at once — while keeping today's single-workflow FO fully supported and the default. The two operating models are user preferences, not a migration:
- Reading A — one FO, many ships in one commission. The current model: one session binds one workflow and fans ensigns out across its entities. Stays the default; unchanged.
- Reading B — one FO across multiple commissions. New, opt-in: one session adopts several discovered workflows and runs the dispatch loop across their union.
Spacedock (and Bridge) should be willing and able to conform to either, depending on operator preference. Neither is hardcoded away.
Filed per CONTRIBUTING.md (issue → discuss → PR). Implementation is intended to roll into PR #435 (bridge-seam-inbox-events) at the author's request — see the Scope note below; it is a separable commit.
⚠️ Revised after an antagonistic review of the original draft
The first draft of this issue made three claims that do not survive contact with the code on bridge-seam-inbox-events. Corrections, with citations:
- It is a SKILL-only change — there is no need for a
--fleet-dir CLI/Go change. The binary already supports a multi-workflow repo:
status --discover already returns the full list of commissioned workflow dirs (internal/status/native_discover_test.go:57-75, TestNativeDiscoverParity). Today the FO prose just picks one (skills/first-officer/references/first-officer-shared-core.md:15 — "multiple → present the list"). Fleet mode changes what the FO does with that list, not the binary.
spacedock new already disambiguates by --workflow-dir when a repo holds more than one workflow (first-officer-shared-core.md:185 — "if the repo holds more than one, new reports the candidates and you pass --workflow-dir"). Multi-workflow-in-one-repo is an already-anticipated topology.
- Every dispatch/status call is already
--workflow-dir-parameterized, so a per-workflow loop composes from existing commands. The original draft's --fleet-dir option is dropped as unnecessary scope.
- The "reconcile is per-workflow" constraint was wrong.
«roster-reconcile» (spacedock dispatch reconcile) sweeps the host roster (the session's live workers) for drift — it is session/team-scoped, not workflow-scoped (fo-dispatch-core.md:100-102). In fleet mode it already covers every worker the session spawned regardless of workflow, so it is not a blocker — it's a thing that already works in our favor.
- The "separate rate-limit bucket per session" benefit was wrong. Rate limits are account-level, not per-session. The genuine isolation benefits of one-FO-per-commission are context-window pressure and blast radius, not rate budget.
Current state (why one session can't do Reading B today)
Two real constraints bind one FO session to one workflow:
- Startup binds to exactly one workflow dir.
first-officer-shared-core.md:14-16 — "Discover the workflow directory… one path → use it; zero → STOP; multiple → present the list." The whole boot then reads one {workflow_dir}/README.md taxonomy (step 4) and one status --boot (step 5).
- The event loop is scoped to one
{workflow_dir}. status --next, dispatch build, status --set all carry a single --workflow-dir (fo-dispatch-core.md:111-120, :140).
So covering N commissions today requires N sessions (the multi-session model Bridge's rail already hints at, and that PR #435's per-target drain serves).
The omitted hard part — per-workflow state & team (what makes this non-trivial)
A fleet loop is not just "call status --next in a for loop." Three subsystems are per-workflow and must be handled once per member workflow each tick/boot:
- Per-workflow boot + state convergence.
«state.boot», «state.ensure-ready», «state.sweep-merged» (first-officer-shared-core.md:150-171) each operate on one workflow's state backend. Critically, each workflow may carry its own split-root .spacedock-state checkout on a separate branch (AGENTS.md:43 — "each workflow may use a per-workflow .spacedock-state checkout"). Fleet mode must run ensure-ready / pull-on-boot / sweep-merged per member workflow, juggling N state checkouts — and honor each one's rebase-conflict halt independently.
- Team & standing teammates. The team is session-scoped; standing teammates are workflow-declared and injected at that workflow's first dispatch (
fo-dispatch-core.md:7, first-officer-shared-core.md:206). In fleet mode one team accumulates the union of every member workflow's standing teammates (e.g. multiple comm-officer-style polishers / intake mods). Routing-usage reads stay per-mod, but collision/identity semantics across workflows need a decision.
- Greet & gate authoring scale per workflow. Interactive boot presents one summary + ready gates (
first-officer-shared-core.md:31); fleet boot presents N. Headless drives all dispatchables across N workflows to gates/terminal. Gate stops stay per-entity/per-workflow and unambiguous — that part composes cleanly.
Bridge side (already mostly there)
Bridge renders a multi-workflow fleet today and tracks per-workflow heartbeats (fo.<slug>.json). PR #435's bridge-inbox drain mod already routes captain intent by a per-record target (<slug> | all; absent ⇒ all). That field — built for the multi-session case — gives fleet mode per-workflow conn/tell granularity for free: a single fleet-mode FO drains all targets, so target=all is fleet-wide conn and target=<slug> scopes to one workflow. A single fleet-mode FO is in fact simpler to route to than N sessions (no addressing race; one cursor owner). The dual-mode principle must hold on Bridge too: present and control a fleet whether driven by one fleet-mode FO or N single-workflow FOs.
Proposed change (sketch — skill-only, compatibility-first)
- Selection (open decision — see PR discussion): an explicit, opt-in operator signal that puts the FO in fleet mode (e.g. a quotable launch directive like the existing single-entity-mode trigger and conn-grant phrasing in
SKILL.md:7-14 / first-officer-shared-core.md:33). Default (no signal) = today's single-workflow behavior, byte-for-byte unchanged.
- Startup: on the fleet signal, adopt all
status --discover paths instead of presenting the list; read each workflow's taxonomy + run each workflow's «state.boot»/«state.ensure-ready»/«state.sweep-merged». Zero-discover still reports-and-stops (must not trip detectBroadSearchAtBoot; verified by internal/ensigncycle/broad_search_detect_test.go + the live TestLiveZeroDiscoverReportsAndStops).
- Event loop: an outer loop over member workflows wrapping the existing
«dispatch.next-action»() per {workflow_dir}; round-robin until nothing is dispatchable across the union, then idle-hook + reconcile + recheck.
- Tests: per
AGENTS.md:54 ("Add skill smoke tests before changing first-officer or ensign command text"), add a fleet-mode smoke test under skills/integration/, and a //go:build live behavioral test mirroring TestLiveEnsignCycle for the multi-workflow drive.
Scope note (antagonistic flag, author-acknowledged)
PR #435 is the bridge seam (drain mod, normalized FO event hooks, event script). Fleet-mode FO is FO-dispatch-loop work — a different subsystem. Rolling it into #435 is at the author's explicit request; it should land as a separable commit so reviewers can isolate the seam from the dispatch-loop change, and so it can be reverted independently if the design needs another pass.
Acceptance criteria
Risks / open questions
- Selection mechanism (launch directive vs skill arg vs a fleet manifest) — the one genuine product-API fork; decide before editing the boot-resident core.
- Context pressure: one session juggling N workflows' state in one context window — the real reason the per-workflow model exists. Document the trade so operators choose deliberately.
- Standing-teammate identity across workflows: union in one team — naming/collision/lifetime semantics need a decision.
- Reconcile / boot cost: N per-workflow boots + sweeps per idle tick may need staggering so the loop stays responsive.
- Fleet boundary: the discovered-workflow set must be unambiguous (the project root's commissioned workflows), distinct from the split-root entity/state dir.
Summary
Add a fleet-mode First Officer so a single
spacedock claudesession can drive multiple commissions (workflows) at once — while keeping today's single-workflow FO fully supported and the default. The two operating models are user preferences, not a migration:Spacedock (and Bridge) should be willing and able to conform to either, depending on operator preference. Neither is hardcoded away.
The first draft of this issue made three claims that do not survive contact with the code on
bridge-seam-inbox-events. Corrections, with citations:--fleet-dirCLI/Go change. The binary already supports a multi-workflow repo:status --discoveralready returns the full list of commissioned workflow dirs (internal/status/native_discover_test.go:57-75,TestNativeDiscoverParity). Today the FO prose just picks one (skills/first-officer/references/first-officer-shared-core.md:15— "multiple → present the list"). Fleet mode changes what the FO does with that list, not the binary.spacedock newalready disambiguates by--workflow-dirwhen a repo holds more than one workflow (first-officer-shared-core.md:185— "if the repo holds more than one,newreports the candidates and you pass--workflow-dir"). Multi-workflow-in-one-repo is an already-anticipated topology.--workflow-dir-parameterized, so a per-workflow loop composes from existing commands. The original draft's--fleet-diroption is dropped as unnecessary scope.«roster-reconcile»(spacedock dispatch reconcile) sweeps the host roster (the session's live workers) for drift — it is session/team-scoped, not workflow-scoped (fo-dispatch-core.md:100-102). In fleet mode it already covers every worker the session spawned regardless of workflow, so it is not a blocker — it's a thing that already works in our favor.Current state (why one session can't do Reading B today)
Two real constraints bind one FO session to one workflow:
first-officer-shared-core.md:14-16— "Discover the workflow directory… one path → use it; zero → STOP; multiple → present the list." The whole boot then reads one{workflow_dir}/README.mdtaxonomy (step 4) and onestatus --boot(step 5).{workflow_dir}.status --next,dispatch build,status --setall carry a single--workflow-dir(fo-dispatch-core.md:111-120,:140).So covering N commissions today requires N sessions (the multi-session model Bridge's rail already hints at, and that PR #435's per-
targetdrain serves).The omitted hard part — per-workflow state & team (what makes this non-trivial)
A fleet loop is not just "call
status --nextin aforloop." Three subsystems are per-workflow and must be handled once per member workflow each tick/boot:«state.boot»,«state.ensure-ready»,«state.sweep-merged»(first-officer-shared-core.md:150-171) each operate on one workflow's state backend. Critically, each workflow may carry its own split-root.spacedock-statecheckout on a separate branch (AGENTS.md:43— "each workflow may use a per-workflow.spacedock-statecheckout"). Fleet mode must run ensure-ready / pull-on-boot / sweep-merged per member workflow, juggling N state checkouts — and honor each one's rebase-conflict halt independently.fo-dispatch-core.md:7,first-officer-shared-core.md:206). In fleet mode one team accumulates the union of every member workflow's standing teammates (e.g. multiplecomm-officer-style polishers / intake mods). Routing-usage reads stay per-mod, but collision/identity semantics across workflows need a decision.first-officer-shared-core.md:31); fleet boot presents N. Headless drives all dispatchables across N workflows to gates/terminal. Gate stops stay per-entity/per-workflow and unambiguous — that part composes cleanly.Bridge side (already mostly there)
Bridge renders a multi-workflow fleet today and tracks per-workflow heartbeats (
fo.<slug>.json). PR #435's bridge-inbox drain mod already routes captain intent by a per-recordtarget(<slug>|all; absent ⇒all). That field — built for the multi-session case — gives fleet mode per-workflowconn/tellgranularity for free: a single fleet-mode FO drains all targets, sotarget=allis fleet-wide conn andtarget=<slug>scopes to one workflow. A single fleet-mode FO is in fact simpler to route to than N sessions (no addressing race; one cursor owner). The dual-mode principle must hold on Bridge too: present and control a fleet whether driven by one fleet-mode FO or N single-workflow FOs.Proposed change (sketch — skill-only, compatibility-first)
SKILL.md:7-14/first-officer-shared-core.md:33). Default (no signal) = today's single-workflow behavior, byte-for-byte unchanged.status --discoverpaths instead of presenting the list; read each workflow's taxonomy + run each workflow's«state.boot»/«state.ensure-ready»/«state.sweep-merged». Zero-discover still reports-and-stops (must not tripdetectBroadSearchAtBoot; verified byinternal/ensigncycle/broad_search_detect_test.go+ the liveTestLiveZeroDiscoverReportsAndStops).«dispatch.next-action»()per{workflow_dir}; round-robin until nothing is dispatchable across the union, then idle-hook + reconcile + recheck.AGENTS.md:54("Add skill smoke tests before changing first-officer or ensign command text"), add a fleet-mode smoke test underskills/integration/, and a//go:build livebehavioral test mirroringTestLiveEnsignCyclefor the multi-workflow drive.Scope note (antagonistic flag, author-acknowledged)
PR #435 is the bridge seam (drain mod, normalized FO event hooks, event script). Fleet-mode FO is FO-dispatch-loop work — a different subsystem. Rolling it into #435 is at the author's explicit request; it should land as a separable commit so reviewers can isolate the seam from the dispatch-loop change, and so it can be reverted independently if the design needs another pass.
Acceptance criteria
spacedock claudesession, given the opt-in fleet signal, dispatches ensigns across entities in more than one workflow within one session.«state.ensure-ready»/«state.sweep-merged»/«state.commit»run per member workflow, including independent split-root.spacedock-statecheckouts and their rebase-conflict halts._mods(intake, pr-merge, bridge-inbox) fire correctly, keyed by each workflow's$SLUG.conn/tellhonor the bridge-inboxtargetfield under fleet mode (all= fleet-wide,<slug>= scoped).## Stage Reportreview remain per-entity/per-workflow and unambiguous.skills/integration/smoke test and a//go:build livebehavioral test cover the fleet drive;go test ./...stays green.Risks / open questions