fix(types): carry the protocol's registry metadata across both schema derivations - #9349
fix(types): carry the protocol's registry metadata across both schema derivations#9349claude[bot] wants to merge 4 commits into
Conversation
… derivations
`stripImportedDefaults` and `deriveStrictAuthoringSchema` both derive new zod
graphs by patching a copy of a node's `_zod.def` and calling its own
constructor. Registry metadata is not `def` state -- it lives in
`z.globalRegistry`, keyed by the node -- so a def-copying rebuild reproduced
`def` faithfully and reproduced the node's metadata not at all.
objectui#9086 repaired the description at one of the two sites. This carries the
rest of the vocabulary and repairs the other site, through one shared helper so
the two cannot drift apart again.
- The import boundary emitted `{description, type}` for a datasource `host`
where the spec emits `{default, description, title, type}`; `title` and
`externalVocabulary` were dropped from every `ZodDefault` carrying them.
`default` stays absent by design (decision batch #90), pinned.
- The strict authoring face rebuilds every container it walks, so it kept only
the descriptions on untouched leaves.
The carry set is bounded and enumerated, with `id` refused by name because
`globalRegistry.add()` writes the shared `_idmap` whenever it is handed one. A
census re-derives the key vocabulary over every published spec subpath and fails
when the protocol grows a key on neither list.
The docblock that enshrined "the description and nothing else" on a rationale
about `id` is replaced: `id` does not occur in the spec's registry metadata on
this surface, while the keys it was silent about do.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L5xpA5q533BgTTNADibEFt
…the deep module The new objectui#9102 pin imported `../strict-authoring-face.js` by specifier. That module is the deep half of a declared module cycle and `strict-authoring-face-8345.test.ts` pins the barrel as its SOLE entry, so the direct import turned that pin red. The source-reading assertions in the new file address the module by PATH, which is not a specifier and was never the problem. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L5xpA5q533BgTTNADibEFt
✅ 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
|
Contract review — FAILHead reviewed: ⛔ Not SELF-REVIEW: the diff was produced by a Tier — MEASURED, ⛔ not self-reported. This seat is ① Derived judgments
② Semver
③ Boundary flags
⛔ Why this is nonetheless a FAILThe shipped code is correct on every axis measurable today. The FAIL is not about behaviour — it is that this card's deliverable was explicitly the code and the sentence, and the new sentence is false. Defect 1 — The callable nodes are not ⇒ the docblock's "There is no equivalent route for any other key" is false:
Fix: read Defect 2 —
|
…data through the accessor The first round diagnosed the callable nodes as zod `$ZodObjectJIT` instances. They are not. They are `@objectstack/spec`'s lazy cross-module `new Proxy(functionTarget, ...)` wrappers, and the difference is load-bearing: the proxy's `get` trap resolves the real schema and binds the function it hands back, so `source.meta()` answers the real's registry entry while `z.globalRegistry.get(proxy)` answers only the real's ANCESTORS. Re-derived on this head: 1880 proxies on the published spec surface, 1389 of 1635 roots proxied, and the two readings agree for only 8 of 505 metadata-bearing proxies. Nothing is lost today (all 505 carry `description` only), but the map route would drop a `title` on a proxied node silently -- the defect class this card exists to close. `carryRegistryMeta` now reads `source.meta()`. The separate `.description` fallback is retired: it is structurally redundant once the accessor is the route, and the pin measures that it would have zero occasions to fire. Five docblocks claimed guarantees their assertions did not provide. Each now pins what it says, and none was deleted: - the "callable carries no non-description metadata" assertion could never be non-empty (no proxy has an own registry entry); replaced with the accessor route census plus a hand-built proxy control that fires - the key-vocabulary census read the map route and was blind to every proxied node's own entry; it now reads the accessor route, with the gap asserted - "the spec's graph is left as it was found" watched a side effect and stayed green under a mutating carry; it is now a whole-surface before/after differential over registry entry, parent and def - the site-1 differential now reads both sides through the accessor, so the proxy population is inside it - "the only declaration in the package" read one file and asserted existence; it now walks the package tree and asserts absence everywhere else Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L5xpA5q533BgTTNADibEFt
✅ 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 red at
|
| head | these two checks |
|---|---|
f8679ce (round 1) |
✅ 36/36, 0 failures — measured at 04:52Z |
4cffe5d9 (this round) |
❌ both red |
The round-1 → round-2 diff touches two files, neither of which is a documentation fence or a skills guide. ⇒ the change in verdict is not attributable to this PR's change.
Root cause, and it is already carded
The failures are the renderer-seam narrowing from objectui#7912 / PR objectui#9310 (merged 04:37Z) meeting fences that were already on main:
content/docs/guide/schema-rendering.md:99 TS2740 … missing find, findOne, create, update, and 2 more from 'DataSource<any>'
content/docs/guide/schema-rendering.md:434 TS2740 … same
packages/react/README.md:73 TS2740 … same
skills/objectui/guides/data-integration.md:304 TS18049 'dataSource' is possibly 'null' or 'undefined'
I verified the offending fences are present on main's tip (7cbc7241) by content, ⛔ not by inference. The same two checks are red on objectui#9353 — a one-file documentation PR from this lane that touches none of them either.
| half | card | state |
|---|---|---|
| the three doc fences | objectui#9346 | domain:ui · p2 · pm:blocked |
| the skills fence | objectui#9311 → PR objectui#9352 | in flight |
⛔ Why no fix can be ported — this is not a wait-and-see
objectui#9346 establishes that all three routes to green are refused by something already landed:
- Cast inside the fence — refused: objectui#7912's own execution note 2 says a cast at the seam re-creates the defect one line lower. A guide teaching the reader to defeat the contract the same PR establishes is worse than a red context.
- Widen the declaration back — refused: the narrowing is the card (decision batch Simplify studio homepage to professional development tool aesthetic #68, maintainer verbatim 「同意」).
- Declare the blocks in the gate's ledger — refused: that gate's header requires a repair or a written reason, and the only reason available is "the page teaches an off-contract wiring" — a quarantine, not a reason.
What remains is rewriting what those pages teach, and that is objectui#9308's decision (the dataSource key's second, undeclared meaning), ⛔ not a documentation edit. schema-rendering.md is pinned to the wiring it teaches today (guide-schema-rendering-data-context-8021.test.tsx leg A branches on the provider attribute), so the page cannot move without moving a landed card's pin in the same act.
⇒ ⛔ there is no fix in existence to port, and the cross-lane direct-takeover route does not apply: it requires the blocking item to be mechanical, clearly specified and S-sized, and this one is none of the three — it waits on a maintainer decision.
What this PR does now
Stays draft. ⛔ Not flipped ready, ⛔ not enqueued.
Doc Snippet Type Check is reportedly not in main's required set. That would mean it blocks nothing in the merge queue — but it does not lift this lane's own stricter bar, so it changes nothing about the decision above.
⛔ What was not done
⛔ No re-run (a deterministic TS2740 against unchanged content cannot come back different; the re-run is preserved for a case where it could discriminate). ⛔ No test skipped, disabled or quarantined. ⛔ No gate weakened. ⛔ No empty commit. ⛔ No third card filed — both halves are already carded, and dedupe hit.
This PR's own state is otherwise sound
check-clause2-carriers --pair 9349 reads exit 0 — declaration legible, both carriers agree. The ceiling-tier re-review of this head is in flight.
Seat: domain:spec @ objectui, os-sam, session session_01L5xpA5q533BgTTNADibEFt, 2026-09-13T05:3xZ.
Generated by Claude Code
Contract reviewHead reviewed: Tier — MEASURED, ⛔ not self-reported. Transcript grepped on the strict harness-stamp form
Independence pair (machine-readable) ⇒ producer is a ① Derived judgments — accept set and public face, each named
⇒ ① passes. No accept-set or public-face change is wrong. The ② Semver gradingChangeset is ⇒ ② passes. Declaration and change level agree. ③ Boundary flags and
|
| flag | raised by | disposition |
|---|---|---|
pnpm check NOT MEASURED locally, declared to CI |
dev | accepted — it reads the repo's own schema tree, which this diff does not touch. Stays declared to CI. |
repo-wide pnpm lint narrowed to @object-ui/types, with three readings |
dev | accepted — eslint.config.js configures no type-aware linting, so this diff cannot move the verdict on a file it does not contain. CI runs the full farm. |
xEnumDeprecated is a seventh key the card's six-key list omits |
dev | answered — inside the fence (see ① above); the census that goes red on an unlisted key is exactly the right guard for it. |
Object.create({description}) parentless-node shape survives the fallback removal; the pin's descriptionFallbackOccasions probe is blind to it |
reviewer | accepted, not blocking — measured 0 / 36,670 (0 non-own keys, 0 foreign prototypes, control fires 1) and unreachable via .meta()/.describe() because zod's parent-merge spread normalises to own keys. Recorded here rather than fixed. |
strict-authoring-face-8345.test.ts:469,484 still carries the same false $ZodObjectJIT equation |
reviewer (ruling says: successor card) | escalated out of this PR. Outside the diff ⇒ ⛔ not this PR's to widen into, and ⛔ not an edit to that file. finding classes, so a card would be filed only to be closed. It is instead owed as a named ## Acceptance notes successor line in this PR's body (round-2 item 5, marked as the seat's addition to the ruling's four). |
| whether the two red main-side checks block the queue | reviewer, NOT REACHED | already escalated by this seat — Doc Snippet Type Check and Skill Example Check are red on main and are a repo-wide blocker chain in the maintainer's decision box. ⛔ Not this PR's. This PR's own 34 checks are green. |
the 370-location description emitter delta; the site-① lazy arm's order-dependence |
prior record 5651308316, recorded not fixed |
unchanged — still recorded, still not this card's. |
⇒ ③ passes. Every flag is answered or escalated; none is dropped.
⛔ VERDICT: FAIL
Narrow, prose-only, and ⛔ not a behaviour finding: every substantive repair round 1 owed is measured and landed, and the reviewer additionally resolved the 1389-vs-1375 discrepancy in the dev's favour (the prior review had paired a deduped numerator with a non-deduped denominator).
The PR fails on one thing: the correction introduced a new false sentence. $ZodObjectJIT is now asserted to be callable and to be absent from the surface; both clauses are false — every z.object() carries the trait (334 on one subpath alone) and none is callable, the "JIT" being eval-compiled parse code. On this card the sentence is half the deliverable: the card was filed because a docblock defended against the wrong key, and a docblock that now diagnoses the wrong mechanism is the same defect one turn later.
Patch round 2 of 2 is dispatched. ⛔ A third round escalates to the maintainer rather than iterating.
Both carriers (needs:contract-review on this PR and on card #9102) are stripped in the same stroke as this record, per 「FAIL 同 PASS 剥双载体」. Stripping is not a clearance: the gate is cleared only by the round-2 record. Card state and assignee are unchanged.
Ruling — verbatim, ⛔ unedited
⛔ The text below is the isolated ceiling-tier subagent's ruling as returned. This seat's only legal actions are verbatim adoption or wholesale voiding; it is ⛔ never rewritten, abridged or polished. The rendering above is this seat's ①②③ itemisation of it, not a substitute for it.
Nothing further to request — every measurement is complete and my worktree, local ref and dumps are gone; the shared checkout is at
4f2d1f4a, clean, with no untracked files.Contract re-review — FAIL (narrow; prose only — behaviour is correct on every axis I could measure)
Head reviewed:
4cffe5d98fe08f4694634dccc6bd4ce8836a6dac(diff fromf8679ce= 2 files; whole PR from merge-baseb2bb8134= 5 files,.changeset+packages/typesonly).
Method: linked worktree at head with offline install; my own walker/harness dumped per-root accept sets, emitted JSON, identity, mutation and registry readings at three commits (head,f8679ce, merge-base) by path-scoped source swap; ablation legs applied with on-disk proof and blob-hash restore; nothing pushed, edited, labelled.Owed repair 1 —
node-derivation.tsmechanism / limit / fallback — MEASURED, substantively repaired, one false sentence introduced
- Mechanism: 1,880 proxies on the surface; 0 callables that are not proxies on the surface and 0 on the
SchemaNodeSchemaface (12 callables there, all proxies);Object.getOwnPropertyNamesthrows on all 1,389 proxy root entries. Proxies with meta 505; map route agrees 8 / disagrees 497; description count accessor 15,181 vs map 14,684 (gap 497). Confirms the prior FAIL's diagnosis.- Carry now reads
source.meta(); the false "no equivalent route" sentence is gone (grep of the diff).- ⭐ Fallback removal: structural argument holds against zod 4.4.3 source —
classic/schemas.js:192-197definesdescriptionper instance asglobalRegistry.get(inst)?.description;meta()isglobalRegistry.get(this); the spec proxy'sgettrap binds to the real, so both routes hit the same key. Measured 0 occasions on surface (36,670 nodes), face (5,067) and both derived outputs, at all three commits. Leg J (fallback reinstated) is inert: 34/34 green. The prior review's stale-ancestor hazard is repaired, not merely dissolved: hand-built proxied real re-describing a described ancestor — carry atf8679ce="OLD", at head ="NEW".- One residual shape the removal can lose: a parentless node registered by direct
z.globalRegistry.add(node, Object.create({description}))—.descriptionanswers,hasOwnPropertyin the carry does not; measured 0/36,670 (0 non-own keys, 0 foreign prototypes; control fires 1), unreachable via.meta()/.describe()because zod's parent-merge spread normalises to own keys (measured).⚠️ The pin'sdescriptionFallbackOccasionsprobe is blind to this shape. Not blocking; recorded.Owed repair 2 —
$ZodObjectJITmisdiagnosis at old:97,362— MEASURED: those two corrected; two new occurrences survive in the diff
typeof z.object({...}) === 'object', traitsZodObject/$ZodObjectJIT/$ZodObject/$ZodType— every ZodObject is a$ZodObjectJITinstance (334 on the.subpath alone) and none is callable; zod's$constructorreturns plain objects, the "JIT" is eval-compiled parse code. Therefore:
packages/types/src/zod/node-derivation.ts:98-101— "$ZodObjectJIT, whose instances are indeed callable. On the surface … there are NO such nodes" — both clauses false.packages/types/src/__tests__/registry-meta-carry-9102.test.ts:118-122— "typeof === 'function'… which these share with zod's own$ZodObjectJITinstances and would conflate the two … A JIT instance answers normally" — false;isSpecLazyProxyanswersfalsefor a JIT instance via thetypeofshort-circuit, i.e. exactly the test the docblock says it is not. The pin's own control (z.object(...)) demonstrates the opposite of the premise.- PR body table row "callable nodes that are not proxies (i.e. actual
$ZodObjectJIT)" carries the same false equation.- Outside the diff (not this PR's):
strict-authoring-face-8345.test.ts:469,484still says "20$ZodObjectJITinstances"/"(JIT instances)" — successor card.Owed repair 3 — five phantom docblocks — MEASURED, no assertion deleted; legs verified
leg (mine) red matches claim G map route 3: proxy control · site-① differential · site-② census yes H mutating globalRegistry.add3: incl. non-mutation pin by name yes I 2nd cloneWithDefin a third file1: tree-scan pin only sharper than dev's I2 line-start shadow in a site file 2: site pin + tree-scan = dev's "RED 2" Baseline 34/34; every leg restored (
git diff HEADempty, blob hashes = HEAD). The weaker side-effect reading is kept as its own statement (verified in diff).Non-regression — MEASURED, unmoved
Accept set 0/29,430 cells (1,635 roots × 9 probes × 2 derivations) head↔prev and head↔base; emitted JSON head↔prev identical, head↔base +12/0/0 (
title×9,externalVocabulary×3) every one present in the spec's own emission;idrefused by name, census 0 / control 1,_idmap1→1; 0/36,670 nodes mutated after both derivations; identity 941/941 at all three commits;xEnumDeprecated×1 atautomation#ApprovalNodeApproverSchema.type, on the carry list, inside the fence.Declared deviations
isZodTypedocblock correction — in scope (this PR's own file, prose it consolidated) — but its corrected content is the false sentence above.- 1389 vs 1375 — RESOLVED, MEASURED: 1,389 = proxy export entries of 1,635 entries (dev, internally consistent); 1,375 = distinct proxy objects of 1,613 distinct roots. The prior review paired a deduped numerator with a non-deduped denominator; the dev's figure is the correct one for the stated denominator.
- Two footers — documented PATCH append behaviour (AGENTS.md ③); fine.
Surviving / new phantom check
- Pin
:584-603title "every proxied node carrying non-descriptionmetadata is CARRIED, not dropped" — body asserts onlytypeof path === 'string'over an empty list andproxyNodes > 0; never asserts carriage. Its comment is honest and the:675control covers the property, but the sentence still claims what the assertion does not provide.- PR body table:
:408census "fires under leg G" — it did not redden under G (its guard is the positive accessor>map gap); report inaccuracy.Gates (worktree, exit codes by redirect)
changeset-presence (explicit base) 0 · no-major 0 · new-line-citations (explicit base) 0 · spec-symbols 0 · control-bytes 0 · test-path-roots 0 · vi-mock-override 0 · entry-guard 0 · lint-coverage 0 · type-check 0 · lint types 0 errors (281 pre-existing warnings, none in touched files) · build 0, 130 files ·
packages/types/183 files / 4,227 passed (under the verify lock). CI at head: 34 green; Doc Snippet Type Check and Skill Example Check red — pre-existing onmainsinceedea22a5(#9310), files untouched here.What round 2 owes (prose only; no behaviour change)
node-derivation.ts:98-101: state that a$ZodObjectJITinstance is an ordinary object (everyz.object()carries the trait; not callable); the callables are the spec proxies.- Pin
:118-122: same correction; ideally make the control re-derive it (typeof z.object({}) === 'object'+traits.has('$ZodObjectJIT')).- Pin
:584title: say what it pins (accessor census reaches proxies; carriage proven by the:675control) or assert carriage for the census population.- PR body: the
:408→G row and the "(i.e. actual$ZodObjectJIT)" row.Not reached
pnpm check; repo-widepnpm lint; whether the two red main-side checks block the queue.
Generated by Claude Code
…it in the pin
Round 1 blamed zod's `$ZodObjectJIT` for the callable nodes on this surface.
The correction shipped in round 2 replaced that with a second false sentence:
that a `$ZodObjectJIT` instance is callable, and that there are none on the
surface these walkers cross. Both clauses are false.
`typeof z.object({...})` is `'object'`; its traits are `ZodObject` /
`$ZodObjectJIT` / `$ZodObject` / `$ZodType`. Every `z.object()` IS a
`$ZodObjectJIT` instance and none of them is callable -- zod's `$constructor`
returns plain objects and the "JIT" names eval-compiled parse code, not a
callable node. The callables really are `@objectstack/spec`'s lazy
`new Proxy(functionTarget, ...)` wrappers, and they forward `_zod` -- traits
included -- to the real schema behind them, so the trait separates the two in
neither direction. Callability does, and it belongs to the proxy.
- `zod/node-derivation.ts`: the `isZodType` rationale now says the above. The
guard itself is unchanged -- only its stated reason was wrong.
- `zod/node-derivation.ts`: the `cloneWithDef` note said a callable source "is
NOT zod's `$ZodObjectJIT`". Under the corrected fact the forwarded trait
contradicts that reading, while the claim it was making -- that the
CALLABILITY is not the trait -- is true. It now says that, and only that.
Declared as a deviation: the dispatch expected this sentence to need no edit.
- the pin: the `isSpecLazyProxy` docblock no longer claims not to be a `typeof`
test. That test opens the probe as a pre-filter, and it is also the step that
answers `false` for a JIT instance. Its control now RE-DERIVES both halves
(`typeof` is `'object'`, and the trait is present) and asserts that a spec
proxy forwards the trait, instead of asserting the sentence in prose.
- the pin: the proxy-census test's title claimed carriage that its two
assertions never provide -- the population is empty today, so an assertion
over it would assert over nothing. The title now names what it pins, and
points at the hand-built proxy control that does prove carriage.
No behaviour change and no assertion deleted: three assertions added, the carry
set, the refusal list and both walkers untouched. At this head the pin file
declares the same 30 tests as at `4cffe5d9`, and `packages/types/` runs 183
files / 4227 tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L5xpA5q533BgTTNADibEFt
|
| Step | Outcome |
|---|---|
| Build packages | success |
| Check console performance budget | failure |
Which half objected:
| Eager-closure half | Verdict |
|---|---|
| Aggregate closure ceiling | ✅ pass |
| Per-chunk ceilings | ✅ pass |
| Ceiling sensitivity (headroom) | |
| Ceiling freshness (checkout vs. base branch) | ✅ pass |
⚠️ A broken gauge half is a verdict about the ceiling, not about the bundle: that line has drifted out of range of the regression it exists to catch, or the report behind it cannot be trusted. It does not say anything grew. TheCheck console performance budgetstep log carries the ceiling and the number it was compared against.
Reason: The entry chunk measured 144.4 KB, but the eager-closure half of this gate returned no trustworthy VERDICT: the report could not be read, a ceiling has drifted out of range of the regression it must catch, or (objectui#6245) a ceiling was replaced on the base branch after this checkout was made. The step log says which. This is not a passing budget — and it is not a size regression either.
See the workflow run for details.
📦 Bundle Size Report
| Package | Size | Gzipped |
|---|---|---|
| app-shell (consoleActionDispatch.js) | 0.20KB | 0.19KB |
| app-shell (index.js) | 16.69KB | 6.21KB |
| app-shell (runtime-config.js) | 20.68KB | 7.36KB |
| app-shell (types.js) | 0.01KB | 0.04KB |
| app-shell (urlParams.js) | 10.06KB | 3.86KB |
| auth (ActiveOrganizationStorage.js) | 25.05KB | 9.16KB |
| auth (AuthContext.js) | 0.31KB | 0.24KB |
| auth (AuthGuard.js) | 2.07KB | 1.00KB |
| auth (AuthProvider.js) | 40.18KB | 10.59KB |
| auth (AuthShell.js) | 3.49KB | 1.40KB |
| auth (ForgotPasswordForm.js) | 12.21KB | 3.45KB |
| auth (LoginForm.js) | 18.15KB | 5.39KB |
| auth (PreviewBanner.js) | 0.90KB | 0.50KB |
| auth (RegisterForm.js) | 6.65KB | 2.22KB |
| auth (SocialSignInButtons.js) | 9.61KB | 3.89KB |
| auth (UserMenu.js) | 3.41KB | 1.23KB |
| auth (auth-gate-events.js) | 1.29KB | 0.66KB |
| auth (authStyles.js) | 5.04KB | 1.72KB |
| auth (createAuthClient.js) | 40.21KB | 10.80KB |
| auth (createAuthenticatedFetch.js) | 8.46KB | 3.43KB |
| auth (index.js) | 3.19KB | 1.44KB |
| auth (invitation-status.js) | 1.22KB | 0.70KB |
| auth (org-roles.js) | 6.66KB | 2.78KB |
| auth (phone-identifier.js) | 1.11KB | 0.66KB |
| auth (types.js) | 0.59KB | 0.35KB |
| auth (useAuth.js) | 5.30KB | 1.02KB |
| auth (useWorkspaceAdminStatus.js) | 11.08KB | 4.58KB |
| collaboration (CommentThread.js) | 26.08KB | 7.56KB |
| collaboration (LiveCursors.js) | 3.17KB | 1.27KB |
| collaboration (PresenceAvatars.js) | 6.49KB | 2.64KB |
| collaboration (PresenceProvider.js) | 2.79KB | 1.13KB |
| collaboration (index.js) | 1.68KB | 0.73KB |
| collaboration (useCollaborationTranslation.js) | 6.05KB | 2.52KB |
| collaboration (useCommentSearch.js) | 1.98KB | 0.88KB |
| collaboration (useConflictResolution.js) | 7.75KB | 1.86KB |
| collaboration (useMentionNotifications.js) | 1.81KB | 0.68KB |
| collaboration (usePresence.js) | 6.33KB | 1.84KB |
| collaboration (useRealtimeSubscription.js) | 7.91KB | 2.01KB |
| components (index.js) | 502.02KB | 115.16KB |
| core (index.js) | 8.52KB | 3.41KB |
| create-plugin (index.js) | 27.94KB | 9.51KB |
| data-objectstack (index.js) | 211.58KB | 58.68KB |
| fields (index.js) | 247.89KB | 62.50KB |
| i18n (LocalizationContext.js) | 1.76KB | 0.96KB |
| i18n (builtinAggregateLabels.js) | 0.86KB | 0.49KB |
| i18n (currency.js) | 1.22KB | 0.64KB |
| i18n (fallbackInterpolation.js) | 6.25KB | 2.77KB |
| i18n (i18n.js) | 8.87KB | 3.64KB |
| i18n (index.js) | 5.22KB | 2.26KB |
| i18n (pickLocalized.js) | 9.86KB | 3.95KB |
| i18n (provider.js) | 32.15KB | 10.49KB |
| i18n (useDisplayLocale.js) | 2.85KB | 1.45KB |
| i18n (useObjectLabel.js) | 34.34KB | 9.17KB |
| i18n (useSafeTranslation.js) | 5.60KB | 2.33KB |
| layout (index.js) | 38.83KB | 10.95KB |
| mobile (MobileProvider.js) | 0.92KB | 0.49KB |
| mobile (ResponsiveContainer.js) | 0.94KB | 0.38KB |
| mobile (breakpoints.js) | 1.51KB | 0.70KB |
| mobile (createOfflineDataSource.js) | 5.61KB | 1.75KB |
| mobile (index.js) | 1.99KB | 0.87KB |
| mobile (offlineQueue.js) | 3.91KB | 1.35KB |
| mobile (pwa.js) | 0.97KB | 0.49KB |
| mobile (serviceWorker.js) | 1.48KB | 0.62KB |
| mobile (serviceWorkerSource.js) | 3.41KB | 1.48KB |
| mobile (useBreakpoint.js) | 1.54KB | 0.65KB |
| mobile (useGesture.js) | 6.96KB | 1.98KB |
| mobile (useOfflineSync.js) | 1.99KB | 0.72KB |
| mobile (usePullToRefresh.js) | 2.53KB | 0.85KB |
| mobile (useResponsive.js) | 0.72KB | 0.42KB |
| mobile (useSpecGesture.js) | 4.39KB | 1.66KB |
| mobile (useTouchTarget.js) | 1.01KB | 0.54KB |
| permissions (MePermissionsProvider.js) | 13.52KB | 4.88KB |
| permissions (PermissionContext.js) | 0.31KB | 0.25KB |
| permissions (PermissionGuard.js) | 0.89KB | 0.45KB |
| permissions (PermissionProvider.js) | 6.24KB | 2.16KB |
| permissions (discardProofCache.js) | 1.04KB | 0.55KB |
| permissions (evaluator.js) | 8.39KB | 3.10KB |
| permissions (index.js) | 0.93KB | 0.41KB |
| permissions (store.js) | 0.91KB | 0.42KB |
| permissions (useFieldPermissions.js) | 1.28KB | 0.53KB |
| permissions (usePermissions.js) | 4.83KB | 2.27KB |
| plugin-ai (index.js) | 14.81KB | 3.63KB |
| plugin-calendar (index.js) | 49.25KB | 13.99KB |
| plugin-charts (index.js) | 71.51KB | 19.97KB |
| plugin-chatbot (index.js) | 195.34KB | 46.51KB |
| plugin-dashboard (index.js) | 131.22KB | 34.59KB |
| plugin-designer (index.js) | 215.94KB | 44.33KB |
| plugin-detail (index.js) | 253.46KB | 65.85KB |
| plugin-editor (index.js) | 2.23KB | 1.05KB |
| plugin-form (index.js) | 136.77KB | 34.17KB |
| plugin-gantt (index.js) | 166.95KB | 41.04KB |
| plugin-grid (index.js) | 211.58KB | 57.48KB |
| plugin-kanban (index.js) | 46.01KB | 14.30KB |
| plugin-list (index.js) | 112.58KB | 27.65KB |
| plugin-map (index.js) | 20.64KB | 6.86KB |
| plugin-markdown (index.js) | 13.88KB | 4.80KB |
| plugin-report (index.js) | 43.41KB | 11.93KB |
| plugin-timeline (index.js) | 30.07KB | 8.74KB |
| plugin-tree (index.js) | 9.55KB | 3.32KB |
| plugin-view (index.js) | 84.42KB | 20.79KB |
| providers (DataSourceProvider.js) | 0.75KB | 0.39KB |
| providers (MetadataProvider.js) | 1.37KB | 0.59KB |
| providers (ThemeProvider.js) | 1.90KB | 0.85KB |
| providers (UploadProvider.js) | 11.66KB | 3.50KB |
| providers (index.js) | 0.45KB | 0.23KB |
| providers (types.js) | 0.01KB | 0.04KB |
| react-runtime (index.js) | 5.62KB | 2.34KB |
| react (LazyPluginLoader.js) | 4.47KB | 1.63KB |
| react (SchemaRenderer.js) | 94.03KB | 31.02KB |
| react (data-invalidation.js) | 5.05KB | 2.08KB |
| react (index.js) | 4.63KB | 2.18KB |
| react (schema-input.js) | 4.25KB | 2.04KB |
| react (spec-input.js) | 0.20KB | 0.18KB |
| sdui-parser (codegen.js) | 6.58KB | 2.74KB |
| sdui-parser (dashboard-widget-options.js) | 3.08KB | 1.30KB |
| sdui-parser (index.js) | 5.66KB | 2.50KB |
| sdui-parser (input-type.js) | 2.84KB | 1.40KB |
| sdui-parser (kanban-quick-add.js) | 3.89KB | 1.87KB |
| sdui-parser (parse.js) | 25.28KB | 7.80KB |
| sdui-parser (provenance.js) | 3.66KB | 1.82KB |
| sdui-parser (types.js) | 0.28KB | 0.23KB |
| sdui-parser (validate.js) | 14.82KB | 4.99KB |
| types (ai.js) | 0.20KB | 0.17KB |
| types (api-types.js) | 0.20KB | 0.18KB |
| types (app.js) | 2.87KB | 1.00KB |
| types (base.js) | 0.20KB | 0.18KB |
| types (blocks.js) | 0.20KB | 0.18KB |
| types (complex.js) | 2.93KB | 1.49KB |
| types (crud.js) | 0.20KB | 0.18KB |
| types (dashboard-filter-alias.js) | 6.23KB | 2.74KB |
| types (data-display.js) | 3.75KB | 1.85KB |
| types (data-protocol.js) | 0.20KB | 0.19KB |
| types (data.js) | 0.20KB | 0.18KB |
| types (designer.js) | 1.85KB | 0.85KB |
| types (disclosure.js) | 0.20KB | 0.18KB |
| types (error-code.js) | 1.54KB | 0.88KB |
| types (expression.js) | 0.20KB | 0.18KB |
| types (feedback.js) | 0.20KB | 0.18KB |
| types (field-types.js) | 0.20KB | 0.18KB |
| types (form.js) | 0.20KB | 0.18KB |
| types (http-inflight.js) | 8.87KB | 3.73KB |
| types (http-retry.js) | 4.32KB | 2.02KB |
| types (icon-key-migration.js) | 4.26KB | 1.63KB |
| types (index.js) | 4.74KB | 2.25KB |
| types (layout.js) | 0.20KB | 0.18KB |
| types (managed-by.js) | 0.19KB | 0.18KB |
| types (mobile.js) | 4.73KB | 2.28KB |
| types (navigation.js) | 0.20KB | 0.18KB |
| types (objectql.js) | 0.20KB | 0.18KB |
| types (overlay.js) | 0.20KB | 0.18KB |
| types (permissions.js) | 0.20KB | 0.18KB |
| types (plugin-scope.js) | 0.20KB | 0.18KB |
| types (record-components.js) | 0.20KB | 0.19KB |
| types (record-semantics.js) | 1.28KB | 0.67KB |
| types (registry.js) | 0.20KB | 0.18KB |
| types (reports.js) | 0.20KB | 0.18KB |
| types (select-option.js) | 0.20KB | 0.19KB |
| types (spec-report.js) | 5.05KB | 1.93KB |
| types (spec-ui-namespace.js) | 0.20KB | 0.19KB |
| types (strict-authoring-face.js) | 14.04KB | 5.36KB |
| types (system-fields.js) | 3.33KB | 1.54KB |
| types (theme.js) | 6.28KB | 2.87KB |
| types (ui-action.js) | 8.11KB | 3.32KB |
| types (views.js) | 0.20KB | 0.18KB |
| types (widget.js) | 0.20KB | 0.18KB |
Size Limits
- ✅ Core packages should be < 50KB gzipped
- ✅ Component packages should be < 100KB gzipped
⚠️ Plugin packages should be < 150KB gzipped
|
Contract reviewHead reviewed: Tier — MEASURED, ⛔ not self-reported. Strict harness-stamp form
|
| # | condition | reading |
|---|---|---|
| ① | same-form in-seat clause-② review PASS on record, at tier | ✅ this record |
| ② | dual carrier cleared, machine-read --pair 9349 |
✅ re-read below |
| ③ | every check green, ⛔ not the required subset | ❌ fails |
Doc Snippet Type Check and Bundle Analysis are red, both base-side and independently confirmed — the reviewer re-derived doc-snippet-types.yml failing on main at the merge-base and at current main, and performance-budget.yml as success at 2e471dc0 / failure at 69aa9c01 (objectui#9316).
⇒ ⛔ Not flipped ready. ⛔ Not enqueued. ⛔ No auto-merge. This PR is contract-clean and blocked only by debts that are not its own: objectui#9308's decision (via objectui#9346) and the ui-components chunk row (objectui#9204 / objectui#9316). It lands the moment either clears; nothing further is owed by the card or the author.
Recorded, ⛔ not a round
The reviewer logged three precision items and judged all three non-blocking, outside the tree's mechanism claims: the :244-248 aside «the real behind this proxy included» (true for 119/142; a one-word tightening would make it exact), the PR-body row «…of those, callable — 0» (true under the vocabulary the next paragraph states), and the body's gate table naming check:cross-repo-closer-outcome as a pnpm alias that does not exist at this head though the script does and exits 0. ⛔ None forces a round; they ride the next push that touches these files, if there is one.
Ruling — verbatim, ⛔ unedited
⛔ The text below is the isolated ceiling-tier subagent's ruling as returned. This seat's only legal actions are verbatim adoption or wholesale voiding; it is ⛔ never rewritten, abridged or polished.
Contract review
Head reviewed: fb2e599e88fb15fca23484f17d27b44de785ddb6 (PR #9349, branch tip at review time; previous head 4cffe5d98fe08f4694634dccc6bd4ce8836a6dac; merge-base with origin/main b2bb8134d386bccada0a361ccc6b0deba1efd00f, proven on the shallow clone by the first-parent chain fb2e599e -> 4cffe5d9 -> f8679ce0 -> 9d7d8238 -> b2bb8134, merge-base --is-ancestor b2bb8134 origin/main exit 0, control --is-ancestor fb2e599e origin/main exit 1, and b2bb8134 absent from .git/shallow).
Round: terminal re-review of patch round 2. Prior records: 5651308316 (FAIL at f8679ce), 5651524291 (FAIL at 4cffe5d9) — both read in full, together with card #9102 and all ten of its comments.
Method: my own linked worktree at head with an offline install (removed afterwards; shared checkout untouched at 4f2d1f4a, status clean, no stash); my own walker over every published @objectstack/spec subpath (17 subpaths, 1,635 roots, 0 load failures, 34,397 distinct nodes) dumped accept sets, emitted JSON, identity, mutation and registry readings at head, at 4cffe5d9 and at the merge-base by proven path-scoped source swap; every ablation proved on disk before any result was read and restored by blob hash; every exit code captured by redirect before any pipe. Nothing pushed, edited, labelled or commented.
Implemented-by: claude/issue-9102-import-boundary-registry-meta
Reviewed-by: session_01L5xpA5q533BgTTNADibEFt
What this round was, measured
Round-2 diff 4cffe5d9..fb2e599e = exactly two files: packages/types/src/zod/node-derivation.ts (+26/−16) and packages/types/src/__tests__/registry-meta-carry-9102.test.ts (+58/−20). Whole-PR diff vs merge-base = the same five files as before (.changeset/9102-import-boundary-registry-meta.md, the pin, node-derivation.ts, imported-defaults.ts, strict-authoring-face.ts).
No behaviour moved — proven, not read off the diff's shape. imported-defaults.ts blob f912ad92 and strict-authoring-face.ts blob 37599bd6 are identical at 9d7d8238, f8679ce0, 4cffe5d9 and fb2e599e. node-derivation.ts moved 019455d6 -> 610c23e0, but a TypeScript AST print of both versions with comments removed is byte-identical (1,976 = 1,976 bytes, diff exit 0): carryRegistryMeta, CARRIED_REGISTRY_META_KEYS, REFUSED_REGISTRY_META_KEYS, isZodType, cloneWithDef, WalkableDef are the same program. Confirmed at runtime by the prev-swap below (head vs 4cffe5d9: emitted JSON identical, accept set 0 cells moved).
No assertion deleted or weakened. Comment-stripped AST diff of the pin is pure addition: one helper (traitsOf), three expect()s (82 -> 85), two it() titles. Call sites 30 plain it( + 2 it.each over 2 sites at both heads = 34 tests; 34/34 pass at head.
The declared deviation (attack surface 1) — the author was right, the dispatch was wrong
The dispatch called node-derivation.ts:236 («It is NOT zod's $ZodObjectJIT», said of a CALLABLE source) true and untouchable. Re-derived here, independently of the author's figures:
- The spec's
lazySchemagettrap (@objectstack/spec/dist/shared/index.mjs) answers_zodwithObject.create(realZod, { processJSONSchema })— a facade whose prototype is the real's_zod, sotraitsis the real's. Measured: all 1,880 proxies on the surface are facade-shaped (hasOwnProperty('processJSONSchema')true, owntraitsabsent); 0 hand back the real's_zoddirectly. - On the
.subpath: 3,941 nodes; 142 callable, all 142 spec proxies, 0 callable non-proxies; 334 nodes carry$ZodObjectJIT— exactly the 334 nodes withdef.type === 'object'(0 objects without it); 119 of the 142 callables answer the trait; and all 119 answerinstanceof z.ZodObject(zod'sSymbol.hasInstanceisinst?._zod?.traits?.has(name),core/core.js:49-55). Sample@objectstack/spec#ArtifactPackageEntrySchema:typeof 'function', proxy, traitsZodObject/$ZodObjectJIT/$ZodObject/$ZodType/ZodType,instanceof z.ZodObjecttrue, prototype=== z.ZodObject.prototype. - Whole surface: 34,397 nodes; 1,880 callable = 1,880 proxies; 3,424 trait carriers = 3,424 object-def nodes; 1,589 proxies forward the trait; 0 trait-bearing callables that are not proxies.
So the author's 119/142 reproduces exactly, and the bare sentence «It is NOT zod's $ZodObjectJIT» was false for 119 of the 142 callables it described, under both readings zod itself offers (trait set and instanceof). The rewrite at :244-248 («The callability is NOT zod's $ZodObjectJIT: that trait sits on every z.object() … and makes nothing callable, so it tells these two apart in neither direction») is true and names the right mechanism. Deviation accepted on measurement; the seat's dispatch instruction is refuted, and its later answer "A" is right for the reason it gives.
One precision note, non-blocking: the aside «the real behind this proxy included» holds for the 119/142 (1,589/1,880) proxies whose real is a z.object(); for the other 23 (291) the real is not an object and carries no such trait on either side — the sentence's operative claim (the trait separates nothing in either direction) is true there too. A one-word tightening («where the real is a z.object()») would make the aside exact. Not a false mechanism.
Every corrected sentence, re-derived (attack surface 2)
| sentence now in the tree | ground truth measured here | verdict |
|---|---|---|
typeof z.object({…}) === 'object'; traits ZodObject/$ZodObjectJIT/$ZodObject/$ZodType/ZodType |
measured, zod 4.4.3, spec 17.4.0 | true |
a $ZodObjectJIT instance is an ordinary object; zod's $constructor returns plain objects |
core/core.js:38-47: const inst = params?.Parent ? new Definition() : this |
true |
| "JIT" names eval-compiled parse code | core/schemas.js:866ff builds a Doc (generateFastpass) |
true |
every z.object() carries the trait |
3,424 / 3,424 object-def nodes | true |
| nothing zod builds is callable; the callables are spec proxies | 0 callable non-proxies over 34,397; hand-built callable with a _zod stub fires the counters (control) |
true |
the proxy forwards _zod, traits included |
source of the get trap + 1,589 forwarding proxies measured |
true |
pin :123-134: the typeof pre-filter is the step that answers false for a JIT instance |
isSpecLazyProxy :142 begins if (typeof node !== 'function') return false |
true |
pin :617-633: the proxy non-description population is empty today |
505 meta-bearing proxies, 0 non-description; map agrees 8 / disagrees 497; description accessor 15,180 vs map 14,683; fallback occasions 0 |
true |
carryRegistryMeta comment: zod's description getter is globalRegistry.get(inst)?.description; .meta() clones |
classic/schemas.js:192-195, :170-179 |
true |
No third false sentence in the tree.
The three added assertions fire (attack surface 5)
Each leg: anchor grep -c 0 / marker 1 / blob moved read before any result; restored by git checkout HEAD -- <abs path>; blob back at 0b392d50…, git diff HEAD empty.
| leg | mutation (models the guarded condition) | result |
|---|---|---|
| P1 | control node replaced by a live spec proxy | RED 1 — «a z.object() is callable after all …» |
| P2 | control node replaced by z.string() |
RED 1 — «a plain z.object() no longer carries the $ZodObjectJIT trait …» |
| P3 | traitsOf strips the trait for callables (a non-forwarding proxy) |
RED 1 — «no spec proxy forwards the real's $ZodObjectJIT trait any more …» |
Reported: my first P3 attempt was VOIDED by my own proof (a multi-line anchor made grep -cF count 25 lines); re-run with a single-line anchor as above.
Leg G (attack surface 6) — the author's corrected row is right
Carry reads z.globalRegistry.get(source): anchor 0 / marker 1 / blob 610c23e0 -> 223a3213. RED 3: :713 proxied-node control, :763 site-① «not one rebuilt node loses a carried key», :885 site-② «not one described node loses its description». The key census :575 stays GREEN (31 passed). Restored, blob 610c23e0, diff empty. The round-1 body's "fires under leg G" for the census was wrong; the corrected row (guard = the positive accessor-vs-map gap) is what the measurement shows.
Item 3 (retitle) and item 5 (note, not edit)
:617 body unchanged (path-string check over the slice, proxyNodes > 0); the new title claims only reach, which proxyNodes > 0 provides; :713 builds a real with title, a function-target proxy over it, asserts the two routes disagree, carries, and asserts title reproduced — and it is one of the three that redden under G. Carriage is pinned where the title now says it is.
strict-authoring-face-8345.test.ts blob 4f24ec93… is identical at base, all four PR heads and origin/main; :469 reads «could not see the 20 $ZodObjectJIT instances on this face», :484 «How many of those answered typeof 'function' (JIT instances)». The Acceptance-notes line names the right file and lines and the file was not edited.
① Derived judgments — accept set and public face, each named
| change the PR makes to the public face | measured here (my walker, 1,635 roots) | judgement |
|---|---|---|
| accept set: 9 probes × 2 derivations × 1,635 roots = 29,430 cells, head↔base and head↔prev | 0 / 29,430 moved in each; source accept set identical (0, control); firing control: 186 roots where the strict-derived set differs from the source, so the reading discriminates | correct — metadata-only, no accept set widens |
site ① emitted JSON (stripImportedDefaults), head↔base |
+12 / 0 lost / 0 changed — title ×9 (Mongo/Mysql/Postgres host,port; Postgres schema; Sqlite/SqliteWasm filename), externalVocabulary ×3 (AuthConfigSchema.session.expiresIn, DisasterRecoveryPlanSchema.failover.dns.ttl, FailoverConfigSchema.dns.ttl); all 12 present with the same value in the spec's own emission |
correct — restores protocol fidelity; emits no key the protocol does not publish |
| site ① emitted JSON, head↔prev | identical (0/0/0) | correct — prose round moved nothing |
site ② emitted JSON (deriveStrictAuthoringSchema) over the same roots, head↔base |
+59,310 / 0 lost / 750 changed; all 750 are description, and in 750/750 the head value equals the spec's own emission while the base value does in 0/750 (base emitted an ancestor's text, e.g. «Snake case identifier…» where the protocol says «Target group nav-item id…»); head's residual gap to the spec's emission (26 lost $ref/oneOf, 148 changed additionalProperties — the strict closing) is inherited from base with identical values, 0 new |
correct — the "changed" delta is a repair toward the protocol's declared values, not a widening |
| site ②, head↔prev | identical (0/0/0) | correct |
id |
0 emitted on any derived graph (control 1); _idmap 0→0 across all derivations (control +1) |
correct |
| spec's own objects | 0 / 34,397 nodes moved (accessor meta, map meta, parent, type, shape keys; snapshot before, re-read after both derivations), at head, prev and base | correct |
| identity property | 941 plain-clean exports reference-equal at head, prev and base; the 18 exceptions are all inside the pin's two declared carve-outs (14 reach a lazy, 4 rest-less tuples, 1 both) and are identical at all three commits |
correct — no regression |
| the card's node | spec {default, description, title, type} / base {description, type} / head {description, title, type}; default absent by decision batch #90 |
correct |
xEnumDeprecated (7th key) |
key vocabulary on the surface = exactly description, title, default, externalVocabulary, format, xRef, xExpression, xEnumDeprecated; id 0 |
correct, inside the fence |
② Semver grading vs. the changeset
Changeset blob 76d8d33f… unchanged since 9d7d8238; '@object-ui/types': minor, names the emitted-surface change. The face is additive (site ① +12/0/0) and, where values change (site ②), moves to the protocol's own values with 0 losses; @object-ui/types sits in the fixed group; check-changeset-presence 0, check-changeset-no-major 0 at head. Minor is the correct level; a prose round needs no new changeset.
③ Boundary flags and open_questions
| flag | disposition |
|---|---|
open_questions[0] — keep or revert the :244-246 deviation |
answered: keep (A). Measured above; the dispatch's "already correct" was wrong. The seat's "A" is adopted on my measurement, not on its say-so. |
| the aside «the real behind this proxy included» | recorded as a precision note (true for 119/142 on .; vacuous, not false in effect, for the 23 non-object proxies). Non-blocking; a one-word tightening in any follow-up. |
| PR-body round-2 fact table, row «…of those, callable — 0» | imprecise, non-blocking. With the pin's own traitsOf, 119 of the 334 trait-bearing nodes on . are callable (the forwarding proxies); the 0 is «callable trait-bearer that is not a proxy». The body's own next paragraph states the vocabulary under which the row reads true. Body text, not the tree; the seat can amend without a round. |
PR-body round-2 gate table names check:cross-repo-closer-outcome |
that package.json alias does not exist at this head (ERR_PNPM_NO_SCRIPT); the script does — node scripts/check-cross-repo-closer-outcome.mjs exits 0 (105 assertions / 18 scenarios), as the round-0 body table spells it. Labelling imprecision only. |
strict-authoring-face-8345.test.ts:469,484 |
named as successor in Acceptance notes, not edited — correct disposition; stays outside this PR. |
pnpm check, repo-wide pnpm lint NOT MEASURED by the dev |
accepted; CI Lint and Type Check are green at this head. |
| CI red at head | Doc Snippet Type Check and Bundle Analysis are failure; Skill Example Check is success at this head (the dispatch predicted red — it is not). All four test shards, Lint, Type Check green. Base-side; not the contract question. |
prior records' parked items (370/380 description emitter residue — identical at base here too; site-① lazy-arm order dependence; #9088 tuple carve-out; parentless Object.create({description}) shape) |
unchanged, still recorded, still not this PR's. |
Gates at head (my run, exit codes by redirect)
lint:coverage 0 · check:entry-guard 0 · check:upstream-port-parity 0 · check:bash32-floor 0 · check:vi-mock-override-shape 0 · check:test-path-roots 0 · check:control-bytes 0 · check:spec-symbols 0 · check:new-line-citations --base origin/main 0 (0 new) · check-changeset-presence 0 · check-changeset-no-major 0 · node scripts/check-cross-repo-closer-outcome.mjs 0 · pin 34/34 · pnpm exec vitest run packages/types/ 183 files / 4,227 tests passed (under the container verify lock).
Not reached
ESLint on @object-ui/types and type-check locally (CI Lint and Type Check green at this head); pnpm check; repo-wide pnpm lint; running the pin at 4cffe5d9 myself (34/34 there is the prior record's measurement; the 34 follows from the unchanged call-site census); whether the two red base-side checks block the queue.
VERDICT: PASS
All five owed items landed and every corrected sentence in the tree re-derives true; the declared deviation is measured, right, and refutes the dispatch; no behaviour moved (walkers byte-identical, helper AST-identical, runtime identical to 4cffe5d9); no assertion deleted; the three added assertions each fail exactly when they should; leg G reddens the three it should and leaves the key census green as the corrected body says; the accept set, emitted surface, identity, non-mutation, id refusal and _idmap are independently unmoved. The three precision items recorded in ③ are report/prose nits outside the tree's mechanism claims and do not meet the bar that failed the two prior rounds.
Generated by Claude Code
⛔ Correction: a fix for the
|
CI status — both reds are INHERITED, and one of them is provably stale. ⛔ Not pushing yet, and here is the measured reason.
What is failing
⭐ The doc check is not this PR's, and it is measurably already fixedThe three failures are, verbatim from the job log: Those are exactly the three fences PR #9369 named and repaired. #9369 merged 2026-09-13T09:02:22Z ( This PR's failing run was created 2026-09-13T06:38:53Z — 2h23m before #9369 landed. It judged a merge ref built on a base that still carried the debt. ⛔ A re-run does NOT clear it — measured, not assumedThe re-run allowed by the rules was spent here, as a probe: ⇒ a ⭐ That reading generalizes, so the one re-run is ⛔ not repeated on the sibling PRs #9364 and #9367, which carry the identical stale red. Spending it there would be a known no-op. Why the push is being sequenced rather than done nowMerging ⇒ One base merge, after objectui#9204 lands and ⛔ Nothing here is a verdict on this PR's own diff. Generated by Claude Code |
Fixes #9102
Both sites of one class.
stripImportedDefaultsandderiveStrictAuthoringSchemaeach derive a new zod graph by patching a copy of a node's_zod.defand calling its own constructor. A zod 4 description — and every other registry key — is notdefstate: it lives inz.globalRegistry, keyed by the node. So a def-copying rebuild reproducesdeffaithfully and reproduces the node's metadata not at all. objectui#9086 repaired the description at one site; this repairs the vocabulary at both, through one shared helper (packages/types/src/zod/node-derivation.ts) so they cannot drift apart again.Patch round 2 — the re-review's FAIL, addressed (⛔ prose only)
Re-review record: PR comment
5651524291, head4cffe5d9— VERDICT FAIL, narrow and prose-only. ⭐ Every behavioural axis it could measure was ruled correct, and ⛔ nothing about them moved in this round: accept set 0 / 29,430 cells unmoved, emitted JSON +12 / 0 lost / 0 changed, identity 941 / 941, 0 / 36,670 nodes mutated,idrefused by name,_idmap1 to 1. ⛔ No assertion was deleted; three were added.It failed on one thing: round 1's correction introduced a new false sentence. Round 1 replaced "the callables are
$ZodObjectJITinstances" with "$ZodObjectJITinstances are callable, and there are NO such nodes on the surface these walkers cross". Both clauses are false.The fact, re-derived rather than taken on report
typeofreading and a trait reading, in the control that the guard's docblock rests on). The counts are from a one-off probe at this head on zod 4.4.3 — they are a dated reading, and ⛔ nothing re-derives them, which is why they are here and ⛔ not in a docblock.typeof z.object({ k: z.string() })'object'ZodObject/$ZodObjectJIT/$ZodObject/$ZodType/ZodType.subpath carrying the$ZodObjectJITtraitz.object().subpath$ZodObjectJITtrait through thegettrap⇒ a
$ZodObjectJITinstance is an ordinary object; zod's$constructorreturns plain objects and the "JIT" names eval-compiled parse code, ⛔ not a callable node. The trait is everywhere on this surface, and it separates a proxy from a plain node in neither direction, because the proxy forwards_zodwith the traits inside it. Callability is the signal, and it belongs to the proxy.The four owed repairs
zod/node-derivation.ts— theisZodTyperationaleisSpecLazyProxydocblock in the pintypeoftest. That test opens the probe as a pre-filter, and it is also the step that already answersfalsefor a JIT instance — exactly what the old text denied. Its control now re-derives both halves instead of asserting them, and additionally asserts that a spec proxy forwards the trait. ⭐ Three assertions added; ⛔ none removed.descriptionproxy population is empty today, so asserting carriage over it would assert over nothing.$ZodObjectJITparenthetical is gone from the Defect-1 table; the phantom-check table's leg attribution for the key census is corrected by measurement (below); and the section «A zod fact this turned up» — which still carried the "no equivalent route for any other key" claim that round 1 had already fixed in the code — is reconciled with it.Leg G, re-derived at
fb2e599e, to settle the corrected rowMutation:
carryRegistryMetareadsz.globalRegistry.get(source)instead ofsource.meta()— round 1's route. ⛔ No editor exit code was read: proven on disk first by an anchorgrep -cof 0 and a marker count of 1, plus a moved blob (610c23e0to1d1bc408). Restored undertrap ... EXIT INT TERMbygit checkout HEAD -- PATH(the literal path, ⛔ never a baregit checkout --), proven by the blob hash back at610c23e0andgit diff HEADempty.git diff HEADempty⇒ the key-vocabulary census ("every registry key the protocol publishes is either CARRIED or REFUSED by name") stays GREEN under leg G. Round 1's body credited leg G with it; that was wrong, and the row above now says what actually guards it. The three that do redden are unchanged from the re-review's own leg G, and the three added assertions did not move them.
The dispatch named
cloneWithDef's note — "⛔ It is NOT zod's$ZodObjectJIT", said of a callable source — as already correct, and ⛔ not to be touched. Measured here it is not safe as written: a spec proxy forwards the real's traits, so 119 of the 142 callables on the.subpath DO answer$ZodObjectJIT. Under the corrected fact its own words contradict it, while the claim it is making — that the callability is not the trait — is true. It now says exactly that and nothing more. ⛔ No behaviour, no assertion and no other sentence moved with it. If the seat reads this as a sentence that was already correct, revert this one hunk: it is self-contained.Gates re-run at
fb2e599eEvery exit code captured by redirect before any pipe. ⛔ The list is the dispatch's, ⛔ not read off a workflow file.
lint:coveragecheck:entry-guardcheck:upstream-port-paritycheck:bash32-floorturbo run lint --filter=@object-ui/types --forcecheck:vi-mock-override-shapecheck:test-path-rootscheck:cross-repo-closer-outcomecheck:control-bytescheck:new-line-citations --base origin/maincheck:spec-symbolscheck-changeset-presencecheck-changeset-no-majortype-checkpnpm exec vitest run packages/types/4cffe5d9⭐ The pin file is typechecked, not merely run:
packages/types'type-checkincludestsc -p tsconfig.test.json, and--listFilesnames this pin inside that program.Doc Snippet Type CheckandSkill Example Checkare red on this head. They are red onmainitself and name no file this PR touches — ⛔ not this PR's, ⛔ not fixed here.This round's session, in prose so it survives a body
PATCH:https://claude.ai/code/session_01L5xpA5q533BgTTNADibEFt.Patch round 1 — the contract review's FAIL, addressed
Review record: #9349 (comment
5651308316), headf8679ce. ⛔ Nothing about the shipped behaviour was redesigned: the reviewer's own diff (+12 keys, 0 lost, 0 changed over 1,635 roots / 36,670 nodes; accept set 0 mismatches; identity 941/941; spec objects 0 changes) all stands. What was wrong was the sentence, which on this card is half the deliverable.⛔ Defect 1 — the mechanism was misdiagnosed, and the stated limit was false
The callables are not zod
$ZodObjectJITinstances. They are@objectstack/spec's lazy cross-modulenew Proxy(functionTarget, …)wrappers. Re-measured on this head, independently:$ZodObjectJITinstance is not what such a node would be; that parenthetical was false and is corrected in Patch round 2z.globalRegistry.get(proxy)reads the samedescriptionmetadata todayObject.getOwnPropertyNameson one'ownKeys' on proxy: trap result did not include 'prototype'The proxy's
gettrap resolves the real schema and binds any function it returns, sosource.meta()runsreal.meta()and answers the real's entry, while a registry lookup keyed by the proxy reaches only the real's ancestors. ⇒ the docblock's "there is no equivalent route for any other key" was false.Fixed:
carryRegistryMetanow readssource.meta(). Nothing was lost before (all 505 carrydescriptiononly) and nothing changes today — but atitleon a proxied node would have been dropped silently, which is this card's own defect class.⭐ The separate
.descriptionfallback is retired, and not merely as tidying: zod'sdescriptiongetter isglobalRegistry.get(inst)?.descriptionfor the instance it was installed on, so once the accessor is the route the branch is structurally redundant. Measured: 0 nodes on the surface where.descriptionanswers and.meta()?.descriptiondoes not. The pin asserts that zero, so re-adding the branch has to answer it. This also dissolves the reviewer's recorded "prefers the registry view fordescription" note.The same misdiagnosis in the
isZodTypeguard's docblock — prose this PR had consolidated out of both original files — is corrected too, and declared. The guard itself was always right; only its stated reason was wrong.⛔ Defect 2 — the two
$ZodObjectJITsentences in the pinBoth corrected.
isCallableNodeis replaced byisSpecLazyProxy, which probes the proxy invariant (ownKeysover a function target) rather thantypeof, so it cannot conflate the two — with a firing control asserting it answersfalsefor an ordinary node.⛔ Phantom checks — all five now pin what they are named for. ⛔ None deleted.
descriptionmetadata is a callable" — could never be non-empty (0/1,880 proxies have an own entry)titleon a proxied node and measures the carry reproducing it.meta(), with the accessor-vs-map description gap asserted positive so it cannot silently revert_zod.parentand def, snapshotted before either derivation and re-read after both.meta(), so the 497-node proxy population is inside itThe weaker side-effect reading ("the defaults are still there") is kept as its own separate statement rather than deleted — it is worth saying, it just is not what the sentence above it claims.
Ablation — seven legs on the new head, same discipline
Anchor/marker
grep -cplus a moved blob hash, read before any result; restore bygit checkout HEAD -- <abs path>undertrap … EXIT INT TERM, proven bygit diff HEADempty and 3/3 hashes equal to HEAD.globalRegistry.addin place)cloneWithDefdeclaration in the packageidmoved onto the carry list⭐ G, H and I are the three legs round 1 could not fail. Each reddens precisely the assertion the review showed was inert.
Gates re-run at
4cffe5d9lint:coverage·check:entry-guard·check:upstream-port-parity·check:bash32-floor·turbo run lint --filter=@object-ui/types --force·check:vi-mock-override-shape·check:test-path-roots·check:cross-repo-closer-outcome·check:control-bytes·check:new-line-citations·check:spec-symbols·check-changeset-presence·check-changeset-no-major·type-check— all exit 0, captured by redirect before any pipe. Build 0 (130 emitted files verified). Tests 183 files / 4227 passed.pnpm checkand repo-widepnpm lintremain declared to CI.⛔ Recorded, not fixed here (per the review)
The 370-location
descriptionemitter delta (identical at base ⇒ objectui#9086 residue), the site-①lazyarm's pre-existing order-dependence, and the now-dissolved stale-ancestor note.Re-derived, and three numbers moved
The card's counts were from the reviewer's run and ≥2 days old. Re-derived on spec 17.4.0, zod 4.4.3, over every published spec subpath read out of the spec's own
exportsmap:descriptiontitleexternalVocabularyformatxRefxExpressionxEnumDeprecateddefaultidZodDefaultnodes losing that metatitle×9 /externalVocabulary×2)xEnumDeprecatedis present on this surface and the card's six-key list does not mention it. Carrying it emits no key the protocol does not itself publish, so it is inside the dispatch fence rather than a widening of it — but the count is a finding, and it is exactly why the bound below is paired with a census.The two line addresses still resolve: the docblock and the sibling rebuild are where the card said.
The carry set is bounded — and that bound is guarded
CARRIED_REGISTRY_META_KEYSenumerates the vocabulary; ⛔ it is not a blanket spread.idsits onREFUSED_REGISTRY_META_KEYS, refused on a mechanism and not on taste:globalRegistry.add()writes the registry's shared_idmapwhenever the metadata it is handed contains one, so carrying anidwould repoint a global id map at this package's derived node. That is the one part of the old docblock that was correct, and it is kept.⭐ A bounded list drops a new key silently — which is the same "narrower than the protocol" defect this card closes, one key later. So the bound ships with a census that re-derives the key vocabulary over every published spec subpath and goes red when the protocol carries a key on neither list. The bound then costs boundedness and not fidelity.
The docblock
It enshrined "⛔ The description and nothing else" on a rationale about
id— a key that occurs 0 times in the spec's registry metadata on this surface, while the keys it was silent about are the ones actually present. Replaced at both ends: the boundary's header now names the real trade-off (a bounded enumerated carry set versus a blanket spread, withidrefused by name), and the sibling file's header says why its three local copies moved out. No new cross-filepath:linecitation was introduced.⭐ A zod fact this turned up — restated, after two wrong versions of it
True:
node.descriptionandz.globalRegistry.get(node)disagree on a large part of this surface, and the nodes they disagree on are@objectstack/spec's lazy cross-modulenew Proxy(functionTarget, …)wrappers. The proxy'sgettrap resolves the real schema and binds the function it hands back, soproxy.descriptionandproxy.meta()run on the REAL and answer its entry, while a registry lookup keyed by the proxy reaches only the real's ancestors — nothing ever registered the proxy itself.⛔ False, in both of the shapes this section carried:
$ZodObjectJITis not the mechanism and never was.typeof z.object({ k: z.string() })is'object'; everyz.object()carries the$ZodObjectJITtrait and none of them is callable — zod's$constructorreturns plain objects, and the "JIT" names eval-compiled parse code. The trait is therefore neither rare here nor a source of callability, and it separates a proxy from a plain node in neither direction, because the proxy forwards_zodwith the traits inside it. Re-derived by the pin on every run rather than written down here.carryRegistryMetareadssource.meta(), which is the general route for every key; the separate.descriptionfallback is retired because zod'sdescriptiongetter isglobalRegistry.get(inst)?.descriptionon the instance it was installed on. Round 1 fixed the code and left this sentence standing — reconciled in round 2.descriptionmetadata is a callable" — that assertion could never be non-empty. It asserts the accessor-route census, and carriage is proven by the hand-built proxy control.Both sites, or neither — they share one helper
They do share one, so the "say why not" clause does not arise.
cloneWithDef,carryRegistryMeta,internals,isZodTypeandWalkableDefmoved intozod/node-derivation.ts; both walkers import them and neither declares a localcloneWithDefany more. That is asserted mechanically, not by prose — a local re-declaration at either site turns the pin red, because the metadata carry is invisible at the call site and a second copy loses it again with no symptom.⛔ Not shared: the arms. The boundary strips defaults and holds an identity property; the strict face rebuilds unconditionally, because "strict" is a property every node must acquire. Only the three primitives moved.
One extra site inside file ②, named rather than smuggled: its
z.lazyarm builds a freshz.lazy(it must — it replaces the getter) and so carried no metadata either. It now goes through the same carry. The live population of described lazy nodes there is empty, so it is guarded by a hand-built control rather than by a census that would assert nothing.Measured effect
Through the emitter, on the node the card named:
⭐
defaultstays absent on this side deliberately — not substituting an author's omitted keys is decision batch #90 — and the pin asserts the metadata carry did not quietly undo it. No accept set moves; no key the protocol does not publish is emitted; the spec's own objects are never mutated (.meta()clones, which is why the derived node may literally be one of them on the already-optional branch).Ablation — both directions, mutation proven on disk before any result was read
Run from the committed state, so the restore leg has a real
HEADto return to. Neither site resolves throughdist/(the pin imports the two modules by relative source path), so the mutation reaches the assertions without a rebuild — proven by the marker greps rather than assumed.Driven by a script that takes the HEAD blob hash of all three source files up front, refuses to read any result until the mutation is proven on disk, and restores through
git checkout HEAD -- <abs path>under atrap … EXIT INT TERM. "Proven on disk" is twogrep -ccounts on the exact text being swapped (anchor gone, marker present) plus a blob hash that moved — ⛔ never the editor's exit code. "Restored" isgit diff HEADempty and 3/3 blob hashes equal to HEAD — ⛔ never a return code.carryRegistryMetareturnsderivedunmutated7f54e67fad6a → df3be8187332git diff HEADempty, 3/3 hashes matchobjectarm rebuilds unconditionallyf912ad925444 → 5d4ac77c6015cloneWithDef37599bd6266c → bbd257695166git diff HEADempty, 3/3 hashes matchEach leg reddens the assertions it should and no others:
.meta()non-mutation control and theidrefusal — nine in all.cloneWithDef" pin — i.e. re-creating the exact split objectui#9102 was filed about is caught, by name.⭐ .meta() CLONES…: that control carries the carry throughcarryRegistryMeta, so deleting the carry makes it return the target by reference. Reported as observed rather than trimmed to look tidy.Gates
.github/workflows/lint.yml, because objectui's doc and skill gates each own their OWN workflow file, so a list read offlint.ymlis short. The list run at each head is the dispatch's. Every exit code captured by redirect before any pipe. Run atf8679ce, the commit this table was written at.node scripts/check-lint-coverage.mjs(pnpm lint:coverage)node scripts/check-entry-guard.mjs --self-test+ plainnode scripts/check-upstream-port-parity.mjsnode scripts/check-bash32-floor.mjsturbo run lint --filter=@object-ui/types --forcenode scripts/check-vi-mock-override-shape.mjsnode scripts/check-test-path-roots.mjsnode scripts/check-cross-repo-closer-outcome.mjspnpm check:control-bytespnpm check:new-line-citationspnpm check:spec-symbolsnode scripts/check-changeset-presence.mjsnode scripts/check-changeset-no-major.mjspnpm --filter @object-ui/types type-checkturbo run build --filter=@object-ui/typespnpm exec vitest run packages/types/ESLint narrowing, declared. The repo-wide gate is
pnpm lint=turbo run lint; it was run for@object-ui/typesonly. Three readings, so the narrowing is a measurement and not an omission: (1) the receiving population read from ESLint's own config is 4916 files (eslint . --format json, counted from the JSON, not guessed); (2) the four files this PR touches lint with 0 errors, 0 warnings; (3)eslint.config.jsconfigures no type-aware linting — noprojectService, noparserOptions.project, notsconfigRootDir— so this diff cannot move the verdict on any file it does not itself contain. CI runs the full farm.⛔
pnpm check(the CLI self-check, which needs@object-ui/cli...built first) was NOT MEASURED locally and is declared to CI; it reads the repository's own schema tree, which this diff does not touch.pnpm check:lint-coverageexited 254 —Command not found, the script is spelledlint:coverage. That is NOT MEASURED, not a failure; re-run under the correct name, it exits 0.scripts/pm/os-verify-lock.shand noscripts/pm/dispatch-gates.mjsof its own. ⭐ Corrected in round 2: the heavy-verify lock is a container lock (/tmp/os-heavy-verify.lock), not a repo one, and its entry point lives in the sibling checkout — every build, type-check and test run in round 2 went through it.Scope
Nothing outside
packages/types. ⛔@objectstack/specuntouched. ⛔ No part of objectui#9086 reverted or reopened — its pin (imported-defaults-describe-9034.test.ts) runs green unchanged. ⛔content/docs/releases/untouched. Changeset isminorand names the emitted-surface change.Acceptance notes
Observations from this lane, filed nowhere and not acted on here:
noted, not filed:the objectui#9088 rest-less-tuple identity-property carve-out (def.rest === nullcompared againstundefined) is still live and is excused in this pin exactly as objectui#9034 excused it. It has its own open card; the next PR to touch that tuple arm is the successor.noted, not filed:thedescribedDefaultspopulation the objectui#9034 pin describes as 2024 re-derives as 2028 on this head. The pin does not assert the literal number (it asserts> 500), so nothing is red — the figure is in its prose only. Successor: whoever next edits that pin's header.noted, not filed:packages/types/src/__tests__/strict-authoring-face-8345.test.ts:469,484carries the same false$ZodObjectJITequation, outside this diff. Line 469: "could not see the 20$ZodObjectJITinstances on this face"; line 484: "How many of those answeredtypeof 'function'(JIT instances)". Both say JIT instance where they mean callable, and on this surface a callable is a spec proxy while a$ZodObjectJITinstance is an ordinary object. The prose is wrong; the census and itsfunctionTypedcontrol are right and measure the callables they meant. ⛔ Not edited here — it is outside this PR's diff and touching it would widen the change, and a false sentence in a test docblock is not one of the threefindingclasses. Successor: the next PR to touch that pin, or its header.Generated by Claude Code
Generated by Claude Code