LearnNewsExamplesServices
Frontmatter
title>-
authorneo-fable-clio
stateMerged
createdAtAug 15, 2026, 11:23 AM
updatedAtAug 15, 2026, 3:23 PM
closedAtAug 15, 2026, 2:52 PM
mergedAtAug 15, 2026, 2:52 PM
branchesdev ← feature/17157-binding-unavailable-code
urlhttps://github.com/neomjs/neo/pull/17159
contentTrust
projected
quarantined0
signals[]
Merged
neo-fable-clio
neo-fable-clio commented on Aug 15, 2026, 11:23 AM

Resolves #17157

Refs #16737

The identity-binding third signal is now typed end-to-end: the plane client stamps planeBlockerCode: 'viewer-binding-unavailable' at the two refusal sites that KNOW they are binding-class (the identity-oracle's no-canonical-identity and identity-mismatch refusals in connectProven — "the one refusal that protects every admission decision downstream"), the thrown session-lost errors CARRY the stamp verbatim (a shared sessionLostError builder copies, never derives), the presence adapter passes a recognized code through as capability.reasonCode behind its own closed set (PRESENCE_CAPABILITY_REASON_CODES — an unrecognized stamp is dropped, never admitted), and the cockpit DTO delivers it intact. A post-admission viewer-binding failure — binding drift or a failed reacquisition — is now RENDERABLE as "binding unavailable" from a typed field — never inferred from prose, and never mistakable for an empty fleet (every roster row still answers unknown); the initial-admission refusal stays terminal by design (devFleetServer exits before any capability DTO exists) and fabricates nothing. Ambiguous failures (transport, timeout, 5xx) deliberately stay unstamped: ambiguity never classifies, per the fleetMailboxMirrorAdapter anti-misclassification precedent (bare-Unauthorized warning) this design follows.

Evidence: L2 (scoped unit receipts across all three chain hops — client refusal/throw sites via the scripted-plane harness, adapter passthrough via the reader seam, cockpit DTO via the pure map) → L2 sufficient for the close-target ACs (every AC adapter/client/DTO-side and specable). Residual: C3's render-side consumption of the typed field (the pane state itself), Residual-Owner: #16737.

Deltas from ticket

One narrowing, stated: the ticket's Fix step 1 mentioned enumerating plane-side admission-denial texts as a second stamp class; the enumeration found the presence path's binding-class failures all route through the identity-oracle refusals (who_is_online is roster-wide — no per-subject admission scope like the mirror's CAN_READ_INBOX_OF), so the two connectProven sites are the complete stamp set for this chain. No tool-text matching was added anywhere — which is the stricter reading of the ticket's own anti-misclassification requirement.

Post-merge body correction (reviewer phase-wording catch, @neo-gpt): the renderability claim above originally said "a broken viewer binding" unqualified, overclaiming by one phase — what this PR types is the post-admission phase only; the initial binding refusal was and remains terminal before any capability DTO exists. No code change.

Test Evidence

  • npm run test-unit -- test/playwright/unit/ai/planeMailboxClient.spec.mjs test/playwright/unit/ai/services/fleet/fleetPresenceStateAdapter.spec.mjs → 45 passed (2.8s).
  • npm run test-unit -- test/playwright/unit/ai/services/fleet/fleetCockpitStatus.spec.mjs → 21 passed (2.4s).
  • New coverage: mismatch refusal toEqual now VERIFIES the stamp; the changed-identity reconnect test asserts the thrown error carries planeBlockerCode; ambiguity-never-classifies (500-class → no stamp); binding-classified read → reasonCode + full roster of unknown rows (never an empty fleet); unclassified failure → no reasonCode field; unrecognized stamp → dropped (closed-set guard); degraded presence capability with reasonCode reaches the cockpit DTO intact.
  • Surface ai/services/fleet (plane client + presence axis + cockpit map): the three spec files above are the existing coverage, extended; no app/UI surface touched.

Post-Merge Validation

  • All three touched spec files re-runnable on dev post-merge (self-contained scripted-plane/fixture harnesses, no deployment dependency).

Branch note: sibling PR #17154 (leg 1, in verification) touches the same adapter on adjacent lines (envelope capturedAt line vs the added reasonCode spread; different let/const blocks) — zero-to-trivial merge in either landing order; whichever lands second rebases mechanically.

Authored by Clio (Claude Fable 5, Claude Code). Session 1deebbe1-b7e6-4f76-b39d-9cfcbe342596.

tobiu
tobiu APPROVED reviewed on Aug 15, 2026, 2:52 PM

No review body provided.