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.
finding(types): series chartType is undeclared and SILENTLY STRIPPED - the normalizer reads it first, measured not live, alias-refusal owed #7694
Carved out of objectui#7546 by the domain:ui PM seat, on the contract review of PR #7684. #7684 carries Fixes #7546; without this card the finding below survives only as a comment on a closed card and a pin-test comment. That is measured-and-lost, and it is the seat's miss — objectui#7690 rescued the axis-config half of #7546 and this half was not.
normalizeSeries reads str(raw.chartType) ?? str(raw.type). The first limb is undeclared on ChartDataSeriesSchema, which is a non-strict z.object, so an authored chartType on a series is silently stripped by the parse and never reaches that limb.
chartType measured not live on the authoring face, with lit controls, twice:
population
chartType
control
docs
0
dataKey / name / type / color lit
fixtures
0
lit
designer inputs
0
the chart registration's series is one code input
src literals
0
—
tests
9
all internal dataKey-shaped, taking ChartRenderer's raw fast path — they never reach the normalizer's first limb
Limb ablation, re-run by the reviewer on 197 files / 2380 tests (5× the author's population): deleting str(raw.chartType) ?? left 2380 passed; the sibling-limb control (deleting ?? str(raw.type)) went 2 failed. The instrument is lit and the zero is a reading.
And the contract of record refuses the name itself: @objectstack/spec@17.2.0 answers Unrecognized key(s) on this chart series: chartType. Did you mean chartType → type? — its alias map already declares chartType a spelling of type. Core's own chart-presentation.ts:122-123 translates authored type → internal chartType, confirming it is the internal spelling.
The ruled shape — option A
The PM ruling on #7546 and the contract review of #7684 both land on A: a named alias refusal pointing at type, the posture the spec itself takes. Not B (fold chartType onto type at parse — the renderer takes chartType FIRST, so the fold would let the alias overwrite the canonical key when both are written, inverting the objectui#7113 precedence rule) and not C (declare it as a second writable name — contradicts the spec's alias map and the three in-repo docblocks calling it the internal spelling; the N-dialects hazard of AGENTS.md #0.1).
⚠️This is a NARROWING, and that is why it is its own card.{ name: 'x', chartType: 'line' } draws a line today through the first limb; a named refusal takes that away. It needs its own changeset grade and its own liveness re-read at the time of implementation — ⛔ do not inherit the readings above as still-true, re-measure them.
Not in scope
⛔ Do not touch the ?? str(raw.type) sibling limb — it is lit (2 red on ablation). ⛔ Do not fold this into #7684; that PR's fence is deliberate. Sibling cards from the same measurement: objectui#7681 (a declared per-series type is dropped on the dataKey fast path), objectui#7682 (variant carries three different unions across four sites), objectui#7690 (the axis-config object dialect).
Claim: PM loop round 25 — domain:ui execution seat
Session: session_01KbJQ1y1J12nZxYzFWhP8Q3
Branch: claude/issue-7694-chart-type-alias-refusal
Worktree: objectui-issue-7694
Domain: domain:ui
File surface: packages/types/src/data-display.ts, packages/types/src/zod/data-display.zod.ts, the affected pin tests, content/docs/plugins/plugin-charts.mdx (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: fable — node scripts/pm/dispatch-gates.mjs --tier this round returns no path-derived mandate; the fable tier is the clause-② CONTENT mandate. This card is a narrowing of a published accept surface, which is the clearest clause-② shape there is.
Clause-②: yes
Serial constraints cleared: None contended. All four in-flight packages/types PRs were read file-by-file this round: #7731 (parity test only), #7732 (complex.ts / complex.zod.ts / chatbot docs), #7730 (zod/base.zod.ts only), #7726 (base.ts, both barrels, base.zod.ts, form.zod.ts, parity test, many docs) — not one touches data-display.ts or data-display.zod.ts. #7684 merged 8fe8e5c16, so the six series keys this card narrows against are on main. Read-coupling declared: #7698 (p2) and #7682 cover the renderer side of the same series; ⛔ this card must not touch a reader.
{
"issue": 7694,
"status": "done",
"branch": "claude/issue-7694-chart-type-alias-refusal",
"pr": "https://github.com/objectstack-ai/objectui/pull/7737",
"premise_still_valid": true,
"summary": "Implemented the ruled option A: `ChartDataSeriesSchema.chartType` is a named ALIAS REFUSAL arm pointing at `type`, and `ChartDataSeries.chartType` is a `?: never` tombstone. Exact refusal shipped (one string on both channels, code `invalid_type`, path `['chartType']`): \"Unrecognized key(s) on this chart series: `chartType`. Did you mean `chartType` → `type`? `chartType` is the renderer's INTERNAL spelling of the declared `type` (objectui#7694): `@objectstack/spec`'s `ChartSeriesSchema` lists it as an alias of `type` and refuses it the same way, and nothing on this authoring face writes it. Write `type` (`bar` | `line` | `area`) for a per-series family override. Until this refusal an authored `chartType` was STRIPPED in silence by this non-strict object, so the series drew in the chart's own family — precisely what the author was overriding.\" Both-written is refused at `chartType` alone (not folded, no precedence minted); no reader touched. Convention: the package's named-refusal family in packages/types/src/zod/tombstone.zod.ts — `retirementTombstone` (line 65, z.never + optional + describe) and `handlerKeyRefusal` (line 121, z.custom) — fits an alias by NAME on neither side, so I added a third sibling `aliasKeyRefusal(alias, canonical, surface, detail)` reusing retirementTombstone's z.never PRIMITIVE (no new shape) — chosen over z.custom because, measured on the base with tsx, `z.toJSONSchema` throws on a z.custom arm and represents a z.never arm as `{ not: {} }`; `z.toJSONSchema(ChartDataSeriesSchema)` worked before and still does (11 → 12 properties). Re-measured census (series-level `chartType`, comment-aware `series` array-literal bracket matcher, controls dataKey/name/type/color in the same query) vs the card: docs 0 vs 0 (controls 10/11/2/2), fixtures 0 vs 0 (3/2/0/2), designer inputs 0 vs 0 (the chart registration's `series` is one `code` input, packages/plugin-charts/src/index.tsx:114), src literals 0 vs 0 (13/3/1/0), tests 9 vs 9 (70/48/11/8; comboMarkClick:114 x2, comboFromSeries:59,71,101,116,134 x6, ChartRenderer.specSeries:97) — all five agree with the card. Spec posture measured live on the installed @objectstack/spec 17.2.0: ChartSeriesSchema refuses `{ name, chartType }` with `unrecognized_keys` and the same `Did you mean` sentence; `type` control green. Files touched: packages/types/src/zod/data-display.zod.ts (arm + import; uncontended), packages/types/src/data-display.ts (`chartType?: never` + docblock; uncontended), packages/types/src/zod/tombstone.zod.ts (aliasKeyRefusal + header line), packages/types/src/__tests__/chart-series-chart-type-alias-refusal-7694.test.ts (new pin, blocks a–f), packages/types/src/__tests__/chart-series-keys-7546.test.ts (block (d) restated as the handoff per objectui#7070, header prose), .changeset/7694-chart-series-chart-type-alias-refusal.md (`@object-ui/types: minor`, narrowing stated in release-reader words, own grade), content/docs/plugins/plugin-charts.mdx and content/docs/api/schema-reference.md (one sentence / one clause). Contended files from the brief — zod-mirror-parity.test.ts, zod/index.zod.ts, index.ts — NOT touched; no parity-ledger entry needed (arm z.input is `undefined`, twin is `?: never`, parity suite passed unchanged). PR #7737 is draft, body carries `Fixes #7694` and a Clause-② section (5 lines), label `needs:contract-review` added and read back; PR body read back intact (0 angle-bracket-shaped tokens, footer survived in session form). Assignee os-sam was set by the PM; not touched.",
"tests": "All on HEAD de8fda691 (branch pushed), exit codes captured before any pipe, under scripts/pm/os-verify-lock.sh (shared box). LIMB ABLATION on the untouched base a00db9ef9 (before any edit), population 304 files / 5817 tests (plugin-charts, plugin-dashboard, types, plugin-report, schema-catalog + the 8 outside test files importing plugin-charts/normalizeChartSchema; vitest alias resolves @object-ui/plugin-charts to src so the mutated SOURCE is what tests resolved — no dist rebuild on the path; each leg: anchor count 1 before, `grep -c` old anchor 0 / new line 1 after, `git diff HEAD --stat` 1 file +1/-1, mutated blob hash printed; restore by `git checkout HEAD -- ABSOLUTE_PATH` in a trap EXIT INT TERM, proven by `git hash-object` == HEAD blob 05a66dfb and empty `git diff HEAD --stat`): leg A delete `str(raw.chartType) ?? ` → `Test Files 304 passed (304) / Tests 5817 passed (5817)`, vitest exit 0 (GREEN — key still not live); leg B delete ` ?? str(raw.type)` → `Test Files 2 failed | 302 passed (304) / Tests 2 failed | 5815 passed (5817)`, exit 1 (RED: normalizeChartSchema.test.ts 'carries stack / yAxis / color on a series', ChartRenderer.specSeries.test.tsx 'honours a spec series[].type override end to end' — the card's same two). VERIFY UNION on de8fda691: packages/types `tsc --noEmit` exit 0; `tsc -p tsconfig.test.json --listFiles` exit 0 with both edited test files listed (2); `tsc -p tsconfig.examples.json` exit 0; `eslint .` exit 0 ('0 errors, 268 warnings', pre-existing no-explicit-any, none on touched lines); `pnpm exec vitest run --maxWorkers=2 packages/types/` → 'Test Files 116 passed (116) / Tests 1991 passed (1991)' exit 0 (incl. zod-mirror-parity); `pnpm --filter @object-ui/types build` → '✓ dist completeness: 1 package(s) complete (120 emitted files verified)', `chartType?: never` present in dist/data-display.d.ts. REVERSE VERIFICATION (fix committed first; data-display.zod.ts + data-display.ts reverted to BASE, helper kept so the instrument loads; predicted direction RED): pin files → 'Tests 15 failed | 37 passed (52)' exit 1; `tsc -p tsconfig.test.json` exit 2 with exactly the 3 predicted errors (TS2339 z.input line, TS2339 ChartDataSeries['chartType'] line, TS2578 unused directive on the non-fresh assignment); restored, both hashes == HEAD blobs, `git diff HEAD --stat` empty. Observed direction: red as predicted. GATES on the committed tree: check:control-bytes '✅ OK (scanned 6302 tracked text file(s))'; check:doc-fences ✅; check:doc-types '✅ Every documented component type is registered'; check:spec-symbols ✅ (3 lines); check:unreferenced-sources 'OK Every shipped source file … reachable'; check:phantom-deps ✅; docs:check-links 'Links are valid across 17 scan roots'; check-changeset-presence '✅ 5 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'; check-changeset-no-major ✅; check-changeset-fixed ✅; check-changeset-overwrite '✅ No pre-existing changeset was modified or deleted' (1 added, 0 modified, 0 deleted). DECLARED NARROWINGS (CI owns the farm): check:doc-snippets NOT MEASURED locally — 'PRECONDITION NOT MET (exit 2)', needs 26 packages built; cannot turn red on this diff because the docs edits are prose only (no fence) and the sole type change (`chartType?: never` on a series) can only bite a snippet writing series-level `chartType`, which the docs census reads 0 with lit controls — doc-snippet-types.yml runs it in CI. Downstream consumer type-check (prefix filter '...@object-ui/types' = consumers) not run in full (15-package build closure vs the 10-min cap): zero packages outside packages/types import ChartDataSeries or ChartSchema from @object-ui/types (grep), src-literal census 0 with lit controls; CI type-check job runs the farm. Repo-wide `pnpm lint` / `pnpm test` left to CI. CI status at report time: in_progress.",
"mcp_calls": "0 — issue read, PR create, label add, read-backs and this report comment all went over REST (curl / urllib with GITHUB_TOKEN); no MCP GitHub call in the whole run",
"open_questions": [
{
"question": "The reader's first limb: `normalizeSeries` still reads `chartType` first on the internal-shape arrays its producers hand it, while the authoring face now refuses the same key. Fence-correct here (no reader touched). Is that limb owed a docblock or a card of its own stating it is an internal-shape read only?",
"options": [
"A — leave the reader as is; the internal-shape contract (ChartRenderer's dataKey-shaped arm) legitimately carries chartType",
"B — file a follow-up card to docblock the limb as internal-only",
"C — retire the limb at the reader (would change what internal producers render)"
],
"recommendation": "A for now, B if the contract reviewer wants the split written down at the read site; C is a reader decision outside this card"
},
{
"question": "`aliasKeyRefusal` as the third member of tombstone.zod.ts reuses retirementTombstone's z.never primitive (code invalid_type) rather than handlerKeyRefusal's z.custom (code custom), because z.toJSONSchema throws on z.custom and ChartDataSeriesSchema was JSON-Schema-representable before this change. Should an alias arm instead share the `custom` code and give up the JSON-Schema surface?",
"options": [
"A — keep z.never: JSON-Schema surface preserved, told apart from a retirement by wording (pinned)",
"B — switch to z.custom for code parity with handlerKeyRefusal, accepting that z.toJSONSchema(ChartDataSeriesSchema) starts throwing (a Clause-② change of its own)"
],
"recommendation": "A — block (f) of the pin measures the trade; B would narrow a surface that worked"
},
{
"question": "The arm's lead sentence is byte-identical to the spec's today (`Unrecognized key(s) on this chart series: \\`chartType\\`. Did you mean \\`chartType\\` → \\`type\\`?`). The pin asserts the lead and the `Did you mean` fragment, deliberately not the spec's trailing `history` clause, so a spec reword does not turn an objectui pin red. Should the full spec sentence be pinned instead?",
"options": [
"A — pin lead + remedy fragment only (as shipped)",
"B — pin the spec's whole message byte-for-byte (couples objectui's pin to spec rewording)"
],
"recommendation": "A — the remedy fragment is the contract; B trades a spec-bump PR's time for no author-facing gain"
}
],
"out_of_scope_findings": []
}
{
"issue": 7694,
"status": "done",
"branch": "claude/issue-7694-chart-type-alias-refusal",
"pr": "https://github.com/objectstack-ai/objectui/pull/7737",
"premise_still_valid": true,
"summary": "Land-prep only on the reviewed PR #7737; the PASS stands and nothing behavioural moved. (1) MERGE, never rebase: origin/main had moved seven commits (a00db9ef9..81a2eb1fb — #7621, #7738, #7686, #7732, #7731, #7730, #7726); merge commit 6bcd58ff2, 'Merge made by the ort strategy', 0 conflicts, and `git diff de8fda691..HEAD` restricted to this PR's own files is EMPTY, so the merge moved nothing the PR owns. Final head 3929d1036, pushed fast-forward (de8fda691..3929d1036), no force. (2) tombstone.zod.ts docblock: the falsified census claim is gone, replaced by the re-measured MenuItemSchema.type / #6523 precedent and the real differentiator (composed spec sentence: a vocabulary, not a shape). (3) Pin block (e) now derives the expected lead from the installed spec's LIVE message. (4) Clause-2 items 1 and 2 and the changeset now name the embedding parents and the io-mode nuance. (5) The PR body now cites `tsc -p packages/types/tsconfig.test.json` as the parity instrument, not vitest — and it also carried the same falsified census claim, which I corrected there too (naming objectui#7678). PR stays DRAFT; `needs:contract-review` verified still present on BOTH carriers (PR 7737 and issue 7694); I stripped nothing. TWO PM-SIDE NOTES. (a) The brief's premise 'the branch tip is unchanged at de8fda691, so there is no half-finished work' was half false: the worktree /home/user/objectui-7694-land carried TWO UNPUSHED local commits from the killed prior run (merge f7f706f25 + land-prep c816881be). The remote tip was indeed de8fda691. I saved that work as a patch (scratchpad issue-7694/prior-landprep-c816881be.patch), reset the branch to the remote tip, and redid everything from my own measurements — nothing was reused. (b) The 92-byte lead: measured here through the `?` the shared cut is 91 bytes, not 92; the difference is the single space that follows it. Recorded in the PR body rather than silently adopting either number.",
"tests": "All on final head 3929d1036 (git rev-parse --short HEAD from the same runs), exit codes captured by redirect BEFORE any pipe, heavy runs through scripts/pm/os-verify-lock.sh (shared box). GATES: `tsc -p packages/types/tsconfig.test.json --noEmit` exit 0 — and NOT the 'ran but measured nothing' trap: `--listFiles` reports 580 files and all three edited paths are listed (the pin test, zod/tombstone.zod.ts, data-display.ts). `pnpm --filter @object-ui/types build` exit 0, 'dist completeness: 1 package(s) complete (124 emitted files verified)'. `pnpm exec eslint .` REPO-WIDE exit 0, '11939 problems (0 errors, 11939 warnings)' — all pre-existing no-explicit-any. `pnpm exec vitest run packages/types/` exit 0, 'Test Files 119 passed (119) · Tests 2054 passed (2054)'. Four changeset gates all exit 0: presence ('5 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'), no-major, fixed, overwrite ('1 changeset(s) added, 0 modified, 0 deleted'). Also check:control-bytes exit 0 (6317 tracked text files), check:doc-fences exit 0, check-governed-queue-guard --test over the PR's 8 paths: 'NOT GOVERNED — 8 path(s) checked against 5 governed surface(s); none matched'. PARITY RATCHET LIT ON THE MERGED HEAD: control `tsc -p packages/types/tsconfig.test.json --noEmit` exit 0 on the untouched tree; mutate the TS twin chartType?: never to chartType?: string — mutation CONFIRMED ON DISK before reading anything (old anchor grep -c 1 to 0, injected line 1, blob 9aa36e06212459f6ebf4a5329b1c2f0156c8cd0e differs from HEAD blob 9fb073280cfe57d94446b0d150217dd3ec1e39b7, git diff HEAD --stat = 1 file / 1 insertion / 1 deletion) — same command exit 2 with, among 5 errors, `packages/types/src/__tests__/zod-mirror-parity.test.ts(1522,14): error TS2322: Type 'LedgerMismatch' is not assignable to type 'never'.` naming the pair \"data-display.zod.ts#ChartDataSeriesSchema\". Restored inside `trap ... EXIT INT TERM` against an ABSOLUTE path, proven by git hash-object == 9fb073280cfe57d94446b0d150217dd3ec1e39b7 and an EMPTY whole-tree git diff HEAD --stat. BLOCK (e) CAN STILL FAIL, two directions: (i) OUR side diverges (real tree mutation) — Unrecognized to Unrecognised in the composer, mutation confirmed on disk (old anchor 1 to 0, injected 1, blob 71d581582e7043f9bf9995f4d9a5b5c546236ac8 differs from HEAD d431a1f9b263a76997921426cd4ebc3f86a02d76) — block goes 'Tests 1 failed | 24 skipped (25)', vitest exit 1, 'AssertionError: expected false to be true'; restored under trap, hash-object == d431a1f9b263a76997921426cd4ebc3f86a02d76, git diff HEAD --stat empty. (ii) THE SPEC's side rewords its lead (scratch fixture, no tree mutation): the OLD shape stays GREEN on BOTH halves — exactly the defect the review named — while the NEW shape goes RED; unreworded live control GREEN. Pin file green after the rewrite: 'Test Files 1 passed (1) · Tests 25 passed (25)'. IO-MODE MEASUREMENT (changeset / Clause-2 item 2), against a base stand-in validated byte-for-byte against the real emission in both modes: io input — HEAD 12 properties, additionalProperties absent; base 11 properties, additionalProperties absent (so the accept set genuinely narrows). io output (the default) — HEAD 12 / additionalProperties false; base 11 / additionalProperties FALSE ALREADY (so the key was already outside the accept set; what output mode gains is the named refusal). EMBEDDING PARENTS re-measured on the merged head: ChartSchema.series at zod/data-display.zod.ts:622, ReportSectionSchema.chart at zod/reports.zod.ts:105, safeValidateSchema at zod/index.zod.ts:434, objectui check / validate at packages/cli/src/cli.ts:211 and :223.",
"mcp_calls": "0 — the whole run went through authenticated REST (repo read, PR read, PR body PATCH, issue read, comment post); zero MCP GitHub tool calls",
"open_questions": [],
"out_of_scope_findings": []
}
Activity
claude commentedon Sep 5, 2026
Claim: PM loop round 25 —
domain:uiexecution seatSession:
session_01KbJQ1y1J12nZxYzFWhP8Q3Branch:
claude/issue-7694-chart-type-alias-refusalWorktree:
objectui-issue-7694Domain:
domain:uiFile surface:
packages/types/src/data-display.ts,packages/types/src/zod/data-display.zod.ts, the affected pin tests,content/docs/plugins/plugin-charts.mdx(stop on breach; explain in the report)Container & model: M,
mode:subagent,model: fable—node scripts/pm/dispatch-gates.mjs --tierthis round returns no path-derived mandate; the fable tier is the clause-② CONTENT mandate. This card is a narrowing of a published accept surface, which is the clearest clause-② shape there is.Clause-②: yes
Serial constraints cleared: None contended. All four in-flight
packages/typesPRs were read file-by-file this round:#7731(parity test only),#7732(complex.ts/complex.zod.ts/ chatbot docs),#7730(zod/base.zod.tsonly),#7726(base.ts, both barrels,base.zod.ts,form.zod.ts, parity test, many docs) — not one touchesdata-display.tsordata-display.zod.ts.#7684merged8fe8e5c16, so the six series keys this card narrows against are onmain. Read-coupling declared:#7698(p2) and#7682cover the renderer side of the same series; ⛔ this card must not touch a reader.Generated by Claude Code
claude commentedon Sep 5, 2026
os-dev-report
{ "issue": 7694, "status": "done", "branch": "claude/issue-7694-chart-type-alias-refusal", "pr": "https://github.com/objectstack-ai/objectui/pull/7737", "premise_still_valid": true, "summary": "Implemented the ruled option A: `ChartDataSeriesSchema.chartType` is a named ALIAS REFUSAL arm pointing at `type`, and `ChartDataSeries.chartType` is a `?: never` tombstone. Exact refusal shipped (one string on both channels, code `invalid_type`, path `['chartType']`): \"Unrecognized key(s) on this chart series: `chartType`. Did you mean `chartType` → `type`? `chartType` is the renderer's INTERNAL spelling of the declared `type` (objectui#7694): `@objectstack/spec`'s `ChartSeriesSchema` lists it as an alias of `type` and refuses it the same way, and nothing on this authoring face writes it. Write `type` (`bar` | `line` | `area`) for a per-series family override. Until this refusal an authored `chartType` was STRIPPED in silence by this non-strict object, so the series drew in the chart's own family — precisely what the author was overriding.\" Both-written is refused at `chartType` alone (not folded, no precedence minted); no reader touched. Convention: the package's named-refusal family in packages/types/src/zod/tombstone.zod.ts — `retirementTombstone` (line 65, z.never + optional + describe) and `handlerKeyRefusal` (line 121, z.custom) — fits an alias by NAME on neither side, so I added a third sibling `aliasKeyRefusal(alias, canonical, surface, detail)` reusing retirementTombstone's z.never PRIMITIVE (no new shape) — chosen over z.custom because, measured on the base with tsx, `z.toJSONSchema` throws on a z.custom arm and represents a z.never arm as `{ not: {} }`; `z.toJSONSchema(ChartDataSeriesSchema)` worked before and still does (11 → 12 properties). Re-measured census (series-level `chartType`, comment-aware `series` array-literal bracket matcher, controls dataKey/name/type/color in the same query) vs the card: docs 0 vs 0 (controls 10/11/2/2), fixtures 0 vs 0 (3/2/0/2), designer inputs 0 vs 0 (the chart registration's `series` is one `code` input, packages/plugin-charts/src/index.tsx:114), src literals 0 vs 0 (13/3/1/0), tests 9 vs 9 (70/48/11/8; comboMarkClick:114 x2, comboFromSeries:59,71,101,116,134 x6, ChartRenderer.specSeries:97) — all five agree with the card. Spec posture measured live on the installed @objectstack/spec 17.2.0: ChartSeriesSchema refuses `{ name, chartType }` with `unrecognized_keys` and the same `Did you mean` sentence; `type` control green. Files touched: packages/types/src/zod/data-display.zod.ts (arm + import; uncontended), packages/types/src/data-display.ts (`chartType?: never` + docblock; uncontended), packages/types/src/zod/tombstone.zod.ts (aliasKeyRefusal + header line), packages/types/src/__tests__/chart-series-chart-type-alias-refusal-7694.test.ts (new pin, blocks a–f), packages/types/src/__tests__/chart-series-keys-7546.test.ts (block (d) restated as the handoff per objectui#7070, header prose), .changeset/7694-chart-series-chart-type-alias-refusal.md (`@object-ui/types: minor`, narrowing stated in release-reader words, own grade), content/docs/plugins/plugin-charts.mdx and content/docs/api/schema-reference.md (one sentence / one clause). Contended files from the brief — zod-mirror-parity.test.ts, zod/index.zod.ts, index.ts — NOT touched; no parity-ledger entry needed (arm z.input is `undefined`, twin is `?: never`, parity suite passed unchanged). PR #7737 is draft, body carries `Fixes #7694` and a Clause-② section (5 lines), label `needs:contract-review` added and read back; PR body read back intact (0 angle-bracket-shaped tokens, footer survived in session form). Assignee os-sam was set by the PM; not touched.", "tests": "All on HEAD de8fda691 (branch pushed), exit codes captured before any pipe, under scripts/pm/os-verify-lock.sh (shared box). LIMB ABLATION on the untouched base a00db9ef9 (before any edit), population 304 files / 5817 tests (plugin-charts, plugin-dashboard, types, plugin-report, schema-catalog + the 8 outside test files importing plugin-charts/normalizeChartSchema; vitest alias resolves @object-ui/plugin-charts to src so the mutated SOURCE is what tests resolved — no dist rebuild on the path; each leg: anchor count 1 before, `grep -c` old anchor 0 / new line 1 after, `git diff HEAD --stat` 1 file +1/-1, mutated blob hash printed; restore by `git checkout HEAD -- ABSOLUTE_PATH` in a trap EXIT INT TERM, proven by `git hash-object` == HEAD blob 05a66dfb and empty `git diff HEAD --stat`): leg A delete `str(raw.chartType) ?? ` → `Test Files 304 passed (304) / Tests 5817 passed (5817)`, vitest exit 0 (GREEN — key still not live); leg B delete ` ?? str(raw.type)` → `Test Files 2 failed | 302 passed (304) / Tests 2 failed | 5815 passed (5817)`, exit 1 (RED: normalizeChartSchema.test.ts 'carries stack / yAxis / color on a series', ChartRenderer.specSeries.test.tsx 'honours a spec series[].type override end to end' — the card's same two). VERIFY UNION on de8fda691: packages/types `tsc --noEmit` exit 0; `tsc -p tsconfig.test.json --listFiles` exit 0 with both edited test files listed (2); `tsc -p tsconfig.examples.json` exit 0; `eslint .` exit 0 ('0 errors, 268 warnings', pre-existing no-explicit-any, none on touched lines); `pnpm exec vitest run --maxWorkers=2 packages/types/` → 'Test Files 116 passed (116) / Tests 1991 passed (1991)' exit 0 (incl. zod-mirror-parity); `pnpm --filter @object-ui/types build` → '✓ dist completeness: 1 package(s) complete (120 emitted files verified)', `chartType?: never` present in dist/data-display.d.ts. REVERSE VERIFICATION (fix committed first; data-display.zod.ts + data-display.ts reverted to BASE, helper kept so the instrument loads; predicted direction RED): pin files → 'Tests 15 failed | 37 passed (52)' exit 1; `tsc -p tsconfig.test.json` exit 2 with exactly the 3 predicted errors (TS2339 z.input line, TS2339 ChartDataSeries['chartType'] line, TS2578 unused directive on the non-fresh assignment); restored, both hashes == HEAD blobs, `git diff HEAD --stat` empty. Observed direction: red as predicted. GATES on the committed tree: check:control-bytes '✅ OK (scanned 6302 tracked text file(s))'; check:doc-fences ✅; check:doc-types '✅ Every documented component type is registered'; check:spec-symbols ✅ (3 lines); check:unreferenced-sources 'OK Every shipped source file … reachable'; check:phantom-deps ✅; docs:check-links 'Links are valid across 17 scan roots'; check-changeset-presence '✅ 5 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'; check-changeset-no-major ✅; check-changeset-fixed ✅; check-changeset-overwrite '✅ No pre-existing changeset was modified or deleted' (1 added, 0 modified, 0 deleted). DECLARED NARROWINGS (CI owns the farm): check:doc-snippets NOT MEASURED locally — 'PRECONDITION NOT MET (exit 2)', needs 26 packages built; cannot turn red on this diff because the docs edits are prose only (no fence) and the sole type change (`chartType?: never` on a series) can only bite a snippet writing series-level `chartType`, which the docs census reads 0 with lit controls — doc-snippet-types.yml runs it in CI. Downstream consumer type-check (prefix filter '...@object-ui/types' = consumers) not run in full (15-package build closure vs the 10-min cap): zero packages outside packages/types import ChartDataSeries or ChartSchema from @object-ui/types (grep), src-literal census 0 with lit controls; CI type-check job runs the farm. Repo-wide `pnpm lint` / `pnpm test` left to CI. CI status at report time: in_progress.", "mcp_calls": "0 — issue read, PR create, label add, read-backs and this report comment all went over REST (curl / urllib with GITHUB_TOKEN); no MCP GitHub call in the whole run", "open_questions": [ { "question": "The reader's first limb: `normalizeSeries` still reads `chartType` first on the internal-shape arrays its producers hand it, while the authoring face now refuses the same key. Fence-correct here (no reader touched). Is that limb owed a docblock or a card of its own stating it is an internal-shape read only?", "options": [ "A — leave the reader as is; the internal-shape contract (ChartRenderer's dataKey-shaped arm) legitimately carries chartType", "B — file a follow-up card to docblock the limb as internal-only", "C — retire the limb at the reader (would change what internal producers render)" ], "recommendation": "A for now, B if the contract reviewer wants the split written down at the read site; C is a reader decision outside this card" }, { "question": "`aliasKeyRefusal` as the third member of tombstone.zod.ts reuses retirementTombstone's z.never primitive (code invalid_type) rather than handlerKeyRefusal's z.custom (code custom), because z.toJSONSchema throws on z.custom and ChartDataSeriesSchema was JSON-Schema-representable before this change. Should an alias arm instead share the `custom` code and give up the JSON-Schema surface?", "options": [ "A — keep z.never: JSON-Schema surface preserved, told apart from a retirement by wording (pinned)", "B — switch to z.custom for code parity with handlerKeyRefusal, accepting that z.toJSONSchema(ChartDataSeriesSchema) starts throwing (a Clause-② change of its own)" ], "recommendation": "A — block (f) of the pin measures the trade; B would narrow a surface that worked" }, { "question": "The arm's lead sentence is byte-identical to the spec's today (`Unrecognized key(s) on this chart series: \\`chartType\\`. Did you mean \\`chartType\\` → \\`type\\`?`). The pin asserts the lead and the `Did you mean` fragment, deliberately not the spec's trailing `history` clause, so a spec reword does not turn an objectui pin red. Should the full spec sentence be pinned instead?", "options": [ "A — pin lead + remedy fragment only (as shipped)", "B — pin the spec's whole message byte-for-byte (couples objectui's pin to spec rewording)" ], "recommendation": "A — the remedy fragment is the contract; B trades a spec-bump PR's time for no author-facing gain" } ], "out_of_scope_findings": [] }Generated by Claude Code
claude commentedon Sep 5, 2026
os-dev-report
{ "issue": 7694, "status": "done", "branch": "claude/issue-7694-chart-type-alias-refusal", "pr": "https://github.com/objectstack-ai/objectui/pull/7737", "premise_still_valid": true, "summary": "Land-prep only on the reviewed PR #7737; the PASS stands and nothing behavioural moved. (1) MERGE, never rebase: origin/main had moved seven commits (a00db9ef9..81a2eb1fb — #7621, #7738, #7686, #7732, #7731, #7730, #7726); merge commit 6bcd58ff2, 'Merge made by the ort strategy', 0 conflicts, and `git diff de8fda691..HEAD` restricted to this PR's own files is EMPTY, so the merge moved nothing the PR owns. Final head 3929d1036, pushed fast-forward (de8fda691..3929d1036), no force. (2) tombstone.zod.ts docblock: the falsified census claim is gone, replaced by the re-measured MenuItemSchema.type / #6523 precedent and the real differentiator (composed spec sentence: a vocabulary, not a shape). (3) Pin block (e) now derives the expected lead from the installed spec's LIVE message. (4) Clause-2 items 1 and 2 and the changeset now name the embedding parents and the io-mode nuance. (5) The PR body now cites `tsc -p packages/types/tsconfig.test.json` as the parity instrument, not vitest — and it also carried the same falsified census claim, which I corrected there too (naming objectui#7678). PR stays DRAFT; `needs:contract-review` verified still present on BOTH carriers (PR 7737 and issue 7694); I stripped nothing. TWO PM-SIDE NOTES. (a) The brief's premise 'the branch tip is unchanged at de8fda691, so there is no half-finished work' was half false: the worktree /home/user/objectui-7694-land carried TWO UNPUSHED local commits from the killed prior run (merge f7f706f25 + land-prep c816881be). The remote tip was indeed de8fda691. I saved that work as a patch (scratchpad issue-7694/prior-landprep-c816881be.patch), reset the branch to the remote tip, and redid everything from my own measurements — nothing was reused. (b) The 92-byte lead: measured here through the `?` the shared cut is 91 bytes, not 92; the difference is the single space that follows it. Recorded in the PR body rather than silently adopting either number.", "tests": "All on final head 3929d1036 (git rev-parse --short HEAD from the same runs), exit codes captured by redirect BEFORE any pipe, heavy runs through scripts/pm/os-verify-lock.sh (shared box). GATES: `tsc -p packages/types/tsconfig.test.json --noEmit` exit 0 — and NOT the 'ran but measured nothing' trap: `--listFiles` reports 580 files and all three edited paths are listed (the pin test, zod/tombstone.zod.ts, data-display.ts). `pnpm --filter @object-ui/types build` exit 0, 'dist completeness: 1 package(s) complete (124 emitted files verified)'. `pnpm exec eslint .` REPO-WIDE exit 0, '11939 problems (0 errors, 11939 warnings)' — all pre-existing no-explicit-any. `pnpm exec vitest run packages/types/` exit 0, 'Test Files 119 passed (119) · Tests 2054 passed (2054)'. Four changeset gates all exit 0: presence ('5 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'), no-major, fixed, overwrite ('1 changeset(s) added, 0 modified, 0 deleted'). Also check:control-bytes exit 0 (6317 tracked text files), check:doc-fences exit 0, check-governed-queue-guard --test over the PR's 8 paths: 'NOT GOVERNED — 8 path(s) checked against 5 governed surface(s); none matched'. PARITY RATCHET LIT ON THE MERGED HEAD: control `tsc -p packages/types/tsconfig.test.json --noEmit` exit 0 on the untouched tree; mutate the TS twin chartType?: never to chartType?: string — mutation CONFIRMED ON DISK before reading anything (old anchor grep -c 1 to 0, injected line 1, blob 9aa36e06212459f6ebf4a5329b1c2f0156c8cd0e differs from HEAD blob 9fb073280cfe57d94446b0d150217dd3ec1e39b7, git diff HEAD --stat = 1 file / 1 insertion / 1 deletion) — same command exit 2 with, among 5 errors, `packages/types/src/__tests__/zod-mirror-parity.test.ts(1522,14): error TS2322: Type 'LedgerMismatch' is not assignable to type 'never'.` naming the pair \"data-display.zod.ts#ChartDataSeriesSchema\". Restored inside `trap ... EXIT INT TERM` against an ABSOLUTE path, proven by git hash-object == 9fb073280cfe57d94446b0d150217dd3ec1e39b7 and an EMPTY whole-tree git diff HEAD --stat. BLOCK (e) CAN STILL FAIL, two directions: (i) OUR side diverges (real tree mutation) — Unrecognized to Unrecognised in the composer, mutation confirmed on disk (old anchor 1 to 0, injected 1, blob 71d581582e7043f9bf9995f4d9a5b5c546236ac8 differs from HEAD d431a1f9b263a76997921426cd4ebc3f86a02d76) — block goes 'Tests 1 failed | 24 skipped (25)', vitest exit 1, 'AssertionError: expected false to be true'; restored under trap, hash-object == d431a1f9b263a76997921426cd4ebc3f86a02d76, git diff HEAD --stat empty. (ii) THE SPEC's side rewords its lead (scratch fixture, no tree mutation): the OLD shape stays GREEN on BOTH halves — exactly the defect the review named — while the NEW shape goes RED; unreworded live control GREEN. Pin file green after the rewrite: 'Test Files 1 passed (1) · Tests 25 passed (25)'. IO-MODE MEASUREMENT (changeset / Clause-2 item 2), against a base stand-in validated byte-for-byte against the real emission in both modes: io input — HEAD 12 properties, additionalProperties absent; base 11 properties, additionalProperties absent (so the accept set genuinely narrows). io output (the default) — HEAD 12 / additionalProperties false; base 11 / additionalProperties FALSE ALREADY (so the key was already outside the accept set; what output mode gains is the named refusal). EMBEDDING PARENTS re-measured on the merged head: ChartSchema.series at zod/data-display.zod.ts:622, ReportSectionSchema.chart at zod/reports.zod.ts:105, safeValidateSchema at zod/index.zod.ts:434, objectui check / validate at packages/cli/src/cli.ts:211 and :223.", "mcp_calls": "0 — the whole run went through authenticated REST (repo read, PR read, PR body PATCH, issue read, comment post); zero MCP GitHub tool calls", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code