Repository navigation
Data pillar: the object header moves into the content column, Records / Form / Validations / Hooks / Actions / API / Settings become one underline tab row, and the Records toolbar and grid lose their nested containers #12450
Description
Source
Epic #12446, decisions 1 and 3, open decision B. Measured on objectui main a2225f91 at /studio/app.objectstack.hotcrm/data?surface=object:crm_account: the object header ("Account · crm_account · 37 fields") is a full-width row above the Objects rail, so the title sits over the rail while the content it names starts 208 px to the right; the content column opens with a muted pill group (Records / Form / Advanced ▾) floating in 16 px padding, then a bordered rounded card holding the grid's own toolbar and borders — three nested containers before the first row; the toolbar mixes four labelled buttons with four icon-only buttons and sits right-aligned over an empty left half; the record count is in the footer only.
Target
The content column (right of the 240 px rail) becomes:
- Object header row, 18 px top padding, 24 px side padding: a 32 px primary-soft tile with the object's icon, the label at 18 px / 600, the api name as an 11 px mono chip, "N fields" at 12 px muted; at the right an Object settings icon button and a more button. The read-only card adds its link here.
- Underline tab row, 36 px, 2 px primary underline on the active tab, 13 px: Records, Form, Validations, Hooks, Actions, API, Settings — the five Advanced entries flattened. Decision B was answered on 2026-10-15: as designed, seven flat tabs (the ruling comment on the epic quotes the maintainer's words). Below a
min-widthof 1000 px (container query on the content column) the last five fold back into "Advanced ▾" with the objectui#11794 behavior; above it they are flat. - Records toolbar, 12 px vertical padding: left — "N records" at 13 px muted, then labelled outline chips (28 px) Hide fields, Filter, Group, Sort with their glyphs; right — search and refresh as 28 px icon buttons with
aria-labels, a divider, then "Add record" as a primary-soft chip. The grid's own header toolbar is suppressed (renderStudioGridListpasses the host's toolbar) or, if plugin-grid cannot hide it, this card touchespackages/plugin-gridonly to expose that option. - Grid, flush with the column: a 1 px top border, a sidebar-tinted header row (36 px, 12 px / 500 muted), 40 px body rows, a 48 px row-number column with 24 px left padding, 1 px vertical dividers, the row actions kebab pinned as the last column. Lookup cells render a 20 px initials avatar plus the display value instead of a primary-colored link. Mono font for autonumber columns.
- Footer, 10 px padding, 12 px muted: "N records" at the left, the range at the right.
The nested card and the pill group are removed. No metadata behavior changes: column inspector, hidden system fields (objectui#11780), grid projection and the Form tab's panels work as before.
File surface
packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx (DataPillar: header, tab strip, Records tab body, renderStudioGridList), ObjectListViewInspector.tsx only if its trigger moves; packages/plugin-grid only for the toolbar-suppression option; packages/app-shell/src/views/metadata-admin/i18n.ts for tab labels already present under engine.studio.data.*.
Acceptance
- At 1280 px the object title's left edge equals the grid's row-number column's left edge (24 px inside the content column), pinned by a test comparing offsets or classes.
- Seven tabs visible at 1280 px; "Advanced ▾" returns below the breakpoint, with the objectui#11794 name rule;
DataPillar.advancedKeepsName-11794.test.tsxis kept for the folded state and a new test pins the flat state. - Every icon-only toolbar button has an
aria-label; the toolbar has no unlabelled button. DataPillar.gridProjection,gridColumns,hiddenSystemFields-11780,inspectorBelongsToView-11781,surfaceRequest,StudioDesignSurface.listViewInspector-11823stay green.- Before/after screenshots at 1280 px, light and dark.
Constraints
- Tailwind utilities and theme tokens only; no inline style.
- Existing
data-testids and i18n keys stay. - Gates:
pnpm --filter @object-ui/app-shell test,lint,build;pnpm --filter @object-ui/plugin-grid testif touched. - Lands after the layout skeleton card (the Blocked-by line below).
Reference artboard
docs/design/studio-redesign/data-records.html on objectui main (landed in #12458); data-records.png is its 1280 px render. The folder's README carries the shared numbers and what a pull request against this card must show: two screenshots per artboard it touches (light and dark, 1280 px, cropped to the artboard's region), a deviation list with a reason per line, and the card's named tests green or updated. Where this card and the artboard disagree, this card's numbers win and the disagreement is reported here.
Blocked-by: #12459
Body refreshed 2026-10-15T09:52Z by the epic PM session: a Reference artboard section added now that #12458 is on main. Earlier refresh 2026-10-15T09:13Z: decision B answered; the earlier refresh of 2026-10-15T08:17Z pointed the Blocked-by line at #12459.
Parent: #12446
Activity
objectstack-fleet commentedon Oct 15, 2026
Decision B answered · 2026-10-15
Maintainer, in the epic PM session on 2026-10-15, verbatim:
Decision B is the second clause: the seven flat tabs ship. The Target section's item 2 stands as written with the breakpoint at 1000 px (not infinity): flat at 1000 px and above, "Advanced ▾" with the objectui#11794 name rule below it. The full record, with the premise to re-check (the Advanced menu holds exactly the five entries named), is on the epic #12446. This card stays blocked on #12459 (#12460, enqueued).
Written 2026-10-15T09:12Z by the epic PM session
session_cdb7a717-def7-4e71-9988-f089fbaa18ff.objectstack-fleet commentedon Oct 15, 2026
Claim: PM loop round 1 (epic #12446 subtree)
Session:
session_01SJn1GEkrbbBQkhGC26wEDwAccount:
os-sales(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-12450-data-pillar-layoutWorktree:
objectui-issue-12450Domain:
domain:uiSeat:
domain:ui#4File surface:
packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx(region:DataPillarandrenderStudioGridListonly), new tests beside it,ObjectListViewInspector.tsx(trigger only, if it moves),packages/plugin-grid(only to expose a toolbar-suppression option),packages/app-shell/src/views/metadata-admin/i18n.ts(engine.studio.data.*keys), the screenshots underdocs/design/studio-redesign/after/named12450-*.png(the light and dark pairs the reference README asks for, filed as the skeleton PR filed its own), one.changeset/12450-*.md(stop on breach; explain in the report)Container & model:
L,mode:subagent,model: opusClause-②: no
Responsibility:
n/a — not a defect cardThread-read: 6188628732
Serial constraints cleared: objectui#12459 merged as PR objectui#12460 (
cf430cd), the skeleton this card builds on. In flight on the same file at claim time: objectui#12452 (shell header nav region) and objectui#12454 (AutomationsPillarregion), both this seat, regions disjoint fromDataPillar. objectui#12451 (Form tab) waits on this card. objectui#12418 (seat 1) may touchpackages/plugin-gridonly where column widths are reported, a different region from the toolbar option. No other open PR touchesstudio-design/(3 open PRs read).Ruling-ref: 6188628732
objectstack-fleet commentedon Oct 15, 2026
os-dev-report
{
"issue": 12450,
"status": "needs_decision",
"branch": "claude/issue-12450-data-pillar-layout",
"pr": "#12477",
"session": "session_01SJn1GEkrbbBQkhGC26wEDw",
"premise_still_valid": true,
"summary": "Draft PR objectui#12477 (head 4c06d3a, assignee os-sales) is complete for the part the card's file surface reaches, and its local verification is done. Object header: an Object settings icon button (opens the Settings panel) and a More actions menu; Add field moved into More, because the artboard has no header Add field. Tab row: the seven flat tabs of ruling B. A CSS container query on the content column (
@containerplus@min-[1000px]:classes in the newdataTabsFold.ts, no JS width read) folds the last five into Advanced below 1000 px. Measured in Chromium: column 1040 px gives seven flat tabs; 940 px, or the 1280 px viewport with the field inspector open (740 px), folds them. Records body: the framed card and body padding are removed, so the list runs flush with the column. The Studio list now uses the wide toolbar form (compactToolbar: false: Hide fields, Filter, Group and Sort labelled, then Refresh, Search, Add record; density off). The toggles moved from the retired bare show-flag spellings touserActions. One producer fix: anaria-labelon plugin-list's icon-only Search trigger (card acceptance 3). Premise B held on cf430cd: Advanced held exactly rules, hooks, actions, api and settings. PM mechanism assumptions: 1, 2, 3 and 5 confirmed. 4 is partly falsified: the Records toolbar belongs to plugin-list's ListView, not plugin-grid. A host toolbar cannot replace it, because its controls own ListView-internal state (filters, sort, grouping, hidden fields, search keyword, refresh key), so suppression alone would remove the controls. What the card still asks for is outside its file surface: the artboard's toolbar arrangement, grid metrics (36 px tinted header, 40 px rows, 24 px row-number inset, column dividers, avatar lookups, mono autonumber) and footer range. These are ListView and ObjectGrid/data-table internals, so card acceptance 1 (title edge equals row-number edge at 24 px) is not met. That remainder is the one open question below. The PR's first line isFixes #12450, as the dispatch asked. Every option for the remainder lands in other packages under another card. If the seat prefers to keep #12450 open while the question stands, the line becomesPart of #12450(a seat-side body edit).","tests": "All from the worktree root through os-verify-lock (slot os-dev-12450), at HEAD 4c06d3a unless noted. (1)
pnpm exec vitest run --maxWorkers=3over the 98 studio-design test files that are not StudioDesignSurface.*: Test Files 98 passed (98), Tests 773 passed (773), VERDICT command-exit 0. (2)pnpm exec vitest run --maxWorkers=3 packages/app-shell/src/views/studio-design/StudioDesignSurface.: Test Files 76 passed (76), Tests 511 passed (511), VERDICT command-exit 0. (3)pnpm exec vitest run --maxWorkers=3 packages/plugin-list/: Test Files 133 passed (133), Tests 1432 passed (1432), VERDICT command-exit 0. Run after the plugin-list commit cb56e47; plugin-list has not changed since. (4) The 6 app-shell and plugin-list files that read the search trigger: Test Files 6 passed (6), Tests 214 passed (214). New test: DataPillar.flatTabs-12450.test.tsx, 9 tests, pins the flat state and the header buttons. Ablation, once: via /home/user/objectstack/scripts/ablation-replace.mjs, anchorclassName={DATA_TAB_FLAT_ONLY}x1 to x0, replacementclassName={undefined}x0 to x1, blob 12a4c43efdae to 0c671a29edcd. Run: Tests 1 failed | 8 passed (9); the failure is the fold-class pin. Restored: blob == HEAD (12a4c43efdae),git diff HEADempty. The subject is imported from source by a relative path, so no dist build was involved. Kept for the folded state, unmodified and green: DataPillar.advancedKeepsName-11794. Harness-only edits (assertions unchanged) to 14 existing suites: field adds go through More; power panels open by their flat tab; the 'is Add field offered' reads use the More trigger. Three of these suites are on the stay-green list (gridProjection, gridColumns, inspectorBelongsToView-11781); hiddenSystemFields-11780, surfaceRequest and listViewInspector-11823 are unmodified. All are green in runs (1) and (2). Browser: own stack, hotcrmobjectstack dev --fresh --seed-admin -p 4150plus the console vite on :5250, Chromium at /opt/pw-browsers/chromium, 1280x900 light and dark, plus 1180x900. Both servers were stopped by their recorded PID trees, and the hotcrm and objectui worktrees were removed.","gates": "Local subset, each exit code captured before any pipe.
pnpm exec turbo run build --filter='@object-ui/app-shell^...' --concurrency=2exit 0 (28 tasks, 27 cached).pnpm --filter @object-ui/app-shell --filter @object-ui/plugin-list type-checkexit 0, both scripts echoedtype-check;tsc -p tsconfig.test.json --listFileslists DataPillar.flatTabs-12450.test.tsx, tests/dataPillarAddField.ts and dataTabsFold.ts. eslint on the 19 touched app-shell files and plugin-list src/ListView.tsx: exit 0, 0 errors (existing warnings only).pnpm --filter @object-ui/plugin-list --filter @object-ui/app-shell buildexit 0; the aria-label is in plugin-list dist/index.js and the fold classes are in app-shell dist dataTabsFold.js. At 4c06d3a, exit 0 each: node scripts/check-changeset-presence.mjs (20 source files of 2 released packages, 1 changeset), node scripts/check-changeset-no-major.mjs, node scripts/check-changeset-fixed.mjs, pnpm check:changeset-claims, pnpm check:i18n-keys, pnpm check:i18n-drift (2 keys added, 0 en values changed), pnpm check:i18n-designer-parity, pnpm check:control-bytes, pnpm check:new-line-citations (0 new), pnpm check:test-path-roots. node scripts/check-governed-queue-guard.mjs --test on the 24 paths: NOT GOVERNED. objectui has no dispatch-gates derivation; this list was hand-derived from objectui's package.json files and the diff. CI on PR objectui#12477 (head 4c06d3a), read once at report time, 36 check-runs: label success; dependabot, Test (coverage) and Test (coverage shard 1..8) skipped. In progress, no conclusion yet: Lint, Type Check, Test (shard 1/16 through 16/16), Test (dist pins), Build & E2E, Build Docs, Bundle Analysis, Repo Gates, Line Citation Gate, Spec Main Shape Gate, Governed Surface Queue Guard, Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Overwrite Report, Changeset Claim Re-read, Live E2E (informational). Not waited on, per the contract; mergeable_state read 'behind' (main moved three commits, none touching this diff).","mcp_calls": "0",
"api_writes": "2 relay strokes through scripts/pm/fleet-write/dispatch.mjs, each one POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]: (1) pr_create, which issued POST /repos/objectstack-ai/objectui/pulls (draft) then POST /repos//issues/12477/assignees [os-sales]; read-back: 11041 bytes sent, 11041 stored, identical; (2) this os-dev-report comment, POST /repos//issues/12450/comments. Zero label writes. git push is not a REST write. The scratch backend writes (package create, object draft, publish, 8 records) went to my own localhost:4150 stack, not GitHub.",
"deviations": [
"File surface: packages/plugin-list/src/ListView.tsx (one aria-label on the Search trigger) is outside the claim's surface. The claim names packages/plugin-grid, but the toolbar's producer is plugin-list. The dispatch's producer clause authorizes the fix; it is declared in the PR body.",
"Harness edits to 14 existing suites, assertions unchanged, including 3 on the stay-green list (gridProjection, gridColumns, inspectorBelongsToView-11781). Reasons: Add field moved into the More menu, and the flat tabs duplicate the names that three suites' passthrough menu mocks draw as buttons. New shared helper: studio-design/tests/dataPillarAddField.ts.",
"Card acceptance 1 is not met, and the artboard's toolbar arrangement, grid metrics and footer range are not built. Reason: ListView, ObjectGrid and data-table internals, outside the card's surface. Listed as deviations 1-3 in the PR; the decision is the open question.",
"Screenshots use a writable 'Acme CRM' base seeded on the local backend, not HotCRM. HotCRM is read-only under objectstack dev, the server refuses to duplicate it (DUPLICATE_SOURCE_NOT_A_BASE), and a read-only package draws no More button. A third screenshot, 12450-data-records-folded-light.png, shows the fold.",
"Conflict noted, not resolved silently: the dispatch asked for
Fixes #12450, while part of the card's acceptance waits on the open question. I kept Fixes because every option puts the remainder on another card. The seat switches the line toPart of #12450if it keeps the card open instead."],
"files_changed": [
".changeset/12450-data-pillar-layout.md",
"docs/design/studio-redesign/after/12450-data-records-dark.png",
"docs/design/studio-redesign/after/12450-data-records-folded-light.png",
"docs/design/studio-redesign/after/12450-data-records-light.png",
"packages/app-shell/src/views/metadata-admin/i18n.ts",
"packages/app-shell/src/views/studio-design/DataPillar.authorRefusal-11785.test.tsx",
"packages/app-shell/src/views/studio-design/DataPillar.choiceWithoutOptions-11253.test.tsx",
"packages/app-shell/src/views/studio-design/DataPillar.designerRegistryPopulated.test.tsx",
"packages/app-shell/src/views/studio-design/DataPillar.flatTabs-12450.test.tsx",
"packages/app-shell/src/views/studio-design/DataPillar.gridProjection.test.tsx",
"packages/app-shell/src/views/studio-design/DataPillar.heldIncomplete-11786.test.tsx",
"packages/app-shell/src/views/studio-design/DataPillar.inspectorBelongsToView-11781.test.tsx",
"packages/app-shell/src/views/studio-design/DataPillar.panelGate.test.tsx",
"packages/app-shell/src/views/studio-design/DataPillar.picklistServedOptions-10202.test.tsx",
"packages/app-shell/src/views/studio-design/StudioDesignSurface.autosaveInFlight-11204.test.tsx",
"packages/app-shell/src/views/studio-design/StudioDesignSurface.autosaveSwitch-11232.test.tsx",
"packages/app-shell/src/views/studio-design/StudioDesignSurface.cancelledLoad-11331.test.tsx",
"packages/app-shell/src/views/studio-design/StudioDesignSurface.draftVersionConflict-11773.test.tsx",
"packages/app-shell/src/views/studio-design/StudioDesignSurface.formFields.test.tsx",
"packages/app-shell/src/views/studio-design/StudioDesignSurface.gridColumns.test.tsx",
"packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx",
"packages/app-shell/src/views/studio-design/tests/dataPillarAddField.ts",
"packages/app-shell/src/views/studio-design/dataTabsFold.ts",
"packages/plugin-list/src/ListView.tsx"
],
"line_budget": "n/a — no skills/** surface",
"open_questions": [
{
"question": "The rest of the artboard's Records body belongs to packages the card's file surface excludes: the toolbar arrangement (left-aligned 28 px chips after an 'N records' count, Search and Refresh, a divider, a primary-soft Add record), the grid metrics (36 px header on the sidebar surface, 40 px rows, a 48 px row-number column with the 24 px inset that acceptance 1 pins, column dividers, initials-avatar lookup cells, mono autonumber) and the footer range. They are ListView (plugin-list) and ObjectGrid/data-table (plugin-grid, components) internals. A host-side toolbar cannot stand in for them, because ListView's controls own its internal filter, sort, grouping, hidden-fields, search and refresh state. Which way does the Studio Records body go?",
"options": [
"A — Restyle the shared runtime list and grid to the artboard, so every console list page changes. Cost: this reopens what landed today in objectui#12423 (the compact Display toolbar and the data-table's leading row cell). It touches three published packages and every list page's look and its pins. Workload L-XL, on a list-page card of its own.",
"B — Studio-only presentation: new React host props on ListView and ObjectGrid (toolbar layout, header surface, row metrics, row-number inset), passed only by renderStudioGridList. Cost: one renderer gets two looks, and the Studio preview stops showing what end users see (renderStudioGridList's stated same-renderer principle). It adds public props to three published packages with no pull beyond this artboard. Workload M-L, one card across plugin-list, plugin-grid and components.",
"C — Keep the runtime list look inside Studio, which is what objectui#12477 ships: Records draws the list exactly as a list page does, in its wide form. The artboard's grid details and acceptance 1's 24 px alignment stand as recorded deviations, and the card closes. Cost: the Records body differs from the artboard in those details. Workload zero."
],
"recommendation": "C. 实际业务需求 (real business need): the Records tab is the author's preview of what users get. B shows authors a grid no user ever sees, and nothing besides this artboard asks for a Studio-only look. Measured: renderStudioGridList is the only host that renders the Studio grid, and the console list pages moved the other way today (objectui#12423). 项目长远合理性 (long-term soundness): A and C keep one renderer and one look. B forks presentation across three published packages, a standing cost with no contract gain. 防 AI 写错 (preventing AI mistakes): as long as B stays on React host props, no option adds a metadata key, so this axis barely separates them; B does add host-prop surface an agent could mistake for node keys. 创业阶段不扩散需求 (no scope creep at this stage): C adds nothing; B adds unpulled public props; A is the largest. If the maintainer does want the artboard's grid on every list, that is A, on its own list-page card weighed against objectui#12423 — not on this card. Recommend recording the answer on the epic, objectui#12446, so #12450 is not itself held in the decision box."
}
],
"out_of_scope_findings": [
"carrier: none · noted, not filed — In Studio on HotCRM crm_account at 1280x900, the Records grid drew 6 of the 9 rows its footer counts, and the last drawn row stretched to the bottom of the column. The skeleton's before-screenshot (cf430cd) shows the same shape, and an 8-row object does not reproduce it. Not root-caused and no location named, so this is not a class-(a) filing; it is in the PR's Acceptance notes. Dedupe words: studio records grid stretched last row, rows missing, crm_account grid gap.",
"carrier: none · noted, not filed (read from source, not measured) — Below the sm breakpoint, ListView's Filter and Sort triggers hide their label spans and carry no aria-label, in both toolbar forms, so on a phone they would have no accessible name. Not measured in a browser, so not filed; in the PR's Acceptance notes. Dedupe words: list toolbar filter sort aria-label mobile, icon-only unnamed button."
]
}
objectstack-fleet commentedon Oct 15, 2026
ACCEPT → PR #12477 comment 6190857251 (head
4c06d3a, read 2026-10-15T11:21Z). The artboard's grid remainder (acceptance 1) goes to decision D on the epic #12446; every option carries it on another card. Finding filed from this review: #12480.Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
Landed · PR #12477 merged as
7a5f4c1onmain· read 2026-10-15T12:40Zorigin/main7a5f4c1studio-design/dataTabsFold.tsDATA_TAB_FLAT_ONLYinStudioDesignSurface.tsxdata-object-settings(header button)overflow-auto bg-backgroundwithdata-records-body… rounded-lg border bg-background p-6) still present, 1 hitaria-label={searchTerminplugin-list/src/ListView.tsxDataPillar.flatTabs-12450.test.tsxState change: the card closed as completed through
Fixes #12450.pm:dispatchedis removed in this act;pm:epic,domain:uiandarea:studiostay. objectui#12451's lastBlocked-by:target is now closed, so it is next in this seat's queue. The artboard's grid remainder stays with decision D on the epic (6190886772).Generated by Claude Code