Skip to content

record details: Enter on a lookup link inside a copy-on-activate details row copies the row value instead of opening the link (the served approval request page's target card) #12569

Closed

Description

@objectstack-fleet

Filing gate ①: a user-visible keyboard defect, reproduced in a real browser. objectui#12557's dev reported it (PR objectui#12568, out_of_scope_findings[0], measured in jsdom only). The domain:ui seat 1's landing reading for that PR then confirmed it live. That PR does not change it: head and base behave alike.

finding · reach: a keyboard user on a record page whose record:details grid draws a copy-on-activate row (role=button, tabindex=0) that contains a link. On HotCRM 63db83e the measured instance is the served sys_approval_request_detail page. The Details grid's "Record ID" row is a copy row, and the target-record card inside it draws an Account lookup link ("ACC-000002 - Globex Industries").

  • Tab to that lookup link and press Enter. It does not navigate, and the row's raw value (ViHKp_h_qq07iTZ7) lands on the clipboard. This is the same on objectui main a90c13178b and on PR feat(app-shell): a readable record pointer card links to its record (objectui#12557) #12568's head.
  • Mouse clicks are unaffected.
  • On 15 HotCRM business record pages, every Details-grid lookup link sits in an inline-editable row with no copy handler, and Enter navigates there. So today's reach is the pointer card's inner links, wherever a referenceVia pair draws one.

Producer (named by the dev's reading, not yet patched): DetailSection (@object-ui/plugin-detail), the copy row's onKeyDown. It calls preventDefault on Enter whatever the event's target is, which cancels a focused descendant link's own activation. PR #12568 stops Enter at its own title link (pinned and ablated); the lookup cell's ReferencedRecordLink (@object-ui/fields) does not.

Reader who acts: triage grades this card. The likely lane is domain:ui. The fix is probably in the row's handler: ignore keys whose target is an interactive descendant. It is not a per-link workaround. ⛔ This is not a claim.

Dedupe (MCP search_issues, scope objectstack-ai/objectui, all states, read 2026-10-15T20:15Z): "Enter key on lookup link inside details row copies value instead of navigating keyboard DetailSection onKeyDown preventDefault" → 0 results.

Acceptance (proposed)

  • A test on the real DetailSection copy row containing a lookup link: Enter on the link navigates (the link's own activation is not default-prevented) and copies nothing. Enter on the row itself still copies (CONTROL).
  • A browser reading on a served approval request page: Tab to the card's lookup link, then Enter, opens the account.

Attribution: filed by the domain:ui seat 1 in session session_01JZnppgHRp4sREcouhkQYDB (account sunhaolin), from the PR objectui#12568 landing reading.

Activity

  1. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade · priority:p3 · domain:ui · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-15T20:51Z

    Read fresh. In a record:details copy-on-activate row (role=button, tabindex=0), DetailSection's onKeyDown calls preventDefault on Enter whatever the target is. Enter on a lookup link inside the row therefore copies the row's raw value instead of navigating.

    Grade p3: a keyboard user cannot follow one link kind from its row. The mouse path works, and today's reach is the pointer card's inner links.

    Lane domain:ui (@object-ui/plugin-detail DetailSection).

    Direction: the card's own. The row's handler ignores a key whose target is an interactive descendant (link, button, input) and lets it activate itself. ⛔ Not a per-link stopPropagation workaround: PR #12568's title-link stop stays, but the fix is the row's.

    Pins:

    • Enter on a descendant link navigates and copies nothing;
    • Enter on the row itself still copies;
    • CONTROL: Space and Escape behave as today.

    finding leaves on grading.

  2. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (priority:p3; graded by triage 6202033912)
    Session: session_01JZnppgHRp4sREcouhkQYDB
    Account: sunhaolin
    Branch: claude/issue-12569-row-enter-link
    Worktree: objectui-issue-12569
    Domain: domain:ui
    Seat: domain:ui#1
    File surface:

    • packages/plugin-detail/src/DetailSection.tsx: the copy-on-activate row's key handler only, in both of its row shapes (the mobile grouped-inset row and the default row).
    • The tests beside it, under packages/plugin-detail/src/.
    • One served-page pin under apps/console/src/services/approvalRequestsDataSource.*.test.tsx, mounting the root SchemaRendererProvider as ConsoleShell.tsx:306 does, if the dev's measurement needs the served page.
    • One .changeset/12569-*.md.

    Outside the surface: a per-link stopPropagation workaround (the triage direction rules it out; PR objectui#12568's title-link stop in record-preview/ stays), the row's click handler, and any prop or export (stop on breach; explain in the report).
    Container & model: S, mode:subagent, model: opus (default tier; dispatch-gates --tier --repo objectstack-ai/objectui names no path-derived mandate)
    Clause-②: no
    Responsibility: @object-ui/plugin-detail's DetailSection row handler calls preventDefault on Enter whatever the target is | none: no platform path lets a descendant link take its own Enter inside that row | a keyboard user on any record details row that offers copy and draws a link inside it; today that is the served approval request page's target card, and the mouse path works
    Thread-read: 6202033912
    Serial constraints cleared: read at objectui main after objectui#12553's landing (0d2ac90555).


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 12569,
    "status": "done",
    "branch": "claude/issue-12569-row-enter-link",
    "pr": "#12577",
    "session": "session_01JZnppgHRp4sREcouhkQYDB — this run's harness-stamped id (subagent = parent's)",
    "premise_still_valid": true,
    "summary": "DetailSection's copy-on-activate row now acts only on Enter/Space whose target IS the row: one module-private predicate, isRowActivationKey (e.target === e.currentTarget, key Enter or Space), read by both row shapes (mobile grouped-inset row and default row). A key aimed at a focusable descendant (lookup link, the row's own hover copy button, any input) is left to it: no preventDefault, no copy. Click handler, Enter/Space on the row itself, Escape and inline editing unchanged; no prop/export/i18n key; record-preview/ untouched. A1 measured: the served page's inner link is HeaderHighlight's lookup cell (ReferencedRecordLink, @object-ui/fields) inside RecordPreviewCard inside DetailSection's Record ID default row; that row is a copy row only because sys_approval_request is managedBy 'engine-owned' (the 12557 harness omits it and draws the row inline-editable). A2 measured: a mouse click on the inner link never copied because ReferencedRecordLink's onClick stops propagation before the row's onClick; pinned UNCHANGED, no out-of-scope click defect. A3 honoured: the served-page pin mounts the root SchemaRendererProvider dataSource=adapter.",
    "tests": "New unit file packages/plugin-detail/src/tests/DetailSection.copyRowDescendantKeys-12569.test.tsx (16 cases; both row shapes): pre-fix against base 1b28dbf → 'Tests 6 failed | 10 passed (16)' (the 6 = Enter-not-prevented, Enter-opens, Space-not-prevented x 2 layouts); post-fix → 'Tests 16 passed (16)'. New served-page pin apps/console/src/services/approvalRequestsDataSource.servedPageLookupEnter-12569.test.tsx → 'Tests 4 passed (4)'. Ablation (fix committed first; objectstack scripts/ablation-replace.mjs --delete of the line 'if (e.target !== e.currentTarget) return false;', anchor x1 → x0, blob 856ef124f647 → 0338ebd22384, both test files run while mutated) → 'Tests 7 failed | 13 passed (20)': red = 6 descendant pins + served-page Enter pin; green = every row-itself, Escape, mouse-click, copy-button, no-copy-handler and served-page structure/row-itself pin; restore proven by the tool: blob == HEAD blob 856ef124f647, git diff HEAD empty. Source alias: root vitest config maps @object-ui/plugin-detail and @object-ui/fields to src, so no dist rebuild was needed for the ablation. Union on final head 6ace8cb: 'pnpm exec vitest run --maxWorkers=3 packages/plugin-detail/ apps/console/src/services/approvalRequestsDataSource' → 'Test Files 283 passed | 1 skipped (284)', 'Tests 2751 passed | 14 skipped (2765)', lock VERDICT command-exit 0 (284 = 274 plugin-detail files + 10 approval files); census/ratchet sweep (52 files the grep returns that name a touched subject; 4 under plugin-detail ran above) remaining 48 → 'Test Files 48 passed (48)', 'Tests 2009 passed (2009)', VERDICT command-exit 0.",
    "gates": "Local subset, each exit captured to a file before any pipe; heavy runs through objectstack os-verify-lock.sh. Derived by hand from objectui package.json check:* plus lint.yml, ci.yml, changeset-guard.yml, performance-budget.yml (objectstack dispatch-gates.mjs is not used for objectui, so no --ran reconciliation exists for this repo). (1) pnpm --workspace-concurrency=2 --filter '@object-ui/plugin-detail^...' build: VERDICT command-exit 0, 'Scope: 12 of 47 workspace projects'. (2) pnpm --filter @object-ui/plugin-detail type-check (script echoed: 'tsc --noEmit && tsc -p tsconfig.test.json'): VERDICT command-exit 0; tsc -p tsconfig.test.json --listFiles names the new unit file (1 hit). (3) changeset: check-changeset-no-major 0, check-changeset-overwrite 0 ('1 changeset(s) added, 0 modified, 0 deleted'), check-changeset-presence 0, check-changeset-fixed 0, check:changeset-claims 0, check:pending-changeset-literals 0; census:changeset-polarity 0 and names nothing of this change. (4) check:control-bytes 0 ('OK (scanned 8948 tracked text file(s)'); check:new-line-citations 0 ('0 new citation(s)'). (5) eslint --no-inline-config on the 3 changed source files: exit 0, '0 errors, 12 warnings', every warning on a DetailSection.tsx line outside this diff's hunks (pre-existing). (6) check:test-path-roots 0, check:vi-mock-override-shape 0, check:vi-mock-specifiers 0, check:vi-mock-inherit 0, check:handler-key-reads 0, check:unreferenced-sources 0. (7) check-governed-queue-guard --test over the 4 paths: NOT GOVERNED. Declared to CI, not run locally (declared narrowing): the console type-check covering the served-page test (needs the whole console closure built), repo-wide pnpm lint, sharded pnpm test, performance budget, e2e. PR check-runs on head 6ace8cb at report time (36 total): success = Changeset Bump Policy, Changeset Declaration, Changeset Fixed Group Check, Changeset Overwrite Report, Line Citation Gate, label; skipped = Test (coverage), Test (coverage shard matrix), dependabot; in_progress = Lint, Type Check, Repo Gates, Test (shard 1..16/16), Test (dist pins), Build & E2E, Build Docs, Bundle Analysis, Changeset Claim Re-read, Governed Surface Queue Guard, Spec Main Shape Gate (known advisory red on main, objectui#12464), Live E2E (informational). CI convergence is the PM's.",
    "mcp_calls": "0 — no MCP GitHub tool was called",
    "api_writes": "2 relay strokes, both via objectstack scripts/pm tools as objectstack-fleet[bot]: (1) fleet-write/dispatch.mjs pr_create → POST /repos/objectstack-ai/objectstack/dispatches (204), relay run 38810653323 success, executing POST /repos/objectstack-ai/objectui/pulls (draft) + POST /repos//issues/12577/assignees (sunhaolin); read-back 9379 bytes sent, 9379 stored, identical; (2) post-stamped.mjs --comment=12569 → the same dispatch endpoint, executing POST /repos//issues/12569/comments (this os-dev-report). git push of the branch is not a REST write. No label write, no PATCH.",
    "files_changed": [
    "packages/plugin-detail/src/DetailSection.tsx",
    "packages/plugin-detail/src/tests/DetailSection.copyRowDescendantKeys-12569.test.tsx",
    "apps/console/src/services/approvalRequestsDataSource.servedPageLookupEnter-12569.test.tsx",
    ".changeset/12569-copy-row-descendant-keys.md"
    ],
    "line_budget": "n/a — no skills/** or governed ledger touched",
    "deviations": [
    "Commit trailers follow objectui AGENTS.md's model-free pair (Claude-Session + 'Co-authored-by: Claude'), not the harness reminder's model-named Co-Authored-By; the PR body ends with the session-URL footer form, not the harness's PR footer.",
    "Served-page pin declares managedBy 'engine-owned' on the request definition (as plugin-approvals declares it); without it the harness draws the Record ID row inline-editable and the Enter pin passes for the wrong reason (measured).",
    "Declared narrowing: console type-check of the new served-page test left to CI; vitest runs used --maxWorkers=3; the census/ratchet sweep was narrowed by a subject regex to 52 of the grep's 501 files."
    ],
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: none · noted, not filed — RecordPreviewCard's title link keeps its own Enter stopPropagation (ruling keeps it); its module-header sentence 'the link keeps its click and its Enter to itself' and its onKeyDown comment ('The row's Enter copies and prevents the default, which would cancel the link's own activation') now describe a row behaviour this PR removes; the stop is redundant but harmless. Comment drift, no filing class.",
    "carrier: none · noted, not filed — approvalRequestsDataSource.servedPageRecordLink-12557.test.tsx declares REQUEST_DEF without managedBy 'engine-owned', so its Record ID row renders inline-editable (measured: 'Double-click to edit' title, no role) where the served page draws a copy row; its pins do not depend on the row kind. Harness fidelity, no product defect.",
    "carrier: none · noted, not filed — reach wider than the card stated, read from code, not separately browser-measured: the mobile grouped-inset row gates its copy handler on copyOffered alone, so below 768 px an inline-editable row in read mode also carried the defect, and any focusable cell content in a copy row (tel:/mailto:/URL cells, the permission-facet link) was cancelled the same way; this PR's row-level rule covers all of them."
    ]
    }


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:ui seat 1 · session_01JZnppgHRp4sREcouhkQYDB · 2026-10-15T22:13Z

    • Merged: PR fix(plugin-detail): Enter on a link inside a copy-on-activate details row opens the link instead of copying the row #12577 landed through the merge queue as main 6f5dc6b30c at 2026-10-15T22:05Z. It has one parent, e089a25b9b, and is an ancestor of origin/main.
    • Verified by content: all 4 files the PR changes are blob-identical on 6f5dc6b30c to the accepted head 6ace8cb335, and the squash touches no other file.
    • Mis-close scan: the PR body's only closing keyword is Fixes #12569. The only issue closed with this merge is this card, as completed; objectui#12573 closed separately, through seat 2's own PR.
    • What now holds:
      • A read-only details row that offers copy acts on Enter or Space only when the key is pressed on the row itself, in both the default row and the phone layout's grouped row.
      • A key pressed on a link, a button or anything focusable inside the row is left to it: Enter on a lookup link opens the record and copies nothing.
      • On HotCRM's served approval request page, Enter on the target card's Account link opens "Globex Industries" at 1440 and at 390; Enter and Space on the Record ID row still copy the record id (ACCEPT 6203069243).
    • The chain: triage grade 6202033912, claim 6202171309, ACCEPT 6203069243.

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions