…6896)
`ChartDataSeries.data` was REQUIRED on every authored series and read by
nothing. `normalizeChartSchema`'s `normalizeSeries` (`@object-ui/plugin-charts`)
reads `dataKey`/`name`, `label`, `chartType`/`type`, `variant`, `opacity`,
`dashArray`, `stack`, `yAxis` and `color` — `data` is not among them. Rows come
from the chart-level `data`, a key `ChartSchema` never declared at all, which
survives only because `BaseSchema` carries an index signature and so suppresses
excess-property checking on chart literals.
Retired through the ADR-0049 kit rather than deleted: `?: never` on the
interface, `retirementTombstone()` on the Zod mirror. `ChartDataSeriesSchema` is
a non-strict `z.object`, so a deletion would strip an authored value in silence —
one silent no-op traded for another. Immediate tombstone, no dual-reading
window, per the maintainer's ruling of 2026-08-31.
`categories` is NOT retired. It keeps its behaviour — an alternative series list
consulted only when `series` is absent, each entry normalized as `{ dataKey }` —
and its prose was corrected to say so, on the docblock, the `ChartDataSeries`
header and the Zod `.describe()`. Prose follows machine.
The census was re-measured on this branch's merge-base with a control that had
to hit in the same query (4 sites in `report-schema-authoring-face.test.ts`). It
corrects the record: one non-test authoring site sits inside `packages/`
(`packages/types/examples/data-display-examples.json`) and four more in
`content/docs/`. All five are unreferenced documentation authored against the
documented-but-unimplemented model, not consumers; their migration is filed
separately.
Pinned in `chart-inline-data-retired.test.ts`, including a counter-probe that
builds the deletion this retirement did not choose and measures the contrast in
the same run, and in `normalizeChartSchema.test.ts`, where the `categories` read
now has behaviour coverage it never had.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
Fixes#6896
⛔ Draft, and it stops here. Clause ② applies: this narrows the accept set of a published
@object-ui/typessurface, so it waits on contract review. Not ready, not enqueued, noauto-merge — and the dispatch tier for this build was opus under the maintainer's standing
quota exemption (2026-08-13) after fable returned HTTP 429, so the merge-queue prohibition is
unchanged.
needs:contract-reviewattached with this PR, not before it.Implements the maintainer's ruling of 2026-08-31 (决裁批 #20 ③, 「其他同意」). Both halves land;
the ruling named half (a) alone as not a valid landing.
(a)
ChartDataSeries.data— RETIRED, announceddata: number[]was required on every authored series and read by nothing.normalizeSeries(packages/plugin-charts/src/normalizeChartSchema.ts:232) readsdataKey/name,label,chartType/type,variant,opacity,dashArray,stack,yAxis,color.datais not among them. Rows come from the chart-leveldata— a keyChartSchemanever declared at all, surviving only becauseBaseSchemacarries an indexsignature, which is why no gate ever saw this.
data: number[]→data?: neverplusretirementTombstone()on the Zod mirror. Deletingthe member was the option not taken:
ChartDataSeriesSchemais a non-strictz.object, so adeletion strips an authored value in silence — one silent no-op traded for another. Immediate
tombstone, no dual-reading window.
The accept set moves in both directions, both deliberate:
{ name: 'Revenue', data: [1,2,3] }{ name: 'Revenue' }datarequired)(b)
categories— live, with corrected proseNot retired. It is read as an alternative series list, consulted only when
seriesisabsent, each entry normalized as
{ dataKey }— so the strings name columns to plot. Thecategory axis comes from
xAxisKey/xAxis(:290-292). The docblock said "X-axislabels/categories", so an author following the documentation got a different chart from the
documented one. Prose follows machine: the
ChartSchema.categoriesdocblock, theChartDataSeriesheader (which promised numbers "positionally aligned with the chart'scategories", a model that never existed) and the Zod.describe()now state the read.Behaviour is byte-identical;
categoriesstays writable.The census, re-measured — with a correction to the record
Re-measured at merge-base
2c3cd1b75, with a control that had to hit in the same query. Theinstrument was not blind: it scores 4 authoring sites in
packages/types/src/__tests__/report-schema-authoring-face.test.ts.series[].dataoutside tests acrosspackages//apps//examples/. That is off by one site inside those roots:packages/types/examples/data-display-examples.json— 2 series (insidepackages/)content/docs/api/schema-reference.md— 3 series (outside the scanned roots)content/docs/core/report-schema.mdx— 1 series (outside the scanned roots)The ruling's conclusion is unaffected, and these strengthen it. None is a consumer: nothing
imports, type-checks, parses or renders any of them (
packages/typesships onlydist,README.md,CHANGELOG.md,LICENSE, soexamples/is not published; the mdx site sits in aplaintextfence, so no doc gate covers it). Each authors the documented model — month namesin
categoriesbeside inlineseries[].data— which renders an empty chart today, becausedatais dropped andcategoriesis ignored wheneverseriesis present. They are instancesof the divergence this PR closes, not users of a working inline-data model.
They are out of scope here by file surface and filed separately; until that lands, an author
copying them trips the tombstone and reads the remedy instead of being dropped in silence.
Mechanism note for the reviewer
The dispatch expected an "ADR-0087 retirement registry and its generated baselines". objectui has
no such files: neither ADR-0049 nor ADR-0087 exists under
docs/adr/here — both areupstream
@objectstackADRs referenced by convention, and the retirement "registry" in this repois the declaration pair (
?: never+retirementTombstone()) plus its pin test. That is theshape #6949 landed (changeset, pin test, interface, Zod mirror — no registry file, no generated
baselines), and this PR follows it. Nothing was skipped; the artifacts do not exist.
Verification
Run on the exact commit under review,
9d53bee5d. Exit codes captured before any pipe.vitest(4 affected files)Test Files 4 passed (4) · Tests 70 passed (70)vitestfulltypes+plugin-chartsTest Files 117 passed (117) · Tests 1266 passed (1266)@object-ui/typestype-checkDone—tsc --noEmit && tsconfig.examples.json && tsconfig.test.json@object-ui/plugin-chartstype-checklint(both packages)0 errors(260 + 267 pre-existing warnings)check:changeset-no-majorNo changeset declares a major bump.check:changeset-presence5 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)check:doc-snippets271 of 271 block(s) judged, 0 failed(sentinel controls fired)check:doc-typesEvery documented component type is registered.check:spec-symbol-derivation1329 files scanned … 0 untriaged collisionscheck:readme-exports386 self-imports judged (386 real, 0 wrong-path, 0 fabricated)check:control-bytesOK (scanned 5883 tracked text file(s))check:phantom-deps/self-import/vi-mock-specifiers/esm-specifiersTwo gates first returned NOT MEASURED, and neither is reported as a pass:
check:doc-snippetsanswered
PRECONDITION NOT MET (exit 2)andcheck:readme-exportsexit 1 withtype entry not on disk— both the unbuilt dependency closure. Both were re-run to exit 0 afterbuilding what they name.
plugin-chartstype-checkfailed the same way (TS2307 on@object-ui/core/components/react) before its closure was built.@ts-expect-errorenforcement is real here and was verified rather than assumed:tsc -p tsconfig.test.json --listFilesconfirms both the new pin and the edited pin are inside theproject, so a re-widened declaration fails the build on the unused directive.
The counter-probe, and the ablation that proves it fires
chart-inline-data-retired.test.tsbuilds the deletion this retirement did not choose — thesame series schema with
datasimply absent — and measures the contrast in the same run: thedeletion accepts an authored
dataand strips it silently, the shipped schema refuses and sayswhy. Announcement is pinned separately (
datastays in.shape; the description names both theremedy and
xAxisKey), so "gone" cannot pass as "retired".Ablated to prove it is not vacuous. The tombstone was removed from the Zod mirror; the mutation
was confirmed on disk —
data: retirementTombstone(occurrences dropped to 0 and the blobhash moved from
71a1b768— and the pin then went red: 5 failed / 10 passed, the failuresbeing exactly the refusal, the whole-node refusal, the shape/announcement pin, the counter-probe
and the
categories-contrast pin. No rebuild leg applies: the pin imports the mirror by relativesource path (
../zod/data-display.zod), not throughdist, so nothing stale can answer for it.Restore was proven the same way rather than by exit code — blob hash back to
71a1b768,git diff HEADempty, tombstone present again — and the pin re-run green (70/70) on that restoredtree, which is the commit under review. The mutation ran under a
trap ... EXIT INT TERMwithabsolute paths.
Generated by Claude Code