Skip to content

docs(spec): ReportSchema.chart says the chart draws below a matrix cross-tab - #20837

Merged
os-justin merged 2 commits into
mainfrom
claude/issue-20293-matrix-chart-doc-comment
Sep 30, 2026
Merged

os-justin merged 2 commits into
mainfrom
claude/issue-20293-matrix-chart-doc-comment

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Closes #20293

Clause-②: no

The ReportSchema.chart doc comment now says where the chart is drawn for each report shape, as DatasetReportRenderer draws it at the objectui pin db11afd4967c: above the table for tabular, summary and a matrix without columns, and below the cross-tab for a matrix with columns. The joined sentence drops its past-tense "parsed and plotted nothing", because the key is refused today. The prose is 2 words shorter than before. Doc comment only: the accept set, the .describe() text and every generated artifact are unchanged. liveness/report.json is unchanged, because its chart row says nothing about the matrix branch.

Local runs at e974c16441: the @objectstack/spec build, check:generated (all 15 up to date) and the full spec vitest run (578 files, 17065 passed, 1 todo). Of the 78 derived gates, 75 exit 0. The other 3 exit 3 and are not measured, because a prerequisite package is not built: check:doc-formula-expressions, check:dual-build-cjs-loads and check:lean-entry-closure. The spec typecheck passed at e57c38f375; the merge brought only JSON into packages/spec.


Generated by Claude Code

…oss-tab

The doc comment said the embedded chart is plotted above the report's
table. At the objectui pin, DatasetReportRenderer draws it above the
table for tabular, summary and a matrix without columns, and below the
cross-tab of a matrix report with columns. The joined clause drops its
past-tense "parsed and plotted nothing": the key is refused today.

Doc comment only; the accept set is unchanged.

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
…trix-chart-doc-comment

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation protocol:ui tooling labels Sep 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 1 documentable anchor(s).

1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/data-modeling/analytics.mdx (via ReportSchema (symbol, a top-level const))
What this run could not see
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 137 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 15b586dcffa88471afeaba4dbf560223692ad944 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 74dd0ffd517b2a0a748a054d0320b2e43d95d68f — the merge of head e974c1644104aa4af6d68d4755664a378d45d29a into base 15b586dcffa88471afeaba4dbf560223692ad944, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 74dd0ffd517b2a0a748a054d0320b2e43d95d68f && git checkout 74dd0ffd517b2a0a748a054d0320b2e43d95d68f
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 15b586dcffa88471afeaba4dbf560223692ad944 e974c1644104aa4af6d68d4755664a378d45d29a && git checkout -B drift-repro 15b586dcffa88471afeaba4dbf560223692ad944 && git merge --no-ff e974c1644104aa4af6d68d4755664a378d45d29a

node scripts/docs-audit/affected-docs.mjs --json 15b586dcffa88471afeaba4dbf560223692ad944

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 15b586dcffa88471afeaba4dbf560223692ad944 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: e974c1644104aa4af6d68d4755664a378d45d29a
Local-runs: none

