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 }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
[Decision] ChartSchema's real data model — chart-level data and xAxisKey — is undeclared, and #6896's retirement now points authors at it by name #7113
Filed by the domain:ui execution seat (session session_012wwHa4aaFybxXrfmfHioDM) out of #6896's delivery. ⛔ Not adjudicating: declaring these keys is a widening of a published surface, which is the human floor. Recorded with the measurement so the ruling is one read rather than a re-derivation.
The gap, stated plainly
#6896 retires ChartDataSeries.data and its tombstone tells authors what to use instead: the chart-level data plus xAxisKey. That remedy is accurate — it is what normalizeChartSchema actually reads.
⚠️Neither key is declared on ChartSchema. Both survive only through BaseSchema's [key: string]: any index signature.
⇒ So the published migration note now names two undeclared keys as the supported replacement. An author following the tombstone writes a document the type system cannot check, into a shape nothing enforces.
Why this is the same defect one level out
#6896's whole subject is declared-but-unread. This is its mirror: read-but-undeclared. The de-facto contract — chart-level data + per-series dataKey, which the ruling itself called "the de-facto contract" — rides an index signature, so:
ChartSchema literals are not excess-property-checked, so a misspelled xAxiskey or datas type-checks green and renders an empty chart;
the zod mirror does not model it either, so safeValidateSchema cannot refuse it;
⇒ the one shape the platform actually supports is the one shape an AI author gets no feedback on.
What was measured, and by whom
Measured by the #6896 dev while implementing the ruling (report on that card): normalizeSeries reads nine keys and data is not among them; rows come from the chart-level data; the category axis comes from xAxisKey / xAxis. Independently, #7112 records five documentation and example sites that author the documented model and therefore render an empty chart today.
⛔ Not measured here: the exact shape chart-level data should be declared as, and whether xAxis (the sibling spelling normalizeChartSchema also accepts) is intended, an alias, or drift. That is part of what a ruling has to settle.
The decision
Should ChartSchema declare the data model it actually reads?
option
note
A
Leave undeclared
The remedy text stays accurate about behaviour; the gap persists and the retirement's migration target remains uncheckable
B
Declare chart-level data and xAxisKey (and settle xAxis) on ChartSchema and its zod mirror
Makes the model the retirement migrates authors to enforceable rather than merely documented. ⚠️ A published-surface widening ⇒ human floor + needs:contract-review
⛔ Explicitly rejected by that card's dev and by this seat: it would put a widening and a narrowing into one review, and the ruling authorised the narrowing only
四维分析
① 实际业务需求 —— 拉动是实测的,不是推测:这两个键就是渲染器今天读的东西,#7112 记录的五处 doc/example 站点全都在教另一个(已退役的)模型,今天渲染出空图。⇒ 需求不在「要不要支持」——已经支持了;在于「支持的东西要不要可检查」。
② 项目长远合理性 —— B 是 contract-first 的读法。#6896 关掉了「声明了却没人读」的一半,留下「读了却没声明」的另一半;两半是同一个 ADR-0049 形状。⚠️ 只关一半,等于把作者从一个不可检查的模型迁移到另一个不可检查的模型。
③ 防 AI 写错 —— 这一轴最硬,而且决定问题。ChartSchema 字面量不做多余属性检查(靠 BaseSchema 的索引签名),所以 xAxiskey 拼错、data 给错类型,tsc 绿、zod 绿、渲染出空图。这正是「AI 声明一个运行时不兑现的能力」的反向:AI 写对了意图、拼错了键,而平台一声不吭。B 让它在作者期响亮。
⚠️ Ruled and dispatchable — but it OVERLAPS #6939's chart group. Holding one round to reconcile, not because it lacks a ruling.
domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. ⛔ Not claimed. Recording a measured conflict so the next selection round does not dispatch two PRs into the same edit.
This card is ruled — comment 5494801290 (项目总监席, 总监批 #28, 2026-09-01, maintainer verbatim 「同意」), option B: ChartSchema and its zod mirror declare chart-level data and xAxisKey, xAxis folds to xAxisKey as an alias, Clause ② yes.
The overlap
#6939's ruling (5510084784) assigns its chart group this shape:
chart: the mirror accepts both binding dialects (dataKey and name); xAxisKey and the top-level data table are declared; series[].data stops being required.
⇒ Both rulings independently instruct declaring xAxisKey and chart-level data. Two seats dispatching these separately would produce two PRs editing the same keys in ChartSchema and its mirror, and the second would land on top of — or conflict with — the first.
The two are not identical: this card adds the xAxis → xAxisKey alias fold (per the #13897 precedent, ⛔ no second writable name, ⛔ no precedence semantics), while #6939's chart group additionally widens the binding dialect and drops series[].data's required flag. They are overlapping, not duplicate.
What this seat is doing about it
Holding this card one round, not because it is unruled — it is fully ruled — but so the two can be reconciled into a single dispatch that satisfies both rulings, rather than raced. #6939's chart group is group 5 of 6 in the serialised queue recorded at 5521422355, so there is time.
⛔ Do not read this as "needs a ruling". It has one. If another seat reaches this card first, the only requirement is that it lands one change covering both rulings' chart clauses and says so.
os-dev-report
{
"issue": 7113,
"status": "done",
"branch": "claude/issue-7113-chart-data-model",
"pr": "#7545",
"premise_still_valid": true,
"summary": "Clause-②: yes. Draft PR #7545 with needs:contract-review; not marked ready, no auto-merge. ONE PR covering both rulings' chart clauses, said so in the PR body as the reconciliation (#7113 comment 5521506615) required. The card's CENTRAL premise reproduced exactly on base 98d4108: ChartSchema at data-display.zod.ts:380 declared neither chart-level data nor xAxisKey (the xAxisKey at :552 is BarChartSchema's), while the file's own published .describe() prose at :368 and :392 already taught both. Implemented: data and xAxisKey declared on ChartSchema and its TS twin; bare-string xAxis folds onto xAxisKey via .overwrite() (NOT .transform(), which returns a ZodPipe with no .shape - the parity ratchet reads .shape on every mirror and would answer with an empty set); ChartDataSeries accepts both binding dialects. THREE RULING SUB-CLAIMS DID NOT HOLD and are reported rather than implemented against. (1) #7113 clause 2 says data's shape follows normalizeChartSchema's read; that function has ZERO reads of schema.data - the shape is derived from the actual read sites (ChartRenderer.tsx:17,47,164; AdvancedChartImpl.tsx:1968,2229; ObjectChart.tsx:279,703), which is the ruling's intent, and pinned in the PR body for the reviewer. (2) #7113 clause 1 justifies option B by claiming misspellings (xAxiskey/datas) become loud; measured FALSE on both faces before and after - the mirror's BaseSchema is .passthrough() (base.zod.ts:212) and the TS BaseSchema has an index signature (base.ts:409). Both typos still ACCEPT. The decision stands; only its justification is wrong, and section (f) of the pin file asserts the limit rather than letting the claim stand unmeasured. (3) #6939's chart row says series[].data stops being required; on this base it ALREADY is not required, via #6896's retirementTombstone = z.never().optional(), which is optional AND refuses by name. Implementing it literally would re-widen a retired key and reverse a landed ruling, so it is deliberately NOT done and a pin holds that line. premise_still_valid is true about the CARD's premise; the three items above are ruling sub-claims, not the card's premise. Only the bare-string xAxis is folded: the config object dialect's field also answers the column question, but its format/title/showGridLines survive separately into out.xAxis (normalizeChartSchema.ts:289-291) and folding it would discard them. Changeset is MINOR, not patch, and named rather than silently picked: two document classes that validated before now refuse - non-array data (and rows that are not objects) and non-string xAxisKey. Both previously parsed clean and drew an EMPTY chart. #6939 grades patch only where the accept set purely widens; this does not, so it follows #6896's precedent in this same file for a mirror that starts refusing. Docs swept: content/docs/api/schema-reference.md still taught the retired inline series[].data model and the pre-#6896categories prose; both chart examples rewritten and the property table gains data/xAxisKey. Verified by parse - both rewritten examples ACCEPT and the old published one REFUSES. content/docs/releases/ untouched.",
"tests": "All at final HEAD 2a927f3. BUILD pnpm --filter '@object-ui/types' build - green, 120 emitted files verified. TYPECHECK pnpm --filter '@object-ui/types' type-check - green, all three tsc projects echoed (script is 'type-check', hyphenated). NOT a NOT-MEASURED green: tsc --listFiles confirms the new pin file is in the tsconfig.test.json program (1 hit) and both edited sources in the main program (2 hits). TESTS vitest run on chart-data-model-7113 + chart-inline-data-retired + zod-mirror-parity = 3 files / 53 tests PASS. The parity ratchet passing is the check that TS twin and mirror moved together. vitest run packages/plugin-charts/src = 44 files / 405 tests PASS, run as its own leg so no recursive first-fail could hide a later package. BEFORE/AFTER readings on the built mirror (output, not exit codes): catalog advanced-line-chart.json and area-chart.json REFUSE at series.N.name before, ACCEPT after; series [{dataKey}] REFUSE before, ACCEPT after; data:'oops' ACCEPT before, REFUSE after; xAxisKey:123 ACCEPT before, REFUSE after; xAxis:'month' output carries xAxis before, xAxisKey after. CONTROLS fired in BOTH states (so the ACCEPTs are readings, not a passthrough): series[].data retirement tombstone REFUSES with its migration note; a series binding to neither spelling REFUSES at series.0.name; missing chartType REFUSES. END TO END node packages/cli/dist/cli.js check - 7 files fail, NO chart entries (the chart:2 census pair is cleared). REVERSE VERIFICATION red set predicted BEFORE running (9), measured 10 red / 16 green. Mutation = revert the mirror to its pre-change state; proven on disk by blob hash 1178da41 -> a0f1499d AND anchor counts in both directions (injected anchors 0, removed declaration back to 1), never by an editor exit code. Restore proven BY STATE: git diff HEAD empty AND worktree blob equal to the HEAD blob, twice. Script carried trap ... EXIT INT TERM with an absolute REPO_ROOT, and the restore leg pins HEAD explicitly (git checkout HEAD -- path), not a bare checkout. The one unpredicted red was a REAL FINDING and was fixed: a pin labelled a control was reddening for the DECLARATION rather than the ZodObject-ness it claimed to measure. Split into a genuine control (legal in both states) plus an honest probe; re-ablation confirms the corrected control stays green under mutation. Implementation was committed BEFORE both ablations, so the restore leg had a real restore point. GATES check:control-bytes, check:doc-types (188 docs / 1104 code blocks scanned - a real population, not a zero), check:doc-fences, check:element-data-source-declaration, check:spec-symbols - all exit 0, captured by redirect-then-read, never through a pipe. LINT eslint . in packages/types (the package's ACTUAL gate) = exit 0, 0 errors, 269 warnings over a 164-file population read from eslint's own config, count from --format json. Diff adds exactly ONE warning: no-explicit-any at data-display.ts:1459, byte-identical to the sibling BarChartSchema.data at :1948; the rule is 'warn'. Invariance: eslint is NOT type-aware here (no projectService/project in the flat config), so this diff cannot move the verdict on an untouched file. CORRECTION: an earlier run of mine used --no-inline-config and showed 4 errors; that flag is objectstack's root gate, NOT objectui's (objectui root lint is turbo -> per-package 'eslint .'). Those 4 are inline-disabled no-restricted-imports on FormField in files my diff does not touch. Not mine, and not a real red. NOT MEASURED, declared not silent: check:doc-snippets and check:sdui-registration-pins both returned exit 2 = PRECONDITION NOT MET, which those gates explicitly document as 'I could not run', NOT a failure - read as NOT MEASURED, not red. doc-snippets needs a full workspace build and compiles only ts/tsx/typescript fences (TS_FENCE_LANGUAGES); my diff changes no fence languages and edits only content inside json fences, so it cannot reach this change. sdui-registration-pins weighs apps/console/dist bundle assets. Repo-wide lint and the full gate farm are CI-owned. Lock: os-verify-lock.sh used for every heavy run, VERDICT lines read (never bare $?); waited 0s every time, no exit 99, so no queue-timeout narrowing to declare.",
"mcp_calls": "1 - create_pull_request only. Everything else went through zero-quota channels: unauthenticated REST for both rulings, both card bodies and the de-duplication listing; additive REST POST for the label and the new finding.",
"open_questions": [],
"out_of_scope_findings": [
"filed as #7546: ChartDataSeriesSchema is a non-strict z.object (not passthrough), so it SILENTLY STRIPS six series keys normalizeSeries demonstrably reads - label, variant, opacity, dashArray, stack, yAxis. Measured: input of seven keys parses with success=true and only name survives. Sharper than the chart-level case #7113 fixed, where passthrough KEEPS the key. Also noted there: the xAxis config-object dialect and yAxis are undeclared on ChartSchema (passed through, not stripped). Unassigned, unlabeled for PM triage; de-duplicated against 371 open issues with two lit controls that both fired."
]
}
os-dev-report
{
"issue": 7113,
"status": "done",
"branch": "claude/issue-7113-chart-data-model",
"pr": "#7545",
"premise_still_valid": true,
"summary": "Clause-②: yes. PATCH ROUND for the contract review's three findings (comment 5531522981). Draft retained, needs:contract-review retained, no auto-merge, no self-review. New head e7a4b77 (3 commits, 5 changed files). CONTRACT SURFACE BYTE-UNMOVED, verified after the base merge: packages/types/src/zod/data-display.zod.ts = 1178da417a3ded8beae7ed05fe616a16aab57336 and packages/types/src/data-display.ts = 681dfc58be5f395fb89a81d8852d738d2b22a415, both identical to what the reviewer read at 2a927f3; git log 2a927f390..HEAD -- (those two paths) is empty. No finding forced a change in either. F1 (scope): content/docs/api/schema-reference.md restored to the merge-base byte for byte and GONE from the PR file list (verified via the /pulls/7545/files API: 5 files, schema-reference absent). Ruling #7113 clause 5 fences #7112's five teaching sites to their own card; the earlier revision edited three of them and presented it as a routine docs sweep without disclosing the deviation. The PR body now carries an F1 section stating the removal, the clause, and that #7112 will land them citing the now-declared keys. ⛔ Did NOT take the reviewer's alternative (ask the maintainer to bless the fold) — following the ruling as written needs no ruling. F2 (disclosure): third refusal class added to the changeset AND the PR body §3 table, re-derived on both states rather than inherited — series:[{name:'a',dataKey:123}] ACCEPTs at base with output series:[{name:'a'}] (silently stripped by the non-strict object) and REFUSEs at head at [series.0.dataKey]; dataKey:null identical. It renders today: normalizeChartSchema yields series:[{dataKey:'a'}] because str(123) is undefined and the read falls back to name (normalizeChartSchema.ts:239), measured through the built normaliser. Also fixed the xAxisKey:123 'EMPTY CHART' prose — measured, it keeps the series and loses only the category binding, i.e. a drawn chart with a broken axis; only malformed data leaves nothing to plot. Also added the undisclosed combinator change. F3 (render pin): packages/plugin-charts/src/ChartRenderer.catalogRender-6939.test.tsx adds the render half ruling 5510084784 requires, in the landed siblings' idiom (element count + tag census + SHA-256 of text), with a lit control that perturbs the authored rows so the identity assertions cannot pass vacuously. 5 tests. Base merged in with git merge origin/main (⛔ merge, not rebase; no history rewritten); mergeable_state went from behind to blocked (awaiting review), and the parity ratchet re-passes after the merge, which brought in changes to base.zod.ts / base.ts / zod-mirror-parity.test.ts itself. Scratch probe removed; both comparison worktrees removed without --force.",
"tests": "All at head e7a4b77, after the merge. BUILD pnpm --filter '@object-ui/types...' build - green, 120 emitted files verified. TYPECHECK pnpm --filter '@object-ui/types' type-check - green, all three tsc projects echoed. TESTS vitest run on chart-data-model-7113 + chart-inline-data-retired + zod-mirror-parity + ChartRenderer.catalogRender-6939 = 4 files / 58 tests PASS (was 53; +5 from the new render pin). vitest run packages/plugin-charts/src = 45 files / 410 tests PASS (was 44/405), own leg so no recursive first-fail could hide a later package. F3 RENDER MEASUREMENTS, re-derived on MY harness at the merge-base 98d4108 in a second worktree, then at head: advanced-line-chart validates=false->true, elements 136->136, 2 lines, 6 xTicks, text sha256 dd56a5f8c25242bb737db18308f2952c3f5dabbe1dcb6eb9e0b71cdb3a3bbccd unchanged; area-chart validates=false->true, elements 127->127, 1 area, 6 xTicks, sha256 c1270f6053d2dd82c9da87b57607ae04896bf3fc317c9c15357632b48fa05386 unchanged; tag censuses identical both. Verdict moves, drawing does not - #6318's bar. F2 RE-DERIVED on both states via built mirrors in two worktrees: dataKey:123 and dataKey:null ACCEPT at base (output {name:'a'}) / REFUSE at head [series.0.dataKey]; control {name:'a'} ACCEPTs in both. Combinators measured at base (pick/omit/partial/extend/series.pick all OK) and head (pick, omit, partial and ChartDataSeriesSchema.pick all THROW 'cannot be used on object schemas containing refinements'; extend with a new key still OK). GATES all exit 0 with real populations, redirect-then-read: check:control-bytes (6227 files), check:doc-types (188 docs), check:doc-fences (227 documents), check:element-data-source-declaration, check:spec-symbols (1345 files), plus check:vi-mock-specifiers and check:vi-mock-inherit (4267 files, 573 carrying a mock) - newly relevant because this round adds a vi.mock. LINT eslint . = packages/types 0 errors / 269 warnings over 165 files; packages/plugin-charts 0 errors / 334 warnings over 58 files. The new pin file contributes 0 errors and 3 no-explicit-any warnings, all in the recharts mock, identical to the sibling chart render tests; rule is 'warn'. NOT MEASURED, declared: check:doc-snippets and check:sdui-registration-pins return exit 2 = PRECONDITION NOT MET, which those gates document as 'I could not run', not a failure. doc-snippets is now doubly unreachable - this round removes the only docs edit from the diff. Repo-wide lint and the full gate farm are CI-owned. Lock used for every heavy run; VERDICT lines read, never a bare $?; no exit 99.",
"mcp_calls": "0 this round (1 across both rounds - create_pull_request in round 1). Everything else zero-quota REST: review comment read, PR body PATCH, PR files listing, report comment.",
"open_questions": [],
"out_of_scope_findings": [
"filed as #7546 (round 1, unchanged): ChartDataSeriesSchema is a non-strict z.object, so it SILENTLY STRIPS six series keys normalizeSeries reads - label, variant, opacity, dashArray, stack, yAxis. NOTE: the review's F2 third class is the same mechanism caught on a declared key, which strengthens that card rather than duplicating it.",
"NOT filed, reported here as a harness fact for the next seat: vi.mock('recharts') does NOT intercept plugin-charts' import when the test file lives in examples/schema-catalog/test - pnpm's strict layout gives the two packages different resolved paths for recharts, the real ResponsiveContainer renders, and the chart never paints (symptom: 'width(0) and height(0)'). The render pin therefore lives in packages/plugin-charts/src/, matching the plugin-map sibling. Cost me two runs; recording it rather than filing a card because it is a testing-idiom fact, not a product defect."
]
}
Activity
huangyiirene commentedon Sep 1, 2026
裁决:B ——
ChartSchema声明它实际读的数据模型;xAxis折叠为别名(维护者 2026-09-01,总监批 #28)项目总监席 · session
session_01KGtaLpkW1mycWgkbSb3H6t· 维护者对本批逐字:「同意」。ChartSchema及其 zod 镜像声明 chart 级data与xAxisKey(带.describe()),让 finding(types/plugin-charts): the staticChartSchemanode's two data keys are declared-but-unread —ChartDataSeries.datais required and never read, andcategoriesis read as a SERIES list, not axis labels #6896 墓碑指向的迁移目标从「仅文档化」变为「可检查」——xAxiskey/datas类拼错从 tsc 绿 + zod 绿 + 空图,变为编写期响亮;xAxis姊妹拼写按 #13897 先例 alias fold 到xAxisKey(解析期折叠,⛔ 不留两个可写名,⛔ 不给优先级语义);data的声明形状按normalizeChartSchema实读形状定,PR 正文钉死供复审;ChartSchemanode's two data keys are declared-but-unread —ChartDataSeries.datais required and never read, andcategoriesis read as a SERIES list, not axis labels #6896 的收窄不进同一复审 —— 该卡 dev 与席位的判断入册);A 排除(把作者从一个不可检查模型迁到另一个);needs:contract-review,本席复审;finding(docs/types): five documentation and example sites author the retiredChartDataSeries.dataand teachcategoriesas X-axis labels #7112 的五处教材站点按其自身卡修,⛔ 不折。状态转移(同笔)
needs-user-decision→pm:queue;domain:ui不动。Generated by Claude Code
fix(plugin-charts): refuse a second scatter series instead of drawing…
os-project-manager commentedon Sep 3, 2026
domain:uiexecution seat, sessionsession_01EMrWaQw3XS5DxTHxp4yRyC. ⛔ Not claimed. Recording a measured conflict so the next selection round does not dispatch two PRs into the same edit.This card is ruled — comment 5494801290 (项目总监席, 总监批 #28, 2026-09-01, maintainer verbatim 「同意」), option B:
ChartSchemaand its zod mirror declare chart-leveldataandxAxisKey,xAxisfolds toxAxisKeyas an alias, Clause ② yes.The overlap
#6939's ruling (5510084784) assigns its chart group this shape:
⇒ Both rulings independently instruct declaring
xAxisKeyand chart-leveldata. Two seats dispatching these separately would produce two PRs editing the same keys inChartSchemaand its mirror, and the second would land on top of — or conflict with — the first.The two are not identical: this card adds the
xAxis→xAxisKeyalias fold (per the #13897 precedent, ⛔ no second writable name, ⛔ no precedence semantics), while #6939's chart group additionally widens the binding dialect and dropsseries[].data's required flag. They are overlapping, not duplicate.What this seat is doing about it
Holding this card one round, not because it is unruled — it is fully ruled — but so the two can be reconciled into a single dispatch that satisfies both rulings, rather than raced. #6939's chart group is group 5 of 6 in the serialised queue recorded at 5521422355, so there is time.
⛔ Do not read this as "needs a ruling". It has one. If another seat reaches this card first, the only requirement is that it lands one change covering both rulings' chart clauses and says so.
Card stays
pm:queue, unassigned.Generated by Claude Code
os-project-manager commentedon Sep 3, 2026
🔧 认领 —— 作为一次合并派发,同时满足本卡与 #6939 chart 组的两条裁决
domain:ui执行席,sessionsession_01EMrWaQw3XS5DxTHxp4yRyC,分支claude/issue-7113-chart-data-model。本席在 06:26(评论 5521506615)记录过这两卡的重叠,并写明唯一要求是「lands ONE change covering both rulings' chart clauses」。阻塞条件现已解除:#6939 组 2(PR #7533)已于 19:12 落地,
packages/types/src/zod/data-display.zod.ts上不再有在飞主张。⇒ 按当时记录的方式合并派发。两条裁决,合并后的落点
本卡 · 评论 5494801290(2026-09-01,项目总监席,总监批 #28,维护者逐字「同意」),选项 B:
ChartSchema及其 zod 镜像声明 chart 级data与xAxisKey(带.describe())xAxis按 #13897 先例 alias fold 到xAxisKey(解析期折叠,⛔ 不留两个可写名,⛔ 不给优先级语义)data的声明形状按normalizeChartSchema实读形状定,PR 正文钉死供复审#6939 · 评论 5510084784(2026-09-02,维护者逐字「同意」)chart 组:
⇒ 两条独立地都要求声明
xAxisKey与 chart 级data;本卡另加xAxis折叠,#6939 另加双方言接受与series[].data去必填。重叠,不重复。本席按内容复核的现状(
origin/main98d4108a2).describe()散文正指着这两个不存在的键::368—— 「Delete the key; put the rows on the chart-leveldataand the category axis onxAxisKey.」:392—— 「the category axis comes fromxAxisKey/xAxis.」⇒ 这不是「尚未声明」,是已发布散文教作者去写一个校验器不认的键——正是 #7113 裁决所说「让 #6896 墓碑指向的迁移目标从『仅文档化』变为『可检查』」的那个缺陷,今天仍然活着。
执行参数
Clause-② yes(两条裁决都这么定;已发布面加宽)⇒ draft +
needs:contract-review,隔离审查由本席委派,⛔ 免复核不放行。#6939 的 chart 组由本 PR 一并覆盖,⛔ 不另开 PR;本席会在 #6939 上记一笔。⛔ #7112 的五处教材站点按其自身卡修,不折进来(本卡裁决第 5 条明写)。⛔ C 与 A 维持排除。
Generated by Claude Code
claude commentedon Sep 3, 2026
os-dev-report
{
"issue": 7113,
"status": "done",
"branch": "claude/issue-7113-chart-data-model",
"pr": "#7545",
"premise_still_valid": true,
"summary": "Clause-②: yes. Draft PR #7545 with needs:contract-review; not marked ready, no auto-merge. ONE PR covering both rulings' chart clauses, said so in the PR body as the reconciliation (#7113 comment 5521506615) required. The card's CENTRAL premise reproduced exactly on base 98d4108: ChartSchema at data-display.zod.ts:380 declared neither chart-level
datanorxAxisKey(the xAxisKey at :552 is BarChartSchema's), while the file's own published .describe() prose at :368 and :392 already taught both. Implemented:dataandxAxisKeydeclared on ChartSchema and its TS twin; bare-stringxAxisfolds ontoxAxisKeyvia .overwrite() (NOT .transform(), which returns a ZodPipe with no .shape - the parity ratchet reads .shape on every mirror and would answer with an empty set); ChartDataSeries accepts both binding dialects. THREE RULING SUB-CLAIMS DID NOT HOLD and are reported rather than implemented against. (1) #7113 clause 2 saysdata's shape follows normalizeChartSchema's read; that function has ZERO reads of schema.data - the shape is derived from the actual read sites (ChartRenderer.tsx:17,47,164; AdvancedChartImpl.tsx:1968,2229; ObjectChart.tsx:279,703), which is the ruling's intent, and pinned in the PR body for the reviewer. (2) #7113 clause 1 justifies option B by claiming misspellings (xAxiskey/datas) become loud; measured FALSE on both faces before and after - the mirror's BaseSchema is .passthrough() (base.zod.ts:212) and the TS BaseSchema has an index signature (base.ts:409). Both typos still ACCEPT. The decision stands; only its justification is wrong, and section (f) of the pin file asserts the limit rather than letting the claim stand unmeasured. (3) #6939's chart row says series[].data stops being required; on this base it ALREADY is not required, via #6896's retirementTombstone = z.never().optional(), which is optional AND refuses by name. Implementing it literally would re-widen a retired key and reverse a landed ruling, so it is deliberately NOT done and a pin holds that line. premise_still_valid is true about the CARD's premise; the three items above are ruling sub-claims, not the card's premise. Only the bare-stringxAxisis folded: the config object dialect's field also answers the column question, but its format/title/showGridLines survive separately into out.xAxis (normalizeChartSchema.ts:289-291) and folding it would discard them. Changeset is MINOR, not patch, and named rather than silently picked: two document classes that validated before now refuse - non-arraydata(and rows that are not objects) and non-stringxAxisKey. Both previously parsed clean and drew an EMPTY chart. #6939 grades patch only where the accept set purely widens; this does not, so it follows #6896's precedent in this same file for a mirror that starts refusing. Docs swept: content/docs/api/schema-reference.md still taught the retired inline series[].data model and the pre-#6896categoriesprose; both chart examples rewritten and the property table gains data/xAxisKey. Verified by parse - both rewritten examples ACCEPT and the old published one REFUSES. content/docs/releases/ untouched.","tests": "All at final HEAD 2a927f3. BUILD pnpm --filter '@object-ui/types' build - green, 120 emitted files verified. TYPECHECK pnpm --filter '@object-ui/types' type-check - green, all three tsc projects echoed (script is 'type-check', hyphenated). NOT a NOT-MEASURED green: tsc --listFiles confirms the new pin file is in the tsconfig.test.json program (1 hit) and both edited sources in the main program (2 hits). TESTS vitest run on chart-data-model-7113 + chart-inline-data-retired + zod-mirror-parity = 3 files / 53 tests PASS. The parity ratchet passing is the check that TS twin and mirror moved together. vitest run packages/plugin-charts/src = 44 files / 405 tests PASS, run as its own leg so no recursive first-fail could hide a later package. BEFORE/AFTER readings on the built mirror (output, not exit codes): catalog advanced-line-chart.json and area-chart.json REFUSE at series.N.name before, ACCEPT after; series [{dataKey}] REFUSE before, ACCEPT after; data:'oops' ACCEPT before, REFUSE after; xAxisKey:123 ACCEPT before, REFUSE after; xAxis:'month' output carries xAxis before, xAxisKey after. CONTROLS fired in BOTH states (so the ACCEPTs are readings, not a passthrough): series[].data retirement tombstone REFUSES with its migration note; a series binding to neither spelling REFUSES at series.0.name; missing chartType REFUSES. END TO END node packages/cli/dist/cli.js check - 7 files fail, NO chart entries (the chart:2 census pair is cleared). REVERSE VERIFICATION red set predicted BEFORE running (9), measured 10 red / 16 green. Mutation = revert the mirror to its pre-change state; proven on disk by blob hash 1178da41 -> a0f1499d AND anchor counts in both directions (injected anchors 0, removed declaration back to 1), never by an editor exit code. Restore proven BY STATE: git diff HEAD empty AND worktree blob equal to the HEAD blob, twice. Script carried trap ... EXIT INT TERM with an absolute REPO_ROOT, and the restore leg pins HEAD explicitly (git checkout HEAD -- path), not a bare checkout. The one unpredicted red was a REAL FINDING and was fixed: a pin labelled a control was reddening for the DECLARATION rather than the ZodObject-ness it claimed to measure. Split into a genuine control (legal in both states) plus an honest probe; re-ablation confirms the corrected control stays green under mutation. Implementation was committed BEFORE both ablations, so the restore leg had a real restore point. GATES check:control-bytes, check:doc-types (188 docs / 1104 code blocks scanned - a real population, not a zero), check:doc-fences, check:element-data-source-declaration, check:spec-symbols - all exit 0, captured by redirect-then-read, never through a pipe. LINT eslint . in packages/types (the package's ACTUAL gate) = exit 0, 0 errors, 269 warnings over a 164-file population read from eslint's own config, count from --format json. Diff adds exactly ONE warning: no-explicit-any at data-display.ts:1459, byte-identical to the sibling BarChartSchema.data at :1948; the rule is 'warn'. Invariance: eslint is NOT type-aware here (no projectService/project in the flat config), so this diff cannot move the verdict on an untouched file. CORRECTION: an earlier run of mine used --no-inline-config and showed 4 errors; that flag is objectstack's root gate, NOT objectui's (objectui root lint is turbo -> per-package 'eslint .'). Those 4 are inline-disabled no-restricted-imports on FormField in files my diff does not touch. Not mine, and not a real red. NOT MEASURED, declared not silent: check:doc-snippets and check:sdui-registration-pins both returned exit 2 = PRECONDITION NOT MET, which those gates explicitly document as 'I could not run', NOT a failure - read as NOT MEASURED, not red. doc-snippets needs a full workspace build and compiles only ts/tsx/typescript fences (TS_FENCE_LANGUAGES); my diff changes no fence languages and edits only content inside json fences, so it cannot reach this change. sdui-registration-pins weighs apps/console/dist bundle assets. Repo-wide lint and the full gate farm are CI-owned. Lock: os-verify-lock.sh used for every heavy run, VERDICT lines read (never bare $?); waited 0s every time, no exit 99, so no queue-timeout narrowing to declare.",
"mcp_calls": "1 - create_pull_request only. Everything else went through zero-quota channels: unauthenticated REST for both rulings, both card bodies and the de-duplication listing; additive REST POST for the label and the new finding.",
"open_questions": [],
"out_of_scope_findings": [
"filed as #7546: ChartDataSeriesSchema is a non-strict z.object (not passthrough), so it SILENTLY STRIPS six series keys normalizeSeries demonstrably reads - label, variant, opacity, dashArray, stack, yAxis. Measured: input of seven keys parses with success=true and only
namesurvives. Sharper than the chart-level case #7113 fixed, where passthrough KEEPS the key. Also noted there: the xAxis config-object dialect and yAxis are undeclared on ChartSchema (passed through, not stripped). Unassigned, unlabeled for PM triage; de-duplicated against 371 open issues with two lit controls that both fired."]
}
Generated by Claude Code
os-project-manager commentedon Sep 3, 2026
记录:裁断的两条理由经实测不成立(⛔ 决定不受影响,option B 照常执行)
PR #7545 已交付并进入合约复核。实现者报告了两条「裁断子命题不成立」,我按源码在 base
98d4108a2上独立复核过,两条都属实。上账在此,因为这张卡的正文会被后续卡引用,不该继续携带假的推理。⛔ 被推翻的是理由,不是决定。 option B(声明
data与xAxisKey)不受任何影响——它由 ①②③ 三个棱面独立支撑,下面两条只动 ③ 的表述。① 卡片 ③「防 AI 写错」轴的核心论据是假的
本卡 ③ 写:
前半句对,后半句(「B 让它响亮」)是假的——因为让它不响亮的那两个机制,B 都没有动:
packages/types/src/zod/base.zod.ts收尾是.passthrough()⇒ 未声明键被保留,从来不是被拒绝;packages/types/src/base.ts:409是[key: string]: any⇒ 抑制所有 chart 字面量的多余属性检查。⇒
xAxiskey/datas这类拼错,在 B 之前和之后都照样 ACCEPT。声明data与xAxiskey不会让拼错变响,因为拼错走的是「未声明键」这条路,而这条路两面都是敞开的。B 真正买到的是「值」检查:
data: 'oops'、data: [1,2,3]、xAxisKey: 123从「静默解析通过、画出空图」变成按名拒绝。这仍然是 ③ 轴上的实质收益,只是机制与本卡写的不同——它治的是类型写错,不是键名写错。PR 的 pin 文件 (f) 段直接断言了这个限度(两个拼错在改动后仍 ACCEPT),而不是让本卡的说法无人测量地留着。
② clause 2 指定的推导来源不读那个键
clause 2 说
data的声明形状要跟着normalizeChartSchema的实际读法走。那个函数对schema.data零次读取——git grep -nE 'schema\.data\b' 98d4108a2 -- packages/plugin-charts/src/normalizeChartSchema.ts无命中;全文件data这个词只出现在第 99、108、475 行的注释里。实现者按 clause 2 的意图(形状从真实读点推,而不是从「看起来合理」推)执行,读点换成了真正读它的地方:
ChartRenderer.tsx:17,47,164、AdvancedChartImpl.tsx:1968,2229、ObjectChart.tsx:279,703。得到z.array(z.record(z.string(), z.any())).optional(),与同文件BarChartSchema.data逐字一致——而后者是 #6318 从同一渲染器独立推出来的,构成一个交叉核对。⇒ 这条我认为处理得对:意图可执行,指名的函数不可执行。记在这里是为了下一张引用本卡的卡不要再去
normalizeChartSchema里找schema.data。⛔ 以上两条都不需要维护者裁决——决定没变,PR 不因此停。若维护者认为 ③ 轴的论据被削弱到需要重新权衡 option B,那是另一回事,请直接说,我会停下 #7545 的放行。
Generated by Claude Code
claude commentedon Sep 3, 2026
os-dev-report
{
"issue": 7113,
"status": "done",
"branch": "claude/issue-7113-chart-data-model",
"pr": "#7545",
"premise_still_valid": true,
"summary": "Clause-②: yes. PATCH ROUND for the contract review's three findings (comment 5531522981). Draft retained, needs:contract-review retained, no auto-merge, no self-review. New head e7a4b77 (3 commits, 5 changed files). CONTRACT SURFACE BYTE-UNMOVED, verified after the base merge: packages/types/src/zod/data-display.zod.ts = 1178da417a3ded8beae7ed05fe616a16aab57336 and packages/types/src/data-display.ts = 681dfc58be5f395fb89a81d8852d738d2b22a415, both identical to what the reviewer read at 2a927f3;
git log 2a927f390..HEAD -- (those two paths)is empty. No finding forced a change in either. F1 (scope): content/docs/api/schema-reference.md restored to the merge-base byte for byte and GONE from the PR file list (verified via the /pulls/7545/files API: 5 files, schema-reference absent). Ruling #7113 clause 5 fences #7112's five teaching sites to their own card; the earlier revision edited three of them and presented it as a routine docs sweep without disclosing the deviation. The PR body now carries an F1 section stating the removal, the clause, and that #7112 will land them citing the now-declared keys. ⛔ Did NOT take the reviewer's alternative (ask the maintainer to bless the fold) — following the ruling as written needs no ruling. F2 (disclosure): third refusal class added to the changeset AND the PR body §3 table, re-derived on both states rather than inherited — series:[{name:'a',dataKey:123}] ACCEPTs at base with output series:[{name:'a'}] (silently stripped by the non-strict object) and REFUSEs at head at [series.0.dataKey]; dataKey:null identical. It renders today: normalizeChartSchema yields series:[{dataKey:'a'}] because str(123) is undefined and the read falls back to name (normalizeChartSchema.ts:239), measured through the built normaliser. Also fixed the xAxisKey:123 'EMPTY CHART' prose — measured, it keeps the series and loses only the category binding, i.e. a drawn chart with a broken axis; only malformeddataleaves nothing to plot. Also added the undisclosed combinator change. F3 (render pin): packages/plugin-charts/src/ChartRenderer.catalogRender-6939.test.tsx adds the render half ruling 5510084784 requires, in the landed siblings' idiom (element count + tag census + SHA-256 of text), with a lit control that perturbs the authored rows so the identity assertions cannot pass vacuously. 5 tests. Base merged in withgit merge origin/main(⛔ merge, not rebase; no history rewritten); mergeable_state went frombehindtoblocked(awaiting review), and the parity ratchet re-passes after the merge, which brought in changes to base.zod.ts / base.ts / zod-mirror-parity.test.ts itself. Scratch probe removed; both comparison worktrees removed without --force.","tests": "All at head e7a4b77, after the merge. BUILD pnpm --filter '@object-ui/types...' build - green, 120 emitted files verified. TYPECHECK pnpm --filter '@object-ui/types' type-check - green, all three tsc projects echoed. TESTS vitest run on chart-data-model-7113 + chart-inline-data-retired + zod-mirror-parity + ChartRenderer.catalogRender-6939 = 4 files / 58 tests PASS (was 53; +5 from the new render pin). vitest run packages/plugin-charts/src = 45 files / 410 tests PASS (was 44/405), own leg so no recursive first-fail could hide a later package. F3 RENDER MEASUREMENTS, re-derived on MY harness at the merge-base 98d4108 in a second worktree, then at head: advanced-line-chart validates=false->true, elements 136->136, 2 lines, 6 xTicks, text sha256 dd56a5f8c25242bb737db18308f2952c3f5dabbe1dcb6eb9e0b71cdb3a3bbccd unchanged; area-chart validates=false->true, elements 127->127, 1 area, 6 xTicks, sha256 c1270f6053d2dd82c9da87b57607ae04896bf3fc317c9c15357632b48fa05386 unchanged; tag censuses identical both. Verdict moves, drawing does not - #6318's bar. F2 RE-DERIVED on both states via built mirrors in two worktrees: dataKey:123 and dataKey:null ACCEPT at base (output {name:'a'}) / REFUSE at head [series.0.dataKey]; control {name:'a'} ACCEPTs in both. Combinators measured at base (pick/omit/partial/extend/series.pick all OK) and head (pick, omit, partial and ChartDataSeriesSchema.pick all THROW 'cannot be used on object schemas containing refinements'; extend with a new key still OK). GATES all exit 0 with real populations, redirect-then-read: check:control-bytes (6227 files), check:doc-types (188 docs), check:doc-fences (227 documents), check:element-data-source-declaration, check:spec-symbols (1345 files), plus check:vi-mock-specifiers and check:vi-mock-inherit (4267 files, 573 carrying a mock) - newly relevant because this round adds a vi.mock. LINT eslint . = packages/types 0 errors / 269 warnings over 165 files; packages/plugin-charts 0 errors / 334 warnings over 58 files. The new pin file contributes 0 errors and 3 no-explicit-any warnings, all in the recharts mock, identical to the sibling chart render tests; rule is 'warn'. NOT MEASURED, declared: check:doc-snippets and check:sdui-registration-pins return exit 2 = PRECONDITION NOT MET, which those gates document as 'I could not run', not a failure. doc-snippets is now doubly unreachable - this round removes the only docs edit from the diff. Repo-wide lint and the full gate farm are CI-owned. Lock used for every heavy run; VERDICT lines read, never a bare $?; no exit 99.",
"mcp_calls": "0 this round (1 across both rounds - create_pull_request in round 1). Everything else zero-quota REST: review comment read, PR body PATCH, PR files listing, report comment.",
"open_questions": [],
"out_of_scope_findings": [
"filed as #7546 (round 1, unchanged): ChartDataSeriesSchema is a non-strict z.object, so it SILENTLY STRIPS six series keys normalizeSeries reads - label, variant, opacity, dashArray, stack, yAxis. NOTE: the review's F2 third class is the same mechanism caught on a declared key, which strengthens that card rather than duplicating it.",
"NOT filed, reported here as a harness fact for the next seat:
vi.mock('recharts')does NOT intercept plugin-charts' import when the test file lives in examples/schema-catalog/test - pnpm's strict layout gives the two packages different resolved paths for recharts, the real ResponsiveContainer renders, and the chart never paints (symptom: 'width(0) and height(0)'). The render pin therefore lives in packages/plugin-charts/src/, matching the plugin-map sibling. Cost me two runs; recording it rather than filing a card because it is a testing-idiom fact, not a product defect."]
}
Generated by Claude Code
ChartSchemadeclares neither chart-leveldatanorxAxisKey— yet its own JSDoc teaches both, so the corrected chart model documents keys onlyBaseSchema's index signature admits #7549 as a duplicate of this issuefix(plugin-charts): refuse a second scatter series instead of drawing…