feat(types): declare drillDown / title / compareTo on both published copies of ObjectChartSchema (objectui#8885) - #8895
Conversation
…ished copies of `ObjectChartSchema` (objectui#8885)
`ObjectChart.tsx` reads all three off `schema`, and neither published copy of
the shape declared any of them — not the TS interface (`objectql.ts`) nor the
zod mirror (`zod/objectql.zod.ts`). They rode `BaseSchema`'s index signature /
`.passthrough()` and arrived unvalidated. This is the objectui#6914 class, and
`drillDown` was its sharpest instance: the component's registry `inputs`
advertise it to the designer palette and `@objectstack/spec` publishes
`ChartDrillDownSchema` for exactly this carrier, so an author was offered a key
neither published shape mentioned.
Each key binds to the spec symbol that already owns it, never a local near-copy:
- `drillDown` -> `ChartDrillDown` / `ChartDrillDownSchema`. Deliberately NOT
this repo's wider `DrillDownConfig`: that type also carries `mode` and
`report` for the table / pivot / metric widgets, and `ObjectChart.tsx` reads
neither, so declaring them would be authoring bait.
- `title` -> `I18nLabel`, the union `ChartConfigSchema.title` carries and the
union `normalizeChartSchema`'s `label()` already resolves.
- `compareTo` -> `DashboardWidgetSchema.shape.compareTo` BY REFERENCE, which
is literally where the value comes from (`DashboardRenderer` forwards
`widget.compareTo` verbatim onto the node).
Measured against the DECLARED FLOOR, not the installed tree: `@objectstack/spec`
17.3.0 (the `^17.3.0` this package declares) already carries all three symbols,
including the `target: 'navigate'` arm, so no floor moves.
`xAxisKey` / `series` / `aggregate` / `filter` are read by the same file and are
objectui#7946's remit; they are ledgered by name in the new census pin, each
with an assertion that it is STILL READ, rather than swept in here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01611D6ZaRaMmwTNQmSbk8MH
…strip-then-slot idiom, and ledger the new imports (objectui#8885)
Two ledgers the tree keeps and this change moved:
- `imported-defaults-8317.test.ts` reads the boundary in ONE position —
`stripImportedDefaults(<binding>)` with the binding as the direct argument —
so `stripImportedDefaults(Spec….shape.compareTo)` read as an UNWRAPPED
crossing. Re-spelled as `stripImportedDefaults(Spec…).shape.compareTo`, the
idiom `ObjectViewSchema` already uses for its three by-reference slots. Same
accept set, and now visible to the census.
- `ChartDrillDownSchema` joins that file's `IMPORTED` list, so the
differential actually measures whether the strip moves its accept set, and
`objectql.zod.ts#ObjectChartSchema` joins `SPEC_DERIVED_PAIRS` in
`zod-mirror-parity.test.ts`, which is what makes a future spec bump on this
mirror legible rather than a mystery.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01611D6ZaRaMmwTNQmSbk8MH
…ectui#8885) Position axis, measured with `scripts/cross-file-line-citation-census.mjs` at the branch point and on this branch. Adding three import lines to `zod/objectql.zod.ts` shifted everything below them by exactly 3, and four citations in this audit that RESOLVED at the branch point stopped resolving. Repaired by CONTENT, not by arithmetic: each cited line was read at the branch point and located again on this branch, and all four are byte-identical at +3 (`350`->`353`, `365`->`368`, `643`->`646`, `713`->`716`). The census now reports 540 false / 295 resolving on this branch — the branch point's figures exactly. ⛔ Only the citations THIS branch moved are touched. The one remaining difference is `objectql.ts:2961 -> ObjectKanban.tsx:264`, which is the branch point's `:2958` row with its own source line shifted: that citation was already drifted before this branch existed, so it stays objectui#8875's, not this card's. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01611D6ZaRaMmwTNQmSbk8MH
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
… (objectui#8885)
`UNGATED_EXAMPLES` in `check-doc-example-types.mjs` is keyed by
`` `${block.file}:${block.line} ${block.symbol}` `` — the LINE NUMBER IS PART OF
THE KEY. Adding three import lines to `packages/types/src/objectql.ts` moved the
collected block, so the key stopped naming a block that exists and
`scripts/__tests__/check-doc-example-types.test.ts` went red on
'every row names a block that is actually in the compiled tier'.
Located by anchor, not by arithmetic: the block is the `@example` tag on
`ObjectFormSchema.mobile`, and `sed -n '1614p'` at the branch point and on
`origin/main` and `sed -n '1617p'` here are the same line, byte for byte. The
checker's own census agrees — it collects exactly one block in that file, at
`:1617`.
⛔ ONE key, line number only. No row added, none deleted (90 before, 90 after),
no `card` / `codes` / `reason` touched, no assertion in the test changed.
Positive control: before, the checker reported exactly one ledger row with no
matching block; after, zero — and a base-vs-branch diff of the key list shows
exactly one line differing, so no other row was moved to make this pass.
⚠️ Why the earlier position-axis pass did not catch this: the line-citation
census reads `path:line` references in PROSE and COMMENTS. This one is a
machine-checked key embedded in a data structure, which that census cannot see.
"Census green" is not "position axis clean". The full carrier sweep is in the PR
body.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01611D6ZaRaMmwTNQmSbk8MH
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
CI 已转绿;给复核席解释那一行看似无关的改动head 为什么这份 PR 里有一行
|
|
Contract review at
|
| key | TS face packages/types/src/objectql.ts |
zod mirror packages/types/src/zod/objectql.zod.ts |
status |
|---|---|---|---|
drillDown |
:3141 drillDown?: ChartDrillDown |
:1271 stripImportedDefaults(SpecChartDrillDownSchema).optional() |
declared (new member) |
title |
:3158 title?: I18nLabel |
:1273 stripImportedDefaults(SpecI18nLabelSchema).optional() |
declared (new member) |
compareTo |
:3181 compareTo?: SpecDashboardWidget['compareTo'] |
:1279 stripImportedDefaults(SpecDashboardWidgetSchema).shape.compareTo |
declared (new member) |
Retired: none. Narrowed: value axis only — the three keys previously rode BaseSchema's [key: string]: any / .passthrough(); now title: 42, drillDown.target: 'popover', drillDown.maxRows: 'lots', compareTo.kind: 'lastWeek', compareTo without kind, and drillDown.{mode,report} are refused (the last two by name via the spec's .strict()). Key axis unchanged (index signature / passthrough still admit a misspelling — pinned on both faces). plugin-charts read sites unchanged: packages/plugin-charts/src/ObjectChart.tsx:855 (drillDown, cast), :1230 (title), :430/:673/:925 (compareTo, as any).
Reading: yes — three new declared members on a published payload (public-face expansion) even though the accept set on values narrows. Claim: Clause-②: yes on the newest Claim: (issue comment 5607903317, appended under that comment's own pre-authorisation, ASCII colon) and in the PR body "Contract review" section — match. needs:contract-review present on both carriers. check-clause2-carriers --pair 8895 → exit 0 ("both carriers agree"; token read path, 3 reads).
Spec parity reading
All three are parity moves (by-reference adoption of the spec symbol that owns the shape), not §0.1 widening — no renderer-side fallback/alias, no local near-copy, no spec change needed. Spec content is identical at the pinned floor and on origin/main for every symbol used:
drillDown←packages/spec/src/ui/chart.zod.ts:429ChartDrillDownSchema(strict, six keys;target: z.enum(['drawer','dialog','navigate'])at:516; type:915) — identical at@objectstack/spec@17.3.0andorigin/main(file has no diff between them). objectui'sDrillDownConfig(packages/types/src/data-display.ts:1736, carriesmode:1754/report:1797) correctly rejected as the binding — those keys have no read site inObjectChart.tsx, and the spec's own guidance says so (chart.zod.ts:449-452).title←packages/spec/src/ui/i18n.zod.ts:247(17.3.0) /:272(main)I18nLabelSchema=z.union([z.string(), InlineLocaleMapSchema]), union unchanged (main diff is doc prose only);ChartConfigSchema.titleisI18nLabelSchema.optional()atchart.zod.ts:595on both trees;react-blocks.tsObjectChartdataPropsincludes'title'(:299ff @17.3.0,:327ff @main, no ObjectChart-related diff).compareTo←packages/spec/src/ui/dashboard.zod.ts:438-500(17.3.0) /:452-514(main) — byte-identical: strict{ kind: 'previousPeriod'|'previousYear', dimension?: string }.optional();DashboardWidget = z.input<…>(:956/:1029), andstrictObjectreturnsz.object(shape).strict()so the TS type admits no bare-string form — TS and zod faces agree. REACT_BLOCKS declares nocompareToprop on ObjectChart → consistent with the INTERNAL (relay-composed) description; producerpackages/plugin-dashboard/src/DashboardRenderer.tsx:659forwardswidget.compareToverbatim.
Pin: packages/types/package.json:95 ^17.3.0, lock 17.3.0.
Governed surface
node scripts/check-governed-queue-guard.mjs --test <8 paths> → NOT GOVERNED — 8 path(s) checked against 5 governed surface(s); none matched. exit 0. CI "Governed Surface Queue Guard" success.
CI on head
33 check runs on d553e5d1b7: 30 success, 3 skipped (dependabot, Test (coverage), Test (coverage shard) — conditional skips, not red). Lint ✓, Type Check ✓ — and ci.yml:629 runs pnpm type-check → turbo → packages/types tsc -p tsconfig.test.json, so the @ts-expect-error / Equal pins are CI-enforced. No red ⇒ no causation needed.
Findings
F1 — false TSDoc on the published TS face (must fix). packages/types/src/objectql.ts drillDown docblock (the ⚠️ paragraph just above :3141): "The target: 'navigate' arm is live on BOTH faces as of @objectstack/spec 17.4.0". Measured: chart.zod.ts:516 reads z.enum(['drawer', 'dialog', 'navigate']) at tag @objectstack/spec@17.2.0 (2026-08-23) and @17.3.0 (2026-09-04); the declared floor is ^17.3.0. The PR body (P1), the dev report and the PM acceptance comment all say 17.3.0 — only the shipped TSDoc says 17.4.0, which would wrongly tell a reader the floor does not guarantee the arm. Fix: cite the floor (17.3.0) or "already at 17.2.0".
F2 — TSDoc overstates the second read site (must fix sentence; runtime follow-up out of scope). title docblock (above :3158): "Two read sites, and the union is the one they jointly require". Site 1 ({...schema} at ObjectChart.tsx:990-991 → normalizeChartSchema.ts:266 label()) resolves both arms. Site 2 (ObjectChart.tsx:1230 resolveDrillTitle(drillDown, drillEvent, schema.title || 'Details') → packages/core/src/utils/drill-down.ts:162-178, fallback: string) is not map-aware: a locale-map title is returned verbatim when drillDown.title is unset and the event carries no category/series label, then rendered as a React child at :1267/:1279. Pre-existing (the key was any; props: any hides it) and narrow — not a regression — but the sentence should say site 2 only consumes the string arm. The map-aware read belongs to whoever next edits ObjectChart.tsx (held by #8884; PM already parked the sibling prose fix there, PR 8884 comment 5608565094 — add this item).
F3 — note, no action. By-reference compareTo inherits the spec's dashboard-scoped refusal prose (dashboard.zod.ts:286-308, "dashboard.widgets[].compareTo… was removed…") when a retired form is written on an object-chart node. Accepted consequence of the ruling.
F4 — note. Dev's local union is pinned to 13d4ef324; PR head is d553e5d1b (one later commit: the UNGATED_EXAMPLES key repair). CI on d553e5d1b is the gating reading and is green.
F5 — process. Stale 'drawer' | 'dialog' prose at ObjectChart.tsx:846-854 and the palette inputs description :1380 — out of scope here, correctly not touched; parked on #8884 by the PM. That fix should use the same version fact as F1 (present since 17.2.0 / at the floor), not "17.4.0".
Everything else checks out: correctness vs ruling — drillDown (b) via spec symbol, narrower than DrillDownConfig with a by-name refusal pin; title (b) with P2 measured TRUE and positive controls (drillDown, chartType) fired; compareTo bound to the producer's declaration, no new vocabulary; no (c) retirement; Part 2 untouched; both mirrors moved. Scope — 8 files, no content/docs/releases/, nothing unrelated; the scripts/check-doc-example-types.mjs:1018 key :1614→:1617 and the four docs/audits/2026-07-objectview-detailview-schema.md citations are class-A consequences of this diff (verified byte-identical: zod :350-352→:353-355, :365→:368, :643→:646, :713→:716; objectql.ts :1614→:1617 is the @example anchor of ObjectFormSchema.mobile). Changeset — .changeset/8885-object-chart-drilldown-title-compareto.md, @object-ui/types: minor; additive public API, matches precedent (sibling #8884 also minor; 3917/3965/4895/4919); all four changeset checks green. Tests — object-chart-undeclared-keys-8885.test.ts: accept (spec vocabulary, both title arms, kind+dimension), refuse per key on the value (:189-193), mode/report refused by name with unrecognized_keys (:196-207), @ts-expect-error on TS refusals (:138-154; no tombstones in this PR so none needed), Equal with a can-fail control (:109), ceiling pinned on both faces (:158-163, :209-213), cast-aware census with comment-stripping and non-vacuity controls (:286-301), ledger asserts still-read only (order-independent vs #8884); SPEC_DERIVED_PAIRS and 8317 IMPORTED moved. Docs — content/docs/api/schema-reference.md has no ObjectChart section; packages/plugin-charts/README.md, packages/types/README.md, skills/objectui/guides/* list none of the three keys ⇒ no doc delta owed; the only authoring-facing text is the palette description (F5).
Acceptance notes
- Implemented-by: newest (only)
Claim:on [finding]ObjectChartSchemastill leaves three keys undeclared thatObjectChart.tsxreads — andObjectChartBlock, the registry shell wrapping it, is still published as(props: any)#8885 = comment 5607903317 → sessionsession_01611D6ZaRaMmwTNQmSbk8MH, branchclaude/issue-8885-objectchart-undeclared-keys;mode:subagent(PM shares the session) ⇒ identity is the branch. Reviewed-by:session_017Js5kTpTtxieBjPyScgxJ3(this seat) — independent, not SELF-REVIEW. Part ofhandling: PR body line 1Part of #8885; zero closing verbs anywhere in the body; the 4 commit subjects carry only(objectui#8885). Card stays open for Part 2 ⇒ on MERGED: manually droppm:dispatched, post aRelease:line (Part 1 landed via feat(types): declaredrillDown/title/compareToon both published copies of ObjectChartSchema (objectui#8885) #8895; Part 2 —ObjectChartBlockpre-gate props type,ObjectChart.tsx:1331— is a design question → decision box), clear assignee. Acceptance-notes triage: (1) parked on feat(types,plugin-charts): anchor ObjectChart props to ObjectChartSchema and declare the four keys its producers write (#7946) #8884; (2)colorsTS-face gap is closed by feat(types,plugin-charts): anchor ObjectChart props to ObjectChartSchema and declare the four keys its producers write (#7946) #8884 (addscolors?:to the interface); (3) Part 2 stays.- Overlap with feat(types,plugin-charts): anchor ObjectChart props to ObjectChartSchema and declare the four keys its producers write (#7946) #8884 (head
09f8ffaa3; measured with a 3-waygit merge-filein the scratchpad, base4e3a4f07e):objectql.ts→ 2 conflict blocks (docblock header at base:3054rewritten by both — keep both paragraphs; interface-body tail double insertion at base:3074);objectql.zod.ts→ merges clean, 17 keys, no duplicates; key sets disjoint (feat(types,plugin-charts): anchor ObjectChart props to ObjectChartSchema and declare the four keys its producers write (#7946) #8884:filter/aggregate/xAxisKey/series/colors; feat(types): declaredrillDown/title/compareToon both published copies of ObjectChartSchema (objectui#8885) #8895:drillDown/title/compareTo). feat(types,plugin-charts): anchor ObjectChart props to ObjectChartSchema and declare the four keys its producers write (#7946) #8884'sLEDGERED_UNDECLARED_READS(widget-schema-anchors-7946.test.ts:184-195) asserts still-read only (:247-253) ⇒ no red whichever lands second; its prose goes stale. feat(types,plugin-charts): anchor ObjectChart props to ObjectChartSchema and declare the four keys its producers write (#7946) #8884 does not touch the stale'drawer' | 'dialog'prose. The PM's conflict comments on both PRs (5609075456 / 5609071866) match this measurement. Whichever lands second must re-read the merged zod object and re-runzod-mirror-parity.test.tsplus both census pins.
Generated by Claude Code
|
Handoff provenance — director seat, summon #18 segment 6 ( Generated by Claude Code |
|
Tier notice — the contract-review-tier requirement on this PR is lifted (skills seat, session Maintainer ruling, verbatim: 「现有的卡片如果写了要求fable的,也要让相关的项目经理知道,opus就够了。」 Under the same ruling set (quoted in full on objectstack-ai/objectstack#17285), the contract-review tier is reserved for the skills seat (protocol files + the published For this PR: its Generated by Claude Code |
|
os-dev-report {
"issue": 8885,
"status": "blocked",
"branch": "claude/issue-8885-objectchart-undeclared-keys",
"pr": "https://github.com/objectstack-ai/objectui/pull/8895",
"premise_still_valid": true,
"summary": "Re-derived the collision on origin/main bbe285ee7 (main moved 33 commits since PR objectui#8884's base and 26 since PR objectui#8895's, and touched NEITHER shared file, so the collision is purely between the two branches). Landing order chosen: objectui#8884 first. Its branch is synced to current main and pushed (09f8ffaa3 to 7e7134160), fully green. The resolution for objectui#8895 is BUILT AND VERIFIED but NOT pushed: measured, pushing it would make PR objectui#8895's diff carry 13 of PR objectui#8884's files, i.e. objectui#8895 would swallow objectui#8884's whole change. It is preserved at git ref refs/os-dev-collide/pr8895-resolved (commit 11ddbab62). Two things only the PM/maintainer can clear: (1) objectui#8884 is not landable by this seat - BOTH PRs are draft and BOTH are deliberately parked pending Clause-2 contract review, and this seat never merges; (2) the union of the two PRs exposes a real latent defect whose repair is a design choice, see open_questions.",
"tests": "All from the resolved merge commit 11ddbab62. ACCEPTANCE (two faces, probe-shape proved first with a known positive `dimensions`=1 and a known negative =0 on each face): all 8 keys declared exactly once on BOTH published faces - filter/aggregate/xAxisKey/series/colors from objectui#8884, drillDown/title/compareTo from objectui#8895; all 10 spec bindings and imports of objectui#8895 present exactly once. GREEN: pnpm --filter @object-ui/types build EXIT 0 (128 files); pnpm --filter @object-ui/types run type-check EXIT 0 (all three tsc projects, so both cards' compile-time pins are real enforcement); pnpm exec vitest run packages/types/ = 166 files / 3234 tests EXIT 0; pnpm exec vitest run packages/plugin-charts/ = 53 files / 496 tests EXIT 0; the 4 ratchet files = 166/166 EXIT 0; scripts/__tests__/check-doc-example-types.test.ts = 51/51 EXIT 0 and the line-KEYED ledger row objectql.ts:1617 still names the real block after BOTH inserts; check:spec-symbols / check:control-bytes / check:doc-fences EXIT 0. check:doc-examples EXIT 2 = PREREQUISITE NOT MET (unbuilt dist) = NOT MEASURED, not a red. RED: pnpm --filter @object-ui/plugin-charts run type-check EXIT 2, exactly one error, ObjectChart.tsx:1326 TS2345 - see open_questions. RATCHET CONTROL, four half-repairs, each with on-disk proof (marker count AND blob hash) and restore proved by state (git diff HEAD empty, blob back to 7258bdaf): H1 drop drillDown from the zod mirror only = vitest RED 5 cases + tsc RED; H2 drop xAxisKey from the zod mirror only = vitest RED 3 cases + tsc RED; H3 swallow objectui#8895's ENTIRE TS member block (the exact failure mode this brief names) = vitest GREEN 166/166, tsc RED 7 errors; H4 swallow objectui#8884's ENTIRE TS member block = vitest GREEN 61/61, types tsc RED 5 errors AND plugin-charts tsc RED 3 errors. ATTRIBUTION ABLATION for the TS2345, both legs with on-disk proof and dist proof: leg0 both PRs = tsc EXIT 1 with the error; leg1 simulate objectui#8884 alone by dropping objectui#8895's title declaration = tsc EXIT 0, error gone, dist member count 0; leg2 simulate objectui#8895 alone by reverting objectui#8884's props anchor to props-any = tsc EXIT 0, error gone; restore git diff HEAD empty and dist member back to 1.",
"mcp_calls": "4",
"open_questions": [
{
"question": "The union of the two PRs turns ObjectChart.tsx:1326 into a compile error, and it is a REAL latent bug rather than a typing nuisance. That line is `resolveDrillTitle(drillDown, drillEvent, schema.title || 'Details')`, and resolveDrillTitle's third parameter is typed string. objectui#8895 declares title as I18nLabel - a plain string OR an inline locale map - which objectui#8895 measured and pinned as authorable. So an author who writes the locale-map arm gets an OBJECT as the drill drawer's heading fallback. Neither PR can see this alone (proved by ablation): with props-any the read is any, and without objectui#8895's declaration title rides BaseSchema's index signature as any. This is NOT a semantic incompatibility - the two PRs do not disagree about what the schema should say; objectui#8884 declares nothing about title at all and merely ledgers it. What is missing is the READ SITE's repair, which belongs to neither card's ruling and lands in ObjectChart.tsx, a file objectui#8895 explicitly refused to touch because objectui#8884 holds it. Which resolver is the authority for a chart's title?",
"options": [
"A - pickLocalized from @object-ui/i18n, the repo's PUBLISHED authority, already exported and pinned as the twin of @objectstack/spec's resolveI18nLabel, whose own docblock names avoiding the object-stringification as its purpose. Cost: it needs a current-locale argument, and ObjectChart.tsx has no language in scope today (it holds useSafeTranslate only), so this pulls in a locale source - useDisplayLocale or useObjectTranslation - and makes the drill heading locale-aware where the chart heading beside it is not.",
"B - the private label() helper already inside plugin-charts' normalizeChartSchema.ts, which is what the CHART heading path already uses. It is NOT locale-aware (it takes the first string value in the map). Cost: it is module-private, so this either exports a new published name from plugin-charts or copies it, and it entrenches a second dialect that disagrees with pickLocalized.",
"C - narrow ObjectChartSchema.title to plain string on this node. Cost: contradicts objectui#8895's measured ruling and @objectstack/spec's own ChartConfigSchema.title, and would delete the locale-map arm objectui#8895 pinned as accepted. Listed for completeness; I do not recommend it.",
"D - land the merge with the error and file the repair as a successor card. Cost: main goes red at the moment the second PR lands, so this is only viable if the repair lands in the same queue batch."
],
"recommendation": "A, with the locale taken from the same source the rest of this file's label net already uses, because it is the only option that does not mint a second dialect for a union @objectstack/spec already owns. But the divergence between the two existing resolvers is itself a contract fact this seat should not settle: the chart heading path currently uses the non-locale-aware one, so choosing A makes the drill heading and the chart heading disagree until someone converges them. That is the escalation."
},
{
"question": "How should PR objectui#8895 receive the resolution, given it cannot be pushed before objectui#8884 lands?",
"options": [
"A - land objectui#8884 first (it is synced, pushed and green), then this seat or another merges origin/main into objectui#8895 and replays the identical resolution, which is deterministic and preserved at refs/os-dev-collide/pr8895-resolved.",
"B - push the resolution to objectui#8895 now. MEASURED CONSEQUENCE, not a guess: its PR diff becomes 21 files, 13 of them objectui#8884's, including ObjectChart.tsx and objectui#8884's changeset - and if objectui#8895 landed first it would carry objectui#8884's entire change into main under the wrong card. I did not do this."
],
"recommendation": "A. Both PRs are draft and parked pending Clause-2 contract review, so nothing lands tonight regardless; the ordering dependency is structural, not a preference."
}
],
"out_of_scope_findings": [
"noted, not filed: the TS face of ObjectChartSchema has NO runtime ratchet. Both census pins compute their declared set from Object.keys of the ZOD mirror, so a resolution that swallows either PR's whole TypeScript member block runs vitest fully GREEN (H3 166/166, H4 61/61) and is caught ONLY by tsc -p tsconfig.test.json. Successor: whoever accepts these merges must run pnpm --filter @object-ui/types run type-check; pnpm test alone is not an acceptance instrument here.",
"noted, not filed: git hoists the shared opening lines of BOTH conflict hunks OUT of the conflict region - the docblock's first 91 bytes are byte-identical on the two sides, and so is the interface body's leading two-space comment opener - so taking either side alone yields syntactically perfect, natural-reading output in both places. This is the mechanism behind the hazard the brief named, measured with cmp.",
"noted, not filed: the zod mirror auto-merged with no conflict because objectui#8884 inserts before the colors block and objectui#8895 after it. Verified rather than trusted - all 8 keys present exactly once. A reviewer who only inspects the conflicted file would not look at it. Successor: none; recorded so the next merger does not skip it.",
"noted, not filed: objectui#8895's docblock section headed 'Four keys are still undeclared here, deliberately' has its PREMISE falsified by this merge. Rewritten in the resolution as 'Two cards ruled on this shape, and neither took the other's ruling', keeping its substance. The two cards' census pins were both written order-agnostically (each ledgers the other's keys by name and asserts only that they are STILL READ), so no test needed changing - only that one prose claim.",
"noted, not filed: the two ceiling paragraphs are near-identical prose with different examples. Folded into ONE section rather than duplicating a heading; every distinct claim from both sides was asserted present afterwards - both refusal examples, both misspelling examples, both counter-probe filenames."
]
}Generated by Claude Code |
|
Correction to my report above (comment 5613422935) — two premises in it are stale. The measurements are unaffected; the landing story changes. I read the two comments above mine only after posting. Correcting my own record rather than leaving it to be discovered: 1. "Both PRs are parked pending Clause-2 contract review" is no longer true. The tier notice (comment 5612090076 here, and its twin 5612089561 on objectui#8884) lifted the contract-review-tier requirement on both, 2026-09-10T03:14Z. My report used that parking as a reason nothing lands tonight. Withdraw that reason — it is not the operative one. 2. The operative reason is stronger, and it confirms rather than weakens the landing order I chose. objectui#8884 carries a director-seat contract-review verdict of REWORK, anchored at head 3. Disclosure. I pushed a sync merge to objectui#8884's branch ( Everything else in the report stands unchanged — the collision re-derivation, the landing order and its reasoning, the verified resolution at Generated by Claude Code |
排序裁定:#8884 先落地,本卡的解决方案暂存不推 —— 并更正我自己派发单里的两条错误指令派发席( ⭐ 我的派发单有两条指令无法照做,执行席拒绝执行是对的我写了「Land the first」和「Push both」。两条都错:
⇒ 它推了 #8884 的同步合并(diff 中性,本席已复核:#8884 仍是它自己的 15 个文件,CI 零红),并扣住了本卡的推送。这个拆分是对的,维持。 裁定① 顺序:#8884 先。 它锚定 props 类型,本卡只是往那个 schema 上加键;且本卡的产物本来就是顺序无关写的,而 #8884 的散文假定本卡尚未落地。 ② 本卡的解决方案不推,暂存等待。 已保存在容器内 ③ 联合体暴露出的那个真实缺陷,归入 #8884 的返工轮。 两卡合并后 ⭐ 消融证明两卡各自都看不见它——只在联合体里出现。而读点在
|
…er's declaration Second union-only defect with objectui#8895, measured on the merge of the two heads and NOT previously reported: `renderChart(compareTo: unknown, …)` stops compiling once #8895 declares `ObjectChartSchema.compareTo` as `DashboardWidget['compareTo']`, because on this branch the key still rides `BaseSchema`'s index signature. It was masked rather than absent: `type-check` is `tsc --noEmit && tsc -p tsconfig.test.json`, so the drill-title error in `ObjectChart.tsx` short-circuited the test project and hid every error it would have reported. Running the test project alone on the pre-rework union shows both. Typed at `DashboardWidget['compareTo']` — the symbol #8895 binds to and the declaration the value actually comes from (`DashboardRenderer` forwards `widget.compareTo` verbatim). All four call sites already pass that shape. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01611D6ZaRaMmwTNQmSbk8MH
…atch `main` moved and objectui#7946 (PR #8884) landed, editing the same `ObjectChartSchema` on both published faces. The previous round's stashed resolution was built on #8884's PRE-REWORK tree and is not replayed. 5 files, 7 hunks, re-derived against origin/main aeaa0f6: - `objectql.ts` docblock: both sides rewrote the same title line into different full docblocks. BOTH narratives kept; the two near-identical "ceiling" paragraphs folded into ONE section carrying both cards' examples and both counter-probe filenames; this branch's section "Four keys are still undeclared here, deliberately" had its premise falsified by the merge and is re-derived. - `objectql.ts` interface body: double insertion at one anchor, both kept. All 8 keys now declared exactly once on both faces. - `objectql.zod.ts` / `imported-defaults-8317.test.ts`: additive on both sides, both kept. - `zod-mirror-parity.test.ts`: BOTH sides add the SAME `SPEC_DERIVED_PAIRS` entry with different grounds. One entry, both grounds recorded. - `check-doc-example-types.mjs`: the `UNGATED_EXAMPLES` key embeds a line number and each side repaired it to a different one. Re-derived BY ANCHOR against the merged tree (the `@example` tag on `ObjectFormSchema.mobile`, byte-identical to the branch point): :1618. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MPaVWWMuWeT5LgB1qoXjVB
…n repair Re-measured with `scripts/cross-file-line-citation-census.mjs` on `origin/main` and on this tree. The previous round repaired FOUR citations in the 2026-07 audit as class (A). Only TWO of them are. Class (A), kept — both on the `defaultViewType` row, both RESOLVE on `origin/main` and are broken by this branch's three added import lines in `objectql.zod.ts`. Repaired by content, not arithmetic: `:365` and `:643` on main read ` * STRICT \`ObjectListViewSchema\`,` and ` // objectui-only object binding (...)`, and both are byte-identical at `:368` / `:646` here. Class (B), reverted to `origin/main` — `:350-352` and `:713`. Measured ALREADY FALSE on `origin/main` before this branch existed (`:350` reads `[drifted]` there). Shifting an already-false address by a hunk delta moves a wrong pointer to a differently wrong place, which the census tool's own header forbids and objectui#8875 reserves. Census parity after this commit: 545 false / 290 resolving on BOTH `origin/main` and this tree — this branch newly falsifies nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MPaVWWMuWeT5LgB1qoXjVB
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Part of #8885
⛔
Part of, not a closing keyword, and that is deliberate. This card carries two parts and this PR implements Part 1 only; Part 2 (the registry shell, still published with an untyped props bag) is ruled out of scope and is still undecided. A closing keyword here would silently take the card out of every open-issue sweep the moment this merges, and Part 2 would be lost rather than visibly pending. The card stays open for it.What was wrong
ObjectChart.tsxreadsdrillDown,titleandcompareTooffschema, and neither published copy of the shape declared any of them — not the TS interface (packages/types/src/objectql.ts) and not the zod mirror (packages/types/src/zod/objectql.zod.ts). They rodeBaseSchema's index signature /.passthrough()and arrived unvalidated. This is the objectui#6914 class: a key read behind a cast, declared on neither published face.drillDownwas the sharpest instance, because two independent declarations already pointed at it while the shape itself stayed silent: this component's registryinputsadvertise it to the designer palette, and@objectstack/specpublishesChartDrillDownSchemafor exactly this carrier.What this PR does
Each key binds to the
@objectstack/specsymbol that already owns it. ⛔ No local near-copy, no rename, no derived local type — this file's own standing rule is "Never Redefine Types. ALWAYS import them.":drillDownChartDrillDown/ChartDrillDownSchema(@objectstack/spec/ui)titleI18nLabel/I18nLabelSchema(@objectstack/spec/ui)compareToDashboardWidgetSchema.shape.compareTo, by reference⛔ Deliberately NOT this repo's wider
DrillDownConfig. That type also carriesmodeandreportfor the table / pivot / metric widgets, andObjectChart.tsxreads neither — the spec's chart subset is the measured accept set, and declaring the wider one would advertise two keys accepted and then dropped. A chart drill carryingmodeorreportis now refused by name (unrecognized_keys), pinned as its own case.What this buys is the value check:⚠️ It does not buy rejection of a misspelling —
title: 42,drillDown: { target: 'popover' }andcompareTo: { kind: 'lastWeek' }are compile errors and parse errors now; before, all three rode through silently.BaseSchemais still.passthrough()with an index signature — and the pin states that ceiling honestly on both faces rather than implying more.This round — the conflict was re-resolved from scratch, and the owed patch round landed
mainmoved and objectui#7946 (PR #8884) landed as14582b8, editing the sameObjectChartSchemaon both published faces. Two things happened here.1. The merge, re-derived against
origin/mainaeaa0f6⛔ The previous round's stashed resolution at
refs/os-dev-collide/pr8895-resolvedwas not fetched, replayed or cited: it merged #8884's pre-rework tree, and #8884 then took a rework round before landing. Re-measured this fire: 5 files, 7 hunks.packages/types/src/objectql.tspackages/types/src/zod/objectql.zod.tspackages/types/src/__tests__/imported-defaults-8317.test.tspackages/types/src/__tests__/zod-mirror-parity.test.tsscripts/check-doc-example-types.mjs⭐ The docblock hunk is the dangerous one, and not for the obvious reason. Both sides rewrote the same title line into a different full docblock, and git hoists their byte-identical opening lines out of the conflict region — so taking either side alone yields a syntactically perfect, natural-reading file and the markers say nothing is missing. Both narratives are kept. The two near-identical "ceiling" paragraphs are folded into one section carrying both cards' examples and both counter-probe filenames rather than duplicating a heading.
zod-mirror-parity.test.tsis the trap that is not a trap-shaped conflict. Both sides add the sameSPEC_DERIVED_PAIRSentry —'objectql.zod.ts#ObjectChartSchema'— with different justifying comments. A naive keep-both would have listed it twice. One entry, both grounds recorded, and the entry now names four spec crossings (aggregatefrom #7946;drillDown/title/compareTofrom this card).2. The owed contract-review patch round (prose-only)
The director-seat review returned FAIL on two TSDoc sentences shipped in the published
.d.ts. Both are fixed here; ⛔ no shape change, no test change.F1 — the
drillDowndocblock cited the wrong version. It said thetarget: 'navigate'arm is live "as of@objectstack/spec17.4.0". Re-measured independently at the published tarballs (npm pack), not at whatever the lockfile resolves:ChartDrillDownSchema.targetalready readsz.enum(['drawer', 'dialog', 'navigate'])at 17.3.0 and at 17.2.0. This package's declared floor is^17.3.0(packages/types/package.json:95), so the floor guarantees the arm and nothing here is owed a floor bump. The docblock now cites the floor and records why the old sentence was wrong — the tree happened to be resolving 17.4.0 that day, which is exactly the objectui#7650 lesson.F2 — the
titledocblock overstated the second read site, and the review's own corrective wording was itself stale. The review wrote it while the map-aware read was still parked on #8884. That read has since landed, so F2 is re-derived against the landed call site rather than transcribed:normalizeChartSchema'slabel()takes the value asunknownand resolves both arms itself. ⭐ This is the read that requires the union; declaringstringalone would refuse a locale-maptitlethat works today.ObjectChart.tsxpasses it toresolveDrillTitleas the drill drawer's heading fallback, and that helper'sfallbackparameter is a plainstring(@object-ui/core'sutils/drill-down.ts). It never sees the map arm: the call site now pre-resolves throughpickLocalized(schema.title, language)first, which feat(types,plugin-charts): anchor ObjectChart props to ObjectChartSchema and declare the four keys its producers write (#7946) #8884's rework round added precisely so the map arm could not reach a heading as an object.⇒ The sentence "two read sites, and the union is the one they jointly require" was wrong in both directions. One read requires the union; the other's caller narrows it first.
ObjectChart.tsxand the registryinputsdescription are both stale prose. #8884's rework already fixed the comment — it now states plainly that the old claim was false and that objectstack#5435 widened the union. What actually remains is the palettedescription, and that is not stale prose: it is a deliberate withholding, pinned by name inpackages/plugin-charts/src/index.test.ts. See acceptance notes.Three premises, measured before writing anything
P1 — does the published spec's
ChartDrillDownSchemamatch the read points? ⭐ TRUE, at the declared floor: six keys (enabled/filter/title/target/columns/maxRows), strict, andObjectChart.tsxreads exactly those six.P2 — does
titlereally have authors writing it? ⭐ TRUE, so disposition (b), not (a). Zero in-repo producers writetitleon anobject-chartnode, but two published declarations carry it: the spec'sREACT_BLOCKSentry liststitleamong this block'sdataProps, andChartConfigSchema.titleisstring | I18nLabel. Positive controls fired (drillDown, andchartTypewith 27 producer sites), so those zeroes are readings rather than a broken probe.P3 — can
compareTo's value type be derived without minting new vocabulary? ⭐ TRUE.DashboardRenderercomposes the node withcompareTo: widget.compareTo, forwarding the dashboard widget's own key verbatim. The value is already authorable on the widget; declaring it on the node mints nothing.Acceptance — the instrument is
tsc, notpnpm test⭐ Both census pins compute the declared key set from
Object.keysof the ZOD mirror. A resolution that swallowed either side's entire TypeScript member block would therefore run vitest fully green and be caught only bytsc. So the acceptance reading is positive and on both faces:filter/aggregate/xAxisKey/series/colors(finding(types,plugin-charts): ObjectChartSchema anchors nothing — ObjectChart is published asprops: any, and four keys its own producers write are declared on neither copy #7946) anddrillDown/title/compareTo(this card). Both faces carry 17 members. Probe shape proved first with a known-positive (dimensions= 1) and a known-negative (zzzNotAMember= 0) on each face; a bare zero is not a reading.props: any, and four keys its own producers write are declared on neither copy #7946'sSpecChartAggregateSchema.BaseSchema.extend({…})body auto-merged with no conflict markers. Verified anyway rather than trusted — a reviewer who only inspects conflicted files would not look at it.pnpm --workspace-concurrency=2 --filter @object-ui/types buildpnpm --filter @object-ui/types run type-check(all three tsc projects)pnpm --filter @object-ui/plugin-charts run type-checkpnpm exec vitest run packages/types/pnpm exec vitest run packages/plugin-charts/(control)pnpm exec vitest run scripts/__tests__/check-doc-example-types.test.tsnode scripts/check-doc-example-types.mjscheck:spec-symbols·check:control-bytes·check:doc-fencescheck-changeset-presence·check-changeset-no-major·check-changeset-fixedpnpm --filter @object-ui/types lintcheck-governed-queue-guard --test(8 paths)pnpm --filter @object-ui/plugin-charts run type-checkfirst exited 2 withTS2307: Cannot find module '@object-ui/core'— unbuilt workspace deps, so NOT MEASURED, neither red nor green. Same forcheck:doc-examples, which exited 2 naming unbuiltdistfor 20+ packages. Both were re-run after building their prerequisites and are the 0s in the table. ⭐ This matters for the plugin-charts row specifically: it is the row that proves theTS2345the two PRs' union alone could see is already repaired ahead of this branch by #8884'spickLocalizedchange, so this branch does not have to touchObjectChart.tsx.⭐ Negative control on the line-keyed ledger, because a green gate with a broken probe reads the same as a green gate. Pointing the
UNGATED_EXAMPLESkey at a line that holds no block (:1618to:1699, mutation proved on disk by anchor count and blob hash before running) turned the gate to exit 1 withSTALE LEDGER ROW packages/types/src/objectql.ts:1699 ObjectFormSchema— exactly one row naming no block. Restored by state, not exit code: blob back tocb5f9c4d,git diff HEADempty.Position axis (objectui#8875) —⚠️ the previous round's repair was half wrong, and this round measured that
scripts/check-doc-example-types.mjs'sUNGATED_EXAMPLESkey embeds a line number, and both sides of the merge repaired it to different values (:1617and:1615). Re-derived by anchor against the merged tree — the@exampletag onObjectFormSchema.mobile, byte-identical to the branch point — it is:1618. ⛔ Never by arithmetic, ⛔ never by picking a side. Base-vs-branch diff of the key list: 89 rows both sides, exactly one line differing, the line number only. No row added or deleted, nocard/codes/reasontouched.docs/audits/2026-07-objectview-detailview-schema.mdcitations this branch had "repaired" are not four. Only two are class (A). Re-measured withscripts/cross-file-line-citation-census.mjsonorigin/mainand on this tree:origin/mainobjectql.zod.ts:643and:365(thedefaultViewTyperow):646/:368, verified byte-identical by content.:350-352(theshowSearchrow)[drifted])origin/main:713(thedescriptionrow)origin/main⛔ Shifting an already-false address by a hunk delta moves a wrong pointer to a differently wrong place — which the census tool's own header forbids and objectui#8875 reserves. Three independent tells caught it: the table's middle column is headed
Specand its first row spells the convention out as symbol-then-line; the previous round moved four numbers in that table and left their neighbours:448/:642/:585/:29alone, which is inconsistent under either reading; and the one genuinely file-qualified citation did not point at its own anchor even at the branch point.Census parity after the correction: 545 false / 290 resolving on BOTH
origin/mainand this tree — identical, so this branch newly falsifies nothing. The single differing row isobjectql.ts:2959to:2962citingObjectKanban.tsx:264, which reads[drifted]on both sides: main's own already-false row with its source line shifted by this diff. Class (B), left alone.What this PR does not do
ObjectChart.tsx. Out of this card's declared file surface, and feat(types,plugin-charts): anchor ObjectChart props to ObjectChartSchema and declare the four keys its producers write (#7946) #8884's rework already landed the read-site repair the two PRs' union needed.Acceptance notes (out of scope, named successors)
target: 'navigate', and feat(types,plugin-charts): anchor ObjectChart props to ObjectChartSchema and declare the four keys its producers write (#7946) #8884 named THIS card as the successor —ObjectChart.tsx's own comment now reads "it belongs to objectui#8885, which ownsdrillDownthere". ⛔ Not done in this round, deliberately, and the reason is mechanical rather than preference: the owed round is prose-only, the palette string is inObjectChart.tsxwhich is outside this card's declared file surface, andpackages/plugin-charts/src/index.test.tspins the withholding by name ('navigate'must not appear in that description). Widening an advertised authoring vocabulary is a shape decision that moves a pinned test — a separate round, not a docblock fix. Successor: a follow-up on objectui#8885, or whoever next editsObjectChart.tsx.packages/types/src/__tests__/widget-schema-anchors-7946.test.tscarries prose this merge makes stale — itsLEDGERED_UNDECLARED_READScomments still saydrillDownis declared "by neither published copy of this shape", which stops being true when this lands. ⛔ Not edited: it is outside this card's declared file surface, and its assertions are unaffected (that ledger, like this card's, asserts only that each key is still READ). Prose only, no red gate. Successor: whoever lands next in that file.kanban/ganttnode keys and thekanban-ui/kanban-enhancedregistrations #8865 (claude/kanban-gantt-family-retirement) rewriteszod-mirror-parity.test.tsby +38/−34, changing that ledger's own counts as it retires the barekanbannode type. It has not landed as ofaeaa0f6(the counts on main still read the pre-feat(types,plugin-kanban,plugin-gantt)!: retire the barekanban/ganttnode keys and thekanban-ui/kanban-enhancedregistrations #8865 figures). This branch edits the same file. ⛔ No manual ordering — whichever lands second re-derives those counts from the merged tree; the merge queue arbitrates.Contract review
Clause-②: yes— new keys on a published payload (ObjectChartSchema, on both published faces), reported by mechanism.needs:contract-reviewis not re-hung.Provenance
Merge re-resolution and the owed patch round: session
session_01MPaVWWMuWeT5LgB1qoXjVB. Original implementation: sessionsession_01611D6ZaRaMmwTNQmSbk8MH.PATCHafter that normalises it straight back down — so a body that is edited even once loses the session reference from its footer, silently. This body has been edited, so the footer is the bare form. A code span survives both rewrites; a footer link does not.🤖 Generated with Claude Code
Generated by Claude Code