Context
Carved from #17596 (AgentOS cockpit e2e reds) per its own AC-1 — "reclassified with a named cause and its own successor ticket". Three of that ticket's failures share one cause; the rest do not and stay with the parent.
Two of the three were not in #17596's original census. That ticket lists five reds; FleetCardLifecycleNL carries two more, invisible because the census was a chosen subset of specs plus a toEqual([{method grep that structurally cannot see a shared assertion helper.
The Problem
installFleetBridge.mjs builds every fleet call through createFleetWireRequest, then:
envelope = await transportSend(shellTransport
? {method: wireRequest.method, params: wireRequest.params}
: wireRequest)Browser mode sends the protocol offer; shell mode strips it back to {method, params}. Since #16743 ("version the client wire contract", merged as 040fa1cb27) that offer is {capabilities: ['method-schema-v1', 'closed-response-states-v1'], versions: [1]}.
Any spec wiring an HTTP loopback therefore records the full envelope. Two lifecycle specs still asserted the pre-#16743 shape with toEqual exact equality:
| spec |
site |
shape |
FleetGridKeyboardA11y.spec.mjs |
the Stop-request assertion |
inline toEqual([{method, params}]) |
FleetCardLifecycleNL.spec.mjs |
expectMinimalLifecyclePayload |
helper toEqual({method, params}), used 3× |
Three failing tests, one stale expectation shape. The assertions were correct when written — the wire grew and they did not move with it.
The Architectural Reality
.github/workflows/test.yml runs integration / parity / unit / components; there is no e2e suite in CI (no workflow references test-e2e or playwright.config.e2e). A wire-contract change therefore cannot redden the e2e layer at merge time — it reddens it silently, whenever someone next runs it locally on a browser-capable seat (#17595 owns why that population narrowed).
toEqual against a whole request object is the fragile shape: it couples a payload assertion to envelope structure. The specs' stated intent is minimality of the payload — one agent id, no credential-shaped bytes, no drill-shaped extras — which the envelope never violated.
The contract's absolute values are already owned elsewhere: lint-fleet-vocabulary-parity.mjs and fleetVocabularyParity.spec.mjs compare the ai/services/fleet authority against the apps/agentos/config twin, and prove an insufficient offer fails closed via unsupportedCapability. So a spec that hardcodes capability literals would duplicate that guard brittlely rather than add one.
The Fix
Assert the offer through the exported createFleetWireOffer() builder instead of a literal, at both sites.
Exact equality is kept — it is what enforces the minimal-payload guarantee, and it still rejects any stray key. What changes is that the expectation tracks the contract rather than freezing a snapshot of it, so the next envelope revision moves these specs without editing them.
Acceptance Criteria
Out of Scope
- Rows 2–5 of #17596 —
AgentCardSynthesisRenderNL, FleetCockpitDrillRoundTripNL, and two FleetMailboxTabNL journeys. Undiagnosed, unclaimed, different causes; the parent keeps them.
- Changing the wire contract itself. #16743's envelope is deliberate and stays.
- Adding e2e to CI — separate decision, own cost.
- #17595 — the launch-capability boundary, not a spec failure.
Avoided Traps
- Hardcoding the capability literals. It would have looked stricter while duplicating the parity lint's job and creating a second place to update — the guard that matters (an insufficient offer fails closed) already exists and is not improved by a copy in an e2e spec.
- Reaching for
toMatchObject. It would green the specs by permitting any extra key, silently discarding the minimal-payload property these specs exist to enforce. The bug is a stale expectation, not an over-strict one.
- Trusting the census that produced the parent. Two of these three were missing from it. The instrument is now recorded with the ticket, so the count can be reproduced instead of believed.
Related
Parent #17596 · #16743 (the envelope this stale-dated) · #17595 (why the layer went unrun) · PR #17593 (surfaced the parent set)
Retrieval Hint: fleet wire protocol envelope createFleetWireOffer toEqual exact equality e2e lifecycle spec stale expectation browser mode shell mode installFleetBridge
Origin Session ID: 1b0d28eb-3461-40b6-bb35-88d6bf09ec94
Context
Carved from #17596 (AgentOS cockpit e2e reds) per its own AC-1 — "reclassified with a named cause and its own successor ticket". Three of that ticket's failures share one cause; the rest do not and stay with the parent.
Two of the three were not in #17596's original census. That ticket lists five reds;
FleetCardLifecycleNLcarries two more, invisible because the census was a chosen subset of specs plus atoEqual([{methodgrep that structurally cannot see a shared assertion helper.The Problem
installFleetBridge.mjsbuilds every fleet call throughcreateFleetWireRequest, then:envelope = await transportSend(shellTransport ? {method: wireRequest.method, params: wireRequest.params} : wireRequest)Browser mode sends the protocol offer; shell mode strips it back to
{method, params}. Since #16743 ("version the client wire contract", merged as040fa1cb27) that offer is{capabilities: ['method-schema-v1', 'closed-response-states-v1'], versions: [1]}.Any spec wiring an HTTP loopback therefore records the full envelope. Two lifecycle specs still asserted the pre-#16743 shape with
toEqualexact equality:FleetGridKeyboardA11y.spec.mjstoEqual([{method, params}])FleetCardLifecycleNL.spec.mjsexpectMinimalLifecyclePayloadtoEqual({method, params}), used 3×Three failing tests, one stale expectation shape. The assertions were correct when written — the wire grew and they did not move with it.
The Architectural Reality
.github/workflows/test.ymlruns integration / parity / unit / components; there is no e2e suite in CI (no workflow referencestest-e2eorplaywright.config.e2e). A wire-contract change therefore cannot redden the e2e layer at merge time — it reddens it silently, whenever someone next runs it locally on a browser-capable seat (#17595 owns why that population narrowed).toEqualagainst a whole request object is the fragile shape: it couples a payload assertion to envelope structure. The specs' stated intent is minimality of the payload — one agent id, no credential-shaped bytes, no drill-shaped extras — which the envelope never violated.The contract's absolute values are already owned elsewhere:
lint-fleet-vocabulary-parity.mjsandfleetVocabularyParity.spec.mjscompare theai/services/fleetauthority against theapps/agentos/configtwin, and prove an insufficient offer fails closed viaunsupportedCapability. So a spec that hardcodes capability literals would duplicate that guard brittlely rather than add one.The Fix
Assert the offer through the exported
createFleetWireOffer()builder instead of a literal, at both sites.Exact equality is kept — it is what enforces the minimal-payload guarantee, and it still rejects any stray key. What changes is that the expectation tracks the contract rather than freezing a snapshot of it, so the next envelope revision moves these specs without editing them.
Acceptance Criteria
dev.toMatchObjectpermissiveness).Out of Scope
AgentCardSynthesisRenderNL,FleetCockpitDrillRoundTripNL, and twoFleetMailboxTabNLjourneys. Undiagnosed, unclaimed, different causes; the parent keeps them.Avoided Traps
toMatchObject. It would green the specs by permitting any extra key, silently discarding the minimal-payload property these specs exist to enforce. The bug is a stale expectation, not an over-strict one.Related
Parent #17596 · #16743 (the envelope this stale-dated) · #17595 (why the layer went unrun) · PR #17593 (surfaced the parent set)
Retrieval Hint:
fleet wire protocol envelope createFleetWireOffer toEqual exact equality e2e lifecycle spec stale expectation browser mode shell mode installFleetBridgeOrigin Session ID: 1b0d28eb-3461-40b6-bb35-88d6bf09ec94