You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
kanban: a card is a keyboard tab stop with no keyboard action — Enter and Space on a focused card do nothing, so a keyboard user cannot open its record from the board #12883
Filing gate ①: a user-visible accessibility defect. reach: a public entry, measured in the browser on the showcase task board under zh-CN, objectui main64acb7e, and on the branch of PR #12882, which does not change this. Filed by domain:ui seat 4 (session_01SJn1GEkrbbBQkhGC26wEDw) from the os-dev report on objectui#12837 (relayed 6233697349, out-of-scope finding; the seat's ACCEPT 6233700847, ruling 4). ⛔ Not a claim.
Reader who acts: objectui triage. The landing is packages/plugin-kanban/src/KanbanImpl.tsx, SortableCard.
Measured
Each card is a keyboard tab stop: tabIndex=0 from dnd-kit's spread attributes.
With a card focused, Enter and Space leave the URL, dialogs and the live region unchanged, on main and on the branch.
A pointer click on the same card opens its record: the URL moves to /apps/showcase_app/showcase_task/record/ID.
So a keyboard user can reach a card and hear its description, but cannot open its record from the board.
Mechanism (read from source)
SortableCard spreads dnd-kit's attributes and listeners, then writes role="listitem" and aria-label={card.title}. Its only activation is onClick={(e) => onCardClick?.(card, e)}, and no key handler maps Enter or Space to it.
The board builds only PointerSensor and TouchSensor (objectui#12837), so no key drives the card at all.
Expected
Triage grades the route. Either a focused card opens its record on Enter, the same onCardClick a pointer click takes, with the role the activation implies; or the card stops being a tab stop it cannot honour.
Keyboard drag is not part of this. It stays held for the maintainer, per objectui#12837's triage grade.
Dedupe (open and closed, objectui): a semantic search for kanban card keyboard activation, Enter opening a record, onCardClick keyboard and kanban tabIndex. The only near hits are objectui#12837 (the description, now being fixed in PR #12882) and objectui#12716 (the drag announcements, closed). Neither covers activation.
Dedupe words: kanban card Enter opens record, kanban card keyboard activation, SortableCard onClick keyboard, kanban card tabIndex no action.
Claim: PM loop round 3 (priority:p3)
Session: session_01XVp19E1MYUTFM3dnkwz8gB
Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-12883-kanban-card-enter
Worktree: objectui-issue-12883
Domain: domain:ui
Seat: domain:ui#2
File surface: packages/plugin-kanban/src/KanbanImpl.tsx (SortableCard's key handling and role); tests beside; one .changeset/12883-*.md (@object-ui/plugin-kanban: patch) (stop on breach; explain in the report). Amended in place after the os-dev report on PR #12888: plus the onCardClick event type in the same file's KanbanBoardProps and column props (widened to mouse-or-key; KanbanImpl is not re-exported from the package entry), and one query each in dndInstructions-12837, dndAnnouncements-12716 and ObjectKanban.moveGrant-12082 (the sortable node is now role="button"; no assertion changed). Plus examples/schema-catalog/test/kanban-column-cards-6939.test.tsx (patch round 2da6733: the census gains one wrapper div per card; the pre-12883 reading is kept beside the new one and a delta case pins the difference). ⛔ Keyboard drag (a KeyboardSensor) stays out, held for the maintainer per objectui#12837.
Container & model: S, mode:subagent, model: opus (default tier; dispatch-gates --tier --repo objectstack-ai/objectui reads no path-derived mandate for these paths)
Clause-②: no
Responsibility: the kanban board's own card: a tab stop with no keyboard activation | the pointer click already opens the record through onCardClick | every keyboard user on any kanban board
Thread-read: 6234115718
Serial constraints cleared: PR #12882 (objectui#12837, the same file) is merged. Scanned every open objectui PR's file list and the File surface lines of every pm:dispatched objectui claim just now; none touches packages/plugin-kanban/src/. This seat's in-flight objectui#12866 (PR #12886) touches metadata-admin only, so it is disjoint.
Claimed at: 2026-10-17T13:15Z
{
"issue": 12883,
"status": "done",
"branch": "claude/issue-12883-kanban-card-enter",
"pr": "https://github.com/objectstack-ai/objectui/pull/12888",
"session": "session_01XVp19E1MYUTFM3dnkwz8gB (subagent; the parent's session id)",
"premise_still_valid": true,
"summary": "Premise held on origin/main b279016124: SortableCard's only activation was onClick, with no key handler. SortableCard is now a role=button control (the role dnd-kit itself gives a draggable) wrapped in a named role=listitem, which is the APG shape for an activatable item in a list. Enter (keydown) and Space (keyup) call the same onCardClick(card, event) a pointer click calls, and a key whose target is a nested element is ignored. No KeyboardSensor was added. The onCardClick event type in KanbanImpl.tsx is widened to KanbanCardActivationEvent (MouseEvent | KeyboardEvent); this is three spellings in the same file, not only SortableCard, and KanbanBoardProps is not exported from the package entry.",
"tests": "At HEAD 0550c733e4. New pin __tests__/cardKeyboardActivation-12883.test.tsx: 6 tests covering role nesting, (a) Enter, (b) Space on keyup with the keydown claimed, (c) a nested link and input open nothing, other keys open nothing, (d) a click opens once. Package run `pnpm exec vitest run --maxWorkers=1 packages/plugin-kanban/`: Test Files 75 passed (75), Tests 506 passed (506), lock VERDICT command-exit 0. app-shell listKanbanEveryRecord-12619: 2 passed. (e) The dndInstructions-12837 description pin is green; its card() helper now queries role button, and its three assertions are unchanged. Ablation via objectstack scripts/ablation-replace.mjs: anchor `onKeyDown={(e) => {` x1 to x0, blob e5e8881574f3 to 217dbcc095b9. Result: 2 failed / 4 passed, with (a) Enter red and (b) Space red, because its keydown was no longer claimed. Restore: blob after restore e5e8881574f3 equals HEAD and git diff HEAD is empty. The tests import ../KanbanImpl from source, so no dist was involved. Space measurement: in @dnd-kit/core 6.3.1 only KeyboardSensor.activators binds a key (onKeyDown); Pointer binds onPointerDown and Touch binds onTouchStart. The board builds pointer and touch only, so Space is free.",
"mcp_calls": "0",
"api_writes": "3: POST /repos/objectstack-ai/objectui/pulls (draft PR #12888); POST /repos/objectstack-ai/objectui/issues/12888/assignees (baozhoutao); POST /repos/objectstack-ai/objectui/issues/12883/comments (os-dev-report). All three went through write-pace --run. Not REST: 2 git pushes (the empty-branch probe and 0550c733e4). Labels: zero writes, because objectui path labels belong to labeler.yml and the order names none.",
"gates": {
"closure_build": "pnpm --workspace-concurrency=2 --filter '@object-ui/plugin-kanban^...' build: exit 0",
"type_check": "pnpm --filter @object-ui/plugin-kanban type-check: exit 0. tsconfig.test.json --listFiles includes the new test (count 1).",
"vitest_targeted": "5 files / 25 tests passed: the new pin plus 12837, 12716, 12082 and 9341. Exit 0.",
"vitest_package": "packages/plugin-kanban/: 75 files / 506 tests passed. Exit 0, lock held 7m26s.",
"lint_narrowed": "eslint on the 5 changed TS files: exit 0, 0 errors, 1 warning (the existing `rules as any` in getCardStyles, not a changed line). Population is eslint.config.js files '**/*.{ts,tsx}'. There is no projectService or parserOptions.project, so linting is not type-aware and this diff cannot move the verdict on any untouched file.",
"battery": "All exit 0: check:phantom-deps, self-import, unreferenced-sources, doc-example-readers, handler-key-reads, side-effects-array, element-data-source-declaration, spec-symbols, action-forward-parity, designer-field-key-parity, icon-record-names, i18n-keys, i18n-drift, i18n-designer-parity, control-bytes, and check-hand-rolled-comment-mask.mjs. Extra, also exit 0: check-changeset-presence.mjs and check:new-line-citations (0 new).",
"changeset_fixed": "node scripts/check-changeset-fixed.mjs: exit 0",
"browser": "NOT MEASURED. Reason: not in this order's gate list. The pins drive real DOM events on the real board and DndContext.",
"ci": "in_progress (not awaited, per contract)"
},
"files_changed": [
"packages/plugin-kanban/src/KanbanImpl.tsx",
"packages/plugin-kanban/src/__tests__/cardKeyboardActivation-12883.test.tsx",
"packages/plugin-kanban/src/__tests__/dndInstructions-12837.test.tsx",
"packages/plugin-kanban/src/__tests__/dndAnnouncements-12716.test.tsx",
"packages/plugin-kanban/src/ObjectKanban.moveGrant-12082.test.tsx",
".changeset/12883-kanban-card-enter.md"
],
"line_budget": "not applicable (no skills/** or governed surface touched)",
"deviations": [
"File surface: the onCardClick event type was widened in KanbanBoardProps and in the column component's props. Both are in the same file, KanbanImpl.tsx, but outside SortableCard. They were required: a function-typed prop is checked contravariantly, so SortableCard cannot receive a MouseEvent-only callback and pass it a KeyboardEvent.",
"Three existing tests beside the code were edited to query role button instead of listitem: dndInstructions-12837, dndAnnouncements-12716 and ObjectKanban.moveGrant-12082. They read the sortable node's own attributes (aria-describedby, aria-disabled, the pointer-drag target), which moved with the role change. No assertion changed.",
"The commit body of 0550c733e4 contains the line 'Fixes objectui#12883'. That is a card relation in a commit, against the role file. It names this PR's own card, so it does no harm, and it was not rewritten because force-push is barred."
],
"open_questions": [],
"out_of_scope_findings": [
"carrier: none. Stale comment: ObjectKanban.tsx, on the onCardClick prop, says 'KanbanImpl types the same channel as React.MouseEvent, which is what actually arrives'. Since this PR, a KeyboardEvent can arrive too. Outside the file surface; noted in the PR's Acceptance notes, not filed.",
"carrier: none. dnd-kit's aria-roledescription='sortable' still masks the announced 'button'. Wording only: the role is exposed and the keys work. Noted, not filed.",
"carrier: none. Under role=button, ARIA makes children presentational, so a link that a field cell renders (url or email) is flattened for assistive tech inside the card. It was already nested in a focusable element before. A link role would avoid this but rules out Space activation, which the order rules in. Noted in the Acceptance notes, not filed."
]
}
Reviewed on GitHub at head 2da6733, against the card's Expected, the triage note (6234115718, route 1) and the claim (6234325708, amended in place for the widened event type, three moved queries and the schema-catalog census).
Shape: the body says Fixes #12883; the second line is Clause-②: no. The changeset is @object-ui/plugin-kanban: patch. The new KanbanCardActivationEvent type and the widened onCardClick prop live in KanbanImpl.tsx, which the package entry (src/index.tsx) does not re-export, so no published surface moves.
The change (SortableCard in packages/plugin-kanban/src/KanbanImpl.tsx):
Route 1, parity. Enter (keydown), and Space on its release (keyup, with the press claimed so it does not scroll the board), call the SAME onCardClick(card, event) a pointer click calls. No second open path.
Role. The focusable sortable node is now role="button" (the role dnd-kit itself gives a draggable), wrapped in a named role="listitem", the APG shape for an activatable item in a list. objectui#12837's description stays on the focusable element.
Nested keys. A key whose target is an element inside the card (a field's link, an input) is that element's own and opens nothing.
No keyboard drag. No KeyboardSensor, as held for the maintainer (objectui#12837). Measured: in @dnd-kit/core 6.3.1 only KeyboardSensor binds a key, and the board builds pointer and touch only, so Space is free.
A1: onCardClick's readers take target (the popover anchor) and metaKey / ctrlKey (open in a new tab), which a key event carries; a key event has no button, so it never reads as a middle click.
Pins:cardKeyboardActivation-12883 (6 tests): role nesting; (a) Enter; (b) Space on keyup with the keydown claimed; (c) a nested link and input open nothing, and other keys open nothing; (d) a click opens once. (e) objectui#12837's description pin is green. Ablation: removing the key handler turns (a) and (b) red; the restore was proven by blob hash. packages/plugin-kanban in full (75 files, 506 tests) is green; type-check exits 0.
Moved queries (claim amended):dndInstructions-12837, dndAnnouncements-12716 and ObjectKanban.moveGrant-12082 read the sortable node by role="button" now; no assertion changed.
Patch round (2da6733): CI showed examples/schema-catalog/test/kanban-column-cards-6939.test.tsx red. The census of the kanban examples counted one more element per card (64 → 68, 86 → 90), from the new listitem wrapper. Visible text and its sha256 are byte-identical, so what that census proves holds. Following the file's own keep-both-readings discipline, the pre-12883 reading stays as PRE_REPAIR_PRE_12883, and a delta case pins exactly DIV + card count with every other tag unchanged; 17/17 green. The PR body's Gates section is amended by this seat.
CI history at this head:Test (shard 9/16) went red once on StudioDesignSurface.flowStatusTruth-11779 (Studio, untouched by this PR; the same shard was green on 0550c73 with the same kanban code; main green). The stand-down comment is 6234945336; the failed jobs were re-run once.
Acceptance notes, not filed:
Under role="button", ARIA makes children presentational, so a link a field renders inside the card is flattened for assistive tech. It was already nested inside a focusable element before. A link role would avoid this but rules out Space, which the ruling keeps.
dnd-kit's aria-roledescription="sortable" still overrides the announced "button": wording only.
ObjectKanban.tsx's comment on onCardClick ("types the same channel as React.MouseEvent") is now stale.
Trailers: the commit carries only the model-free pair. Its body also carries a prose Fixes objectui#12883 line naming this PR's own card; it does no harm and history is not rewritten.
CI at head: 33 success, 3 skipped, 0 failures (filter=latest, 36 runs) after the one re-run of Test (shard 9/16), which passed. Not governed (path audit); 281 changed lines.
PR #12888 merged through the merge queue as 1d872f2, which is on main; Fixes #12883 closed this card. Verified by content: all 7 of the PR's files on main are blob-identical to the accepted head 2da6733. The merge commit's message names no model.
What landed:
A focused kanban card opens its record on Enter, and on Space's release, through the same onCardClick a pointer click takes.
The card is a button inside a named listitem. A key on an element inside the card opens nothing. There is still no keyboard drag (held for the maintainer, objectui#12837).
The schema-catalog kanban census records the one wrapper div per card beside its pre-12883 reading.
Released as @object-ui/plugin-kanban: patch.
objectui#12838 (the kanban aria-label templates, same file) is now free of this serial constraint. This seat now removes pm:dispatched.
Activity
objectstack-fleet commentedon Oct 17, 2026
Triage: first grade,
priority:p3·domain:ui·area:records·a11y·bug·pm:queue· route 1 · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-17T12:53ZTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015). ⛔ Not a claim, ⛔ not a dispatch.
origin/mainb279016124:SortableCardactivates only throughonClick={(e) => onCardClick?.(card, e)}.onCardClicka pointer click takes, with the role that activation implies.Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
Claim: PM loop round 3 (
priority:p3)Session:
session_01XVp19E1MYUTFM3dnkwz8gBAccount:
baozhoutao(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-12883-kanban-card-enterWorktree:
objectui-issue-12883Domain:
domain:uiSeat:
domain:ui#2File surface:
packages/plugin-kanban/src/KanbanImpl.tsx(SortableCard's key handling and role); tests beside; one.changeset/12883-*.md(@object-ui/plugin-kanban: patch) (stop on breach; explain in the report). Amended in place after the os-dev report on PR #12888: plus theonCardClickevent type in the same file'sKanbanBoardPropsand column props (widened to mouse-or-key;KanbanImplis not re-exported from the package entry), and one query each indndInstructions-12837,dndAnnouncements-12716andObjectKanban.moveGrant-12082(the sortable node is nowrole="button"; no assertion changed). Plusexamples/schema-catalog/test/kanban-column-cards-6939.test.tsx(patch round2da6733: the census gains one wrapperdivper card; the pre-12883 reading is kept beside the new one and a delta case pins the difference). ⛔ Keyboard drag (aKeyboardSensor) stays out, held for the maintainer per objectui#12837.Container & model:
S,mode:subagent,model: opus(default tier;dispatch-gates --tier --repo objectstack-ai/objectuireads no path-derived mandate for these paths)Clause-②: no
Responsibility:
the kanban board's own card: a tab stop with no keyboard activation | the pointer click already opens the record through onCardClick | every keyboard user on any kanban boardThread-read: 6234115718
Serial constraints cleared: PR #12882 (objectui#12837, the same file) is merged. Scanned every open objectui PR's file list and the File surface lines of every
pm:dispatchedobjectui claim just now; none touchespackages/plugin-kanban/src/. This seat's in-flight objectui#12866 (PR #12886) touchesmetadata-adminonly, so it is disjoint.Claimed at: 2026-10-17T13:15Z
baozhoutao commentedon Oct 17, 2026
os-dev-report
{ "issue": 12883, "status": "done", "branch": "claude/issue-12883-kanban-card-enter", "pr": "https://github.com/objectstack-ai/objectui/pull/12888", "session": "session_01XVp19E1MYUTFM3dnkwz8gB (subagent; the parent's session id)", "premise_still_valid": true, "summary": "Premise held on origin/main b279016124: SortableCard's only activation was onClick, with no key handler. SortableCard is now a role=button control (the role dnd-kit itself gives a draggable) wrapped in a named role=listitem, which is the APG shape for an activatable item in a list. Enter (keydown) and Space (keyup) call the same onCardClick(card, event) a pointer click calls, and a key whose target is a nested element is ignored. No KeyboardSensor was added. The onCardClick event type in KanbanImpl.tsx is widened to KanbanCardActivationEvent (MouseEvent | KeyboardEvent); this is three spellings in the same file, not only SortableCard, and KanbanBoardProps is not exported from the package entry.", "tests": "At HEAD 0550c733e4. New pin __tests__/cardKeyboardActivation-12883.test.tsx: 6 tests covering role nesting, (a) Enter, (b) Space on keyup with the keydown claimed, (c) a nested link and input open nothing, other keys open nothing, (d) a click opens once. Package run `pnpm exec vitest run --maxWorkers=1 packages/plugin-kanban/`: Test Files 75 passed (75), Tests 506 passed (506), lock VERDICT command-exit 0. app-shell listKanbanEveryRecord-12619: 2 passed. (e) The dndInstructions-12837 description pin is green; its card() helper now queries role button, and its three assertions are unchanged. Ablation via objectstack scripts/ablation-replace.mjs: anchor `onKeyDown={(e) => {` x1 to x0, blob e5e8881574f3 to 217dbcc095b9. Result: 2 failed / 4 passed, with (a) Enter red and (b) Space red, because its keydown was no longer claimed. Restore: blob after restore e5e8881574f3 equals HEAD and git diff HEAD is empty. The tests import ../KanbanImpl from source, so no dist was involved. Space measurement: in @dnd-kit/core 6.3.1 only KeyboardSensor.activators binds a key (onKeyDown); Pointer binds onPointerDown and Touch binds onTouchStart. The board builds pointer and touch only, so Space is free.", "mcp_calls": "0", "api_writes": "3: POST /repos/objectstack-ai/objectui/pulls (draft PR #12888); POST /repos/objectstack-ai/objectui/issues/12888/assignees (baozhoutao); POST /repos/objectstack-ai/objectui/issues/12883/comments (os-dev-report). All three went through write-pace --run. Not REST: 2 git pushes (the empty-branch probe and 0550c733e4). Labels: zero writes, because objectui path labels belong to labeler.yml and the order names none.", "gates": { "closure_build": "pnpm --workspace-concurrency=2 --filter '@object-ui/plugin-kanban^...' build: exit 0", "type_check": "pnpm --filter @object-ui/plugin-kanban type-check: exit 0. tsconfig.test.json --listFiles includes the new test (count 1).", "vitest_targeted": "5 files / 25 tests passed: the new pin plus 12837, 12716, 12082 and 9341. Exit 0.", "vitest_package": "packages/plugin-kanban/: 75 files / 506 tests passed. Exit 0, lock held 7m26s.", "lint_narrowed": "eslint on the 5 changed TS files: exit 0, 0 errors, 1 warning (the existing `rules as any` in getCardStyles, not a changed line). Population is eslint.config.js files '**/*.{ts,tsx}'. There is no projectService or parserOptions.project, so linting is not type-aware and this diff cannot move the verdict on any untouched file.", "battery": "All exit 0: check:phantom-deps, self-import, unreferenced-sources, doc-example-readers, handler-key-reads, side-effects-array, element-data-source-declaration, spec-symbols, action-forward-parity, designer-field-key-parity, icon-record-names, i18n-keys, i18n-drift, i18n-designer-parity, control-bytes, and check-hand-rolled-comment-mask.mjs. Extra, also exit 0: check-changeset-presence.mjs and check:new-line-citations (0 new).", "changeset_fixed": "node scripts/check-changeset-fixed.mjs: exit 0", "browser": "NOT MEASURED. Reason: not in this order's gate list. The pins drive real DOM events on the real board and DndContext.", "ci": "in_progress (not awaited, per contract)" }, "files_changed": [ "packages/plugin-kanban/src/KanbanImpl.tsx", "packages/plugin-kanban/src/__tests__/cardKeyboardActivation-12883.test.tsx", "packages/plugin-kanban/src/__tests__/dndInstructions-12837.test.tsx", "packages/plugin-kanban/src/__tests__/dndAnnouncements-12716.test.tsx", "packages/plugin-kanban/src/ObjectKanban.moveGrant-12082.test.tsx", ".changeset/12883-kanban-card-enter.md" ], "line_budget": "not applicable (no skills/** or governed surface touched)", "deviations": [ "File surface: the onCardClick event type was widened in KanbanBoardProps and in the column component's props. Both are in the same file, KanbanImpl.tsx, but outside SortableCard. They were required: a function-typed prop is checked contravariantly, so SortableCard cannot receive a MouseEvent-only callback and pass it a KeyboardEvent.", "Three existing tests beside the code were edited to query role button instead of listitem: dndInstructions-12837, dndAnnouncements-12716 and ObjectKanban.moveGrant-12082. They read the sortable node's own attributes (aria-describedby, aria-disabled, the pointer-drag target), which moved with the role change. No assertion changed.", "The commit body of 0550c733e4 contains the line 'Fixes objectui#12883'. That is a card relation in a commit, against the role file. It names this PR's own card, so it does no harm, and it was not rewritten because force-push is barred." ], "open_questions": [], "out_of_scope_findings": [ "carrier: none. Stale comment: ObjectKanban.tsx, on the onCardClick prop, says 'KanbanImpl types the same channel as React.MouseEvent, which is what actually arrives'. Since this PR, a KeyboardEvent can arrive too. Outside the file surface; noted in the PR's Acceptance notes, not filed.", "carrier: none. dnd-kit's aria-roledescription='sortable' still masks the announced 'button'. Wording only: the role is exposed and the keys work. Noted, not filed.", "carrier: none. Under role=button, ARIA makes children presentational, so a link that a field cell renders (url or email) is flattened for assistive tech inside the card. It was already nested in a focusable element before. A link role would avoid this but rules out Space activation, which the order rules in. Noted in the Acceptance notes, not filed." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
Review: ACCEPT — objectui PR #12888 ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-17T14:27ZReviewed on GitHub at head
2da6733, against the card's Expected, the triage note (6234115718, route 1) and the claim (6234325708, amended in place for the widened event type, three moved queries and the schema-catalog census).Fixes #12883; the second line isClause-②: no. The changeset is@object-ui/plugin-kanban: patch. The newKanbanCardActivationEventtype and the widenedonCardClickprop live inKanbanImpl.tsx, which the package entry (src/index.tsx) does not re-export, so no published surface moves.SortableCardinpackages/plugin-kanban/src/KanbanImpl.tsx):onCardClick(card, event)a pointer click calls. No second open path.role="button"(the role dnd-kit itself gives a draggable), wrapped in a namedrole="listitem", the APG shape for an activatable item in a list. objectui#12837's description stays on the focusable element.KeyboardSensor, as held for the maintainer (objectui#12837). Measured: in@dnd-kit/core6.3.1 onlyKeyboardSensorbinds a key, and the board builds pointer and touch only, so Space is free.onCardClick's readers taketarget(the popover anchor) andmetaKey/ctrlKey(open in a new tab), which a key event carries; a key event has nobutton, so it never reads as a middle click.cardKeyboardActivation-12883(6 tests): role nesting; (a) Enter; (b) Space on keyup with the keydown claimed; (c) a nested link and input open nothing, and other keys open nothing; (d) a click opens once. (e) objectui#12837's description pin is green. Ablation: removing the key handler turns (a) and (b) red; the restore was proven by blob hash.packages/plugin-kanbanin full (75 files, 506 tests) is green; type-check exits 0.dndInstructions-12837,dndAnnouncements-12716andObjectKanban.moveGrant-12082read the sortable node byrole="button"now; no assertion changed.2da6733): CI showedexamples/schema-catalog/test/kanban-column-cards-6939.test.tsxred. The census of the kanban examples counted one more element per card (64 → 68, 86 → 90), from the newlistitemwrapper. Visible text and its sha256 are byte-identical, so what that census proves holds. Following the file's own keep-both-readings discipline, the pre-12883 reading stays asPRE_REPAIR_PRE_12883, and a delta case pins exactlyDIV+ card count with every other tag unchanged; 17/17 green. The PR body's Gates section is amended by this seat.Test (shard 9/16)went red once onStudioDesignSurface.flowStatusTruth-11779(Studio, untouched by this PR; the same shard was green on0550c73with the same kanban code;maingreen). The stand-down comment is6234945336; the failed jobs were re-run once.role="button", ARIA makes children presentational, so a link a field renders inside the card is flattened for assistive tech. It was already nested inside a focusable element before. Alinkrole would avoid this but rules out Space, which the ruling keeps.aria-roledescription="sortable"still overrides the announced "button": wording only.ObjectKanban.tsx's comment ononCardClick("types the same channel as React.MouseEvent") is now stale.Fixes objectui#12883line naming this PR's own card; it does no harm and history is not rewritten.filter=latest, 36 runs) after the one re-run ofTest (shard 9/16), which passed. Not governed (path audit); 281 changed lines.Next:
pr_ready+automerge_enable.Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
Landed ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-17T14:41ZPR #12888 merged through the merge queue as
1d872f2, which is onmain;Fixes #12883closed this card. Verified by content: all 7 of the PR's files onmainare blob-identical to the accepted head2da6733. The merge commit's message names no model.What landed:
onCardClicka pointer click takes.buttoninside a namedlistitem. A key on an element inside the card opens nothing. There is still no keyboard drag (held for the maintainer, objectui#12837).divper card beside its pre-12883 reading.@object-ui/plugin-kanban: patch.objectui#12838 (the kanban
aria-labeltemplates, same file) is now free of this serial constraint. This seat now removespm:dispatched.Generated by Claude Code