Repository navigation
feat(plugin-report): a report chart draws its seriesBy split - #28305
Conversation
… split Claude-Session: https://claude.ai/code/session_01L9qibvUw5sBjvzer4Z7WHt Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibvUw5sBjvzer4Z7WHt Co-Authored-By: Claude <noreply@anthropic.com>
…ts renderer made false Claude-Session: https://claude.ai/code/session_01L9qibvUw5sBjvzer4Z7WHt Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibvUw5sBjvzer4Z7WHt Co-Authored-By: Claude <noreply@anthropic.com>
📓 Docs Drift Check1 anchor(s) derived from 2 changed package(s); no hand-written page names any of them. What this run could not see
Coarse fallback — 168 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 847bfd6e2f3d34bbd48063b470dada58e9b31cef && git checkout 847bfd6e2f3d34bbd48063b470dada58e9b31cef
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 53749673ba7a0005d09e8a79544da10d5faa10bc 39eb423b391b593f8cd4d981f126ca60af308758 && git checkout -B drift-repro 53749673ba7a0005d09e8a79544da10d5faa10bc && git merge --no-ff 39eb423b391b593f8cd4d981f126ca60af308758
node scripts/docs-audit/affected-docs.mjs --json 53749673ba7a0005d09e8a79544da10d5faa10bc |
|
domain:ui#2 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz Contract-review-claim: |
|
domain:ui#2 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz Contract reviewServed-tier: Inputs: card #28206 (body and all four comments: triage 6528521739 with its one-file cross-domain exception and the S1 ruling, the unblock 6530264156, the seat's claim 6538373765, the dev report 6538795928), PR #28305 (body, the file list of 7 files, the diff of 596 lines against ① Derived judgments
② Semver level
③ Boundary flags
Verdict reasoning. The diff draws the key where the card said, by handing the existing pivot both dimensions rather than re-deriving a split; order and labels follow the derivations the dashboard widget already uses; a chart without the key is byte-for-byte what it was, pinned by the CONTROL and an ablation shown three red / one green. The ledger flip and its generated shard are the triage exception; the lint edits are prose. Semver is patch everywhere. The two open questions are spec-lane follow-ups the body names. Implemented-by: VERDICT: PASS |
|
domain:ui#2 · os-tesla · session_01L9qibxgfM8p8t3fo6y9eFK 维护者速读
席位核对ACCEPT · head
|
Requested by Tesla · project thread
Fixes #28206
Clause-②: no
Metadata-protocol: none
维护者速读
chart.seriesBy后真的按第二个维度拆分系列了(例如每个负责人一根柱,按阶段分段);台账里chart.seriesBy从 planned 改为 live;顺手改掉了 lint 说明里因此变假的两句话。seriesBy已能写、发布时也会校验,但图表只查[xAxis] × [yAxis],拆分从来没画出来;HotCRM 的「按负责人和阶段的商机漏斗」只能退回矩阵表。seriesBy的报表图表行为改变;没写的图表查询和输出逐字节不变(有对照用例钉住)。回滚即 revert 本 PR。ui/packages/plugin-report/src/DatasetReportRenderer.tsx的DatasetReportChart(查询维度、系列排序、系列标签三处)、新测试DatasetReportRenderer.chartSeriesBy-28206.test.tsx、packages/spec/liveness/report.json的seriesBy行。pnpm --dir ui exec vitest run packages/plugin-report/src/__tests__/DatasetReportRenderer.chartSeriesBy-28206.test.tsx,预期 4 个用例全绿。What changes
Renderer (
ui/packages/plugin-report/src/DatasetReportRenderer.tsx,DatasetReportChart).chart.seriesBy. When it is set on a series chart, the chart's own dataset query is[xAxis, seriesBy]×[yAxis]instead of[xAxis]×[yAxis], and the rows go tobuildChartSerieswith both dimensions. That takes the helper's existing pivot branch (the one a two-dimension dashboard widget already takes): one row per x bucket, one series per split value, null group included.chart-series.tsis unchanged.DatasetWidgetruns for its second dimension (seriesOrder), and it uses the same opt-out the categories use: a reportorderthat reaches the chart's selection keeps the server's order.chart.series[]entry names that value (the spec's reading ofseries[].namewhenseriesByis set). Then that entry's presentation lands on it throughmergeAuthoredSeries, and its label resolves throughpickLocalizedin the UI language rather than first-string-wins.seriesBy, the query, the rows and the series are what they were.chart.xAxis×chart.yAxisnow names the split.Liveness (
packages/spec/liveness/report.json).chart.children.seriesBy:planned→live,verifiedAt2026-10-31, the renderer and the new test as evidence.authorWarn/authorHintare dropped (aliverow may not carry them), soos validatestops printing "nothing draws it yet" for the key.chartrow's note dropped its stale sentence about agroupBysub-key (ChartConfigSchemadeclares nogroupBy) and now says the chart also groups byseriesBywhen it is set.packages/spec/liveness/state-counts/report.mdregenerated (gen:liveness-counts): report live 39 → 40, planned 1 → 0.Prose this change made false, corrected (no rule, severity or finding change).
packages/lint/src/validate-chart-bindings.ts, module header: the report-charts bullet quoted the renderer as "queries onlychart.xAxis×chart.yAxis", and thechart-axis-not-selectedsection quoted "the selection is exactly one dimension × one measure ... returns ONE series". Both now quote the current renderer text and say what happens with a split.ChartBinding.ownSelectiondoc comment: "querieschart.xAxis×chart.yAxisalone and derives exactly ONE series" now names the split.packages/lint/src/rule-explanations.ts,chart-axis-not-selectedexplanation (os explain): "the embedded chart queries exactly one dimension × one measure and derives ONE series" now says it queries itschart.xAxisdimension (and itschart.seriesBysplit, when set) × that one measure and, without a split, derives one series.Changeset:
.changeset/28206-report-chart-series-by-renderer.md, patch for@object-ui/plugin-report,@objectstack/spec(the published liveness ledger) and@objectstack/lint(theos explaintext), with both declaration lines.Tests
New
ui/packages/plugin-report/src/__tests__/DatasetReportRenderer.chartSeriesBy-28206.test.tsx, four cases over HotCRM'sopportunity_funnel_owner_stageshape (a bar per owner, split by stage; the table groups by owner alone, so the only selection namingstageis the chart's):{ dimensions: ['owner', 'stage'], measures: ['total_amount'] }, and the chart getsxAxisKey: 'owner'and one row per owner carrying each stage's amount (the null stage under(None));series[]entry naming the valueProposallands its colour on that series and its{ en, zh }label resolves to the zh limb on a zh console; the other series are untouched;seriesByqueriesowneralone and draws onetotal_amountseries labelledTotal Amount, rows passed through.Ablation (one-off, no permanent file):
node scripts/ablation-replace.mjsreplaced the split arm of the chart query ((seriesBy ? [xAxis, seriesBy] : [xAxis]) : [];→[xAxis] : [];, anchor 1 → 0, blob280c006b→983b4e78). Predicted before the run: cases 1–3 red, the control green. Measured: 3 failed, 1 passed. Restore verified: blob back to280c006bandgit diff HEADempty.Acceptance notes
validateChartBindingslives inpackages/lint/src/validate-chart-bindings.ts(@objectstack/lint), not inpackages/spec. The warning wanted isseriesBy === xAxison a report chart.seriesBy. A reportpie/donut/funnelthat setsseriesBynow hands the chart several series, which the chart component draws as its single-series refusal notice rather than a split; before this PR it drew one series and ignored the key. The spec does not refuseseriesByon those types today. Noted for the spec lane beside S1, not filed.series[].namematches with a split. It matches the series key, which is the split value as the rows carry it: the server-resolved display label (ADR-0021), not the stored option value. For a select split, an entry naming the stored value (proposal) lands on nothing, as an unknown name does today. Noted, not filed.Verification
All readings on head
39eb423bunless named.Affected package tests and type-checks
pnpm --dir ui exec vitest run packages/plugin-report/underos-verify-lock: Test Files 31 passed (31), Tests 282 passed, 7 skipped. VERDICT command-exit 0.pnpm --filter @object-ui/plugin-report run type-check(after building its dependency closure with declarations): VERDICT command-exit 0.pnpm --filter @objectstack/lint test: Test Files 181 passed (181), Tests 7679 passed.pnpm --filter @objectstack/lint typecheck: VERDICT command-exit 0.Derived gates (dispatch-gates --commands, reconciled with --ran)
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran→ 71 derived families accounted for: 70 run, all exit 0, 1 NOT MEASURED.pnpm check:dual-build-cjs-loads, exit 3 (PREREQUISITE NOT MET: reads builtdist/of every package; not built locally). Declared to CI.check:livenessfirst went red on the stalestate-counts/report.mdshard; regenerated withgen:liveness-countsand re-run green.node scripts/check-plugin-teardown-shape.mjs --self-testfirst exited 3 on the shallow clone (its pinned fixture commit was missing); green after fetching that commit.node scripts/check-sdui-manifest.mjsand its--self-test: exit 0.Generated by Claude Code