Repository navigation
dashboards and reports waste the first screen: "Refresh All" takes a grid row of its own (~120px band under the filter bar), and report tables sit in a fixed-height empty card #11694
Description
Activity
objectstack-fleet commented
on Oct 6, 2026 ContributorAuthorMore actionsPath: ② the capabilities an end user meets in the app — dashboards and reports | 缺项 | P2
Triage: first grade —
enhancement·priority:p3·domain:ui·area:reports·pm:queue. "Refresh All" joins the filter bar's row, and the report view drops its fixed tall minimumTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-06T06:58Z. ⛔ Not a claim, ⛔ not a dispatch. Re-read on objectuimain(c3623eb116): live, and not a duplicate of objectui#11674–#11690.Triage: lands in
packages/plugin-dashboard/src/DashboardRenderer.tsx(the refresh button as its owncol-span-fullgrid item, about:1335–:1336and:1434) andpackages/app-shell/src/views/ReportView.tsx(min-h-150, about:427) ⇒domain:ui; rationale: the button takes a full grid row with at least 80 px plus the gap, and the report view forces about 600 px.- Correction to the card: the report half is not
LegacyReportRenderer, which draws only pre-spec{data, columns}reports. It isReportView.tsx'smin-h-150. - Done when: the refresh control shares the filter bar's row, or the header's, and a report's height follows its content within the page.
- Pin: the first screen of the measured dashboard shows its first widget row without scrolling at 1440×900.
Generated by Claude Code
- Correction to the card: the report half is not
- addedarea:reportsBusiness reporting — dashboards, reports, the numbers a manager readsBusiness reporting — dashboards, reports, the numbers a manager readsdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seatenhancementNew feature or requestNew feature or requestand removed
on Oct 6, 2026 objectstack-fleet commented
on Oct 6, 2026 ContributorAuthorMore actionsClaim: PM loop round 1
Session:session_01DrKzdPdyLLBW3qpZ4vtk7z
Account:huangyiirene
Branch:claude/issue-11694-first-screen-layout
Worktree:objectui-issue-11694
Domain:domain:ui
Seat:domain:ui#1
File surface:packages/plugin-dashboard/src/DashboardRenderer.tsx(refreshButton, about:1335, and its two render sites about:1376and:1434, plus the filter bar row it should join);packages/app-shell/src/views/ReportView.tsx(the report card'smin-h-150, about:427); the tests beside them; and.changeset/11694-*.md(stop on breach; explain in the report)
Container & model:M,mode:subagent,model: opus(dispatch-gates --tier --repo objectstack-ai/objectui: no path-derived mandate; default tier)
Clause-②: no
Thread-read: 6011106716
Serial constraints cleared:none. No open objectui PR touchespackages/plugin-dashboard/src/**orReportView.tsx(objectui#11727, #11725, #11724, #11722, #11069 and the release PR objectui#11600 were read), and no in-flight claim names either file. Read 2026-10-06T15:46Z.Why
Clause-②: no: the refresh control moves into an existing row and the report card's height follows its content. That is layout inside published components: no export, prop, type member or i18n key is added to anything a package entry reaches, and no accepted input widens. If the fix needs a new prop (for example to place the control from the host), the dev stops and reports instead of adding it.objectstack-fleet commented
on Oct 6, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 11694,
"status": "needs_decision",
"branch": "claude/issue-11694-first-screen-layout",
"pr": "#11737",
"session": "session_01DrKzdPdyLLBW3qpZ4vtk7z (subagent; the dispatching PM's session, as stamped in the commits' Claude-Session trailer)",
"premise_still_valid": true,
"summary": "Draft PR objectui#11737 (head 56995c1) implements everything inside the Clause-② fence. Dashboard Refresh All, with its record-count badge, now sits right-aligned in the filter bar's row; with no filter bar it joins the renderer's own header row; otherwise it stands alone in a button-high row. In the positioned (explicit-columns) grid, the full-width rows above the widgets are explicit auto tracks, so they no longer take the minmax(5rem, auto) floor. ReportView's card drops min-h-150. Real-layout reading at 1440x900 (real DashboardView/ReportView with the console's CSS, AppShell chrome reproduced by size): on Delivery Operations the first chart row went from 569-1026 to 429-886, on Chart Gallery from 473-930 to 429-886, and the report card from 600px (396px empty) to 205px. Premise correction: H1 holds but is half the story. The filter bar is itself a col-span-full grid item, and its row also took the 5rem floor (32px controls stretched to 72px plus mb-2), so the band was two floored rows. H3 holds: nothing inside the card needed the minimum. One case needs a decision (open_questions): on a console dashboard with no filter bar, the only header is DashboardView's page header, which the renderer cannot reach without a new prop or export (fenced by Clause-② no). There the control keeps a row of its own, now 36px plus the gap instead of 80px plus the gap. The PR therefore opens with 'Part of #11694'. If option A is accepted, this PR completes the card.",
"tests": {
"head_gate_union_ran_on": "56995c1",
"gates": [
"pnpm exec turbo run build --filter='@object-ui/plugin-dashboard^...' --concurrency=2 → exit 0 → 'Tasks: 12 successful, 12 total' (on fea4eb0; plugin-dashboard deps unchanged since)",
"pnpm exec turbo run build --filter='@object-ui/app-shell^...' --concurrency=2 → exit 0 → 'Tasks: 28 successful, 28 total' (on fea4eb0)",
"pnpm --filter @object-ui/plugin-dashboard type-check → exit 0 → script echoed 'tsc --noEmit && tsc -p tsconfig.test.json' (on 56995c1; tsconfig.test.json includes src/**/*.test.tsx)",
"pnpm --filter @object-ui/app-shell type-check → exit 0 → script echoed (re-run after the last app-shell test edit, content identical to 56995c1)",
"pnpm --filter @object-ui/plugin-dashboard test --maxWorkers=3 → exit 0 → 'Test Files 173 passed (173)', 'Tests 1704 passed | 6 skipped (1710)' (on 56995c1)",
"pnpm --filter @object-ui/app-shell test --maxWorkers=3 --shard=1/4..4/4 → exit 0 x4 → 259 passed; 258 passed | 1 skipped; 258 passed; 258 passed = 1034 files (on fea4eb0)",
"pnpm exec vitest run (the 14 app-shell test files naming ReportView or DashboardView, incl. the new pin) → exit 0 → 'Test Files 14 passed (14)', 'Tests 85 passed (85)' (on 56995c1)",
"pnpm check:control-bytes → exit 0 → 'check-control-bytes: OK (scanned 7674 tracked text file(s); skipped 85 binary).'",
"pnpm check:new-line-citations → exit 0 → 'VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only → exit 0'",
"pnpm check:changeset-claims → exit 0 → 'No pending changeset names a file this change touches.'",
"pnpm check:pending-changeset-literals → exit 0 → 'No test source names a pending changeset.'",
"ADDED (diff adds vi.mock test doubles): pnpm check:vi-mock-specifiers / check:vi-mock-inherit / check:vi-mock-override-shape → exit 0 each → 'OK' lines",
"ADDED: pnpm check:test-path-roots → exit 0 → 'check-test-path-roots: OK'",
"ADDED: node scripts/check-changeset-presence.mjs → exit 0 → '4 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s): .changeset/11694-first-screen-layout.md.'",
"ADDED: node scripts/check-governed-queue-guard.mjs --test (the 5 changed paths) → exit 0 → 'NOT GOVERNED — 5 path(s) checked against 5 governed surface(s); none matched.'",
"lint, declared narrowing: eslint --format json from each package dir (as each package's lint script runs it) on the 4 touched source/test files → exit 0 → 2+2 files, 0 errors; population = the root flat config each package lint uses; no type-aware linting configured and no custom rule reads another file, so the diff cannot move a verdict on an untouched file; remaining warnings sit on untouched lines. Repo-wide pnpm lint is CI's."
],
"pins": "packages/plugin-dashboard/src/tests/DashboardRenderer.refreshRow-11694.test.tsx (5 cases), packages/app-shell/src/views/ReportView.cardHeight-11694.test.tsx (1 case): 6 passed on the fix",
"reverse_verification": "fix and pins committed first; reverse.sh checked out DashboardRenderer.tsx and ReportView.tsx from base 267b539 under trap 'git checkout HEAD -- (abs paths)'. On-disk marker grid-rows-[auto] went 1 → 0. The ReportView marker min-h-150 counted 1 → 1 (not discriminating: the new comment names it). Proof for that file is the failure diff's received class string ending 'overflow-hidden min-h-150'. Result on the mutated tree: 'Tests 5 failed | 1 passed (6)', exit 1; the passing case is the no-auto-track negative control, green in both directions by design. Restore proven: blob of each file == HEAD blob, and git diff HEAD is empty for both paths.",
"real_layout": "dev-only harness (never committed, deleted): console Vite dev server, real DashboardView/ReportView, AppShell chrome reproduced by size (56px top bar, 16rem sidebar), showcase definitions copied verbatim from objectstack examples/app-showcase at 1bc6ca1d, stub queryDataset, Chromium via /opt/pw-browsers/chromium at 1440x900 and 390x844. Before = 267b539, after = fd0ac51 (toolbar re-read on the 56995c1 change). Numbers are in the PR body.",
"deviations": [
"WRITE REFUSED, not retried: 'node scripts/pm/label-write.mjs --repo objectstack-ai/objectui --issue 11737 --assign huangyiirene' (cwd /home/user/objectstack) → 'Permission for this action was denied by the Claude Code auto mode classifier. Reason: [External System Writes]'. Refused before any network call; the PR has no assignee. Seat to perform.",
"PR first line is 'Part of #11694', not the order's closing-keyword form. The os-dev definition uses Part of when the merge should not close the card, and the console no-filter-bar case is in open_questions. If A is accepted, the seat edits line 1 to the closing form or closes the card after merge.",
"Test invocation: objectui's repo-root form 'pnpm exec vitest run (paths)' and the package test scripts, not the os-dev 'pnpm --filter PKG exec vitest run' form, because objectui's vitest-invocation-guard refuses package-cwd runs (AGENTS.md, running tests). Conflict named, the landing repo's rule followed.",
"app-shell full suite last ran on fea4eb0. The later commits are the changeset, the pin's types (any → typed casts) and the filter bar class flex-1 → grow. On 56995c1 the 14 targeted app-shell files and app-shell type-check were re-run. Declared narrowing; CI runs the full farm.",
"Real-layout chrome is a size reproduction of AppShell, not the full console (no backend boot; the objectstack checkout has no install). Absolute y-figures depend on the real header and description length; the deltas are the mechanism."
],
"files_changed": [
".changeset/11694-first-screen-layout.md",
"packages/app-shell/src/views/ReportView.cardHeight-11694.test.tsx",
"packages/app-shell/src/views/ReportView.tsx",
"packages/plugin-dashboard/src/DashboardRenderer.tsx",
"packages/plugin-dashboard/src/tests/DashboardRenderer.refreshRow-11694.test.tsx"
],
"cleanup": "own Vite dev servers stopped by recorded PID ancestry (port 5194 free); harness files deleted; worktree node_modules removed and 'git worktree remove' succeeded without --force after confirming local HEAD == remote 56995c1"
},
"mcp_calls": "0 — no MCP tool called",
"api_writes": "2 landed + 1 refused. (1) pr_create via scripts/pm/fleet-write/dispatch.mjs: POST /repos/objectstack-ai/objectstack/dispatches (204), relay run 37500146785 success, POST /repos/objectstack-ai/objectui/pulls → objectui#11737, read-back 10268 bytes sent = 10268 stored, identical. (2) this os-dev-report comment via scripts/pm/post-stamped.mjs: POST /repos//issues/11694/comments. (refused) label-write --assign: refused locally by the permission classifier, nothing sent. Not REST: git push of claude/issue-11694-first-screen-layout (one branch; probe push plus three updates).",
"open_questions": [
{
"question": "On a console dashboard with NO filter bar (Chart Gallery), the only header is DashboardView's page header, whose right side is empty. The renderer cannot put Refresh All there without a new seam, which the claim's Clause-② no fences out. Should the control stay in its own button-high row there (this PR), or should a host placement seam be added?",
"options": [
"A — accept this PR's placement: own row, 36px plus the grid gap (was a 5rem row plus the gap). Business need: measured, the Chart Gallery first chart row is on screen at 1440x900 (bottom 886); the remaining cost is one button-high row, and nothing beyond the card's wording asks for header placement. Long-term: one rule for one control (filter row, else renderer header row, else own row), and no second placement path. AI-error: neutral, no metadata or authoring key either way. Startup focus: zero new surface. Cost: on no-filter console dashboards Refresh stays on its own row.",
"B — add a host placement seam: a DashboardRenderer prop (a render slot or portal target), or export useDashboardAutoRefresh plus the control, so DashboardView draws Refresh in its page header (as ReportView draws its Edit button there). Business need: removes the last band, about 52px, on no-filter console dashboards. Long-term: a second placement path whose auto-refresh timer and refreshing indicator must move with it; Clause-② yes (public prop or export, .d.ts change, changeset minor). AI-error: small; it is a host prop, but SchemaRenderer forwards a node's unknown keys as props, so a public prop is reachable from metadata. Startup focus: new public surface for about 52px; new-surface default is no.",
"C — let the renderer draw the console header: DashboardView stops passing hideHeaderText and drops its own header, so Refresh joins the renderer's header row everywhere. Business need: same visual result as B. Long-term: reverses the chrome split that the header-wrapper and root-title retirements were built on; the page title moves into the scroll region (h1 to h2, no longer pinned). AI-error: neutral. Startup focus: the widest blast radius of the three for the smallest gain."
],
"recommendation": "A. Measured, the pin and the card's 'first row of charts below the fold' are met on both dashboards. B and C each buy about 52px with a new public surface (B) or a chrome reversal (C), and the startup-focus axis defaults new surface to no. If A is accepted, the seat switches objectui#11737's first line to the closing form, or closes the card by hand after merge."
}
],
"out_of_scope_findings": [
"carrier: none (承接者:无) · noted, not filed — the record-count badge's word 'records' is a literal English string in DashboardRenderer (pre-existing; this PR moves the badge, not its text); read-only observation, no locale measurement taken; in the PR's Acceptance notes",
"carrier: the plugin-stylesheet family described in scripts/build-plugin-stylesheet.mjs (objectui#4929 lineage) · noted, not filed — @object-ui/plugin-dashboard ships no stylesheet, so its utilities (the new grid-rows-[auto] and grid-rows-[auto_auto] among them) reach a published consumer only through that host's own Tailwind scan; the console scans packages/plugin-dashboard/src; read-only observation; in the PR's Acceptance notes"
]
}
Generated by Claude Code
objectstack-fleet commented
on Oct 6, 2026 ContributorAuthorMore actionsReview: ACCEPT — PR objectui#11737 (head
56995c1) · the dev's open question ruled A by this seatdomain:uiseat 1 ·session_01DrKzdPdyLLBW3qpZ4vtk7z· reviewed against the PR diff and check runs, read 2026-10-06T17:05Z. Report:os-dev-reporton this card.The ruling: console dashboards with no filter bar
A — the refresh control keeps a button-high row of its own there. B (a host placement seam) and C (the renderer draws the console header) are not taken.
- Why the seat rules instead of escalating: existing norms decide it.
- The pm-dispatch decision framework says a new public surface with no measured pull defaults to no.
- The card's pin is measured as met in every case. In the PR body's real-layout reading at 1440×900, the Chart Gallery first chart row ends at y=886 on screen.
- Four-axis reasoning:
- Long term: A keeps one placement rule (filter row, else the renderer's header row, else a button-high row of its own). B adds a second placement path that the auto-refresh timer and the refreshing indicator would have to follow. C reverses the console's chrome split.
- Business need: what remains is about 52px on dashboards with no filter bar, and no user or card asks for it.
- Guarding against AI-authored mistakes: B's public prop would be reachable from metadata, because SchemaRenderer forwards unknown keys as props.
- Startup focus: A adds zero surface.
- What this changes on the card: triage's "shares the filter bar's row, or the header's" is met wherever a header the renderer draws exists. On the one remaining layout the control's own row shrinks from a 5rem row to the button's height. Under this ruling the card is complete: the seat switches the PR's first line from
Part ofto the closing form, and records the ruling in the PR body's "Open question" section. - If the maintainer wants B, that is a new card for the
Clause-②: yesseam, and it does not reopen this one.
Review
- Shape: draft →
main;Clause-②: noat line start. - PR assignee: not set. The dev's
label-write --assignwas refused by its session's permission classifier. The seat does not re-issue it; this is raised with the maintainer. - Scope: 5 files, all inside the claim's surface:
DashboardRenderer.tsx,ReportView.tsx, one pin each, and one changeset. +387/−84; most of the −84 is re-indentation of the unchanged header block. Nothing undercontent/docs/releases/. - Clause-② re-read on the diff: layout and class changes only. No export, prop, type member or locale key changes ⇒
noholds. - Mechanism checked on the diff:
refreshControl(the badge and the button) is placed in the filter row when filters exist, and in the renderer header row when there are none (refreshInHeader), otherwise in its own row.- The positioned grid's full-width rows get explicit
autotracks, so the filter row no longer takes theminmax(5rem, auto)floor. The dev measured this as a second floored row that H1 had missed; the correction is accepted. ReportView's card losesmin-h-150. The comment in the diff gives the reason nothing inside needs it: loading and not-found return earlier, and the chart has a fixed height.
- Changeset (
plugin-dashboardpatch,app-shellpatch). Sentences checked against the diff:- "It now sits at the right end of the filter bar's row": matches.
- "…without filters it sits at the right of the dashboard's own header when the dashboard draws one, and otherwise stands alone in a row only as tall as the button": matches the ruled A.
- "The header and the filter bar row no longer take that minimum": matches.
- "The card is now as tall as the report": matches.
- Tests: 6 new pins (5 dashboard, 1 report). Reverting both source files to base turned 5 red, and the negative control stayed green by design; the restore was proven by the blob hash. Local
plugin-dashboardsuite: 173 files.app-shellfull suite atfea4eb0, plus the 14 targeted files at56995c1(declared narrowing; CI runs the farm). The real-layout before/after is in the PR body. - CI at
56995c1: 42 runs, still converging (15in_progressat this read); no completed run is red. Enqueue waits for every check to be green. - Out-of-scope findings:
- The record-count badge's word "records" is a hard-coded English literal. This PR moves it but does not create it. It is the objectui#11689 family (hard-coded English words in display chrome), so it is carried there in a comment, not a new card.
plugin-dashboardships no stylesheet, so a consumer must scan its source for the new arbitrary utilities: Acceptance notes. The console scans it.
Generated by Claude Code
- Why the seat rules instead of escalating: existing norms decide it.
objectstack-fleet commented
on Oct 6, 2026 ContributorAuthorMore actionsCorrection to the review above (
6021376729): the PR body was not changeddomain:uiseat 1 ·session_01DrKzdPdyLLBW3qpZ4vtk7z· 2026-10-06T17:07Z.The seat's edit of PR objectui#11737's body was refused by this session's permission classifier before anything was sent. The seat has not retried it by any other route, and has raised it with the maintainer. So:
- The PR still opens with
Part of #11694, and its "Open question" section still reads as open. The ruling A in6021376729stands. That comment, not the PR body, is the current record. - Merging the PR will not close this card. What happens to the card at merge is the maintainer's call; the seat has asked.
Generated by Claude Code
- The PR still opens with
objectstack-fleet commented
on Oct 6, 2026 ContributorAuthorMore actionsLanded: PR objectui#11737 →
main02a3896· the card stays open, waiting on the maintainerdomain:uiseat 1 ·session_01DrKzdPdyLLBW3qpZ4vtk7z· verified 2026-10-06T17:36Z.- Merged through the merge queue at 2026-10-06T17:36:18Z. It was added to the queue at 2026-10-06T17:19:51Z.
- Content check against the first parent:
02a3896is one squash commit on parent2b635e5. It changes 5 files, +387/−84, the same as the PR.git diff 56995c1 02a3896over those paths is 0 bytes: the landed bytes are the reviewed head's bytes.- On
main:refreshInHeaderhitsDashboardRenderer.tsx, andReportView.tsx's card no longer carriesmin-h-150.
- The card stays open, deliberately. The PR's first line is
Part of #11694, because the seat's edit to the closing form was refused (6021408428). Under the seat's ruling A (6021376729) nothing on this card remains to build.- Waiting on: the maintainer, asked in the seat's session chat at about 2026-10-06T17:10Z, whether the seat closes this card by hand.
- Until the answer comes,
pm:dispatchedand the assignee stay as they are.
- Landing window: objectui#11714 also closed after 17:19Z. It is another seat's card, closed by its own PR; this PR closes nothing.
Generated by Claude Code
objectstack-fleet commented
on Oct 7, 2026 ContributorAuthorMore actionsClosed by hand: complete under ruling A · PR objectui#11737 →
main02a3896domain:uiseat 1 ·session_01DrKzdPdyLLBW3qpZ4vtk7z· 2026-10-07T02:29Z.- The maintainer's answer, given in this seat's session chat, verbatim: 「dashboards and reports waste the first screen: "Refresh All" takes a grid row of its own (~120px band under the filter bar), and report tables sit in a fixed-height empty card #11694 手动关闭,三个 PR 的 assignee 也补上」. It answers the question left open in the landing note (
6021914187). - Why the card is complete:
- Under the seat's ruling A (
6021376729), nothing on this card remains to build. - "Refresh All" sits in the filter bar's row, or in the renderer's header row when there is no filter bar, or else in a row only as tall as the button.
- The report card has no fixed minimum height.
- The landed bytes match the reviewed head
56995c1(0-byte diff,6021914187).
- Under the seat's ruling A (
- Why the merge did not close it: the PR opened with
Part of #11694, and the seat's edit to the closing form was refused (6021408428). - This close:
completed, andpm:dispatchedis removed with it. If the maintainer later wants option B (a host placement seam for the control on dashboards with no filter bar), that is a newClause-②: yescard and does not reopen this one.
Generated by Claude Code
- The maintainer's answer, given in this seat's session chat, verbatim: 「dashboards and reports waste the first screen: "Refresh All" takes a grid row of its own (~120px band under the filter bar), and report tables sit in a fixed-height empty card #11694 手动关闭,三个 PR 的 assignee 也补上」. It answers the question left open in the landing note (
- added a commit that references this issue
on Oct 7, 2026
Filing gate: ① user-visible UX defect —
reach:every console dashboard, measured in the browser; separate card under the maintainer's quota exception (provenance below).Reader: objectui triage first-touch →
domain:uilane (packages/plugin-dashboard,packages/plugin-report); one PR.Dedup (semantic search, open + closed, objectui): "dashboard Refresh All button occupies its own row, empty vertical space between filter bar and widgets" → 4 hits, all closed, none about layout (objectui#11097 notes the button is on every dashboard since objectui#11062).
Environment: framework
ce577ec4showcase booted withobjectstack dev --ui --seed-adminon its own port and SQLite file; console = objectuif9f4a62d(apps/consoleVite dev server, and a productionvite buildfor timings); Chromium 141 headless, 1440×900, seeded admin.Repro
/apps/showcase_app/dashboard/showcase_ops_dashboard).Where
packages/plugin-dashboard/src/DashboardRenderer.tsx—refreshButtonis aflex justify-end mb-2block withcol-span-full, rendered as an item of the widget grid (so it gets a full grid row plus the grid gap), separate from the filter bar above the grid.packages/plugin-report/src/LegacyReportRenderer.tsx— containers withmin-h-[300px]/min-h-[100px](likely the report-card height; not instrumented in this run).Expected
"Refresh All" shares the filter bar's row (right-aligned), adding no row of its own; a report card is as tall as its content.
Provenance: filed as a separate card on the maintainer's explicit instruction in the dispatching session, which lifts the per-fire filing quota for this batch of seven, verbatim — 「重新思考以上和用户体验相关的问题,哪些需要立卡」 then 「分开立 7 张」. Found during a browser dogfood sweep of the console; companion checklist card #11672. Filed by session
session_01WEARdKfDHDT7rTuEhLyT1k.Generated by Claude Code