fix(types): close two validating-face gaps — the ObjectQL union arms and the DrillDownConfig mirror (objectui#7363, objectui#7352) - #7701
Merged
Conversation
… ObjectQL union on both faces (objectui#7363) PR #7355 (objectui#6576) minted both schemas and deliberately left `ObjectQLComponentSchema` alone, so `AnyComponentSchema` had no arm for an `object-gallery` or `object-data-table` node: `validateSchema` / `safeValidateSchema` / `objectui validate` refused such a document as matching no arm, exactly as before the schemas existed, and a wrong-typed declared key on it could never be diagnosed by name. Both unions gain the two members, in the same order. The pin (`objectql-union-arms-7363.test.ts`) is two-legged per node type: a minimal document validates, and a wrong-typed DECLARED key is refused BY NAME — the by-name half is the load-bearing one, since the documents were refused before too, for the wrong reason. Measured on the ten-arm tree first: 9 of its assertions red (`data` and `className` probes dropped because a sibling arm already named them there). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0114Ytxr5sM1vdW19Y9WAx6E
…IEWED Rescue commit made by the PM seat after the dispatched dev was terminated mid-task by an account session limit (HTTP 429, resets 2026-09-05T06:40Z). Committed so the work survives container reclaim; it is NOT a claim that the change is correct or finished. Observed state at rescue time, and nothing beyond it: M packages/types/src/zod/data-display.zod.ts M packages/types/src/zod/index.zod.ts M packages/types/src/zod/objectql.zod.ts ?? packages/types/src/__tests__/drill-down-config-mirror-7352.test.ts The dev's last recorded status was that the instrument was written and the gap-proof run on BASE (both files, expected red) had not yet been executed. No gate was run against this content. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0114Ytxr5sM1vdW19Y9WAx6E
…idating-face-gaps
…aring mirrors (objectui#7352) Completes the rescued WIP commit f7a428f, which carried the mirror, the barrel export and the behaviour pin, and had never been run through a gate. What that content was missing, measured rather than assumed: - `zod-mirror-parity.test.ts` did not know the new export. Its population census was RED ("New zod export(s) in packages/types/src/zod/ that this guard does not cover: data-display.zod.ts#DrillDownConfigSchema"), and `tsc -p tsconfig.test.json` was RED on the staleness ratchet at line 1478 — both `drillDown` rows in `UnmirroredDeclared` had become stale the moment the mirror wired in. The pair is now registered in MIRRORS / Declared against the LOCAL `DrillDownConfig` (the `ObjectMapConfigSchema` precedent), both rows are retired with the movement recorded, and every count the change moves is updated: 160 → 161 pairs, UnmirroredDeclared 15 / 96 → 13 / 94 (LOCAL 12 / 83 → 11 / 82), the "no entry in either" population 144 → 147. The stale "143 of the 160" beside `assertionRatchetAcceptsCleanPair` — already wrong before this change — reads 147 / 161 with the rest. - One assertion in the pin was wrong about the DECLARATION, not about the mirror: it expected `report: { name, columns }` (an inline report missing `objectName`) to be refused. `DrillDownConfig`'s second arm is `{ name: string }`, which is not exact, so TypeScript accepts that value — refusing it would make the mirror NARROWER than the declaration, the drift class this ledger exists to stop. It moved to the accepted table, annotated `DrillDownConfig` so `tsc -p tsconfig.test.json` is the standing witness. - `widget-schema-anchors-6576.test.ts` listed `drillDown` as a ledgered unmirrored read. The mirror declares it now, so the exception is retired; a stale one widens `declared` for a key nothing checks any more. Changeset added: the new `/zod` export and the accept-set narrowing are published. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0114Ytxr5sM1vdW19Y9WAx6E
This was referenced Sep 5, 2026
Contributor
✅ 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
|
…s, not 161 (objectui#7352) Comment-only. Contract review on PR #7701 caught that the pair count this card moved was stale before it: I added 1 to a number that had been wrong twice, and rewrote one wrong derived figure into a different wrong one. Re-derived by two independent methods that agree, rather than by stepping a delta: a TypeScript AST walk counting `PropertyAssignment` nodes in the `MIRRORS` initializer and `PropertySignature` members in each ledger interface, and a line-oriented parse of the same blocks. MIRRORS pairs 155 (prose said 161) KnownDrift 40 / 56 (unchanged, and correct) UnmirroredDeclared 13 / 94 (unchanged, and correct) RuntimeOnlyDeclared 7 / 24 (unchanged, and correct) union of the two unmirrored ledgers 14 (unchanged, and correct) clean population 141 (prose said 147) pairs with no KnownDrift entry 115 (prose said 121) The same walk run over this file at three revisions confirms how it drifted, so the correction is recorded rather than just applied: `4ca30d044` wrote "160" when `MIRRORS` held 163; `d88e20f55` (objectui#7432) took the registry to 154 without touching the sentence; this branch then added its 1 to the stale baseline. Two consequences beyond the numbers: - The running chain of per-card deltas under the population bullet (141 -> 142 -> 143 -> 144 -> 147) was arithmetic on the wrong base and is not re-derivable from this file. It is replaced by the measurement plus a note saying so, with an explicit instruction to subtract the union from the registry count rather than restart the chain. - `RuntimeOnlyDeclared`'s docblock restated the same debt as "97"; it was stale before this branch too (the ledger held 96) and now reads 94, with the equation balanced the way the other ledger states it. Also in this commit, from the same review, both optional and both measured: - The accepted-table comment in `drill-down-config-mirror-7352.test.ts` said a string `name` satisfies the reference arm "whatever else it carries". That overstates it: the acceptance is shape-specific, and — measured — `{ name, columns: [] }` PARSES TO `{ name }`, because the reference arm is a plain object that strips what it does not declare, while the inline arm keeps extras through `.catchall`. The comment now says both halves. - The changeset gains that parsed-output difference, since consumers read `report` after parsing. Ratchet unaffected: no MIRRORS entry, ledger row or type moved in this commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0114Ytxr5sM1vdW19Y9WAx6E
…idating-face-gaps
Contributor
✅ 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
|
1 similar comment
Contributor
✅ 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
|
os-sam
marked this pull request as ready for review
September 5, 2026 08:15
This was referenced Sep 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #7363
Fixes #7352
Family dispatch, two members with one shape: a thing the TypeScript face declares that the validating face cannot see. One worktree, one commit per member, one changeset per member.
Member 1 — objectui#7363: the two objectui#6576 schemas become arms of the ObjectQL union
Commit
28094b1dd.ObjectGallerySchemaandObjectDataTableSchemawere minted beside the otherObject*Schemamembers and deliberately left out ofObjectQLComponentSchema, soAnyComponentSchemahad no arm for anobject-galleryorobject-data-tablenode:validateSchema/safeValidateSchema/objectui validaterefused such a document as matching no arm, exactly as before the schemas existed. Both unions gain the two members, in the same order, on both faces.Acceptance criterion (named, per the card): a minimal
object-gallery/object-data-tabledocument validates, and a wrong-typed DECLARED key on such a node is refused BY NAME. The by-name half is the load-bearing one — the documents were refused before too, for the wrong reason. Pinned inpackages/types/src/__tests__/objectql-union-arms-7363.test.ts(9 assertions).Member 2 — objectui#7352:
DrillDownConfiggets its zod mirrorCommits
f7a428fbd(a PM rescue WIP, see below) and11834ce87.DrillDownConfigSchemanow mirrorsDrillDownConfigkey for key, and both declarations that carrydrillDownreference it —ChartSchemaandObjectDataTableSchema. Until now no mirror knew the key, so underBaseSchema's.passthrough()adrillDown: { enabled: 'yes' }parsed green and rode through to a widget that readsenabledas truthy.Acceptance criterion (named, per the card): a malformed
drillDownvalue is refused, by name, on both declaring nodes; every value the declaration invites still validates. Pinned inpackages/types/src/__tests__/drill-down-config-mirror-7352.test.ts(60 assertions).Deliberately NOT
@objectstack/spec/ui'sChartDrillDownSchema: that object models the chart-only subset strictly and refusesmodeandreportby name, and both are live keys on the table / pivot / metric widgets that shareDrillDownConfig. Referencing it would make the published validator refuse what the published TypeScript declares — this defect class, in the other direction.What the rescue WIP commit was missing
f7a428fbdwas committed by the PM seat after the previous dev was terminated mid-task by an account session limit. It had never been run through a gate. Three things were wrong or absent, each measured rather than assumed:zod-mirror-parity.test.ts's population census was red (New zod export(s) in packages/types/src/zod/ that this guard does not cover: data-display.zod.ts#DrillDownConfigSchema), andtsc -p tsconfig.test.jsonwas red on the staleness ratchet (error TS2322 ... '"data-display.zod.ts#ChartSchema" | "objectql.zod.ts#ObjectDataTableSchema"' is not assignable to type 'never') — bothdrillDownrows inUnmirroredDeclaredwent stale the moment the mirror wired in. The pair is now registered inMIRRORS/Declaredagainst the LOCALDrillDownConfig(theObjectMapConfigSchemaprecedent), both rows are retired with the movement recorded in place, and every count the change moves is updated: 160 to 161 pairs,UnmirroredDeclared15 / 96 to 13 / 94 (LOCAL 12 / 83 to 11 / 82), the "no entry in either" population 144 to 147. This is the ledger's second and third shrink by REPAIR — the objectui#6639 route, and the first time one change closes two entries. The stale "143 of the 160" besideassertionRatchetAcceptsCleanPair, already wrong before this branch, reads 147 / 161 with the rest.report: { name, columns }— an inline report missingobjectName— to be refused.DrillDownConfig's second arm is{ name: string }, which is not an exact type, so TypeScript accepts that value and the excess-property check passes becausecolumnsbelongs to the other arm. Refusing it would make the mirror NARROWER than the declaration, which is the drift class this ledger exists to stop. The case moved to the accepted table, annotatedDrillDownConfigsotsc -p tsconfig.test.jsonis the standing witness: if the declaration ever refuses it, that line stops compiling.widget-schema-anchors-6576.test.tsstill listeddrillDownas a ledgered unmirrored read. The mirror declares it now, so the exception is retired — a stale one widensdeclaredfor a key nothing checks any more.f7a428fbdis left in the branch history rather than rewritten: this seat does not force-push. Its message states plainly that it was unreviewed.Evidence
Gate union re-run after the final commit, on
11834ce87(git rev-parse --short HEADfrom that run), everything through the shared verify lock:pnpm exec vitest run packages/types/pnpm --filter @object-ui/types type-check(three tsc runs,tsconfig.test.jsonincluded)pnpm --filter @object-ui/types buildpnpm --filter @object-ui/types lintpnpm exec vitest run packages/cli/ examples/schema-catalog/pnpm check:control-bytespnpm check:spec-symbolsnode scripts/check-changeset-presence.mjspnpm check:readme-exports@object-ui/types, which IS built here. CI builds first.The two-leg gap proof. With the implementation committed first, the four source files (
objectql.ts,zod/objectql.zod.ts,zod/data-display.zod.ts,zod/index.zod.ts) were checked out fromorigin/mainwhile both instruments stayed at their branch state. The mutation was confirmed on disk before the run, not inferred from an exit code: theDrillDownConfigSchemamarker went 3 to 0 inobjectql.zod.ts, 3 to 0 indata-display.zod.ts, 1 to 0 inindex.zod.ts, andObjectGallerySchema3 to 1 inobjectql.ts, withgit diff HEAD --statshowing 102 deletions across the four..passthrough()and the control asserting a plaindata-tablestill has nodrillDownarm.git checkout HEAD -- pathunder a trap, each verified by comparinggit hash-objectagainst the HEAD blob hash (four exact matches) withgit diff HEADempty — then 69 passed (69) on the same two files.Blast radius of the union change (measured three ways, corrected at review).
AnyComponentSchemaresolves to 105 distinct roottypeliterals at this branch's head and 103 at its merge-base6e8863093— the two this PR adds, and nothing else. Three derivations, all reading source through the vitest alias rather thandist: a structural walk of_zod.def.options(104 / 102, one lower at both revisions, reconciling to the singleactionarm the walk does not descend into); the error-tree walk restricted to issues reported at['type'](105 / 103); and the module's ownexplainUnionIssue(...).totalArmNames(105 / 103). An earlier revision of this section said 116 — that came from an unrestricted error-tree walk that also swept eleven literals reported under other keys (the eightchartTypevalues, plus create / edit / view), and it is corrected here and on the card below.The consumers that read the unions are
packages/cli(the validate command and its arm diagnostics) andexamples/schema-catalog(the objectui#6318safeValidateSchemacorpus and the objectui#7004 arm-selection pins) — all 2321 of their tests pass against the widened union.packages/plugin-chartsandpackages/plugin-editorname the unions only in comments.Clause-② — this PR parks
Both repairs move a published validator's accept set, in OPPOSITE directions — which is why this paragraph is spelled out rather than summarised:
object-gallery/object-data-tabledocument was refused before this branch, not accepted: it matched no arm. That is the card's own point, and the pin says so where it explains why.success === falseis not the load-bearing assertion (objectql-union-arms-7363.test.ts, the two-leg note: "Before the arms existed the same documents were refused too — for the wrong reason, with no arm naming the key"). After this, a well-formed one validates, and a malformed one is refused BY NAME instead of as "no arm matches" — a widening plus a diagnosis, not a narrowing.drillDownwas refused by nothing and rode through.passthrough(); now it is refused by name on both declaring nodes.Both changesets state their own movement in these terms; this paragraph previously described objectui#7363 as a narrowing, which contradicted its changeset and its pin. Either direction is a published accept-set change, so Clause-② holds for the PR as a whole.
So this stays a draft carrying
needs:contract-review. Not flipped to ready, not enqueued, no auto-merge — contract review happens at tier, on another account, and is a recorded debt rather than a skipped step (see the dispatch note on objectui#7363).Out of scope, filed not fixed
Two stale measured counts in
packages/cli/src/utils/union-arm-diagnostics.ts, filed as #7702 and not repaired here: the arm-name figure ("108 leaf arms and 108 DISTINCTtypeliterals", repeated in six places plus two test comments) OVERCOUNTS — the tree reads 103 at this branch's merge-base and 105 at its head; and the alignment sentence at lines 34-35 says the union has 14 members when it has 13. Nothing mechanically reads either number, which is why the CLI suite stays green. A stale measured count is a different defect class from either member here, and the file is outside this dispatch's surface, so the card carries the three derivations and this PR changes nothing there.Patch round after contract review (head
e46a218c9)Review returned PASS with three corrections; none was a contract defect. All three are in, plus one optional item taken up.
MIRRORSinitializer and each ledger interface, and a line-oriented parse of the same blocks: 155 pairs (the prose said 161), 141 pairs clean in both unmirrored ledgers (said 147), 155 − 40 = 115 (said 121), and "141 of the 155" where the clean-pair note said "147 of the 161". The three ledger figures this card actually moved were already right and are unchanged:KnownDrift40 / 56,UnmirroredDeclared13 / 94,RuntimeOnlyDeclared7 / 24, union 14. Running the same walk over this file at three revisions shows how it drifted —4ca30d044wrote "160" whileMIRRORSheld 163,d88e20f55(objectui#7432) took the registry to 154 without touching the sentence, and this branch added its 1 to that stale baseline. The running chain of per-card deltas under the population bullet was arithmetic on the wrong base and is not re-derivable from the file, so it is replaced by the measurement and a note saying so.RuntimeOnlyDeclared's docblock restated the same debt as "97" (stale before this branch too, the ledger then held 96) and now reads 94. Comment-only: noMIRRORSentry, ledger row or type moved, and the ratchet is green.Optional item, taken because it is measurable: the accepted-table comment claimed the reference arm accepts a value "whatever else it carries". It does not — the acceptance is shape-specific, and the arm STRIPS what it does not declare, so
{ name, columns: [] }parses to{ name }while the inline arm keeps extras through.catchall. The comment now states both halves and the changeset records the parsed-output difference, since consumers readreportafter parsing. The schema itself is unchanged: it is the declaration verbatim, and restructuring it would need a ruling.mainhad moved again (8ad218d58), so it was merged in before the union was run — and it mattered for exactly the reason this file warns about, since CI reads the PR's merge rather than its head. objectui#7104 landed a second key onoverlay.zod.ts#AlertDialogSchema'sKnownDriftentry, taking that ledger from 40 / 56 to 40 / 57; its own commit updated that bullet, the two edits merged cleanly, and the re-derivation was re-run on the merged tree. The entry COUNT did not move, so nothing this round corrected changes: 155 pairs, 141 clean, 155 − 40 = 115,UnmirroredDeclared13 / 94,RuntimeOnlyDeclared7 / 24, union 14. Every numeric claim in the file was then checked against the derivation by pattern — 25 claims, 0 mismatches.Gate union re-run at
e46a218c9(the merge):pnpm exec vitest run packages/types/ packages/cli/ examples/schema-catalog/exit 0 — 153 files / 4174 tests;pnpm --filter @object-ui/types type-checkexit 0 (three tsc runs,tsconfig.test.jsonincluded — the one carrying the compile-time ratchet);pnpm --filter @object-ui/types lintexit 0 (0 errors, 269 pre-existing warnings);check:control-bytesexit 0 (6275 files); changeset presence 2 for 8 source files.Still a draft, still carrying
needs:contract-review, still not enqueued and with no auto-merge armed.Generated by Claude Code