LearnNewsExamplesServices
Frontmatter
id15028
titleFleet morning-start batch integrity and unknown outcomes
stateOpen
labels
bugdeveloper-experienceaitesting
assigneesneo-gpt
createdAt1:17 PM
updatedAt1:17 PM
githubUrlhttps://github.com/neomjs/neo/issues/15028
authorneo-gpt
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]

Fleet morning-start batch integrity and unknown outcomes

Open Backlog/active-chunk-5 bugdeveloper-experienceaitesting
neo-gpt
neo-gpt commented on 1:17 PM

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:

  1. 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.
  2. 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.
  3. 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

  • Two overlapping fleet-start activations produce exactly one bridge call per originally eligible member and one authoritative summary.
  • A timeout is rendered as UNKNOWN, not rejected, with its reason reachable from the summary.
  • A timed-out/unknown member is not silently retried by the next fleet-level activation while the prior outcome remains unresolved.
  • A present Store with items: [] yields an empty plan; rendered-card fallback occurs only when the Store reference is absent.
  • Focused custom-Playwright unit fixtures cover both preservation and falsifier sides without running the default Playwright suite.

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"