console record drawer v2: the bar is ↑ ↓ · ⤢ · ✕ (no object name, no counter); a related row or a lookup opens its record in place with a trail, ← / Esc go back one level; ⤢ carries the trail into the page; on the record page a related row opens a drawer over the page #26679
Description
Part of the record-page-v2 epic #26676 (child 3 of 4). Runs after #26677 (shared files).
Source (origin/main 416a76030e)
- The drawer's bar:
ui/packages/components/src/custom/navigation-overlay.tsx:601-603draws the object label as theSheetTitle,:614-622theMaximize2expand icon (already icon-only, gated ononExpand,aria-labelandtitlefromexpandLabel),:625-633close insideSheetClose; there is no previous / next and no counter anywhere in the file. - A related row inside the drawer leaves it:
ui/packages/app-shell/src/views/RelatedRecordActionsBridge.tsx:553onView: (id) => openRecord(objectName, id)and:390-395openRecord=navigate(path); the file has noembeddedbranch at all (docblock:18「查看详情 → navigate to the child record's detail route」). Whether a lookup value on the detail navigates the same way is not measured here — measure it first. ui/packages/app-shell/src/views/objectViewRecordNavigation.tsholds one open record (RECORD_DRAWER_PARAMat:87), no stack or trail;objectViewRecordLinks.tsbeside it builds the record URLs.ObjectView.tsxis being carved into sibling hooks (slices 5+6 on 2026-10-26, 11a/11b on 2026-10-27): work on the merged code, not on the card's line numbers.
Target
- Bar: left side empty at the root level; right side
↑↓(previous / next through the host list's current rows; disabled at the ends; keyboard ↑ / ↓ and j / k when no input has focus),⤢(icon only, tooltip 「展开为整页」),✕. No object name, no "n / N" (rulings 2 and 3). - In-place descent: a related row, a lookup value, or any record link inside the drawer opens that record in the same drawer and pushes the previous one on a stack; the bar's left side shows
←and the trail 「线索 Mira Costa › 任务」 (each ancestor a link that pops to it);←or Esc pops one level; Esc at the root closes;✕closes the whole stack.↑↓at a deeper level move through that level's sibling set (the parent's related rows of that object), never across levels. Never a second drawer (ruling 5). ⤢at any level navigates to that record's page with the trail carried in router state, so the page breadcrumb reads 「线索 / 全部线索 / Mira Costa / 任务 / 跟进报价」 and 「收为抽屉」 restores the stack.- On the record page (not embedded): a related row or lookup opens its record in a drawer over the page under the same rules; an object declaring
pagenavigates instead (spec(+ui):navigation.modekeepspage | drawer | new_window | none—modal,splitandpopoverare retired; the default becomesdrawerin the schema and in the console (no field-count guess); list-pagerecordActiondropsmodal; the console's split pane becomes a per-user preference read #26677's resolution). - URL: the root record stays
?recordId=; the stack is in-memory (router state), so a reload shows the root record — document this in the component docblock.
File surface
ui/packages/components/src/custom/navigation-overlay.tsx (bar slots: leading trail, prev / next), ui/packages/app-shell/src/views/objectViewRecordNavigation.ts (stack, sibling sets, expand-with-trail), objectViewRecordLinks.ts, objectViewRecordOverlay.tsx, RelatedRecordActionsBridge.tsx (查看详情 when embedded → push), RecordDetailView.tsx (lookup links when embedded, the page-over-drawer host), ui/packages/plugin-detail/src/RelatedList.tsx only if its row click needs a new callback; tests beside; changesets for @object-ui/components and @object-ui/app-shell.
Acceptance
- Tests: open a lead from the list → bar has no title text and no counter; ↓ opens the next row's record; click a related task → trail shows the lead, ← returns to the lead with its tab state; Esc at root closes; ⤢ from the task level lands on the task page with the lead in the breadcrumb.
- Screenshot of the drawer at the root level and one level down (HotCRM lead → task), and of the page opened by ⤢ from the task level.
- Keyboard: ↑ / ↓ / Esc behave as above and never fire while an input has focus.
Rulings (maintainer, 2026-10-27, chat in the filing session — verbatim, not re-decidable here)
- The model, quoted back by the maintainer and ordered built: 「1. 一个对象一个默认打开方式:默认抽屉(带展开整页),
page只给显式声明的对象,modal退役,split / popover 变成用户个人偏好;抽屉和整页是同一张页在两个宽度。」 - On the drawer's top bar: 「左上角的 任务 · 2 / 2 · 分屏 几个字有必要嘛」 — no object name, no mode label on the bar.
- On the position counter: 「我觉得记录详情页显示第几条记录 好像也没必要」 — no "n / N" anywhere on a record; the ↑ ↓ previous / next controls stay.
- On the right pane: 「右侧应该可以拖动吗?展开为整页那个是不是用图标就可以」 — the pane is drag-resizable and "open as full page" is an icon-only control with a tooltip.
- On related records, the maintainer confirmed the in-place model: 「所以关联记录是一层一层的往下点,然后可以一层一层的反悔吗」 — one level down per click, one level back per ← / Esc, never a second drawer.
Filed by the epic PM session session_cdb7a717-def7-4e71-9988-f089fbaa18ff on 2026-10-27, from the record-page / list-page assessment and the clickable prototype the maintainer walked and ruled on in that session (objectstack main 416a76030e, HotCRM 1f3f8880 on platform 18.2.0). The maintainer's instruction to build it, verbatim: 「你直接立卡派发处理这个开发,然后审核。」 Design evidence (never merged): branch claude/record-page-v2-evidence-20261027b at commit 345ec777220843ce1fd7f95b6cc0b2b6dcc81bf8, folder docs/design/record-page-v2-2026-10-27/ (prototype.html, artboards/, assessment.md).
Activity
objectstack-fleet commentedon Oct 27, 2026
Premise reading before dispatch · epic PM
session_cdb7a717-def7-4e71-9988-f089fbaa18ff· origin/main7bb8084590· 2026-10-27T23:57ZTarget unchanged; the Source section's line numbers predate #26804 (merged 23:43Z) and #26758. Readings on the new main (read-only pass, this seat verified the shape):
ui/packages/components/src/custom/navigation-overlay.tsx(587 lines): modes arepage | drawer | split | new_window | none; faces drawn are the drawer (Sheet,:431) and the split pane (:530); the drawer header isSheetHeader:484, title:486-488(resolvedTitle = title || t('detail.recordDetail')), expandMaximize2icon-only:498-507, close:509-518; no previous / next, no counter. The split face draws its ownh3title and close and no expand button.ui/packages/app-shell/src/views/objectViewRecordNavigation.ts(215 lines): returns{ listRecordActions, navOverlay, handleDrawerClose, handleRowClick, handleExpandDrawer };RECORD_DRAWER_PARAM(app-shell/src/urlParams.ts:86,'recordId') read at:107; one open record, no stack;handleExpandDrawercloses the drawer and navigates torecord/:idwith router state{ from: { pathname, label } }.objectViewRecordOverlay.tsxpassestitle={objectLabel(objectDef)}— that is the object name the bar shows today (ruling 2 removes it).RelatedRecordActionsBridge.tsx:553onView: (id) => openRecord(objectName, id);:390-394openRecord=recordPath+navigate(path); noembeddedbranch; mounted atRecordDetailView.tsx:3980for both embedded and page use. A lookup value measured now:ReferencedRecordLink(fields/src/index.tsx~2520-2565) →host.openRecord→ the samenavigate— identical to the related-row path, so one handler covers both.recordPathappends?from=(RECORD_TRAIL_PARAM,appendRecordTrail) when a parent is known. The in-drawer trail of this card composes with it rather than adding a second trail carrier.RecordDetailView.tsxhas noNavigationOverlay/RECORD_DRAWER_PARAMtoday: the page-over-drawer host (Target 4) is new wiring.ObjectView.tsxis carved into sibling hooks almost hourly andRecordDetailView.tsxtook four commits on 2026-10-27; feat(spec)!: the action blocks spell the destructive variantdanger, the word ActionSchema.variant uses #26966 (draft) edits RecordDetailView's action-variant region.Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Claim: PM loop round 2 (epic #26676, child 3 of 4)
Session:
session_cdb7a717-def7-4e71-9988-f089fbaa18ffAccount:
zhuangjianguoBranch:
claude/issue-26679-drawer-in-place-navigationWorktree:
objectstack-issue-26679Domain:
domain:uiSeat: epic #26676 (the epic PM session, not a domain seat)
File surface:
ui/packages/components/src/custom/navigation-overlay.tsx(bar slots: leading trail / back, previous / next),ui/packages/app-shell/src/views/objectViewRecordNavigation.ts(stack, sibling sets, expand-with-trail),objectViewRecordLinks.ts,objectViewRecordOverlay.tsx,RelatedRecordActionsBridge.tsx(查看详情 when embedded → push in place),RecordDetailView.tsxonly the embedded lookup-link handler and the page-over-drawer host region,ui/packages/plugin-detail/src/RelatedList.tsxonly if its row click needs a new callback, i18n keys, tests beside, changesets for@object-ui/componentsand@object-ui/app-shell; ⛔packages/spec/**, ⛔ the Display menu (#26681) (stop on breach; explain in the report)Container & model:
M,mode:subagent(desktop session on the maintainer's macOS; verify lock in declared-unlocked mode; one of two parallel subagents, the other on plugin-list / plugin-grid),model: TIER_DEFAULT(dispatch-gates --tier 2026-10-27T23:59Z: no path-derived mandate),effort: highClause-②: no
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: none
Premise: corrected 6456653001 (line numbers and landing path re-read on origin/main 7bb8084; target unchanged)
Serial constraints cleared: #26804 (this epic) MERGED at 23:43Z — the surface is rebuilt on main
7bb8084590, line numbers in the card are from the old main and are re-read in the dispatch order; #26966 (draft, action variantdanger) editsRecordDetailView.tsxin the action-rendering region, disjoint from the lookup-link / host region this card touches — merge main before opening the PR; #26841 edits a RecordDetailView test only; #26681 (this epic, in parallel) edits plugin-listViewSettingsPopover.tsx, plugin-gridObjectGrid.tsxand the preference write side in app-shellutils/, disjoint (scan 2026-10-27T23:59Z)Dispatched at 2026-10-27T23:59Z as an
os-devsubagent of this session; the dispatch order lives in the subagent's opening prompt.Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
os-dev-report
{
"issue": 26679,
"status": "rework",
"branch": "claude/issue-26679-drawer-in-place-navigation",
"pr": "#27038",
"session": "session_cdb7a717-def7-4e71-9988-f089fbaa18ff — the parent PM session's id (mode:subagent)",
"premise_still_valid": true,
"summary": "Drawer bar: left side empty at the root (title sr-only on every NavigationOverlay host), right side ↑ ↓ / ⤢ / ✕ through three new optional props (onBack, trail, stepper); Esc pops when onBack is set; ↑/↓/k/j skip text fields, composite widgets and modifiers. New app-shell recordDrawerStack.tsx: an in-memory stack over a host-controlled root, ← plus a clickable path, per-level ?tab= save and restore, ↑ ↓ through the level's sibling set (the related list's rows on show, threaded RelatedList onRowClick third arg → onView third arg). RelatedRecordActionsBridge.openRecord (related rows and lookups both use it) opens in the nearest drawer host when the target object opens in the drawer; objects declaring page/new_window/none still navigate. ObjectView's overlay and the RecordDetailView page (new RecordPageDrawerHost) are hosts. ⤢ at any level opens the record page with the levels below as its ?from= trail, and the page's Collapse to drawer link restores the stack, consumed once from history state. Status is rework because one target half is missing: ↑ ↓ at the root of an object list's drawer needs ListView (plugin-list, fenced by this dispatch) to publish its rows, so the PR says Part of #26679. Two premise corrections: (1) the top bar draws no record crumb on main (AppHeader, object routes draw no crumb after the pill), so the path goes through the existing ?from= trail and the page back link, not a breadcrumb; (2) the zh ⤢ tooltip reads 以完整页面打开, not 展开为整页, and I left it unchanged because the dispatch marked the tooltip as done.",
"tests": "Head ec88340 (merged with origin/main 5e99bd9). app-shell^... build plus app-shell type-check (tsc --noEmit and tsconfig.test.json): VERDICT command-exit 0. vitest packages/app-shell/: 1801 files passed, 1 skipped, 16511 tests passed, VERDICT command-exit 0. vitest components+plugin-detail+i18n+react (before the merge): 977 files passed, 1 failed (navigationOverlayConsumers.onRowClickArity-9357 pinned RelatedList onRowClick at exactly 2 params). The pin now takes a per-site arity of 3, with every param after the first required to be optional; rerun 26/26 passed. New tests: navigation-overlay-bar-26679 (9), recordDrawerStack-26679 (12), RelatedList.rowClickSiblings-26679 (2), and a sibling-ids case in RecordRelatedListRenderer.hostactions; all pass. No ablation: the behaviour is new, and each case has a control leg (no onBack → Esc closes; no stepper → no pair; page-mode object → navigates; no rows → two-arg onView). Screenshots NOT MEASURED: no HotCRM or console stack was booted. Every run used os-verify-lock DECLARED UNLOCKED MODE (no flock on macOS), disclosed in the PR body.",
"gates": "dispatch-gates --commands --repo objectstack-ai/objectstack on ec88340: 34 families, each exit captured to file before any pipe. --ran: 34 derived, 34 run, 0 UNRUN (exit codes are in the runner file, not passed to --ran). 33 exit 0. pnpm --dir ui exec vitest run scripts/ exited 1 under load average about 26. The 11 failing files rerun alone left 2. select-affected-tests-wiring exits 127 because macOS bash 3.2 has no mapfile (a host fact). One check-i18n-dead-keys case timed out at 15s. Both are NOT MEASURED, family ui-scripts, reason: host shell and load; CI Test UI and Lint & Repo Gates read them. pnpm check:new-line-citations: 0 new. PR check-runs: in_progress (just opened, not awaited per os-dev.md).",
"mcp_calls": "0",
"api_writes": "3 — POST /repos/objectstack-ai/objectstack/pulls (draft, via with-fleet.sh direct: dispatch named the relay's session-id refusal), POST /repos//issues/27038/assignees, POST /repos//issues/26679/comments (this report, post-stamped); plus git push (not REST). Labels: zero writes; the dispatch named none, and skip-changeset does not apply because the diff publishes",
"open_questions": [
{
"question": "Root ↑ ↓ on the object list's drawer needs the list's displayed rows, and ListView (plugin-list) publishes none (only onTotalChange). How should it land?",
"options": [
"A: follow-up card in objectstack, Blocked-by #26681: ListView gains an onRowsChange host prop (sibling of onTotalChange), and ObjectView passes root.siblings and onRootStep to useRecordDrawerStack (the page host already does both). About 40 lines plus tests.",
"B: ObjectView re-queries the list itself to build the set. A second query with the list's filter/sort/search re-derived, which drifts from what is shown; not recommended.",
"C: no root ↑ ↓; ruling 3 kept the controls, so this would need a maintainer re-ruling."
],
"recommendation": "A, because the producer (ListView) owns the rows it shows, the drawer side is already built and tested, and it only waits for #26681's plugin-list fence to lift."
}
],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted, not filed — InterfaceListPage, ObjectDataPage and ObjectView's split pane embed RecordDetailView but are not drawer hosts, so a related row inside them still navigates; adoption is the same RecordStackDrawer swap (in Acceptance notes)",
"carrier: 承接者:无 · noted, not filed — ui/content/docs/guide/user-state-persistence.md 'Authoring note' still lists modal/split/popover as navigation.mode values retired by #26804 (doc drift, Acceptance notes only)"
],
"line_budget": "n/a — no skills/** or governed ledger touched",
"deviations": "(1) Files outside the dispatch's surface list: ObjectView.tsx (3 lines passing two new overlay props), plugin-detail renderers/record-related-list.tsx and react RelatedRecordActionsContext.tsx (optional sibling-ids arg on the onView seam), and react navigationOverlayConsumers.onRowClickArity-9357 pin (per-site arity 3 for RelatedList). (2) RecordDetailView touched in a third region beyond the two named: the origin back link is now hidden when embedded and sits next to the Collapse to drawer link. (3) The drawer decision reads a context (RecordDrawerContext) instead of the embedded flag, so the record page can be a host too. (4) No PM progress reply mid-run: the probe arrived during verification, and this report is that reply. (5) Worktree cleanup (node_modules, worktree remove) is done after this report.",
"files_changed": [
"/.changeset/26679-drawer-in-place-navigation.md",
"ui/content/docs/guide/user-state-persistence.md",
"ui/packages/app-shell/src/views/{ObjectView.tsx,RecordDetailView.tsx,RelatedRecordActionsBridge.tsx,objectViewRecordNavigation.ts,objectViewRecordOverlay.tsx,recordDrawerContext.ts (new),recordDrawerStack.tsx (new),tests/recordDrawerStack-26679.test.tsx (new)}",
"ui/packages/components/src/custom/navigation-overlay.tsx",
"ui/packages/components/src/tests/navigation-overlay-bar-26679.test.tsx (new)",
"ui/packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts (detail.collapseToDrawer)",
"ui/packages/plugin-detail/src/{RelatedList.tsx,renderers/record-related-list.tsx,tests/RelatedList.rowClickSiblings-26679.test.tsx (new),tests/RecordRelatedListRenderer.hostactions.test.tsx}",
"ui/packages/react/src/context/RelatedRecordActionsContext.tsx",
"ui/packages/react/src/hooks/tests/navigationOverlayConsumers.onRowClickArity-9357.test.ts"
],
"dedupe_words": [
"drawer in-place stack",
"ListView onRowsChange",
"record drawer previous next root",
"collapse to drawer"
]
}
objectstack-fleet commentedon Oct 28, 2026
os-dev-report
{
"round": "patch round 1",
"issue": 26679,
"status": "rework",
"branch": "claude/issue-26679-drawer-in-place-navigation",
"pr": "#27038",
"head": "2baea3a5c7",
"gates": "Head 211c988 (code-final; 2baea3a adds only changeset prose). dispatch-gates --commands --repo objectstack-ai/objectstack derived 34 families, each exit captured to file before any pipe. 33 exit 0.
pnpm --dir ui exec vitest run scripts/exit 1: 3 files. select-affected-tests-wiring exits 127,mapfile: command not found(macOS bash 3.2, a host fact). check-i18n-dead-keys and check-hand-rolled-comment-mask each hit a 15s timeout under load; the latter passed in an isolated rerun earlier this session. Those 3 are NOT MEASURED locally (host shell and load); CI Test UI reads them. On final head 2baea3a the changeset families were rerun: adr-0087-registration, changeset-no-major, empty-changeset, changeset-gate-self-tests, pm-changeset-deadline-census and nul-bytes all exit 0. Tests: console approvalRequestsDataSource.servedPageLookupEnter-12569 plus servedPageRecordLink-12557, packages/components/src/renderers/complex/ (whole dir), recordDrawerStack-26679: 36 files and 254 tests passed, VERDICT command-exit 0. zh-touching tests (navigation-overlay-chrome-i18n, objectView-value-language-3582, viewReadonlyTooltip-semantics-3625, console-namespace-3546): 6 files and 112 tests passed. Reverse verification of the new data-table-rowClickInDialog-26679 test: with the fix committed, restoring the oldclosest dialogline (injected=1, removed_left=0 by grep -c) makes 'a table inside a dialog opens the clicked row' fail (1 failed, 2 passed), and the two control cases stay green. Restored bygit checkout HEAD; git diff HEAD is 0 lines. All runs used os-verify-lock in DECLARED UNLOCKED MODE (no flock on macOS).","line_budget": "n/a — no skills/** or governed ledger touched",
"deviations": "(1) New on-the-spot fix outside the named surface:
ui/packages/components/src/renderers/complex/data-table.tsx. Its row-click heuristic dropped every click whose target had any dialog ancestor. Because the record drawer is a dialog, the HotCRM screenshot run found that related-list rows inside the drawer were pointer rows that opened nothing, so this card's in-place descent never fired in the real app; the jsdom tests do not render data-table and did not catch it. Now only a dialog that does not contain the row (a portalled overlay) stops the click. Pinned by new data-table-rowClickInDialog-26679.test.tsx, reverse-verified, and named in the changeset. (2) zh: the dispatch asked to change onlyconsole.objectView.expandToPage. objectView-value-language-3582 pins it byte-identical todetail.openAsFullPagein every pack, so both zh keys now read 整页打开, and navigation-overlay-chrome-i18n's zh expectation moves with them.console.ai.dock.maximize(a different control) stays 以完整页面打开. (3) The two console pins were flipped, not deleted. Each now asserts that the router stays on the request page, a dialog opens containing the target (ACCOUNT_TITLE / TARGET_TITLE), host.findOne was called for the target, the href still resolves to the record path, and Enter copies nothing. A new case pins that a meta-click is not intercepted (no preventDefault, no dialog). The illegal-shape controls (refused / unresolvable / absent) are unchanged. The approval request page stays a drawer host: its target objects declare no navigation.mode, so ruling 5 applies. (4) Screenshots: measured on HotCRM 1f3f8880 (platform 18.2.0 backend,objectstack dev --fresh) with this branch's console from vite source on port 5197. Three local test tasks were added to lead Mira Costa (fresh temp DB, deleted on exit). HotCRM's servedcrm_lead.all_leadsview declares navigation.mode page, so the drawer was opened from the leads kanban view (crm_lead.kanban_by_status, mode drawer). Evidence branchclaude/issue-26679-evidence-20261028at 8ef7f64, never to merge; committed with --no-verify (screenshots only). Servers stopped by recorded PID; the throwaway evidence worktree was removed. (5) The PR body is not PATCHed (os-dev.md); seat edits are below in pr_body_edits.","pr_body_edits": [
"Replace the 'Screenshots ... NOT MEASURED' bullet with: Screenshots (HotCRM lead Mira Costa → task, this branch's console): drawer root https://github.com/objectstack-ai/objectstack/blob/8ef7f64917413235c23cf1030260423f76b2d382/docs/evidence/issue-26679/1-drawer-root-lead.png · one level down https://github.com/objectstack-ai/objectstack/blob/8ef7f64917413235c23cf1030260423f76b2d382/docs/evidence/issue-26679/2-drawer-one-level-down-task.png · after ⤢ https://github.com/objectstack-ai/objectstack/blob/8ef7f64917413235c23cf1030260423f76b2d382/docs/evidence/issue-26679/3-expanded-task-page.png (evidence branch, never merged).",
"Add under What changes: data-table inside a dialog. A table drawn inside a dialog opens its rows. The click heuristic stopped every click with a dialog ancestor, so related-list rows in the record drawer opened nothing. Now only a dialog portalled out of the row stops the click (data-table-rowClickInDialog-26679.test.tsx).",
"Add under What changes: the served approval-request page is a drawer host like any record page. Its two pins (servedPageLookupEnter-12569, servedPageRecordLink-12557) now assert that the drawer opens over the page with the target record in it, the router does not move, the href stays the record path, and a modified click is the browser's.",
"Replace the zh Acceptance-notes bullet with: zh
⤢label reads 整页打开 (detail.openAsFullPage and console.objectView.expandToPage, kept byte-identical by objectView-value-language-3582).","Tests section: append the patch-round runs (see gates)."
],
"files_changed": [
".changeset/26679-drawer-in-place-navigation.md",
"ui/apps/console/src/services/approvalRequestsDataSource.servedPageLookupEnter-12569.test.tsx",
"ui/apps/console/src/services/approvalRequestsDataSource.servedPageRecordLink-12557.test.tsx",
"ui/packages/components/src/renderers/complex/data-table.tsx",
"ui/packages/components/src/renderers/complex/tests/data-table-rowClickInDialog-26679.test.tsx (new)",
"ui/packages/components/src/tests/navigation-overlay-chrome-i18n.test.tsx",
"ui/packages/i18n/src/locales/zh.ts"
],
"api_writes": "1 — POST /repos//issues/26679/comments (this report, post-stamped); git push x5 (branch) + 1 (evidence branch), not REST",
"mcp_calls": "0",
"not_done_this_round": "Root ↑ ↓ (waits for #27031, per the dispatch)"
}
objectstack-fleet commentedon Oct 28, 2026
os-dev-report
{
"round": "patch round 2",
"issue": 26679,
"status": "done",
"branch": "claude/issue-26679-drawer-in-place-navigation",
"pr": "#27038",
"head": "811a10fa14",
"gates": "Final head 811a10f, after merging origin/main da8e6e7 (which includes #27031, 7414fc8). Libraries built first:
pnpm --filter '@object-ui/console^...' build, VERDICT command-exit 0. Type-check (tsc --noEmit plus tsconfig.test.json): plugin-list then app-shell, VERDICT command-exit 0. dispatch-gates --commands --repo objectstack-ai/objectstack on 811a10f derived 34 families, each exit captured to file first.--ranreconciliation: 34 derived, 34 run, 0 UNRUN. 33 exit 0.pnpm --dir ui exec vitest run scripts/exit 1 with 6 files: 5 'Test timed out in 15000ms' plus select-affected-tests-wiring 'mapfile: command not found' (macOS bash 3.2, a host fact). Of the timeouts, vite-dts-fail-on-type-errors, js-comment-mask-returns-interpolation-9424, check-hand-rolled-comment-mask and check-ui-root-lookups rerun alone pass: 4 files, 44 tests. select-affected-tests-wiring and the check-i18n-dead-keys real-tree timeout stay NOT MEASURED locally (host shell, load); CI Test UI reads them. check:new-line-citations: 0 new. Suites on 811a10f: components + plugin-detail + i18n + react + plugin-list + the two console approval pins: 1148 files passed, 2 skipped, 11809 tests, VERDICT command-exit 0. app-shell full (ObjectView tests included): 1817 passed, 1 failed (ListView.gridRowColorSelect-12393, a loading-state assertion under load); that file rerun alone passes 2/2. New pins: ListView.onRowsChange-26679 (2) and recordDrawerStack.rootStepper-26679 (4), plus recordDrawerStack-26679 (12): 3 files, 18 tests pass. Reverse verification, each with its fix committed and a grep -c count proving the mutation landed. (a) ListView: the emit line is replaced (injected=1, removed_left=0) and both onRowsChange cases go red. (b) Stack: root stepping disabled (if (depth === 0) return undefined, injected=1), and the two presence/end cases go red while the two absence controls stay green. Both restored bygit checkout HEAD: git diff HEAD is 0 lines and the ablation marker count is 0. All runs used os-verify-lock in DECLARED UNLOCKED MODE.","line_budget": "n/a — no skills/** or governed ledger touched",
"deviations": "(1) Two merge conflicts, both resolved toward the dispatch's intent and leaving #27031 as it landed. ObjectView.tsx takes #27031's
useRecordOpenPreference()live hook and comment as landed, plus this branch'sdrawerRoot/drawerRestore/handleListRowsChange/handleRootStepreturns. zh.ts keeps both keys: #27031'sconsole.objectView.openAsPage: 整页打开and round 1'sexpandToPage: 整页打开. (2) onRowsChange semantics: it answers where onTotalChange does (a paged grid drawing the rows it fetched). Any other kind (board, calendar, grouped grid) reports [], so a host never steps through a stale set. The report is keyed on the ids in order, never on identity (ui/AGENTS.md #10). Root stepping writes?recordId=with replace, so stepping stacks no history entries. (3) ObjectView's split face (the reader preference) still draws no ↑ ↓: it is not a stack host. (4) The changeset drops the 'Not yet' root line, gains '@object-ui/plugin-list': patch and describes onRowsChange. The PR body is not edited; the seat setsFixes #26679at close-out. (5) No ObjectView-level integration pin wires ListView into a mounted ObjectView; the producer (ListView) and consumer (stack root) are pinned separately, and the hook glue is about 25 lines.","files_changed": [
".changeset/26679-drawer-in-place-navigation.md",
"ui/packages/plugin-list/src/ListView.tsx (onRowsChange)",
"ui/packages/plugin-list/src/tests/ListView.onRowsChange-26679.test.tsx (new)",
"ui/packages/app-shell/src/views/objectViewRecordNavigation.ts (listRowIds, handleListRowsChange, handleRootStep, drawerRoot.siblings)",
"ui/packages/app-shell/src/views/objectViewListRenderer.tsx (onRowsChange threaded)",
"ui/packages/app-shell/src/views/objectViewRecordOverlay.tsx (onRootStep)",
"ui/packages/app-shell/src/views/ObjectView.tsx (wiring, merge resolution)",
"ui/packages/app-shell/src/views/tests/recordDrawerStack.rootStepper-26679.test.tsx (new)",
"ui/packages/i18n/src/locales/zh.ts (merge resolution only)"
],
"api_writes": "1 — POST /repos//issues/26679/comments (this report, post-stamped); git push x3, not REST",
"mcp_calls": "0"
}
objectstack-fleet commentedon Oct 28, 2026
Review recorded on the PR: #27038
## 席位核对— ACCEPT at head811a10fa14after two patch rounds on the same claim (drawer stack + bar; list-root↑↓once #27031 was onmain); body set toFixes #26679; ready + auto-merge handed to the queue on the maintainer's standing authorization for this epic.pm:dispatchedstays until MERGED; the card closes by the PR'sFixesline. — epic PMsession_cdb7a717-def7-4e71-9988-f089fbaa18ff, 2026-10-28T06:34ZGenerated by Claude Code