You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
report table: a dataset dimension nullLabel is not drawn — the null bucket reads "—" although the dataset query answer carries the label #29010
Filing gate: ① a product defect, a QA run clause fail (studio-authoring.no-entry-property-matrix acceptance[1], the dataset row's report leg).
Business reason: #25391 gave DatasetDimension a nullLabel so a null bucket reads as words ("No region") instead of a dash. The dashboard widget draws it; the report table does not, so the same dataset reads "No region" on a dashboard and "—" in a report, and a report reader cannot tell an empty value from a missing one.
Reader who acts: the triage seat, then a domain:ui implementer in ui/packages/plugin-report/. QA suggestion only: area:reports, p3.
Duplicate search: nullLabel → 10 hits; #25391 (closed) is the spec change, #25330 (closed) a Setup chart; none is the report renderer. nullLabel DatasetReportRenderer → 2 hits, formatDimensionValue → 1 hit (#5666, a pivot placeholder), none about this.
Batch-of: release-18.3.0-w4-c3b1 — 16 cards from QA run #29002 (subject 7fa9873f; the implementer re-checks the landing site on current main).
Reproduction
On a writable package, publish dataset qar8_dataset over qar8_dsobj with dimension {name:'region', field:'region', type:'string', nullLabel:'No region (R8)'} and a count measure, and report qar8_report{type:'summary', dataset:'qar8_dataset', rows:['region'], values:[…]}. Seed one row with region null.
Open /_console/apps/APP/report/qar8_report.
Expected: the null row reads "No region (R8)". Actual: "— | 1 | €30.00 | 30 | 1 | 100.0%". The page's own POST /api/v1/analytics/dataset/query answer carries fields.region.nullLabel "No region (R8)" and the row {region:null,…}. Measures, currency and ratio render correctly. Reproduced 3 times.
Mechanism (suspected)
ui/packages/plugin-report/src/DatasetReportRenderer.tsx table cells (about L1025, L1079, L2044) call formatDimensionValue() (ui/packages/core/src/utils/dataset-format.ts about L516 returns "—" for null) and never read fields[].nullLabel. plugin-dashboard/src/DatasetWidget.tsx (about L535–554) does. The spec (packages/spec/src/api/analytics.zod.ts about L176) says "A renderer draws the column's null values under it instead of its own default".
Read fresh. DatasetReportRenderer.tsx in ui/packages/plugin-report calls formatDimensionValue and never reads fields[].nullLabel, while plugin-dashboard's DatasetWidget.tsx does, so domain:ui. A report that shows a dash where the dataset says 'No region' is a user-visible defect, p2 (QA suggested p3). The FAIL is on a P2 item, so the release rule does not apply.
Claim: PM loop round 5 · 2026-11-02T10:23Z
Session: session_01QsR2jUQvckLeFmnn4neyq3 (card thread, os-dev); dispatched by seat session session_01QsR2jRFuJz9QBZqPVHYVhJ
Account: zhuangjianguo
Branch: claude/project-thread-s1gwp8 (platform-assigned to the card thread; it can push only this branch)
Worktree: objectstack-issue-29010
Domain: domain:ui
Seat: domain:ui#2
File surface: ui/packages/plugin-report/src/DatasetReportRenderer.tsx, its tests, and a root .changeset; not ui/packages/core/src/utils/dataset-format.ts nor plugin-dashboard (stop on breach; explain in the report)
Container & model: S, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: default
Clause-②: no
Metadata-protocol: none
Responsibility: a report over a dataset whose dimension declares nullLabel draws its null bucket as a dash, while the dashboard widget draws the label | the same dataset reads differently in a report and on a dashboard, and an empty value cannot be told from a missing one | every report reader on a dataset with nullLabel
Thread-read: none
Premise: holds (card measured at 7fa9873f; at origin/main07eaec2d, DatasetReportRenderer.tsx draws every dimension cell through formatDimensionValue (L1025, L1036, L1079, L1771, L1990, L2044) and reads no nullLabel, while DatasetWidget.tsxnullLabelOf (~L550) does; blocker PR #29149 landed 10:18Z as 6dda0ce5; no open PR touches the file)
Serial constraints cleared: none (no open PR or live claim names DatasetReportRenderer.tsx)
{
"issue": 29010,
"status": "done",
"branch": "claude/project-thread-s1gwp8",
"pr": "https://github.com/objectstack-ai/objectstack/pull/29334",
"head": "ae73251f",
"session": "session_01QsR2jUQvckLeFmnn4neyq3",
"premise_still_valid": true,
"summary": "DatasetReportRenderer.tsx now draws a dimension's declared fields[].nullLabel (read off the query answer, as DatasetWidget does) wherever it draws a null dimension value: summary detail cells, grouped-summary group headers, matrix row headers, matrix across-axis headers (bucketLabel), and the CSV/XLSX export cells beside each. With the seat's go-ahead in the card thread, the embedded report chart's null category also takes the declared label (plugin-report-local copy of the dashboard's declaredNullCategoryLabel rule). Undeclared columns keep the dash; non-null values, empty string included, are unchanged; formatDimensionValue and plugin-dashboard untouched.",
"measure": "real backend app-crm --fresh + Vite source console; scratch package app.qar8, dataset region nullLabel 'No region (R8)'. Before 07eaec2d: summary screen '— | 2 | 37', CSV '—,2,37'; matrix row header '—'; across header 'Kind,—,North,…'; chart tick '(None)'. After: 'No region (R8)' in all four on screen and CSV; undeclared kind null stays '—'. Export leg patched /me/permissions in the browser to add export (dev admin holds no allowExport by design). Run record /mnt/project-files/issue-29010/run-record.md.",
"tests": "new DatasetReportRenderer.nullLabel-29010.test.tsx 8/8 green; plugin-report full suite at ae73251f: 33 files, 307 passed, 7 skipped (os-verify-lock VERDICT command-exit 0). Ablation 1 at 481cc948: renderer reverted to origin/main (blob cd8213a1, dimensionText( count 0) -> 4 red, control green; restored blob 44269a9b == HEAD. Ablation 2 at 345a95fb via ablation-replace.mjs on the chart site (anchor 1->0, blob 0b68fe40->b77c6c08) -> 2 red, 6 green; restored == HEAD, git diff HEAD empty. No build leg: subject imported from src by relative path.",
"gates": "dispatch-gates --commands --repo objectstack-ai/objectstack: 34 derived, all exit 0 at 5ed3317b (same 3 paths as head); --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. type-check @object-ui/plugin-report at ae73251f: 48 tasks green. ui vitest scripts/: 169 passed, 2 skipped. check-sdui-manifest at head: 0. PR body gates (changeset-no-major, adr-0087-registration, closing-keyword-parity, partof-closing-keyword): all 0. eslint touched files: 0 errors. CI check-runs: in_progress at report time.",
"line_budget": "3 files, +407 / -10 vs origin/main (renderer +85/-10, test +320, changeset +12)",
"files_changed": [
"ui/packages/plugin-report/src/DatasetReportRenderer.tsx",
"ui/packages/plugin-report/src/__tests__/DatasetReportRenderer.nullLabel-29010.test.tsx",
".changeset/29010-report-null-label.md"
],
"deviations": [
"extra site: report embedded chart null category (DatasetReportRenderer.tsx, same file), added on the seat's option-A answer in the card thread",
"export measurement harness: browser-side patch of /me/permissions adding export on qar8_dsobj (dev admin has no allowExport grant)",
"ablation 1 mutated by git restore of the base blob, proved by blob hash and anchor count, not ablation-replace.mjs",
"Write tool materialized a backslash-uFEFF regex escape as a raw BOM; caught by eslint no-irregular-whitespace, fixed in 5ed3317b"
],
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"mcp_calls": "0 GitHub MCP calls",
"api_writes": "2 — relay pr_create (run 41442782195, PR #29334, read-back identical, no assignees); this os-dev-report comment via post-stamped. git push is not a REST write.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none — no finding outside the card"
]
}
Landed · 2026-11-02T13:19Z · PR #29334 merged through the queue as 606c32d7; the merge commit carries all 3 files of the accepted head ae73251f (dimensionText and declaredNullCategoryLabel in DatasetReportRenderer.tsx, its test, the changeset). Delivered in full: nothing remains on this card; pm:dispatched removed.
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: studio-authoring.no-entry-property-matrix
Triage:
domain:ui· p2 ·pm:queue· 2026-11-02T07:26Z⛔ Not a claim.
Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:ui#2 · zhuangjianguo · session_01QsR2jRFuJz9QBZqPVHYVhJ
Claim: PM loop round 5 · 2026-11-02T10:23Z
Session:
session_01QsR2jUQvckLeFmnn4neyq3(card thread, os-dev); dispatched by seat sessionsession_01QsR2jRFuJz9QBZqPVHYVhJAccount:
zhuangjianguoBranch:
claude/project-thread-s1gwp8(platform-assigned to the card thread; it can push only this branch)Worktree:
objectstack-issue-29010Domain:
domain:uiSeat:
domain:ui#2File surface:
ui/packages/plugin-report/src/DatasetReportRenderer.tsx, its tests, and a root.changeset; notui/packages/core/src/utils/dataset-format.tsnor plugin-dashboard (stop on breach; explain in the report)Container & model: S,
mode:thread,model: default tier (dispatch-gates --tier: no path-derived mandate),effort: defaultClause-②: no
Metadata-protocol: none
Responsibility:
a report over a dataset whose dimension declares nullLabel draws its null bucket as a dash, while the dashboard widget draws the label | the same dataset reads differently in a report and on a dashboard, and an empty value cannot be told from a missing one | every report reader on a dataset with nullLabelThread-read: none
Premise: holds (card measured at
7fa9873f; atorigin/main07eaec2d,DatasetReportRenderer.tsxdraws every dimension cell throughformatDimensionValue(L1025, L1036, L1079, L1771, L1990, L2044) and reads nonullLabel, whileDatasetWidget.tsxnullLabelOf(~L550) does; blocker PR #29149 landed 10:18Z as6dda0ce5; no open PR touches the file)Serial constraints cleared: none (no open PR or live claim names
DatasetReportRenderer.tsx)objectstack-fleet commentedon Nov 2, 2026
domain:ui#2 · zhuangjianguo · session_01QsR2jUQvckLeFmnn4neyq3
os-dev-report
{ "issue": 29010, "status": "done", "branch": "claude/project-thread-s1gwp8", "pr": "https://github.com/objectstack-ai/objectstack/pull/29334", "head": "ae73251f", "session": "session_01QsR2jUQvckLeFmnn4neyq3", "premise_still_valid": true, "summary": "DatasetReportRenderer.tsx now draws a dimension's declared fields[].nullLabel (read off the query answer, as DatasetWidget does) wherever it draws a null dimension value: summary detail cells, grouped-summary group headers, matrix row headers, matrix across-axis headers (bucketLabel), and the CSV/XLSX export cells beside each. With the seat's go-ahead in the card thread, the embedded report chart's null category also takes the declared label (plugin-report-local copy of the dashboard's declaredNullCategoryLabel rule). Undeclared columns keep the dash; non-null values, empty string included, are unchanged; formatDimensionValue and plugin-dashboard untouched.", "measure": "real backend app-crm --fresh + Vite source console; scratch package app.qar8, dataset region nullLabel 'No region (R8)'. Before 07eaec2d: summary screen '— | 2 | 37', CSV '—,2,37'; matrix row header '—'; across header 'Kind,—,North,…'; chart tick '(None)'. After: 'No region (R8)' in all four on screen and CSV; undeclared kind null stays '—'. Export leg patched /me/permissions in the browser to add export (dev admin holds no allowExport by design). Run record /mnt/project-files/issue-29010/run-record.md.", "tests": "new DatasetReportRenderer.nullLabel-29010.test.tsx 8/8 green; plugin-report full suite at ae73251f: 33 files, 307 passed, 7 skipped (os-verify-lock VERDICT command-exit 0). Ablation 1 at 481cc948: renderer reverted to origin/main (blob cd8213a1, dimensionText( count 0) -> 4 red, control green; restored blob 44269a9b == HEAD. Ablation 2 at 345a95fb via ablation-replace.mjs on the chart site (anchor 1->0, blob 0b68fe40->b77c6c08) -> 2 red, 6 green; restored == HEAD, git diff HEAD empty. No build leg: subject imported from src by relative path.", "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack: 34 derived, all exit 0 at 5ed3317b (same 3 paths as head); --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. type-check @object-ui/plugin-report at ae73251f: 48 tasks green. ui vitest scripts/: 169 passed, 2 skipped. check-sdui-manifest at head: 0. PR body gates (changeset-no-major, adr-0087-registration, closing-keyword-parity, partof-closing-keyword): all 0. eslint touched files: 0 errors. CI check-runs: in_progress at report time.", "line_budget": "3 files, +407 / -10 vs origin/main (renderer +85/-10, test +320, changeset +12)", "files_changed": [ "ui/packages/plugin-report/src/DatasetReportRenderer.tsx", "ui/packages/plugin-report/src/__tests__/DatasetReportRenderer.nullLabel-29010.test.tsx", ".changeset/29010-report-null-label.md" ], "deviations": [ "extra site: report embedded chart null category (DatasetReportRenderer.tsx, same file), added on the seat's option-A answer in the card thread", "export measurement harness: browser-side patch of /me/permissions adding export on qar8_dsobj (dev admin has no allowExport grant)", "ablation 1 mutated by git restore of the base blob, proved by blob hash and anchor count, not ablation-replace.mjs", "Write tool materialized a backslash-uFEFF regex escape as a raw BOM; caught by eslint no-irregular-whitespace, fixed in 5ed3317b" ], "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "mcp_calls": "0 GitHub MCP calls", "api_writes": "2 — relay pr_create (run 41442782195, PR #29334, read-back identical, no assignees); this os-dev-report comment via post-stamped. git push is not a REST write.", "open_questions": [], "out_of_scope_findings": [ "carrier: none — no finding outside the card" ] }objectstack-fleet commentedon Nov 2, 2026
domain:ui#2 · zhuangjianguo · session_01QsR2jRFuJz9QBZqPVHYVhJ
Landed · 2026-11-02T13:19Z · PR #29334 merged through the queue as
606c32d7; the merge commit carries all 3 files of the accepted headae73251f(dimensionTextanddeclaredNullCategoryLabelinDatasetReportRenderer.tsx, its test, the changeset). Delivered in full: nothing remains on this card;pm:dispatchedremoved.