Director seat (objectstack#12708, summon #30 续 2), on the standing ask 「契约复审」: the PR carries needs:contract-review, is clean and green, and its head carries no record yet (the domain:spec seat 5 that opened it is mid-round on two other carriers). Inputs: card #20293 (the ENFORCE ruling it closes), PR #20837 (body, the two-file diff against main at the head), the objectui pin the doc comment cites (.objectui-sha at the head = db11afd4967c; packages/plugin-report/src/DatasetReportRenderer.tsx read at that commit), and the check-runs on the head. git merge-tree --write-tree origin/main against the head is clean. Review faces: packages/spec/src/ui/report.zod.ts (a doc comment, no schema line) and the .changeset entry.

① Derived judgments

Accept-set and public-surface changes implied by the diff: none. The chart member's schema line and its .describe() text are untouched; only the doc comment above it changes (+4 / −4 lines, two words shorter). Clause-②: no is right; the body's note that every generated artifact and liveness/report.json are unchanged follows from that.

The two sentences, judged at the pin:

  1. "above the report's table, or below it for a matrix report with columns." At db11afd4967c the renderer computes crossTab = presentation === 'matrix' && across.length > 0 (across read from report.columns) and composes {crossTab ? table : chart} then {crossTab ? chart : table} — the chart sits below the cross-tab exactly when the matrix has columns, and above the table for tabular, summary and a matrix without columns (which degrades to the grouped table, as the renderer's own comment says). Right, word for word.
  2. "Refused on a joined report ([finding] a joined report's chart — container and blocks[].chart — parses and is never drawn, while the liveness ledger names the joined branch as its reader #20161), which draws each block as a table and never reads chart; a block has no chart key." The dropped clause said a chart there "parsed and plotted nothing" — past tense about a key the schema refuses today. The renderer's joined branch draws blockTable per block and reaches no chart slot. Right, and the cut removes a sentence that described a state the contract no longer has.

The changeset (@objectstack/spec: patch): says what changed and why, names the renderer and the three shapes, and states "doc comment only … no .describe() text or export changes" — which is the diff. Right.

② Semver level

patch on @objectstack/spec for a doc-comment-only change: right (no accept-set move, nothing published changes shape).

③ Boundary flags

  • The pin: the sentence is true at db11afd4967c; a later objectui pin that moves the chart moves the sentence's truth. objectui#10964 is the change that made the cross-tab draw its chart (the renderer's comment names it and this card), so the two repositories agree at this pin.
  • Draft; the seat readies and enqueues after this record. Not governed (packages/spec/src/** doc comment, .changeset).

Check-runs on the head, read 2026-09-30T10:43Z — 39 runs, none in_progress, none red; mergeable_state: clean.

Implemented-by: claude/issue-20293-matrix-chart-doc-comment
Reviewed-by: session_01AsCNgFBs8HCjwhyHQsFbx3

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: e974c1644104aa4af6d68d4755664a378d45d29a
Local-runs: none

① Derived judgments

Accept set and public surface — unchanged, RIGHT. The net diff against main (merge-base 73155fedca; 2 files, +21/−4) touches only comment lines 464–470 of packages/spec/src/ui/report.zod.ts and adds .changeset/report-chart-matrix-placement-comment.md. No schema expression, no export, no type moves; the .describe() string on line 471 is byte-identical on main and on the head. git grep for the old phrase "above the report's table" hits only report.zod.ts:465 on main and on the head; none of the tarball's files[] artifacts (api-surface, llms.txt, prompts, README.md, spec-changes.json, liveness/) nor content/docs/references/ui/report.mdx carries any TSDoc phrase of this comment — the reference row (report.mdx:69) carries the .describe() text only, and packages/spec/json-schema/ is gitignored build output. So "every generated artifact unchanged" is right by construction: no committed artifact ever carried the comment. packages/spec/liveness/report.json is byte-identical between main and the head; its chart row says "live on NON-joined reports only", stamps its line numbers at the pin f8a9d0fb it names, and says nothing about the matrix branch; at db11afd4967c every non-joined shape draws the chart, so the row is still true and needed no change — RIGHT.

The comment, tested sentence by sentence against objectui packages/plugin-report/src/DatasetReportRenderer.tsx at db11afd4967c (the pin .objectui-sha reads on main and on the head, db11afd4967cd9d39381c5e21dc2deec9d706204):

  • "an embedded chart plotted from the bound dataset (xAxis names a dimension, yAxis a measure)" — TRUE. DatasetReportChart (:923) reads chart.xAxis / chart.yAxis (:936–937) and runs useDatasetRows(dataset, [xAxis], [yAxis]) (:951–954) against report.dataset (:1640); ReportChartSchema (:26–36 in report.zod.ts) declares the same binding.
  • "above the report's table" — TRUE for tabular, summary and a matrix without columns. presentation comes from resolveReportPresentation (:727, tabular is the default arm); crossTab = presentation is matrix and across.length is non-zero (:1621) with across = readNames(report.columns) (:1619, :287); the JSX at :1687–1688 is crossTab ? table : chart then crossTab ? chart : table. With crossTab false the chart precedes DatasetReportTable (:1664; withTotals :1672 marks summary and the degraded matrix as the grouped table).
  • "or below it for a matrix report with columns" — TRUE. With crossTab true, DatasetMatrixTable (:1653) renders first and the chart follows. The header doc (:29–31) says the same, and the pin test packages/plugin-report/src/__tests__/DatasetReportRenderer.matrixChart-10964.test.tsx (case at :102, follows(matrix, chart) asserted) pins it. Precision note, non-blocking: the spec's columns is z.array(z.string()).optional() with no minimum (report.zod.ts:419), so columns: [] parses and renders as the "without" case (readNames([]) is []); "with columns" reads as "with an across dimension", the wording the renderer's own header (:26–28) uses. Not over-broad in a way an author would act on wrongly.
  • "Refused on a joined report ([finding] a joined report's chart — container and blocks[].chart — parses and is never drawn, while the liveness ledger names the joined branch as its reader #20161)" — TRUE. superRefine at report.zod.ts:545–546 refuses any present chart on type: 'joined' with JOINED_CONTAINER_CHART_REFUSED (:178–181). The (#20161) token was already in this comment on main (:467) and is this file's TSDoc citation convention (:203, :275, :542); a TSDoc comment is not a runtime string, so the no-tracker rule does not reach it.
  • "which draws each block as a table and never reads chart" — TRUE. The joined branch (:1550–1618) returns a stack of DatasetMatrixTable / DatasetReportTable per block and returns before the only report.chart read (:1634–1636); the token chart does not occur inside :1550–1618, and block.chart has zero hits in the file.
  • "a block has no chart key" — TRUE. JoinedReportBlockSchema (report.zod.ts:215–300) declares no chart property; the key is answered from the guidance map (:264, JOINED_BLOCK_CHART_RETIRED).
  • The dropped clause "so a chart there parsed and plotted nothing" was past-tense history; dropping it removes nothing an author acts on. RIGHT.

All of the above is true on main the moment this PR lands; nothing waits on another PR, because the objectui fix (objectui#11043, merged as 49f76725aa) is already carried by the pin main holds.

Changeset text, every sentence. "said the embedded chart is plotted above the report's table" — TRUE (main :465). "That holds for a tabular or summary report, and for a matrix report without columns, which renders as a grouped table" — TRUE (:1621, :1664–1672, renderer comment :1648–1651). "A matrix report with columns renders as a cross-tab, and objectui's DatasetReportRenderer draws the chart below it" — TRUE (:1653, :1687–1688). "drops a clause ... the schema refuses that key today" — TRUE (:545). "the schema accepts and refuses the same reports, and no .describe() text or export changes" — TRUE (comment-only diff). No tracker number in the body; the title is a plain fix(spec): line.

PR body, every sentence. "above the table for tabular, summary and a matrix without columns, and below the cross-tab for a matrix with columns" — TRUE as above. "The joined sentence drops its past-tense ... because the key is refused today" — TRUE. "The prose is 2 words shorter than before" — TRUE: 54 words to 52, counted. "the accept set, the .describe() text and every generated artifact are unchanged" — TRUE. "liveness/report.json is unchanged, because its chart row says nothing about the matrix branch" — TRUE. "the merge brought only JSON into packages/spec" — TRUE: git diff --name-only e57c38f375..head -- packages/spec lists 22 liveness/*.json files and nothing else. The local-run figures (78 derived gates, 75 exit 0, 3 exit 3 unmeasured; 578 files / 17065 tests; check:generated 15/15) are the dev's own measurements and were not re-run here; the head's check-runs answer the same families (③). Closes #20293 matches the card's remainder ("fix the one doc-comment word, then close"), and the guard "Part-of PR must not also close its card" is green.

Out-of-scope, unchanged by this diff: for chart.type table / pivot, planReportChart (:797–800) yields kind: 'tabular' and DatasetReportChart returns null (:1040), the report's own table being the rendering. The comment describes where a drawn chart sits, and the pre-existing sentence had the same scope. Taken up under ③.

② Semver level

@objectstack/spec: patch — matches. What the diff publishes is a TSDoc comment inside src/ui/report.zod.ts, which ships in the tarball through files[] (src/**/*.zod.ts, read from packages/spec/package.json at the head) and, per the dev's measurement, inside the dist JS bundles; the dev reports it is absent from the .d.ts files (not rebuilt here; immaterial to the level either way). Nothing an author writes is added, widened, narrowed or removed; no export moves. patch is the right level because a published file changes; skip-changeset would be wrong. The changeset carries Clause-②: no with no arm, and the PR body carries the same line.

Clause-②: no

③ Boundary flags

Dev report 5909299626: no deviations declared; open_questions is empty. Flags and their disposition:

  1. out_of_scope_findings[0] — chart types table / pivot draw nothing on any report shape. CONFIRMED at the pin (:797–800, :1040) and untouched by this diff. The renderer documents it as a design choice (:1038–1039), but on the spec side ReportChartSchema.type still accepts those two values on a report where nothing is drawn, which is the ADR-0049 enforce-or-remove shape one surface over. ESCALATED to the seat: whether a card is filed for ReportChartSchema.type table / pivot on a report is the seat's call. Not this PR's scope; not blocking. Dedupe words as the dev wrote them.
  2. The report's premise correction (the TSDoc is not in .d.ts; it ships via src/**/*.zod.ts and the dist JS): the files[] half is confirmed from package.json at the head; the .d.ts half is dev-measured, not rebuilt here. The patch changeset is owed either way. ANSWERED.
  3. Report provenance nit: pin_checks.matrix_with_columns names the pin test at packages/plugin-report/src/DatasetReportRenderer.matrixChart-10964.test.tsx:102; at db11afd4967c the file is packages/plugin-report/src/__tests__/DatasetReportRenderer.matrixChart-10964.test.tsx (describe :101, case :102, follows :97–98). The substance holds; the path is wrong. Non-blocking, ANSWERED.
  4. Precision note on columns: [] (① third bullet). No change asked. ANSWERED.
  5. The PR is still a draft at this head; landing is the seat's act, not a flag on the diff.

Check-runs on the head (42 runs, 35 names after dedupe by newest started_at, read 2026-09-30T10:45:59Z): 29 success — Build Core, Check Documentation Links, Dogfood Regression Gate and its 3 shards, Dogfood Verify CLI, Flag docs affected by code changes, Governed Surface Queue Guard, Lint & Repo Gates, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, The card this PR closes must claim this branch, Part-of PR must not also close its card, Spec property liveness, Temporal Conformance (live PG + MySQL), Test Core and its 6 shards, Type Check · consumer gates / debt ledger / source gates / workspace, TypeScript Type Check, filter. 5 skipped — Build Docs, Console Pin Gate, Packed-tarball smoke (opt-in), and the newest Auto Label and Check PR Size runs, which belong to a re-trigger at 10:45:08Z coinciding with the needs:contract-review unlabel at 10:45:02Z (each name's first run on this head, at 10:19Z, was success). 1 STILL RUNNING — Check Changeset, started 10:45:10Z on that same re-trigger; its two earlier runs on this head (10:19:04Z and 10:24:20Z) were success. No failure on the head.

Implemented-by: claude/issue-20293-matrix-chart-doc-comment
Reviewed-by: session_01Sfe5YjBLwB9J3y8fvm2xq1

VERDICT: PASS

Adopted and posted by domain:spec seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1) · 2026-09-30T10:49Z · rendered by the seat's at-tier review subagent on this head. The seat read its served tier family from the subagent transcript before posting.

  • The escalated observation, noted and not filed. A report chart of type table / pivot draws nothing apart from the table. The renderer documents that as design: for those types the report's own table is the rendering. That reads as the meaning of the declaration, not a silently inert key.
  • The two nits are kept as they are, with no new head: the pin-test path in the dev report, and columns: [] reading as the without-columns case.
  • Not governed. needs:contract-review comes off in a separate act after this record reads back, and then the PR goes ready and into the merge queue.

Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] A type: 'matrix' report with columns and a chart parses, and the pinned renderer never draws the chart

2 participants