Context
During the exact-head cycle-3 review of PR #15022 at 9d7723a349, the requested participation-status correction passed, but independent whole-PR probes exposed three adjacent batch-integrity edges. They are deliberately captured as one reviewer-owned follow-up instead of forcing a fourth correction loop on the author.
Direct evidence:
- a timeout result renders
0 started · 1 rejected while its reachable detail says timeout;
- a present authoritative Store with
items: [] falls through to a stale rendered card;
- a second fleet-start activation before the first batch completes produces bridge calls
[a, b, a]: the already-settled member is started twice while another member is still pending.
The Problem
The morning-start surface promises an operator-trustworthy final state over N independent lifecycle round-trips. Three edges currently weaken that promise:
summarizeFleetStart() retains timeout detail but places every non-success in the rejected bucket, so the at-a-glance summary collapses an UNKNOWN outcome into a rejection.
- Per-card
pendingAction prevents duplicate writes only while that specific card remains pending. The Button handler does not serialize the async controller method, and the controller has no batch-level re-entry guard. Once one card settles while another remains pending, a second activation can start the settled card again and race the two summaries.
getRosterRecords() uses storeItems?.length to select the authoritative Store. An empty authoritative snapshot is therefore treated as “Store unavailable” and can fall back to rendered-card residue.
The timeout edge is especially important because the adapter timeout races rather than cancels the underlying bridge Promise. Clearing the pending marker does not prove the start failed; immediate fleet retry can overlap an operation whose outcome is still unknown.
The Architectural Reality
FleetCockpitController is the composition root for the fleet-level action. Batch serialization and authoritative Store selection belong there; service-side ordering/backoff remains untouched.
fleetStartPlan.mjs is the pure outcome contract. It must preserve the adapter vocabulary (settled, rejected, unauthorized, timeout) instead of relabeling timeout at the summary boundary.
fleetLifecycleIntentAdapter.mjs owns per-card round-trip state. Its timeout is an unknown outcome, not cancellation evidence; fleet eligibility must not silently retry that state.
- A Store that exists with zero items is authoritative. Rendered-card fallback is valid only when the grid/Store composition is absent.
Decision Record impact: none — this is a local correctness follow-up aligned with the existing #14611 and #14612 contracts.
The Fix
Keep this as one coherent controller/outcome-integrity lane:
- add a batch-level in-flight guard so repeated
onStartFleet() activations join or no-op against the active batch and cannot race summaries;
- preserve timeout as an explicit UNKNOWN outcome in the pure summary/render contract and prevent fleet-level retry while that outcome remains unresolved;
- distinguish an absent Store from a present empty Store in
getRosterRecords();
- add focused pure/controller fixtures for timeout labeling, non-retryability, re-entry, summary ordering, and authoritative empty roster.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
FleetCockpitController.onStartFleet |
one operator action = one staged batch (#14612) |
at most one active batch; repeat activation cannot duplicate writes or race summaries |
return/join the active batch outcome |
method @summary |
controlled deferred-Promise unit fixture |
summarizeFleetStart / renderFleetStartSummary |
adapter terminal vocabulary + #14611 timeout honesty |
timeout remains UNKNOWN at a glance and in detail |
missing result is non-success, never success |
helper JSDoc |
pure mixed-outcome fixture |
| fleet retry eligibility after timeout |
underlying bridge Promise is not cancelled by Promise.race |
unresolved timeout cannot be re-fanned out as a fresh start |
explicit operator/per-card recovery remains available |
plan/adapter rationale |
two-click timeout fixture with bridge call count |
getRosterRecords |
provider-hosted Store is roster SSOT |
present empty Store returns [] |
rendered cards only when Store composition is absent |
method JSDoc |
empty-vs-absent Store fixture |
Acceptance Criteria
Out of Scope
Service-side ordering/backoff · cancellation support in the lifecycle service · redesigning per-card controls · Fleet Manager visual styling · the NL walkthrough owned by #14646.
Avoided Traps
- Do not “fix” timeout honesty by merely changing its display label while leaving immediate retry possible.
- Do not disable every per-card control globally; the invariant is fleet-batch serialization plus honest unknown-state handling.
- Do not treat an empty authoritative collection as missing data.
Related
Follow-up from PR #15022 and #14612 · composes the landed per-card contract #14611 · roster authority #14899 · walkthrough proof remains #14646.
Live latest-open sweep: checked the latest 20 open issues at 2026-07-11T11:17:13.759Z; exact live search and recent A2A claim sweep found no equivalent or competing claim.
Origin Session ID: de713f27-0e82-4960-b4c6-f281e0c36449
Retrieval Hint: "PR 15022 fleet morning start timeout unknown retry batch reentrancy authoritative empty roster"
Context
During the exact-head cycle-3 review of PR #15022 at
9d7723a349, the requested participation-status correction passed, but independent whole-PR probes exposed three adjacent batch-integrity edges. They are deliberately captured as one reviewer-owned follow-up instead of forcing a fourth correction loop on the author.Direct evidence:
0 started · 1 rejectedwhile its reachable detail saystimeout;items: []falls through to a stale rendered card;[a, b, a]: the already-settled member is started twice while another member is still pending.The Problem
The morning-start surface promises an operator-trustworthy final state over N independent lifecycle round-trips. Three edges currently weaken that promise:
summarizeFleetStart()retains timeout detail but places every non-success in therejectedbucket, so the at-a-glance summary collapses an UNKNOWN outcome into a rejection.pendingActionprevents duplicate writes only while that specific card remains pending. The Button handler does not serialize the async controller method, and the controller has no batch-level re-entry guard. Once one card settles while another remains pending, a second activation can start the settled card again and race the two summaries.getRosterRecords()usesstoreItems?.lengthto select the authoritative Store. An empty authoritative snapshot is therefore treated as “Store unavailable” and can fall back to rendered-card residue.The timeout edge is especially important because the adapter timeout races rather than cancels the underlying bridge Promise. Clearing the pending marker does not prove the start failed; immediate fleet retry can overlap an operation whose outcome is still unknown.
The Architectural Reality
FleetCockpitControlleris the composition root for the fleet-level action. Batch serialization and authoritative Store selection belong there; service-side ordering/backoff remains untouched.fleetStartPlan.mjsis the pure outcome contract. It must preserve the adapter vocabulary (settled,rejected,unauthorized,timeout) instead of relabeling timeout at the summary boundary.fleetLifecycleIntentAdapter.mjsowns per-card round-trip state. Its timeout is an unknown outcome, not cancellation evidence; fleet eligibility must not silently retry that state.Decision Record impact: none — this is a local correctness follow-up aligned with the existing #14611 and #14612 contracts.
The Fix
Keep this as one coherent controller/outcome-integrity lane:
onStartFleet()activations join or no-op against the active batch and cannot race summaries;getRosterRecords();Contract Ledger Matrix
FleetCockpitController.onStartFleet@summarysummarizeFleetStart/renderFleetStartSummaryPromise.racegetRosterRecords[]Acceptance Criteria
items: []yields an empty plan; rendered-card fallback occurs only when the Store reference is absent.Out of Scope
Service-side ordering/backoff · cancellation support in the lifecycle service · redesigning per-card controls · Fleet Manager visual styling · the NL walkthrough owned by #14646.
Avoided Traps
Related
Follow-up from PR #15022 and #14612 · composes the landed per-card contract #14611 · roster authority #14899 · walkthrough proof remains #14646.
Live latest-open sweep: checked the latest 20 open issues at 2026-07-11T11:17:13.759Z; exact live search and recent A2A claim sweep found no equivalent or competing claim.
Origin Session ID: de713f27-0e82-4960-b4c6-f281e0c36449
Retrieval Hint: "PR 15022 fleet morning start timeout unknown retry batch reentrancy authoritative empty roster"