feat(agentos): FM cockpit keyboard a11y — roving focus, Enter/Space activation, aria-live + landmarks (#14619)#15094
Draft
neo-opus-vega wants to merge 9 commits into
Draft
feat(agentos): FM cockpit keyboard a11y — roving focus, Enter/Space activation, aria-live + landmarks (#14619)#15094neo-opus-vega wants to merge 9 commits into
neo-opus-vega wants to merge 9 commits into
Conversation
…14619) FM a11y pass (non-colliding aria slice, off dev — ActivityStream is not in the #15063 drill-spine diff): the cockpit's live activity feed had zero aria, so screen-reader users got no announcement of new events. onConstructed now makes the root a named, polite log region (aria-live=polite, role=log, aria-label), set before the first refreshFeed render flushes the vdom. Construct-probe test added. The focus-order half of #14619 follows once #15063 lands (it touches the drill spine); the contrast tokens were routed to Grace earlier this pass.
…ross feed updates (#14619) Adversarial self-review hardening: the construct-probe proved the live region exists at mount, but a live region's whole value is persisting across UPDATES. refreshFeed swaps child items (removeAll + add) and never the vdom root, so the aria-live/role/ aria-label survive — but nothing pinned that, leaving a future refreshFeed refactor free to replace the root and silently re-silence the feed to screen readers after the first event. Now a post-update assertion (stream.events reset → refreshFeed) locks it.
…ns (#14619) The a11y pass's landmark-structure slice, now un-gated by #15063 landing the drill. FleetGrid (roster) and AgentDetail (drill) each become a named `role='region'` with an `aria-label` ('Fleet roster' / 'Agent detail') so screen-reader users can navigate the cockpit's primary surfaces as distinct landmarks — completing the 3-region set with ActivityStream's existing `log` region. Set in onConstructed before the first render flush; both re-render paths preserve the root (refreshGrid mutates the child cards container; applyRecord re-seats via child-reference .set()), so the region survives store-driven re-renders + record re-seats — asserted in both specs. Roving- tabindex focus-order (the keyboard-nav slice) follows on this branch.
…ntCards (Enter-primary) (#14619) The FM cockpit was mouse-only (zero tabindex/keydown across the fleet components). AgentCard is now a keyboard-operable drill target: focusable (tabIndex 0), announced as role=button with the resident's name (record-derived aria-label, updates in place on re-seat), and Enter fires the same agentSelect drill as a body click. onCardSelect is now shared by click + keydown: it filters keydowns to Enter (a click has no data.key and passes) and reuses the control-cluster path carve-out — keydown event data carries path too (DomEvents.getKeyboardEventData), so a keydown bubbling up from a focused control button is a lifecycle intent, not a drill. Space activation + roving-tabindex are deferred to the KeyNavigation follow-up: Space's scroll default cannot be cleanly prevented on a raw keydown across the worker boundary. Enter-primary is the clean, correct baseline.
4 tasks
…rd nav on FleetGrid (#14619) Atop Tier-1's keyboard-operable cards, the FleetGrid card set is now a single tab stop with arrow-key roving. The keys config auto-wires KeyNavigation (Up/Left step back, Down/Right advance); focusIndex tracks the active card (tabIndex 0, every other card -1); moveFocus clamps to the card count and moves DOM focus (mounted-guarded); refreshGrid re-establishes the tab stop on a roster rebuild WITHOUT stealing focus. The neo-selection class opts the card region into the main-thread arrow-key preventDefault rule so navigation never scrolls the viewport. The roving state machine (focusIndex + tabindex + clamp + rebuild-reclamp) is unit-verified (12/12 fleetGrid.spec); the actual DOM focus-movement is the whitebox-e2e layer (mounted-guarded off in units).
…14619-fm-focus-order
…n) — neo-selection scroll-key rule covers Space (#14619)
1 task
…ross roster rebuild; correct AgentCard Space-activation doc (#14619)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Resolves #14619 (FM cockpit accessibility pass — keyboard-operability slice).
Consolidates the two #14619 keyboard-a11y drafts into one coherent PR per the v13.2 convergence window (Euclid's option B). Supersedes #15081 (aria-live + landmark regions), merged in here — one complete keyboard-a11y surface, not two partial ones.
Evidence: 51/51 unit tests green across agentCard/fleetGrid/activityStream/agentDetail (chroma-less runner, head
0f25e1853). Keyboard operability proven at the controller + roving layer; Space/Enter activation wired through the sharedneo-selectionscroll-key rule.What ships
record.agentIdbefore the rebuild and restores that agent's NEW index, clamping to a valid card only when the agent has left the roster (so a joiner/leaver above the focus never silently hands the tab stop to a different agent — @neo-gpt identity-focus falsifier).tabIndex 0+role=button+ record-derivedaria-label; Enter AND Space drill in (ARIA button semantics); control-cluster keydowns carve out to lifecycle intent, not drill.src/main/DomEvents.mjs): theneo-selectionscroll-key rule now covers Space alongside the arrows, so activation never scrolls the page. The app still receives the keydown (sendMessageToAppfires first) — only the browser scroll default is suppressed.Test Evidence
fleetGrid.spec: roving-tabindex — arrows clamp at both ends; a roster rebuild follows the resident agent identity (joiner above the focus → tab stop follows the same agentId to its new index; focused agent removed → clamps to a valid card, never orphaned) AND the pre-existing stale-focusIndex clamp-into-range still holds.agentCard.spec: keyboard-operable drill target — focusable, button-announced, Enter/Space drill, non-activation + control-cluster keydowns carved out.activityStream.spec+agentDetail.spec: aria-live region persists; roster/detail are named landmark regions.tmp/unit-no-chroma.config.mjs, head 0f25e18. Hosted CI runs the authoritative suite + thesrc/mainDomEvents coverage.Post-Merge Validation
DomEvents.onKeyDownis infeasible (it issingleton:true+Neo.setupClass— self-instantiates on import, needs the main-thread worker harness), so the real-browser e2e runs the real primitive. Per NO-CUTS, sequenced (immediate fast-follow), never dropped. The controller-side Space/Enter activation IS already unit-witnessed (agentCard.spec).Deltas
FleetGrid.refreshGrid): the roving tab stop follows the resident agent across a rebuild, not the numeric index (@neo-gpt falsifier).AgentCarddoc corrected: the stale "Space deferred to KeyNavigation, scroll can't be prevented" comment → the reality (Enter AND Space activate viaonCardSelect; Space's scroll default IS suppressed by theneo-selectionDomEvents rule).src/main/DomEvents.mjs: shared-primitive Space scroll-key coverage — a11y-driven, safe (only suppresses the browser scroll default; app still receives the event). Affects everyneo-selectionregion (correct ARIA behavior: Space activates, does not scroll).Authored by Vega (Claude Opus 4.8, Claude Code). Session d3fb82dd.