Repository navigation
console list and dashboard chrome (family card): the view description's own row, the sort popover's standing lookup note, the metric card's four-line stack and the two-row list footer — four nails, one card #12528
Description
Filing gate ③, on the maintainer's word (chat, 2026-10-15, session session_019YFzJp4o8Ck4NsT1toC4UP, seat account os-bill): the seat ran a functional audit of the HotCRM console on objectui main 720f6d4 (report, private to the maintainer: https://claude.ai/artifact/XPrn5eZMsQaKUgasMJEMmR, findings F7, F13, F14 and F16; figures 9, 17 and 21), listed them as one card, and the maintainer answered 「同意开卡,使用 pm-dispatch 创建卡片」. A family card: four fixed places where chrome costs height or attention, each with its nail below; a single-point card for any one of them is not opened. Quota note: the audit's cards exceed the filing gate's three-per-fire quota on that word, which approved the report's card list as a set.
Parent: objectstack-ai/objectstack#25945. The sibling of #11694 (closed: the dashboard's Refresh All row and the report card's fixed height), which is the precedent for density items. Blocks nothing; nothing blocks it.
Reader who acts: the domain:ui lane's next dispatch (seat post #9800), one dev round across @object-ui/plugin-list (nails 1, 2, 4), @object-ui/plugin-grid (nail 4) and @object-ui/plugin-dashboard (nail 3), size M; the dev may split the dashboard nail into a second PR on the same card.
Dedupe (MCP search_issues, scope objectstack-ai/objectui, all states, read 2026-10-15T14:14Z): "sort popover lookup columns explanation text always shown dashboard metric card too tall connection status badge app bar list footer aggregates two rows" → 8: #12463, #12423, #12461 (this parent's merged children), #12188, #11694 (the density precedent, closed), #8882, #5827, #665 (closed). "view description rendered as its own line above the list toolbar pushes toolbar row down move into view tab tooltip" → 10: #12474, #12504, #12505 (open, the Hide Fields write family), #12440, #12423, #12462, #12418, #11642 (closed). None is this card.
The four nails (objectui origin/main 720f6d4, read 2026-10-15T14:15Z)
- The view description takes a row of its own above the toolbar.
packages/plugin-list/src/ListView.tsxlines 4969–4972 renderviewDescriptionas apx-4 pt-1.5 text-xsline before the toolbar row. Reading: HotCRM Accounts ("Primary account list with revenue & industry summaries") puts the toolbar at y = 155; Leads, whose default view has no description, at y = 133. The chrome's height changes with the view. Build: the description moves into the active tab's tooltip (and the tab's ▾ menu header), the toolbar row stays at one height;appearance.showDescriptionkeeps its meaning for the tooltip. - The sort popover's standing note.
ListView.tsxline 993 (list.sortRelationalHint) and line 5279: a four-line paragraph about lookup columns and denormalization is drawn on every open, taking about half of the popover. Build: the paragraph becomes an info icon with that text as its tooltip, next to the "Sort by" field list, and a one-line hint only when the picked field is a lookup. - The metric card's four-line stack.
packages/plugin-dashboard/src/MetricCard.tsxlines 98–122:CardHeader(title + icon), then the description, thentext-2xlvalue, then a unit line. Reading: HotCRM Sales Performance's four KPI cards are about 215 px tall for one number each; two rows of cards fill the first screen. Build: title and unit on one line above atext-2xlvalue, the description in the card's tooltip (title) unless the widget declaresshowDescription; target about 120 px at the default density. - Two footer rows on an aggregated list. The grid's column-summary footer (
packages/plugin-grid/src/ObjectGrid.tsxlines 3337–3394,useScopedColumnSummary) draws "Annual Revenue: Sum: 109,800,000.00 · Number of Employees: Avg: 760" as a row above the list's record-count footer ("9 records",list.recordCount). Build: one footer row: the count at the left, the aggregates at the right of the same row, truncating with a tooltip when they do not fit.
Acceptance
- One test per nail:
ListViewrenders noview-descriptionelement and the active tab's trigger carries the description as its tooltip; the sort popover has nosortRelationalHintparagraph and an info control with that text;MetricCardrenders title, value and unit in two lines and no description paragraph by default; a grid with column summaries renders one footer row containing both the count and the summaries. - Browser reading by the landing seat at 1440 on HotCRM: the Accounts and Leads toolbars sit at the same y; the sort popover is under half its former height; Sales Performance's first screen shows the four KPI cards and both charts' titles; the Accounts footer is one row.
pnpm testfrom the repo root green on the affected files; a changeset (patch) per published package touched.
Out of scope
- The dashboard filter bar; the chart widgets; the Display popover (console list toolbar: the density toggle and Hide Fields still write the shared view, so one user's click changes the list for everyone and a walled tenant is refused (the rest of objectui#12418's family) #12474 / console list toolbar: Hide Fields on a user's saved view that declares no columns is session-only after objectui#12474 (it used to persist by writing the shared view) #12505 family); the 2-second "Connected" flash of
ConnectionStatus.tsx(measured as by design: it hides itself after 2 s on reconnect).
Attribution: written by the domain:ui seat in session session_019YFzJp4o8Ck4NsT1toC4UP (account os-bill) on the maintainer's words quoted above.
Screenshots (added 2026-10-15T14:43Z, on the maintainer's word 「专用分支存图,卡里嵌 raw 链接」)
The images live on the attachment branch claude/ui-audit-2026-10-15-shots of objectstack-ai/objectui (commit 918a251, never merged; index at https://github.com/objectstack-ai/objectui/blob/918a2510a54bd6875b90eb6996d098c3ec0451dd/docs/design/console-audit-2026-10-15/README.md). Full set and figure numbers: the audit report linked above.
Nail 1 and nail 4: the view description's own row above the toolbar; the aggregate row above the record count
Nail 2: the standing lookup note in the sort popover
Nail 3: four KPI cards of about 215 px each



Activity
objectstack-fleet commentedon Oct 15, 2026
Claim: PM loop round 1
Session:
session_01XVp19E1MYUTFM3dnkwz8gBAccount:
baozhoutao(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-12528-list-dashboard-chromeWorktree:
objectui-issue-12528Domain:
domain:uiSeat:
domain:ui#2(the card names the lane's next dispatch under seat 3's post #9800, which is vacant)File surface:
packages/plugin-list/src/ListView.tsx(nail 1: theviewDescriptionrow; nail 2: the sort popover'ssortRelationalHint; nail 4: the record-count footer),packages/plugin-view/src/ViewTabBar.tsx(nail 1: the active tab's tooltip and ▾ menu header) and the console'sViewTabItembuild inpackages/app-shell/src/views/ObjectView.tsxonly to carry the description,packages/plugin-grid/src/ObjectGrid.tsx(nail 4: the column-summary footer),packages/plugin-dashboard/src/MetricCard.tsx(nail 3; revised 2026-10-15T18:44Z: nail 3 is re-scoped onto the path that draws the HotCRM KPI tiles, in a second PR on this card:packages/plugin-dashboard/src/DashboardRenderer.tsx(the shared widget Card for a dataset-bound metric),DatasetWidget.tsx,MetricWidget.tsx, andDashboardGridLayout.tsxonly if it draws the same frame),packages/i18n/src/locales/*.tsonly if a label is added, the tests beside each, and one.changeset/12528-*.md(stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opus(default tier;dispatch-gates --tier --repo objectstack-ai/objectuireads no path-derived mandate)Clause-②: yes (widening; measured by the dev on PR #12561:
ViewTabItem.descriptionin plugin-view andObjectGridComponentProps.onColumnSummaryChangein plugin-grid, both minor). Contract review atCONTRACT_REVIEW_TIERis owed on PR #12561Responsibility:
n/a — not a defect cardThread-read: none
Serial constraints cleared: at
main1f23d5b07, no open PR touchesListView.tsx,ObjectGrid.tsx,MetricCard.tsxorObjectView.tsx. Revised 2026-10-15T16:49Z: open PR #12537 (objectui#12518, the strip's host width) editsViewTabBar.tsx. This card's nail 1 touches that file only for the active tab's tooltip and ▾ menu header, a different region; whichever lands second mergesmainagain (a merge, never a rebase). Seat 1's objectui#12526 (PageHeader.tsx,DashboardView.tsx,AppHeader.tsx) is disjoint.objectstack-fleet commentedon Oct 15, 2026
os-dev-report
{
"issue": 12528,
"status": "needs_decision",
"branch": "claude/issue-12528-list-dashboard-chrome",
"pr": "#12561",
"session": "session_01XVp19E1MYUTFM3dnkwz8gB (subagent: the parent's id)",
"premise_still_valid": false,
"summary": "Draft PR #12561 (Part of #12528) lands nails 1, 2 and 4. Nail 1: ListView draws no view-description row. ViewTabItem gains an optional description that ViewTabBar draws on the active tab only, as the view button's title and as a DropdownMenuLabel heading its ▾ menu. ObjectView fills it with pickLocalized on the display locale, gated like the relay (false shows it nowhere). Where no strip is drawn (the console below sm, any host without toolbarLeading), ListView draws an Info control inside its toolbar row whose title and aria-label are the description. A refused view replaces ListView with the refusal face, unchanged. Nail 2: the standing paragraph is now an info control (sort-relational-info) beside the popover title, plus a one-line hint (new key list.sortRelationalPicked, ten packs) only while the current sort names a linked column. Nail 4: ObjectGrid gains an onColumnSummaryChange host prop. The grid then draws no summary row and reports its summaries, keyed by content, null on unmount. ListView draws them at the right of its record-count row, truncated, with a title tooltip. Nail 3 is NOT built. Its premise is false for the named site: MetricCard has no unit line and no description above the value, and the HotCRM Sales Performance KPI tiles are dataset-bound metric widgets. Those render through DashboardRenderer's shared widget Card (title plus line-clamp-2 description in CardHeader) and DatasetWidget, which draws MetricWidget's bare layout (value, then the measure label). Those files are outside the order's file surface, so the re-scope is open_questions[0]. The spec's dashboard widget declares no showDescription key. Clause-② was measured and corrected to yes (widening): ViewTabItem.description and ObjectGridComponentProps.onColumnSummaryChange; the changeset puts minor on plugin-view and plugin-grid.",
"tests": "Pins, each with a control: plugin-view ViewTabBar.description-12528 (4 tests); app-shell ObjectView.refreshInPlace-10035, new describe (page-level: no view-description or view-description-info, active tab title 'Every task'; showDescription false gives no title); plugin-list ListView.test + descriptionInlineLocale-7199 (info control, host-strip case); plugin-list ListView.relationalSort, new describe (no paragraph, info control, one-line hint; CONTROL: a plain sort shows no line); plugin-grid footerSummaryHost-12528 (report, no own row, null on unmount; CONTROL: no prop keeps the row); plugin-list ListView.footerOneRow-12528 (one record-count-bar with '9 records' and the summaries, truncate, title; CONTROL). Affected and new files under the lock at 9f355da: 14 files, 290 passed, exit 0. Ablations via the objectstack checkout's scripts/ablation-replace.mjs (anchor 1 to 0, blob changed, restore blob == HEAD, git diff HEAD empty; tests import src directly, so no dist): nail 1 tab title removed, 1 failed / 3 passed; nail 2 picked flag deleted, 1 failed / 8 passed; nail 4 grid host branch disabled, 2 failed / 1 passed; nail 4 list not handing the callback, 1 failed / 1 passed. Package dirs under the lock at 840554d: plugin-view 77 files / 716 passed; plugin-grid 217 / 2018 passed; plugin-list 136 / 1444 passed; i18n 83 files, 1321 passed and 13 skipped; all exit 0. NOT MEASURED: packages/app-shell/ dir. Reason: lock queue-timeout (exit 99), four times, behind holder pid 2416 (cwd objectui-issue-12505, a full packages/app-shell/ vitest run, alive at about 200% CPU, holding more than 38 minutes). NOT MEASURED: a re-run of the pins at final head ba5c03f, after the second merge of origin/main, which brought #12537's ViewTabBar host-width change. Same queue-timeout, same holder. That merge was textually clean, and grep confirms this branch's three view.description sites in ViewTabBar.tsx. type-check after a closure build of app-shell^... and app-shell (exit 0): plugin-view, plugin-list, plugin-grid, i18n, app-shell all exit 0 at a1c242a; plugin-grid again at 840554d, exit 0.",
"gates": {
"build app-shell^... + app-shell": "exit 0 (a1c242a)",
"type-check plugin-view/plugin-list/plugin-grid/i18n/app-shell": "exit 0 (a1c242a); plugin-grid exit 0 (840554d)",
"vitest touched+new (14 files)": "exit 0, 290 passed (9f355da)",
"vitest packages/plugin-view": "exit 0, 716 passed (840554d)",
"vitest packages/plugin-grid": "exit 0, 2018 passed (840554d)",
"vitest packages/plugin-list": "exit 0, 1444 passed (840554d)",
"vitest packages/i18n": "exit 0, 1321 passed 13 skipped (840554d)",
"vitest packages/app-shell": "NOT MEASURED: lock queue-timeout exit 99 behind pid 2416 (objectui-issue-12505 full app-shell run)",
"vitest pins at ba5c03f (post second merge)": "NOT MEASURED: same queue-timeout",
"eslint narrowed (25 changed ts/tsx, --format json)": "0 errors; per-file warnings vs origin/main: ObjectGrid 217 to 215, ListView 185 to 185, ViewTabBar 3 to 3, ObjectView 198 to 198; type-aware lint off (no parserOptions.project/projectService) so untouched files cannot move",
"check:phantom-deps": 0,
"check:self-import": 0,
"check:unreferenced-sources": 0,
"check:doc-example-readers": 0,
"check:handler-key-reads": 0,
"check:side-effects-array": 0,
"check:element-data-source-declaration": 0,
"check:spec-symbols": 0,
"check:action-forward-parity": 0,
"check:designer-field-key-parity": 0,
"check:icon-record-names": 0,
"check:i18n-keys": 0,
"check:i18n-drift": 0,
"check:i18n-designer-parity": 0,
"check:control-bytes": 0,
"check-changeset-fixed.mjs": 0,
"check-changeset-presence.mjs": 0,
"check-changeset-no-major.mjs": 0,
"check:new-line-citations": 0,
"hand-rolled-comment-mask test": "not owed (no test reads source)",
"doc-snippet-types": "not owed (no docs changed)"
},
"line_budget": "26 files vs origin/main after merge (the tracked .ts/.tsx/.md set of this branch); about 680 insertions and 67 deletions; one PR, nail 3 not built",
"files_changed": [
"packages/plugin-list/src/ListView.tsx",
"packages/plugin-view/src/ViewTabBar.tsx",
"packages/app-shell/src/views/ObjectView.tsx",
"packages/plugin-grid/src/ObjectGrid.tsx",
"packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts",
"packages/plugin-list/src/tests/ListView.test.tsx",
"packages/plugin-list/src/tests/ListView.descriptionInlineLocale-7199.test.tsx",
"packages/plugin-list/src/tests/ListView.relationalSort.test.tsx",
"packages/plugin-list/src/tests/ListView.sortFieldFallback.test.tsx",
"packages/plugin-list/src/tests/ListView.sortFieldRead-11943.test.tsx",
"packages/plugin-list/src/tests/ListView.sortabilitySignal.test.tsx",
"packages/plugin-list/src/tests/ListView.footerOneRow-12528.test.tsx",
"packages/plugin-view/src/tests/ViewTabBar.description-12528.test.tsx",
"packages/plugin-grid/src/tests/footerSummaryHost-12528.test.tsx",
"packages/app-shell/src/views/ObjectView.refreshInPlace-10035.test.tsx",
"packages/app-shell/src/views/ObjectView.externalRefreshSum-10572.test.tsx",
".changeset/12528-list-dashboard-chrome.md"
],
"deviations": [
"Nail 3 not built: its site is outside the file surface (see open_questions[0]); the PR says Part of #12528 instead of Fixes.",
"Clause-② corrected from the claim's no to yes (widening): ViewTabItem.description (plugin-view) and ObjectGridComponentProps.onColumnSummaryChange (plugin-grid); both minor in the changeset. This is the line the order anticipated.",
"Declared narrowing: the packages/app-shell/ dir run and the final-head re-run are NOT MEASURED (lock queue-timeout behind pid 2416); CI runs them.",
"Commits carry the model-free trailer pair per AGENTS.md; the harness reminder's model-named Co-Authored-By line was not copied.",
"The 10035 test's remount probe moved from the retired view-description node to list-toolbar-leading (also ListView-owned)."
],
"mcp_calls": "0",
"api_writes": "2 relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches): (1) pr_create, executed as POST /repos/objectstack-ai/objectui/pulls (draft) + POST /repos//issues/12561/assignees; (2) comment, executed as POST /repos//issues/12528/comments (this report). Zero label writes (objectui: the order named no label). git pushes are not counted here.",
"open_questions": [
{
"question": "Nail 3: the card's site (MetricCard.tsx) does not draw the HotCRM KPI tiles. Re-scope the nail onto the real path, build it literally on MetricCard, or drop it from this card?",
"options": [
"A. Re-scope onto the drawn path in a second PR on this card: DashboardRenderer.tsx's shared widget Card for a dataset-bound metric (the description leaves the CardHeader and becomes the title's tooltip; the measure label joins the title line), plus DatasetWidget.tsx and MetricWidget.tsx's bare layout (no label line under the value), and DashboardGridLayout if it draws the same frame. No schema key, so the description goes to the tooltip only. Cost: about 150 to 250 lines plus pins across three or four plugin-dashboard files. The surface widens, and the second PR carries Fixes #12528.",
"B. Build nail 3 literally on MetricCard.tsx (title and trend/unit on one line, description into title). Cost: about 60 lines. It changes no screen the card measured, because no HotCRM dashboard draws a metric-card node.",
"C. Drop nail 3 from this card and close the card when #12561 merges (body edited to Fixes by the seat). Cost: none now. The 215 px KPI tile stays until a new card is filed."
],
"recommendation": "A. Business need: the measured pain (four KPI tiles of about 215 px, two rows filling the first screen) is on the dataset-bound path, and only A changes it; B serves no measured screen. Long-term soundness: A fixes the frame that every dataset metric shares, and B adds polish to a component off the console's path. AI-error resistance: neutral. A adds no schema key; the description stays where the spec puts it, and the tooltip-only outcome follows the absence of a widget showDescription. Startup scope: A builds no new capability, just density on an existing surface, and B is speculative. The cost is a file-surface widening that the seat must approve."
}
],
"out_of_scope_findings": [
"carrier: none — noted, not filed. content/docs/plugins/plugin-grid.mdx (Column Summaries) and packages/plugin-view/README.md do not document the two new host props (onColumnSummaryChange, ViewTabItem.description). Outside this order's file surface; AGENTS.md #2 docs debt of this PR. Dedupe words: onColumnSummaryChange docs, ViewTabItem description README, column summary host footer",
"carrier: none — noted, not filed. The lock holder pid 2416 (objectui-issue-12505) ran a full packages/app-shell/ vitest run for more than 38 minutes at --maxWorkers=2. It was alive and progressing (about 200% CPU), but it starved every queued run; this is a reading for the seat, not a defect."
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
Seat answer to the dev's
open_questions[0](nail 3) ·domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-15T18:45ZAnswering the dev report
6199637771. This is a technical re-scope inside the card's own intent, not a product question, so the seat answers it.DashboardRenderer.tsx's shared widget Card,DatasetWidget.tsxandMetricWidget.tsx's bare layout, andDashboardGridLayout.tsxonly if it draws the same frame.Fixes objectstack-ai/objectui#12528; PR feat(plugin-list,plugin-view,plugin-grid): list chrome on fewer rows — description on the active tab, sort note as an info control, one footer row (objectui#12528) #12561 staysPart of objectstack-ai/objectui#12528.showDescription, so the description goes to the title's tooltip only, as the card's build line reads. ⛔ No new widget key in this card.6197415031was revised in place: the file surface now includes the dashboard files, and theClause-②:line reads yes (widening), as the dev measured.Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
os-dev-report
{"issue":12528,"pr":"https://github.com/objectstack-ai/objectui/pull/12561","round":"patch after contract review 6199817751","new_head":"fb6010f28","status":"done","fixed":["ListView.footerOneRow-12528.test.tsx awaits the summaries span (findByTestId); its CONTROL asserts only after the probe has reported (spy awaited, then a flushed act)","ObjectGridFooterSummary exported from packages/plugin-grid/src/index.tsx","docs: content/docs/plugins/plugin-grid.mdx (Column Summaries) documents onColumnSummaryChange with a typed tsx snippet; packages/plugin-view/README.md documents ViewTabItem.description"],"reproduction":"Under the lock: a copy of the OLD test whose probe reports 40 ms late fails with the CI message (Unable to find [data-testid=record-count-summaries]); a copy of the NEW test under the same delay passes (1 failed / 3 passed across the two copies). Both copies were deleted and never committed.","gates":{"vitest packages/plugin-list + plugin-view + plugin-grid at fb6010f":"exit 0, 431 files / 4181 tests","check:doc-snippets":0,"check:doc-examples":0,"check:doc-json":0,"check:doc-example-readers":0,"check:doc-fences":0,"check:doc-example-ids":0,"check:doc-types":0,"vitest packages/app-shell":"NOT MEASURED: run started under the lock, stopped by me after 38 min to free the shared lock (full suite is 1404 files); CI runs it"},"merged":"origin/main merged before fb6010f; merge commits only"}
Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
Landed (part 1 of 2) ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-15T21:08ZPR #12561 (nails 1, 2 and 4,
Part of objectstack-ai/objectui#12528) merged through the merge queue as646caea, which is onmain. Verified by content:origin/mainare blob-identical to the accepted headfb6010f.packages/app-shell/src/views/ObjectView.tsx, differs from that head by exactly PR fix(app-shell): Hide Fields on a saved view that declares no columns is the user's own and survives a reload (objectui#12505) #12567's (objectui#12505)activeViewIsSavedhunks, which landed first. The queue combined the two.What landed:
ViewTabItem.descriptionandonColumnSummaryChange, both minor bumps, both documented.The card stays open. Nail 3 is PR #12572 (
Fixes objectstack-ai/objectui#12528), which lands next.pm:dispatchedstays until then.Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
Landed (part 2 of 2) ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-15T21:24ZPR #12572 (nail 3,
Fixes objectstack-ai/objectui#12528) merged through the merge queue asd88e351, which is onmain. Verified by content: all 5 of its files onorigin/mainare blob-identical to the accepted head43c0b82.DashboardRendererdraws the title and the measure label on one line above the value. The description moves into the title's tooltip, with no card header. Every other widget is unchanged.main:646caea);d88e351).DashboardGridLayout's metric frame. This seat has claimed it.pm:dispatchedleaves with this note. The card closedcompletedon merge.Generated by Claude Code
feat(plugin-dashboard): a dataset-bound metric tile on two lines — ti…