Context
Follow-up carved from my Round-2 review of PR #17593 (https://github.com/neomjs/neo/pull/17593#pullrequestreview-5001585874); @neo-gpt-emmy accepted it and asked for a narrow doc-only successor parented to Epic #17559 rather than reopening the soon-resolved #17553.
Doc-only. No product source, no spec, no archived content.
⛔ BLOCKED BY #17553 — do not start before PR #17593 merges
On origin/dev right now the drill Button still exists, at apps/agentos/view/fleet/roster/card/Container.mjs:156 (cls: ['fm-card-name', 'fm-card-drill', 'neo-selection']). Verified, not assumed.
So the contract row this ticket calls stale is currently accurate against dev, and landing the rewrite first would make the document wrong in the opposite direction — describing a retirement that has not happened. The row only becomes false when PR #17593 merges and resolves #17553.
The window is narrow and the ordering is easy to get wrong precisely because the finding is real; the blocker is recorded here so nobody re-derives it from a passing glance at the retired-Button claim.
Live latest-open sweep 2026-08-23T03:44Z plus state:all searches for CARD-CONTRACT disclosure affordance drill and card contract doc stale drill button retired: zero hits. Only #14560 is adjacent (the cockpit UI/UX epic) and it does not own the card contract document.
The Problem
apps/agentos/CARD-CONTRACT.md is the live, citable contract for what an agent card renders — its own header names it "The single citable contract for what an agent card renders, from which wire fields, under which render rules", with the AgentCard implementation and the detail header listed as consumers.
Its Name drill row now describes a control that no longer exists:
fm-card-drill marks the name as the card's one disclosure route to the detail surface. Deliberately the ONLY disclosure affordance: no ▸ or expander elsewhere, so a non-interactive node never advertises interactivity
none — the drill is unconditional. It is a static class on an always-rendered Button with a bound handler; there is no absent-detail or unauthorized branch, and this row must not invent one
#17553 retired that Button explicitly — "The dedicated drill Button retires (the item itself is the target); agentSelect goes with it" — and made the semantic roster item the selection target, with selection itself running the detail reveal. git grep fm-card-drill finds no app source on the current head; the only remaining hits are this row and archived resources/content/issues|pulls records.
So the document simultaneously:
- documents a control that was deleted, down to implementation detail ("a static class on an always-rendered
Button with a bound handler");
- forbids the interaction the product just adopted — "the ONLY disclosure affordance" is now false, and a reader following it would treat item-click disclosure as a contract violation;
- carries a degrade rule ("none — the drill is unconditional") for a branch that no longer has a subject.
The Architectural Reality
A stale row in a citable contract is worse than a stale comment: the document exists to be cited in review, so it actively arms the wrong objection. The next author who reads it before touching a card will either re-add a retired Button or challenge correct code.
The row's rationale also needs re-expressing rather than deleting, and that is the substance of this ticket. Its stated concern — "a non-interactive node never advertises interactivity" — was sound under the old model, where the card was a non-interactive role=listitem and a dedicated Button carried activation. #17553 resolves that concern differently in BEHAVIOUR: the item is now genuinely interactive — Enter selects the focused row, and item focus / arrow movement belong to the Navigator addon (the base list contract), not to the selection model. The accessible SEMANTICS do not follow, and this ticket must not claim they do (@neo-gpt-emmy, PR #17734 RA-1): Neo.list.Base renders the item as li and emits aria-selected unconditionally, but attaches a role only when itemRole is truthy — and itemRole defaults to null with no roster override. WAI-ARIA 1.2 supports aria-selected on gridcell · option · row · tab, never on listitem. An emitted attribute is evidence of intent, not of conformance.
Deleting the row would drop a real accessibility principle. Rewriting it keeps the principle and updates the mechanism.
The Fix
Rewrite the Name drill row as a disclosure/selection row describing the current contract:
- the semantic roster item is the disclosure route: click or Enter selects, and selection runs the detail reveal;
- the BEHAVIOUR carries it — native
Enter selecting the focused row, Navigator-addon focus and arrow movement — which is how "never advertise interactivity a node does not have" is served now; the row states the accessible-semantics half as unmet, with coordinates, rather than claiming it closed;
- the lifecycle-control carve-out is preserved and stated: in-card lifecycle Buttons (toggle / restart) never change selection, per #17553's AC and its mounted witness;
- the degrade rule is re-derived for the real branches rather than kept as "none — unconditional".
Explicitly not in this change: resources/content/issues/** and resources/content/pulls/** keep every fm-card-drill reference. Those are historical records that correctly describe what was true when written; rewriting them would erase the record.
Acceptance Criteria
Out of Scope
- Any product source or spec change. The behaviour is correct as shipped in PR #17593; this is the document catching up.
- Reopening #17553. It is resolved; this is its successor, parented to Epic #17559.
- The rest of the field table. Only the drill/disclosure row is stale; the other rows were not audited here and no claim is made about them.
- Archived issue/PR content — historical by design.
Avoided Traps
- Deleting the row instead of rewriting it. The old row carries a genuine a11y principle ("a non-interactive node never advertises interactivity"); #17553 satisfies that principle by a different mechanism rather than abandoning it. A deletion would lose the why and leave the contract silent on disclosure entirely.
- Editing the archived records to match. They are correct about the past. A contract doc and a history record decay in opposite directions.
- Assuming the rest of the table is fresh. This ticket found one stale row because a PR review walked into it — not because the document was audited. The other rows carry no clean bill of health from me.
Related
Epic #17559 · #17553 (retired the Button) · PR #17593 (the change that stale-dated the row) · #14605 (the contract document's own ticket) · #14608 (detail header, a listed consumer)
Retrieval Hint: CARD-CONTRACT.md name drill row stale fm-card-drill retired disclosure affordance semantic item click Enter selection aria-selected lifecycle carve-out
Origin Session ID: 1b0d28eb-3461-40b6-bb35-88d6bf09ec94
Truth-synced 2026-08-24 after @neo-gpt-emmy's RA-1 on PR #17734. The three anchors above asserted that aria-selected on the roster li made the semantics honest. It does not — the state rides a role that cannot carry it. The ticket existed because a contract asserted something untrue, and its own body reproduced that one direction over; syncing here so the ticket is not a second stale authority.
Context
Follow-up carved from my Round-2 review of PR #17593 (https://github.com/neomjs/neo/pull/17593#pullrequestreview-5001585874); @neo-gpt-emmy accepted it and asked for a narrow doc-only successor parented to Epic #17559 rather than reopening the soon-resolved #17553.
Doc-only. No product source, no spec, no archived content.
⛔ BLOCKED BY #17553 — do not start before PR #17593 merges
On
origin/devright now the drill Button still exists, atapps/agentos/view/fleet/roster/card/Container.mjs:156(cls: ['fm-card-name', 'fm-card-drill', 'neo-selection']). Verified, not assumed.So the contract row this ticket calls stale is currently accurate against
dev, and landing the rewrite first would make the document wrong in the opposite direction — describing a retirement that has not happened. The row only becomes false when PR #17593 merges and resolves #17553.The window is narrow and the ordering is easy to get wrong precisely because the finding is real; the blocker is recorded here so nobody re-derives it from a passing glance at the retired-Button claim.
Live latest-open sweep 2026-08-23T03:44Z plus
state:allsearches forCARD-CONTRACT disclosure affordance drillandcard contract doc stale drill button retired: zero hits. Only #14560 is adjacent (the cockpit UI/UX epic) and it does not own the card contract document.The Problem
apps/agentos/CARD-CONTRACT.mdis the live, citable contract for what an agent card renders — its own header names it "The single citable contract for what an agent card renders, from which wire fields, under which render rules", with the AgentCard implementation and the detail header listed as consumers.Its Name drill row now describes a control that no longer exists:
#17553 retired that Button explicitly — "The dedicated drill Button retires (the item itself is the target);
agentSelectgoes with it" — and made the semantic roster item the selection target, with selection itself running the detail reveal.git grep fm-card-drillfinds no app source on the current head; the only remaining hits are this row and archivedresources/content/issues|pullsrecords.So the document simultaneously:
Buttonwith a bound handler");The Architectural Reality
A stale row in a citable contract is worse than a stale comment: the document exists to be cited in review, so it actively arms the wrong objection. The next author who reads it before touching a card will either re-add a retired Button or challenge correct code.
The row's rationale also needs re-expressing rather than deleting, and that is the substance of this ticket. Its stated concern — "a non-interactive node never advertises interactivity" — was sound under the old model, where the card was a non-interactive
role=listitemand a dedicatedButtoncarried activation. #17553 resolves that concern differently in BEHAVIOUR: the item is now genuinely interactive —Enterselects the focused row, and item focus / arrow movement belong to the Navigator addon (the base list contract), not to the selection model. The accessible SEMANTICS do not follow, and this ticket must not claim they do (@neo-gpt-emmy, PR #17734 RA-1):Neo.list.Baserenders the item asliand emitsaria-selectedunconditionally, but attaches a role only whenitemRoleis truthy — anditemRoledefaults tonullwith no roster override. WAI-ARIA 1.2 supportsaria-selectedongridcell·option·row·tab, never onlistitem. An emitted attribute is evidence of intent, not of conformance.Deleting the row would drop a real accessibility principle. Rewriting it keeps the principle and updates the mechanism.
The Fix
Rewrite the Name drill row as a disclosure/selection row describing the current contract:
Enterselecting the focused row, Navigator-addon focus and arrow movement — which is how "never advertise interactivity a node does not have" is served now; the row states the accessible-semantics half as unmet, with coordinates, rather than claiming it closed;Explicitly not in this change:
resources/content/issues/**andresources/content/pulls/**keep everyfm-card-drillreference. Those are historical records that correctly describe what was true when written; rewriting them would erase the record.Acceptance Criteria
fm-card-drillas a live affordance.aria-selected.git grep fm-card-drill -- appsreturns zero hits.resources/content/issues/**andresources/content/pulls/**are untouched — verified by the diff's file list, not by intent.Out of Scope
Avoided Traps
Related
Epic #17559 · #17553 (retired the Button) · PR #17593 (the change that stale-dated the row) · #14605 (the contract document's own ticket) · #14608 (detail header, a listed consumer)
Retrieval Hint:
CARD-CONTRACT.md name drill row stale fm-card-drill retired disclosure affordance semantic item click Enter selection aria-selected lifecycle carve-outOrigin Session ID: 1b0d28eb-3461-40b6-bb35-88d6bf09ec94
Truth-synced 2026-08-24 after @neo-gpt-emmy's RA-1 on PR #17734. The three anchors above asserted that
aria-selectedon the rosterlimade the semantics honest. It does not — the state rides a role that cannot carry it. The ticket existed because a contract asserted something untrue, and its own body reproduced that one direction over; syncing here so the ticket is not a second stale authority.