LearnNewsExamplesServices
Frontmatter
id15360
titleFM cockpit focus-order invariant — the delivered AC2(a) witness
stateClosed
labels
enhancementaitesting
assigneesneo-opus-vega
createdAtJul 17, 2026, 4:59 PM
updatedAtJul 17, 2026, 6:18 PM
githubUrlhttps://github.com/neomjs/neo/issues/15360
authorneo-opus-vega
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 17, 2026, 6:18 PM

FM cockpit focus-order invariant — the delivered AC2(a) witness

Closed Backlog/active-chunk-7 enhancementaitesting
neo-opus-vega
neo-opus-vega commented on Jul 17, 2026, 4:59 PM

Context

Split out of #14619 following the delivered-slice precedent (#15316/#15312, #15331/#15332, #15351/#15350): the parent stays open on design-authority material — AC1's disposition half (F1/F2 token deltas) and F3 (hue-only StateDot), both awaiting @neo-opus-grace's ruling — so a PR delivering AC2(a) alone cannot honestly say Resolves #14619, and the agent-PR-body lint requires a Resolves #N on a non-draft. This leaf carries exactly the one open verification AC out of #14619. Same split shape as the sibling slice #15351 (the contrast half).

The Problem

#14619 AC2(a): no tabIndex overrides in the rendered tree — the one focus-order AC not yet witnessed (AC2 (b)/(d) are covered by FleetGridKeyboardA11y.spec.mjs, AC2 (c) by cockpitDockDocument.spec.mjs). The cockpit's design declares no explicit tab topology: focus order IS DOM order. That claim holds iff no tabindex > 0 exists anywhere in the mounted tree — a single positive tabindex pulls its element to the front of the tab sequence regardless of document position, silently detaching keyboard order from visual order. An operator mid-incident cannot be mouse-gated (#14619's own bar).

The Architectural Reality

  • A source grep is the wrong witness. Grepping FleetCockpit.mjs for tabIndex would be a guard aimed at a filename — the #15345 class: any composed component can introduce a tabindex from its own config, a mixin, or a framework default without the shell's source ever containing the string. The claim is about the rendered tree, so the witness must walk the rendered tree.
  • An expected-tab-sequence fixture is the wrong witness too. The original AC sketch (grid → card → controls → stream → rails) would pin a layout preset, not an invariant: dock zones are document-driven — resolveDockComponentRef resolves each componentRef at runtime from cockpitDockDocument.mjs — so a pinned sequence goes red on every preset change and stays silent on every real regression. The invariant (zero reordering tabindex) is preset-independent; the sequence never was.
  • Vacuity control is load-bearing. A walk that reaches an empty tree also finds no tabindex — without proof the tree was really there and genuinely keyboard-reachable, "no overrides" is a vacuous pass (the #13652 negative-result-needs-a-positive-control discipline).
  • Structural placement: owning folder test/playwright/e2e/agentos/ — 14 sibling specs incl. FleetGridKeyboardA11y.spec.mjs (the AC2 (b)/(d) sibling); sibling fast-path, no novel directory.

The Fix

One mounted whitebox-e2e: test/playwright/e2e/agentos/FleetCockpitFocusInvariant.spec.mjs

  • Mounts /apps/agentos/index.html, waits for .agent-shell + .fm-fleet-grid.
  • Walks the mounted shell once: total elements scanned, distinct focusable elements, and every explicit tabindex with enough identity (tag / class / value) to name the offender in the failure message.
  • Asserts, in order: real tree (scanned > 50), keyboard-drivable (focusable > 0), zero tabindex > 0 (0 / -1 are fine — they opt in/out without reordering), and a clean mount (no page errors).

Acceptance Criteria

  • Mounted e2e witnesses the invariant: no tabindex > 0 anywhere in the rendered cockpit shell.
  • Positive controls prove non-vacuity: the walk reaches a real tree AND finds at least one focusable element.
  • A failing run names the offending element (tag, class, tabindex value) — not just a count.
  • Green via npx playwright test test/playwright/e2e/agentos/FleetCockpitFocusInvariant.spec.mjs -c test/playwright/playwright.config.e2e.mjs.

Out of Scope

  • AC1's disposition half and F3 — design-material, stay on #14619 with @neo-opus-grace.
  • Any change to cockpit source. This is a witness, not a fix; if it ever goes red, the fix is its own ticket.

Related

Parent #14619 (stays open) · #15351 (sibling delivered-slice, the contrast half) · PR #15350 (merged measurement) · #15345 (the guard-aimed-at-a-filename class this witness's shape answers) · #14560 (epic, T7.25).

Live latest-open sweep: checked latest 20 open at 2026-07-17T14:59Z (latest #15359); nearest neighbours #15339 (activitySource live half), #15352 (stacked-PR guard), #15251 (dock tear-out seam) — none equivalent. A2A in-flight claim sweep (last 15 messages, ~10h window): no [lane-claim]/[lane-intent] overlapping focus-order/a11y-invariant scope; Fable's 14:51Z lane map places FM cockpit UX with @neo-opus-vega (this author).

Decision Record impact: none.

Origin Session ID: 2dcbf336-4338-4009-82f3-79f1b1d151f1 (spec authored in 6517bb47-c5ad-4f0b-a29d-e67f1fa2d153, the #15351 split session)

Retrieval Hint: "FM cockpit focus order DOM order tabindex invariant mounted witness AC2a"

tobiu referenced in commit 7250fe7 - "test(agentos): focus order IS DOM order — the mounted AC2(a) witness (#15360) (#15361) on Jul 17, 2026, 6:18 PM
tobiu closed this issue on Jul 17, 2026, 6:18 PM