Skip to content

feat(ui): record pane v4 in the console — split by default, maximize in place, full page only from the row and the header menu - #28129

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-bsp3dh
Oct 30, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-bsp3dh

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC

Requested by Bill · project thread

Fixes #27545

Clause-②: yes (widening)
Metadata-protocol: none

维护者速读

What changes

Part B of #27545: the console wiring. Part A, the mechanics (the split face, the non-modal drawer over a list, maximize, the narrowed list faces), merged as #27953.

@object-ui/app-shell

  • Split is the default (Target 1). DEFAULT_RECORD_OPEN_PREFERENCE is 'split'. A reader who stored drawer keeps it: the preference reads back under the same key. RecordOpenPreference gains 'page'; the codec reads all three and ignores anything else.
  • The list stays one element and readable. The console hands the list to the overlay as mainContent in both faces and passes activeListRecordId. The open row is tinted, follows ↑ ↓ and scrolls into view.
  • A click on another row swaps the pane's root record (Target 1). The click pushes ?recordId= and the pane follows the URL while it is open. Nested levels are dropped.
  • The full-page preference. With page stored, a row click on a drawer-mode object opens the full page.
  • A ?recordId= change re-renders none of the list (Target 5).
    • The row-click handler, the router's navigate target and the list's create / delete handlers read their latest inputs from refs. Their identities therefore no longer change with the URL, and renderListView keeps its identity.
    • A render-count test pins this.
  • ⤢ maximizes in place (Target 3). The overlay is maximizable, and nested levels share it. The bar no longer routes: handleExpandDrawer's hop to /record/:id retires.
  • The full page only from ↗ and ⋯ (Target 4).
    • The pane bar no longer draws a page button (no onExpand).
    • The row's ↗ opens the full page and hands it the way back to this list with the record open.
    • The record header's ⋯ ends with 「Open as full page」 as its last host item, above the destructive band, in a record pane only (onOpenAsPage, the path from the root to the record shown).
    • 「Collapse to drawer」 is drawn only on a page the ↗ opened.
  • Stepping never blanks (Target 5). An embedded record keeps the previous record on screen until the next one arrives; there is a skeleton only on the first open. ↑ ↓ warm the related counts of both neighbours (console record page: the related column loads counts first, then the first three rows of only the visible cards, and never blocks the details — one batched request, viewport-lazy cards, a measured budget #27056's prefetch).
  • ObjectViewRecordOverlay.mode is typed NavigationOverlayMode (the overlay's own union), since spec's NavigationMode no longer has 'split'.

@object-ui/plugin-list

  • Display menu, Open records.
    • It draws a third segment, Full page, when the host passes the new optional recordOpen.selectPage. A host that passes none still sees two segments, but in the new order Split | Drawer (it was Drawer | Split), because Split is now the default.
    • recordOpen.preference also accepts 'page'. It is an input the section only reads, and ListView hands recordOpen straight to the popover, so it never travels back to the host.
    • setPreference still takes 'split' | 'drawer' (ViewSettingsRecordOpenPreference is unchanged), so a host's setter keeps its type.
    • The note says a record's ↗ always opens the full page.
  • Beside a record pane the record-count bar prints no count (useRecordListFace() !== 'full'). The console prints it under the list, so a split face no longer shows it twice. The rest of the bar row stays: the grid's column summaries are still drawn.

@object-ui/plugin-grid. Narrowed beside a pane, the row-actions column keeps its header's accessible name (sr-only) but draws no label. It is then only as wide as its ⋮, and the three columns fit the 464 px list beside a 720 px pane and an expanded sidebar.

@object-ui/i18n. list.recordOpenPage in all ten packs; list.recordOpenNote reworded in all ten.

Docs. content/docs/frontend/plugins/plugin-view.mdx gains "The Console's record pane".

Clause-② — widening

The one public type touched is ListViewProps.recordOpen (@object-ui/plugin-list):

  • preference: 'split' | 'drawer' becomes 'split' | 'drawer' | 'page'. It is a property the component only reads. Every value a host passed before is still accepted.
  • selectPage?: () => void is new and optional.
  • setPreference: (next: 'split' | 'drawer') => void is unchanged. A host implementing it for two values still type-checks, because the component never calls it with 'page'.

No export is removed or narrowed. RecordOpenPreference is in @object-ui/app-shell, which is private.

Tests

New:

  • app-shell/src/views/ObjectView.recordPaneV4-27545.test.tsx (4):
    • with no choice made, the pane opens beside the list with the open row marked and no dialog;
    • a click on another row swaps the record, and the mark moves;
    • a ↓ step and a row click leave the ListView render count unchanged;
    • ⤢ keeps the URL and the history length, hides the list, and Esc restores.
  • app-shell/src/views/RecordDetailView.paneOpenAsPage-27545.test.tsx (4):
    • the header ⋯ ends with 「Open as full page」 in a pane and hands the pane the gesture, with a CONTROL for an embedded record no pane hosts;
    • a step keeps the previous record on screen until the next arrives, with a CONTROL for the record page resetting on a route change.
  • plugin-list/src/__tests__/ListView.recordPaneCount-27545.test.tsx (4): full prints the count (CONTROL); columns and cards print none and draw no empty row; columns keeps the column summaries.
  • plugin-grid/src/__tests__/ObjectGrid.recordPaneFace-27545.test.tsx (+1, now 5): narrowed, the actions header is sr-only, with the full CONTROL.

Updated, not deleted (Acceptance 4):

Runs (from ui/, NODE_OPTIONS=--max-old-space-size=4096, on head b0657a05d5):

Gates

Derived on head b0657a05d5 (node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack): 67 families, 1817 changed lines (+1448 / -369, 35 files) against the 3000 threshold. Each was run and its exit code recorded before any pipe. --ran reconciles 67 derived, 67 run, 0 NOT-MEASURED, 0 UNRUN, and every one exited 0. The record is gates-ran-B.txt in the seat's card folder.

Four answered exit 3 or timed out on the first pass because of the container, not the diff. Each was re-run after the cause was fixed and then exited 0:

  • spec check:docs and check:skill-examples need a built @objectstack/spec, so they were re-run after pnpm --filter @objectstack/spec build.
  • check:type-check-debt needed pnpm install to link the @objectstack/types dependency that main added to driver-odata.
  • pnpm --dir ui exec vitest run scripts/ timed out twice in check-i18n-call-site-keys.test.ts while a parallel test run held the CPU; its re-run alone passed (169 passed | 2 skipped (171)).

The pre-commit and pre-push checks (check-regen-pending, check-commit-card-trailers --pre-push) were run by hand: exit 0. Every family CI runs is cited from the PR's check-runs at ACCEPT, not re-run here.

Acceptance notes

Verification

  1. Open an object list in the console (no stored preference) and click a row. The record opens beside the list, the list keeps three columns and the open row is tinted. Click another row and the pane swaps to it. Press ↓ and the tint follows.
  2. Press ⤢. The pane fills the content area beside the sidebar, the URL is unchanged, and Esc restores it.
  3. Open the record header's ⋯. Its last host item is 「Open as full page」.
  4. In the Display menu, choose Full page and click a row. The full page opens.

Generated by Claude Code

…in place, full page only from the row and the header menu

Part B of objectstack#27545; part A (the mechanics) is on main.

- app-shell: the record opens beside the list by default; the list is
  one element and keeps its scroll; a row click swaps the root; the open
  row is tinted and followed; ?recordId= changes re-render none of the
  list; the bar's maximize stays in place; the full page opens from the
  row's arrow and the header menu's last item; stepping keeps the
  previous record and warms both neighbours.
- plugin-list: the Display menu's Full page segment through an optional
  selectPage (the setter keeps its two values); beside a pane the count
  bar prints no count.
- plugin-grid: beside a pane the row-actions header draws no label.
- i18n: list.recordOpenPage, list.recordOpenNote reworded.

Claude-Session: https://claude.ai/code/session_01Utp6CY5hd7qEmdXd6Wg8Ap
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation tests tooling labels Oct 30, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC

Contract-review-claim: session_01Utp6CY2n1JmZZKrHgtp9jC · head b0657a0 · 2026-10-30T13:27Z

Declaration review (TIER_DEFAULT, Clause-②: yes (widening)) of #27545 B.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC

Contract review

Served-tier: TIER_DEFAULT
Head-sha: b0657a05d5b2dc3369a372773b4f92dc08aa5f94
Local-runs: none
Scope: ui/packages/plugin-list/src/ListView.tsx, ui/packages/plugin-list/src/components/ViewSettingsPopover.tsx, ui/packages/plugin-grid/src/ObjectGrid.tsx, ui/packages/i18n/src/locales/*.ts + ui/packages/app-shell/src/views/objectViewRecordNavigation.ts, ui/packages/app-shell/src/views/ObjectView.tsx, ui/packages/app-shell/src/views/recordDrawerStack.tsx, ui/packages/app-shell/src/views/RecordDetailView.tsx, ui/packages/app-shell/src/utils/recordOpenPreference.ts

Face: the Clause-②: yes (widening) declaration alone (PR body, changeset). record-recognisers.mjs --paths on the 35 committed paths answers no path face and defers to the changeset's Clause-②: line, which carries the (widening) arm; check-governed-merges.mjs --test on the same list: 0 of 35 paths hit the register, NOT governed. Nothing under packages/spec/**, nothing declared as narrowing, so no CONTRACT_REVIEW_TIER face applies. Read against merge base f0857d29c1 on origin/main: 35 files, +1448 / −369.

① Derived judgments

  • Widened, correctly declared: @object-ui/plugin-list (published; no private). The one public type touched is ListViewProps.recordOpen (recordOpen?: ViewSettingsRecordOpen, reachable from the entry through the exported ListViewProps; ViewSettingsRecordOpen itself is not exported by name).
    • preference: ViewSettingsRecordOpenPreference becomes ViewSettingsRecordOpenPreference | 'page'. The component only reads it (value={preference}); the onValueChange handler routes 'page' to selectPage?.() and never to setPreference. Every value a host passed before is still accepted.
    • selectPage is new and optional (a zero-argument callback); the Full page segment is drawn only when it is passed.
    • setPreference (taking ViewSettingsRecordOpenPreference) is unchanged. ViewSettingsRecordOpenPreference is re-spelled 'split' | 'drawer' from 'drawer' | 'split': the same set, so a host's setter keeps its type.
    • LIST_DEFAULT_TRANSLATIONS (exported) gains list.recordOpenPage; list.recordOpenNote is reworded. A key added, none removed.
    • The seat's grep of the removed lines of plugin-list/src, plugin-grid/src and i18n/src (non-test) finds one removed export line, export type ViewSettingsRecordOpenPreference = 'drawer' | 'split';, re-added with the same two members, and one removed member line, preference: ViewSettingsRecordOpenPreference;, re-added wider. No export, prop or member is removed or narrowed. So yes (widening) is the honest line.
  • Not widened: @object-ui/plugin-grid changes only the module-internal narrowListColumns (not in index.tsx): the _actions column of a narrowed face keeps its header text in an sr-only span and draws no label. No prop. @object-ui/i18n (./locales/* is public): recordOpenPage added and recordOpenNote reworded in all ten packs; no key removed. @object-ui/app-shell is private: true, so RecordOpenPreference gaining 'page', DEFAULT_RECORD_OPEN_PREFERENCE moving to 'split', handleExpandDrawer → handleOpenAsPage / handleOpenRowAsPage, RecordStackDrawer's onExpand/expandLabel → onOpenAsPage/mainContent, RecordDrawerUpcomingContext becoming an array and the new RecordPaneContext are outside the published surface.
  • Behaviour change for an existing caller, stated:
    • Default preference drawer → split; a stored drawer or split reads back under the same key os:record-open-mode; the codec now also decodes page.
    • A ListView beside a record pane (useRecordListFace() !== 'full') no longer prints the count in its record-count bar; the cap warning and the column summaries stay (dataLimitWarningShown is the former condition, unchanged). The console prints the count under the list (RecordPaneListCount), so the split face prints it once.
    • The pane bar's ⤢ maximizes in place (maximizable) and no longer routes, in the object list and in the drawer over a record page alike; the full page opens from the row's ↗ (with the return state for 「Collapse to drawer」) and from the record header's ⋯ (without it). 「Collapse to drawer」 is therefore drawn only on a page the ↗ opened.
    • A row click on a drawer-mode object with page stored opens the full page; with the pane open, a click on another row swaps the root via ?recordId=.
    • An embedded record keeps the previous record on screen during a step (loadingResetRecordId is null when embedded); a different object still resets.
    • The row click, the router setter, the list's create / delete handlers and onNavigate read their inputs from refs so a ?recordId= change re-renders none of the list; the render-count test pins it.
  • Behaviour change the body does not state as such: the Display menu's segment order is Drawer | Split → Split | Drawer for every host, including one that passes no selectPage; the body and the CONTROL test say such a host sees "Split | Drawer as before", but before it was Drawer | Split. The per-segment data-testids and labels are unchanged, so no selector breaks; it is a visible reorder, not a type change. Noted below, not a must-fix.
  • Metadata-protocol: none holds: no spec file, no metadata key added, removed or read differently; navigation.mode is read as before and split stays out of it.

② Semver level

patch on @object-ui/plugin-grid, @object-ui/plugin-list, @object-ui/app-shell and @object-ui/i18n, following AGENTS.md Post-Task Checklist step 3 (Metadata-protocol: none ⇒ patch; a non-protocol widening ships patch). The changeset carries Clause-②: yes (widening) and Metadata-protocol: none at line start and names each widening: the optional recordOpen.selectPage, preference accepting 'page' as a read-only input, setPreference unchanged, list.recordOpenPage in all ten packs, list.recordOpenNote reworded, the narrowed grid's unlabelled actions header, and the count bar beside a pane. @object-ui/components is not touched in this PR and is rightly absent. The segment reorder is not named in the changeset.

③ Boundary flags

Findings

  1. Body wording, not a must-fix. "A host that passes none sees Split | Drawer as before" (PR body, changeset, and the CONTROL test's title) — the order before this PR was Drawer | Split (value="drawer" first). The reorder is intended (Split is now the default) and harmless to callers, but a reader of the changeset would take it as unchanged. The seat can ask for a one-word fix in the body and changeset at ACCEPT.
  2. Observation, outside the contract. console.objectView.expandToPage stays in all ten packs and has no reader left in non-test source after this diff (expandLabel is no longer passed; NavigationOverlay resolves detail.openAsFullPage itself). A kept key is not a removal, so nothing is narrowed; the dev reports check-i18n-dead-keys exit 0.

Implemented-by: claude/project-thread-bsp3dh
Reviewed-by: session_01Utp6CY2n1JmZZKrHgtp9jC

VERDICT: PASS


Generated by Claude Code

…er, it was Drawer | Split

The changeset and the CONTROL pin said a host with no selectPage sees the
two segments "as before"; they are in a new order since Split is the
default. Wording only, for the declaration review on objectstack#27545 B.

Claude-Session: https://claude.ai/code/session_01Utp6CY5hd7qEmdXd6Wg8Ap
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC

Preview review (proxy for epic:#26676#1) · df97cfe · PASS

This is Acceptance 3 for #27545, as the full walk of the end state against the v4 boards, under #26676 ruling B (6520722066).

Who reviewed. A new review subagent with no development context. Its inputs were:

  • the card's Target and Acceptance;
  • the boards (a7825499aa:docs/design/record-page-v2-2026-10-27/v4/: RecordDrawerV4Peek, Max and Enlarged, plus notes n46, n47, n49 and n50);
  • this PR's "What changes" and "Verification".

What it walked. The live console built from b0657a05d5, on examples/app-crm (leads, contacts and accounts), in a fresh browser context with no stored preference:

  • at 1440 × 900 with the sidebar expanded and collapsed;
  • at 1000 × 800;
  • across the 1047 / 1048 px boundary;
  • at 1280 and in a 420 px-tall viewport.

The current head df97cfe5db differs from b0657a05d5 only in the changeset wording and one test title (2 lines, no source), so the walk stands for it. #27524's page anatomy inside the pane was out of scope.

Reason. Every Target item 1–5 is delivered in the running console. The reviewer measured each one rather than inferring it. There were no page errors and no defect introduced here. The differences from the boards that remain are either #27524's or cosmetic notes on the narrowed list.

Target Observed Shots
1. Split is the default See Target 1 detail below. 01–06, 29, 31–35
2. Overlay drawer as preference and fallback See Target 2 detail below. 07–13
3. ⤢ maximizes in place, sidebar kept See Target 3 detail below. 14–20
4. Full page only from ↗ and ⋯ See Target 4 detail below. 21–25
5. Stepping never blanks See Target 5 detail below. 26–29

Target 1 detail

  • Layout with no stored preference. A row click mounts an aside beside the list: list 464 + pane 720 = content 1184. There is no dialog and no overlay. The same 464 / 720 holds on contacts and accounts.
  • List columns. The list keeps three view columns plus the row-actions column, whose header is sr-only.
  • The open row. It is tinted and marked aria-current.
  • Clicking another row. It swaps the root and the tint moves. With a nested level open, a row click drops that level.
  • Stepping. The pane's ↑ / ↓, the arrow keys and j / k all step. The tint follows, and in a short viewport the active row scrolls into view.
  • Divider. It clamps at 864 = 60vw, where the list is 320 and shows card rows, and at 360. The width is remembered per object across a reload.
  • Columns / cards boundary. A 445 px list shows columns; a 430 px list shows cards.
  • No remount. The list wrapper, table, body and rows keep their identity across the first open, row clicks and steps (checked with DOM markers).

Target 2 detail

  • Display menu. Open records shows Split | Drawer | Full page, with Split on by default.
  • Fallback boundary. It is decided on the content area: 999 → drawer, 1000 → split.
  • The fallback drawer. It has no aria-modal and no overlay. A row behind it swaps the record, and Esc closes it.
  • Drawer preference at 1440. It gives a non-modal 720 drawer over the full list. ⤢ maximizes it to 1184 with the sidebar kept.
  • Full page preference. A row click opens the record page.

Target 3 detail

  • Maximized width. ⤢ gives x 256, width 1184 = 1440 − 256, with the sidebar kept. With the sidebar collapsed it gives x 48, width 1392.
  • URL and history. The URL is unchanged and history.length is unchanged.
  • List and button. The list is covered and inert, and the button reads ⤡ "Restore size".
  • Restoring and closing.
    • ⤡ and Esc both restore to 720 / 464.
    • ✕ closes the whole path.
    • The next open is split again.
    • A sidebar click closes the pane and navigates.
    • ↓ while maximized stays maximized.
  • Nested levels and tabs. A nested level shares the maximize: Esc restores, then the next Esc goes back one level. An active Timeline tab keeps 1184.

Target 4 detail

  • Pane bar. It has ↑ ↓ ⤢ ✕ and no page button.
  • The row's ↗. It opens the full page with 「Collapse to drawer」. That button returns to the list with the pane open and the row tinted.
  • The header ⋯. It reads Add to favorites / Share / Open as full page / — / Delete, so 「Open as full page」 is the last host item above the destructive band. That page draws no 「Collapse to drawer」, and a record page loaded directly has neither.

Target 5 detail

Deviations from the boards inside this card's scope (notes, none REWORK):

  1. Narrowed list chrome.
    • The console keeps the checkbox / row-number column on the left. On the right it keeps a sticky 57 px row-actions column (empty header, ⋮ on hover). The board has neither.
    • At 464 px the table is 477 px wide, so it overflows by 13 px with no visible scrollbar.
    • The widest chip ("Disqualified") ends 1 px before the actions column, so every chip is fully visible, but only just (35). Low.
  2. Prefetch. What is prefetched is console record page: the related column loads counts first, then the first three rows of only the visible cards, and never blocks the details — one batched request, viewport-lazy cards, a measured budget #27056's related-count warm-up for the neighbours, as this PR's body says, not the neighbours' record bodies.
    • The visible promise holds: there is no blank, and the previous record stays.
    • On this local dev build a step completes about 0.65–0.8 s after the key.
    • Low–medium; the call on the Target's wording is the epic seat's.
  3. n49's 1280 px example. The board shows card rows at 1280. In the console the sidebar collapses to its 47 px rail below about 1300, so at 1280 the list is 512 px and draws columns (30). This is the console's responsive sidebar, not a defect.
  4. Drawer position. The Sheet-based drawer (the fallback and the Drawer preference) spans the full viewport height over the right of the app header, while the split pane starts under the header. This is the Sheet's existing behaviour; the card does not rule on it.

#27524 scope, not counted.

  • The pane's top bar has no root breadcrumb and no "n / N" counter (n47 / n49).
  • The pane body is the tabbed Details | Related | Timeline anatomy, not the board's stacked details and panels or its two-column body.
  • An account's Details tab is empty in the pane (36, for comparison with its full page).

Other.

Screenshots (project files, ui-seat2/card-27545/preview-B/). Acceptance 2's four states are 01, 02, 14 and 08.

  • preview-B-01-split-default.png
  • preview-B-02-split-another-row.png
  • preview-B-03-step-down-tint-follows.png
  • preview-B-04-divider-at-60vw-card-rows.png
  • preview-B-05-divider-at-360-min.png
  • preview-B-06-card-rows-just-below-440.png
  • preview-B-07-display-menu-three-options.png
  • preview-B-08-overlay-fallback-1000.png
  • preview-B-09-overlay-fallback-row-swap.png
  • preview-B-10-drawer-preference-1440.png
  • preview-B-11-drawer-maximized.png
  • preview-B-12-display-menu-full-page-chosen.png
  • preview-B-13-full-page-preference-opens-page.png
  • preview-B-14-maximized-sidebar-kept.png
  • preview-B-15-maximized-sidebar-collapsed.png
  • preview-B-16-restored-after-esc.png
  • preview-B-17-after-sidebar-click-while-maximized.png
  • preview-B-18-nested-level-in-pane.png
  • preview-B-19-nested-level-maximized.png
  • preview-B-20-timeline-tab-maximized.png
  • preview-B-21-row-hover-shows-open-as-page.png
  • preview-B-22-full-page-from-row-arrow.png
  • preview-B-23-collapsed-back-to-list-with-record-open.png
  • preview-B-24-header-more-menu.png
  • preview-B-25-full-page-from-header-menu.png
  • preview-B-26-mid-step-60ms-after-arrow-down.png
  • preview-B-27-mid-step-60ms-after-next-button.png
  • preview-B-28-mid-step-60ms-after-arrow-up.png
  • preview-B-29-after-steps.png
  • preview-B-30-split-at-1280.png
  • preview-B-31-short-viewport-active-row-scrolled-into-view.png
  • preview-B-32-contacts-split-default.png
  • preview-B-33-accounts-split-default.png
  • preview-B-34-row-click-drops-nested-level.png
  • preview-B-35-list-columns-zoom.png
  • preview-B-36-account-full-page-for-comparison.png

The boards rendered at 1440 for comparison are in preview-A/boards/.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC

Contract-review-claim: session_01Utp6CY2n1JmZZKrHgtp9jC · head df97cfe · 2026-10-30T13:50Z

Delta review of df97cfe5db (the wording fix for record 6524125710's finding 1).


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC

Contract review

Served-tier: TIER_DEFAULT
Head-sha: df97cfe5db9240a1ea3b049879e7c8591876a29b
Local-runs: none
Scope: .changeset/27545-record-pane-v4.md + ui/packages/plugin-list/src/tests/ViewSettingsPopover.recordOpen-26681.test.tsx

Delta review after record 6524125710 (PASS on b0657a05d5): the diff b0657a05d5..df97cfe5db is two files, +2 / −2, wording only. The rest of the tree is unchanged from the head that record judged; its ① / ② / ③ carry over.

① Derived judgments

  • The changeset line now says that a host passing no selectPage sees two segments in the order Split | Drawer, where it was Drawer | Split. That is finding 1 of 6524125710, the visible reorder for every host, now stated rather than hidden. The dev confirms the order is intended (Split is the default).
  • The CONTROL test's title says "now Split then Drawer". Its assertions are unchanged.
  • No export, prop, member, stored shape, metadata key or i18n key is touched. Clause-②: yes (widening) and Metadata-protocol: none stand.

② Semver level

Unchanged: patch on the four packages.

③ Boundary flags

None new. Finding 2 of 6524125710 (console.objectView.expandToPage with no source reader) is left as is; check-i18n-dead-keys exits 0 (dev). Acceptance 3 is met by the card thread's full proxy preview walk against the boards, 6524396651.

Findings

must-fix 0; should-fix 0.

Implemented-by: claude/project-thread-bsp3dh
Reviewed-by: session_01Utp6CY2n1JmZZKrHgtp9jC

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC

席位核对

ACCEPT · head df97cfe5db9240a1ea3b049879e7c8591876a29b · 2026-10-30T13:56Z

  1. Shape: draft → main, Fixes #27545 (part B of the serial split; A = feat(ui): record pane v4 mechanics — split beside the list, non-modal drawer over a list, maximize in place, narrowed list faces #27953, merged), attribution block on top, root changeset (four @object-ui/* patches). 35 files, +1448 / −369. No assignee, no reviewer.
  2. Card rule met (seat read the diff and the walk):
  3. Gates (check-runs on this head): 53 success, 10 skipped, 0 red. Named runs: Lint & Repo Gates 124097092370, TypeScript Type Check 124098469054, Type Check UI 124094144083, Test UI 124101243887. The dev reconciles 67/67 derived gates, all exit 0.
  4. Contract review: Clause-②: yes (widening), so TIER_DEFAULT. Declaration review PASS 6524125710 on b0657a05d5; its finding 1 (the reorder to Split | Drawer for a host with no selectPage) is now stated in the changeset; delta PASS 6524451830 on this head.
  5. Acceptance 3 (preview verification): ruled B (6520722066). The card thread's full proxy walk against the v4 boards (the end state, as the ruling asks of B) is PASS, 6524396651. It ran on b0657a05d5; this head differs by two wording lines. The surface is examples/app-crm; HotCRM is not required. The epic seat re-checks on main and files cards for any deviation.
  6. Landing: not governed (check-governed-merges --pr 28129: 0 of 35 paths), 1817 changed lines ≤ 3000. The seat lands it: ready + auto-merge now. It closes Record drawer v4: a bounded peek over the list — md cap instead of the field-count bucket, no dim overlay, the list stays readable and clickable, ⤢ maximizes in place at the same URL; the full page is reached only from the row ↗ and the header ⋯ #27545.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 30, 2026 13:57
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 30, 2026 13:57
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 30, 2026
Merged via the queue into main with commit 277275a Oct 30, 2026
64 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-bsp3dh branch October 30, 2026 14:17
os-support-ai pushed a commit that referenced this pull request Oct 30, 2026
Record pane v4 (#28129) hands the pane BOTH neighbours as an array, so the
payload prefetch read a single upcoming record that no longer exists and
asked none. Ask each neighbour's page once this record's has answered, and
pin an in-place step to either one opening from that answer.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufzhCjfxaYJQyy6tFjVr
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xl tests tooling

Projects

None yet

1 participant