Context
Delivered-slice follow-up from @neo-kimi-phoebe's PR #15445 review (her §7.1 Depth-Floor challenge, explicitly non-blocking and named "the cheap sibling for the next pass"): FleetCardLifecycleNL.spec.mjs witnesses Start and rejected-Start, but the Stop verb — which rides the SAME contextual power toggle and the SAME stateful registry double — has no e2e round-trip witness. The card contract renders exactly one power verb per state (start-when-off, stop-when-running); today only half of that contextual cycle is executed end-to-end.
The Problem
The #15242 retirement re-homed the lifecycle surface onto the cards, and #15439 (PR #15445, merged) restored the e2e tier for the Start leg with the discriminating property: Body state advances only from RE-POLLED REGISTRY truth, never from the click. Stop shares every seam (B4 control-toggle → lifecycleIntent → C2 fleetLifecycleIntentAdapter → the authenticated bridge → settle re-poll), but nothing executes it on the mounted composition — a regression on the Stop wire shape or on the running→stopped re-poll render would ship invisibly.
The Architectural Reality
test/playwright/e2e/agentos/FleetCardLifecycleNL.spec.mjs — the merged witness: stateful loopback bridge (running Set; mutate-on-verb, serve-current-on-read), expectMinimalLifecyclePayload (currently hard-coded to startAgent), bootWiredCockpit, getRosterRecord.
apps/agentos/view/fleet/AgentCard.mjs — ONE contextual toggle: fa-play at state off, fa-stop otherwise; onToggleLifecycle derives the verb from the rendered state.
apps/agentos/view/fleet/fleetLifecycleIntentAdapter.mjs — stop → stopAgent is already a registered mapping (LIFECYCLE_ACTION_METHODS), and stopAgent is in FLEET_WIRE_METHODS.
The Fix
Extend the happy-path test into the FULL power cycle (no new boot, no new spec):
- Bridge dispatch gains a
stopAgent handler mirroring startAgent: running.delete(request.params) → {ok: true, result: {id: request.params, state: 'stopped'}}.
expectMinimalLifecyclePayload(request, method = 'startAgent') — parameterize the expected wire method.
- After the existing Start assertions: click the now-Stop toggle → the registry flips to stopped → the settle re-poll renders
fa-play again with no status residue → exactly one minimal stopAgent on the wire → the Body record returns to state: 'off' with pendingAction/controlReason null → the credential sweep runs last over ALL recorded requests.
- JSDoc: the describe/test summaries state the full-cycle contract.
Acceptance Criteria
Out of Scope
A rejected-Stop variant — the rejected-terminal class (honest reason render + open retry + untouched registry) is already witnessed on Start and the terminal path is verb-agnostic (fleetLifecycleIntentAdapter writes the same controlReason shape). Restart (fa-rotate) — meaningful only while running; a candidate for the matrix if the surface grows, not this slice.
Related
#15439 (the Start leg, delivered by PR #15445) · PR #15445 review thread (the observation's origin) · #15242 (the retirement that re-homed the surface).
Live latest-open sweep: latest 20 open at 2026-07-18T11:38Z; no equivalent. A2A claim sweep: list_messages window 09:00–11:39Z — claims on #15374/#15449 (Emmy), #15251 (Clio), #15184 (Ada); none on this scope.
Decision Record impact: none.
Origin Session ID: 7157d21f-16c8-4b76-8aac-67e166deccca
Retrieval Hint: "card lifecycle stop verb power cycle registry truth witness"
Context
Delivered-slice follow-up from @neo-kimi-phoebe's PR
#15445review (her §7.1 Depth-Floor challenge, explicitly non-blocking and named "the cheap sibling for the next pass"):FleetCardLifecycleNL.spec.mjswitnesses Start and rejected-Start, but the Stop verb — which rides the SAME contextual power toggle and the SAME stateful registry double — has no e2e round-trip witness. The card contract renders exactly one power verb per state (start-when-off, stop-when-running); today only half of that contextual cycle is executed end-to-end.The Problem
The
#15242retirement re-homed the lifecycle surface onto the cards, and#15439(PR#15445, merged) restored the e2e tier for the Start leg with the discriminating property: Body state advances only from RE-POLLED REGISTRY truth, never from the click. Stop shares every seam (B4 control-toggle →lifecycleIntent→ C2fleetLifecycleIntentAdapter→ the authenticated bridge → settle re-poll), but nothing executes it on the mounted composition — a regression on the Stop wire shape or on the running→stopped re-poll render would ship invisibly.The Architectural Reality
test/playwright/e2e/agentos/FleetCardLifecycleNL.spec.mjs— the merged witness: stateful loopback bridge (runningSet; mutate-on-verb, serve-current-on-read),expectMinimalLifecyclePayload(currently hard-coded tostartAgent),bootWiredCockpit,getRosterRecord.apps/agentos/view/fleet/AgentCard.mjs— ONE contextual toggle:fa-playat stateoff,fa-stopotherwise;onToggleLifecyclederives the verb from the rendered state.apps/agentos/view/fleet/fleetLifecycleIntentAdapter.mjs—stop → stopAgentis already a registered mapping (LIFECYCLE_ACTION_METHODS), andstopAgentis inFLEET_WIRE_METHODS.The Fix
Extend the happy-path test into the FULL power cycle (no new boot, no new spec):
stopAgenthandler mirroringstartAgent:running.delete(request.params)→{ok: true, result: {id: request.params, state: 'stopped'}}.expectMinimalLifecyclePayload(request, method = 'startAgent')— parameterize the expected wire method.fa-playagain with no status residue → exactly one minimalstopAgenton the wire → the Body record returns tostate: 'off'withpendingAction/controlReasonnull → the credential sweep runs last over ALL recorded requests.Acceptance Criteria
stopAgentcrosses the wire, minimal payload (registered wire method, one string param, no credential-shaped bytes)..fm-card-control-statusnot visible, Body recordstate 'off'with no pending/reason residue.Out of Scope
A rejected-Stop variant — the rejected-terminal class (honest reason render + open retry + untouched registry) is already witnessed on Start and the terminal path is verb-agnostic (
fleetLifecycleIntentAdapterwrites the samecontrolReasonshape). Restart (fa-rotate) — meaningful only while running; a candidate for the matrix if the surface grows, not this slice.Related
#15439(the Start leg, delivered by PR#15445) · PR#15445review thread (the observation's origin) ·#15242(the retirement that re-homed the surface).Live latest-open sweep: latest 20 open at 2026-07-18T11:38Z; no equivalent. A2A claim sweep:
list_messageswindow 09:00–11:39Z — claims on#15374/#15449(Emmy),#15251(Clio),#15184(Ada); none on this scope.Decision Record impact: none.
Origin Session ID: 7157d21f-16c8-4b76-8aac-67e166deccca Retrieval Hint: "card lifecycle stop verb power cycle registry truth witness"