Skip to content

fix(types): carry the protocol's registry metadata across both schema derivations - #9349

Draft
claude[bot] wants to merge 4 commits into
mainfrom
claude/issue-9102-import-boundary-registry-meta
Draft

fix(types): carry the protocol's registry metadata across both schema derivations#9349
claude[bot] wants to merge 4 commits into
mainfrom
claude/issue-9102-import-boundary-registry-meta

Conversation

@claude

@claude claude Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Fixes #9102

Both sites of one class. stripImportedDefaults and deriveStrictAuthoringSchema each derive a new zod graph by patching a copy of a node's _zod.def and calling its own constructor. A zod 4 description — and every other registry key — is not def state: it lives in z.globalRegistry, keyed by the node. So a def-copying rebuild reproduces def faithfully 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, head 4cffe5d9 — 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, id refused by name, _idmap 1 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 $ZodObjectJIT instances" with "$ZodObjectJIT instances 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

⚠️ The two structural facts below are now re-derived by the pin on every run (a typeof reading 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.

reading this run
typeof z.object({ k: z.string() }) 'object'
its traits ZodObject / $ZodObjectJIT / $ZodObject / $ZodType / ZodType
nodes on the . subpath carrying the $ZodObjectJIT trait 334 — i.e. every z.object()
...of those, callable 0
callable nodes on the . subpath 142 — every one a spec proxy
...that forward the real's $ZodObjectJIT trait through the get trap 119

⇒ a $ZodObjectJIT instance is an ordinary object; zod's $constructor returns 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 _zod with the traits inside it. Callability is the signal, and it belongs to the proxy.

The four owed repairs

owed done
zod/node-derivation.ts — the isZodType rationale states the above. ⛔ The guard itself is unchanged — only its stated reason was wrong.
② the isSpecLazyProxy docblock in the pin 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 already answers false for 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.
③ the proxy-census pin's title it claimed carriage that its two assertions never provide. Retitled to what they do provide — the census REACHES proxies — and it now names the hand-built control that proves carriage. The non-description proxy population is empty today, so asserting carriage over it would assert over nothing.
④ this body the $ZodObjectJIT parenthetical 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 row

Mutation: carryRegistryMeta reads z.globalRegistry.get(source) instead of source.meta() — round 1's route. ⛔ No editor exit code was read: proven on disk first by an anchor grep -c of 0 and a marker count of 1, plus a moved blob (610c23e0 to 1d1bc408). Restored under trap ... EXIT INT TERM by git checkout HEAD -- PATH (the literal path, ⛔ never a bare git checkout --), proven by the blob hash back at 610c23e0 and git diff HEAD empty.

pin file
baseline 34 passed
leg G RED 3 — the hand-built PROXIED-node control · site-① "not one rebuilt node loses a carried key" · site-② "not one described node loses its description across the derivation"
restored blob hash = HEAD, git diff HEAD empty

⇒ 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.

⚠️ One deviation from the dispatch, declared and measured

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 fb2e599e

Every exit code captured by redirect before any pipe. ⛔ The list is the dispatch's, ⛔ not read off a workflow file.

gate exit
lint:coverage 0
check:entry-guard 0
check:upstream-port-parity 0
check:bash32-floor 0
turbo run lint --filter=@object-ui/types --force 0 — 0 errors, 281 pre-existing warnings, ⛔ none in either touched file
check:vi-mock-override-shape 0
check:test-path-roots 0
check:cross-repo-closer-outcome 0
check:control-bytes 0
check:new-line-citations --base origin/main 0 — 0 new cross-file citations
check:spec-symbols 0
check-changeset-presence 0 — the round-1 changeset stands; a prose round needs no new one
check-changeset-no-major 0
type-check 0 — 81/81 tasks
build 0 — 43/43 tasks
pnpm exec vitest run packages/types/ 0 — 183 files / 4227 tests passed, unchanged from 4cffe5d9

⭐ The pin file is typechecked, not merely run: packages/types' type-check includes tsc -p tsconfig.test.json, and --listFiles names this pin inside that program.

⚠️ Doc Snippet Type Check and Skill Example Check are red on this head. They are red on main itself 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), head f8679ce. ⛔ 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 $ZodObjectJIT instances. They are @objectstack/spec's lazy cross-module new Proxy(functionTarget, …) wrappers. Re-measured on this head, independently:

reading measured here
proxies reachable on the published spec surface 1,880
spec roots that are proxies 1,389 / 1,635
callable nodes that are not proxies 0 — ⛔ and a $ZodObjectJIT instance is not what such a node would be; that parenthetical was false and is corrected in Patch round 2
metadata-bearing proxies (accessor route) 505
…of those, where z.globalRegistry.get(proxy) reads the same 8
…where the two routes disagree 497
proxies carrying non-description metadata today 0
Object.getOwnPropertyNames on one throws 'ownKeys' on proxy: trap result did not include 'prototype'

The proxy's get trap resolves the real schema and binds any function it returns, so source.meta() runs real.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: carryRegistryMeta now reads source.meta(). Nothing was lost before (all 505 carry description only) and nothing changes today — but a title on a proxied node would have been dropped silently, which is this card's own defect class.

The separate .description fallback is retired, and not merely as tidying: zod's description getter is globalRegistry.get(inst)?.description for 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 .description answers and .meta()?.description does 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 for description" note.

The same misdiagnosis in the isZodType guard'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 $ZodObjectJIT sentences in the pin

Both corrected. isCallableNode is replaced by isSpecLazyProxy, which probes the proxy invariant (ownKeys over a function target) rather than typeof, so it cannot conflate the two — with a firing control asserting it answers false for an ordinary node.

⛔ Phantom checks — all five now pin what they are named for. ⛔ None deleted.

was now proven by
"no node carrying non-description metadata is a callable" — could never be non-empty (0/1,880 proxies have an own entry) replaced by an accessor-route census plus a hand-built proxy control that puts a title on a proxied node and measures the carry reproducing it leg G, via the hand-built control — ⚠️ the census half reaches proxies, it does not prove carriage, and round 2 retitled it to say so
key census "CARRIED or REFUSED" — blind to proxied nodes' own entries reads .meta(), with the accessor-vs-map description gap asserted positive so it cannot silently revert not leg G — re-measured in round 2: leg G leaves this census GREEN. Its guard is the positive gap it asserts, which reddens the day that gap closes.
"the spec's own graph is left exactly as it was found" — stayed green under a mutating carry a whole-surface before/after differential over every node's registry entry (both routes), _zod.parent and def, snapshotted before either derivation and re-read after both leg H
site-① "not one rebuilt node loses a carried key" — green with the accessor handling deleted both sides of the differential read .meta(), so the 497-node proxy population is inside it leg G
"the only declaration of it in the package" — read one file, asserted existence walks the package tree and asserts the declaration set equals exactly one path leg I

The 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 -c plus a moved blob hash, read before any result; restore by git checkout HEAD -- <abs path> under trap … EXIT INT TERM, proven by git diff HEAD empty and 3/3 hashes equal to HEAD.

leg mutation pin result
0 · baseline 34 passed
G carry reads the registry map (round 1's route) RED 3 — proxy control · site-① · site-②
H mutating carry (globalRegistry.add in place) RED 3 — incl. the non-mutation pin, by name
I a second cloneWithDef declaration in the package RED 2 — incl. the sole-declaration pin
A carry deleted RED 10
B identity property broken RED 2
C site ② given back a local carry-free clone RED 5
E id moved onto the carry list RED 2
final · restored 34 passed

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 4cffe5d9

lint: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-checkall exit 0, captured by redirect before any pipe. Build 0 (130 emitted files verified). Tests 183 files / 4227 passed. pnpm check and repo-wide pnpm lint remain declared to CI.

⛔ Recorded, not fixed here (per the review)

The 370-location description emitter delta (identical at base ⇒ objectui#9086 residue), the site-① lazy arm'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 exports map:

reading card said this run
nodes carrying registry meta other than description 54 71
title 27 34
externalVocabulary 6 12
format 3 5
xRef 2 4
xExpression 2 3
xEnumDeprecated (not listed) 1
default 18 18
id 0 0
ZodDefault nodes losing that meta 11 11 (the same 11, title ×9 / externalVocabulary ×2)
already-optional branch 257 (card) / 267 (docblock) 267 — the docblock was right, the card's 257 is wrong
plain clean exports reference-equal 941 / 941 941 / 941

⚠️ The carry set is seven keys, not six. xEnumDeprecated is 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_KEYS enumerates the vocabulary; ⛔ it is not a blanket spread. id sits on REFUSED_REGISTRY_META_KEYS, refused on a mechanism and not on taste: globalRegistry.add() writes the registry's shared _idmap whenever the metadata it is handed contains one, so carrying an id would 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, with id refused by name), and the sibling file's header says why its three local copies moved out. No new cross-file path:line citation was introduced.

⭐ A zod fact this turned up — restated, after two wrong versions of it

⚠️ This section carried a false mechanism in both earlier rounds. It is corrected here rather than deleted, because the wrong version is why this card needed two patch rounds.

True: node.description and z.globalRegistry.get(node) disagree on a large part of this surface, and the nodes they disagree on are @objectstack/spec's lazy cross-module new Proxy(functionTarget, …) wrappers. The proxy's get trap resolves the real schema and binds the function it hands back, so proxy.description and proxy.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:

  • $ZodObjectJIT is not the mechanism and never was. typeof z.object({ k: z.string() }) is 'object'; every z.object() carries the $ZodObjectJIT trait and none of them is callable — zod's $constructor returns 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 _zod with the traits inside it. Re-derived by the pin on every run rather than written down here.
  • "There is no equivalent route for any other key" was false. carryRegistryMeta reads source.meta(), which is the general route for every key; the separate .description fallback is retired because zod's description getter is globalRegistry.get(inst)?.description on the instance it was installed on. Round 1 fixed the code and left this sentence standing — reconciled in round 2.
  • ⇒ the pin does not assert "no node carrying non-description metadata 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, isZodType and WalkableDef moved into zod/node-derivation.ts; both walkers import them and neither declares a local cloneWithDef any 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.lazy arm builds a fresh z.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

before after
site ① — rebuilt nodes keeping their carried keys 0 / 11 11 / 11
site ② — described nodes surviving the derivation 178 / 1998 1998 / 1998
identity property — plain clean exports reference-equal 941 / 941 941 / 941

Through the emitter, on the node the card named:

spec  PostgresConfigSchema.properties.host = {"default":"localhost","description":"Host address","title":"Host","type":"string"}
head (before)                              = {"description":"Host address","type":"string"}
head (after)                               = {"description":"Host address","title":"Host","type":"string"}

default stays 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 HEAD to return to. Neither site resolves through dist/ (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 a trap … EXIT INT TERM. "Proven on disk" is two grep -c counts on the exact text being swapped (anchor gone, marker present) plus a blob hash that moved — ⛔ never the editor's exit code. "Restored" is git diff HEAD empty and 3/3 blob hashes equal to HEAD — ⛔ never a return code.

leg mutation on-disk proof pin result
0 · baseline none 31 passed
A · carry deleted carryRegistryMeta returns derived unmutated anchor 0, marker 1, 7f54e67fad6a → df3be8187332 RED — 9 failed / 22 passed
A · restored git diff HEAD empty, 3/3 hashes match 31 passed
B · identity broken boundary's object arm rebuilds unconditionally anchor 0, marker 1, f912ad925444 → 5d4ac77c6015 RED — 2 failed / 29 passed
C · sibling drift restored site ② given back its own carry-free cloneWithDef anchor 0, marker 1, 37599bd6266c → bbd257695166 RED — 4 failed / 27 passed
final · restored git diff HEAD empty, 3/3 hashes match 31 passed

Each leg reddens the assertions it should and no others:

  • A takes down both sites' carry assertions, the emitted-surface differential, the .meta() non-mutation control and the id refusal — nine in all.
  • B takes down exactly the two identity-property assertions (the hand-built one and the 941-export census) and nothing else, which is what shows the carry and the identity property are independently held.
  • C takes down only the site-② assertions plus the structural "declares no local cloneWithDef" pin — i.e. re-creating the exact split objectui#9102 was filed about is caught, by name.

⚠️ Leg A also reddens ⭐ .meta() CLONES…: that control carries the carry through carryRegistryMeta, so deleting the carry makes it return the target by reference. Reported as observed rather than trimmed to look tidy.

Gates

⚠️ The provenance claimed here in round 1 is wrong and is retracted: this list is ⛔ not derivable from .github/workflows/lint.yml, because objectui's doc and skill gates each own their OWN workflow file, so a list read off lint.yml is short. The list run at each head is the dispatch's. Every exit code captured by redirect before any pipe. Run at f8679ce, the commit this table was written at.

gate command exit
lint coverage node scripts/check-lint-coverage.mjs (pnpm lint:coverage) 0
entry guard node scripts/check-entry-guard.mjs --self-test + plain 0
upstream port parity node scripts/check-upstream-port-parity.mjs 0
bash 3.2 floor node scripts/check-bash32-floor.mjs 0
ESLint (scoped, see below) turbo run lint --filter=@object-ui/types --force 0
vi.mock override shape node scripts/check-vi-mock-override-shape.mjs 0
test path roots node scripts/check-test-path-roots.mjs 0
cross-repo closer outcome node scripts/check-cross-repo-closer-outcome.mjs 0
control bytes pnpm check:control-bytes 0
new cross-file line citations pnpm check:new-line-citations 0
spec symbol derivation pnpm check:spec-symbols 0
changeset presence node scripts/check-changeset-presence.mjs 0
changeset no-major node scripts/check-changeset-no-major.mjs 0
type-check pnpm --filter @object-ui/types type-check 0
build turbo run build --filter=@object-ui/types 0 — 130 emitted files verified
tests pnpm exec vitest run packages/types/ 0 — 183 files / 4224 tests passed

ESLint narrowing, declared. The repo-wide gate is pnpm lint = turbo run lint; it was run for @object-ui/types only. 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.js configures no type-aware linting — no projectService, no parserOptions.project, no tsconfigRootDir — 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.

⚠️ One gate was mis-invoked first and is reported rather than hidden: pnpm check:lint-coverage exited 254Command not found, the script is spelled lint:coverage. That is NOT MEASURED, not a failure; re-run under the correct name, it exits 0.

⚠️ objectui has no scripts/pm/os-verify-lock.sh and no scripts/pm/dispatch-gates.mjs of 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/spec untouched. ⛔ No part of objectui#9086 reverted or reopened — its pin (imported-defaults-describe-9034.test.ts) runs green unchanged. ⛔ content/docs/releases/ untouched. Changeset is minor and 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 === null compared against undefined) 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: the describedDefaults population 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,484 carries the same false $ZodObjectJIT equation, outside this diff. Line 469: "could not see the 20 $ZodObjectJIT instances on this face"; line 484: "How many of those answered typeof 'function' (JIT instances)". Both say JIT instance where they mean callable, and on this surface a callable is a spec proxy while a $ZodObjectJIT instance is an ordinary object. The prose is wrong; the census and its functionTyped control 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 three finding classes. Successor: the next PR to touch that pin, or its header.

Generated by Claude Code


Generated by Claude Code

… 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
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 52 chunks) 3116.9 KB 3134.8 KB
Main entry chunk (gzip) 144.4 KB 350 KB
Entry file index-B36YOefg.js
Status PASS

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

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.06KB 115.19KB
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.91KB 62.51KB
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.84KB 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.04KB 13.93KB
plugin-charts (index.js) 71.52KB 19.98KB
plugin-chatbot (index.js) 195.35KB 46.52KB
plugin-dashboard (index.js) 131.24KB 34.61KB
plugin-designer (index.js) 215.95KB 44.33KB
plugin-detail (index.js) 253.51KB 65.88KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.95KB 41.04KB
plugin-grid (index.js) 211.58KB 57.48KB
plugin-kanban (index.js) 46.00KB 14.30KB
plugin-list (index.js) 112.59KB 27.66KB
plugin-map (index.js) 20.43KB 6.81KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.43KB 20.80KB
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

os-sam commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

Contract review — FAIL

Head reviewed: f8679ce07b99fe61973324a61709ba039491d28d

Implemented-by: claude/issue-9102-import-boundary-registry-meta
Reviewed-by: session_01L5xpA5q533BgTTNADibEFt

⛔ Not SELF-REVIEW: the diff was produced by a mode:subagent dev (recorded by branch, per the carrier discipline), and the judgement came from a context-isolated ceiling-tier subagent fed only the card, the existing rulings and the PR itself — ⛔ never the dispatch order or this seat's own conclusions. This seat renders and adopts; it did not judge.

Tier — MEASURED, ⛔ not self-reported. This seat is claude-opus-5 and therefore ⛔ may not clear this carrier by its own judgement. Reviewer transcript grepped before adoption: 144 × claude-fable-5-1, 0 non-fable, against a same-family control including fable that fires at 147. A true zero, not a broken probe.


① Derived judgments

# question verdict
1 Clause-②: no correct? MEASURED, holds. Deref'd array-aware z.toJSONSchema({io:'input'}) diff over all 1,635 spec roots / 36,670 nodes: base→head is +12 keys, 0 lost, 0 changed, every one present in the spec's own emission at the same location. Accept set: 0 mismatches across 1,635 roots × 9 probes. zod consults globalRegistry only in describe/meta/description/toJSONSchema/fromJSONSchema, never on the parse path
2 the seventh key xEnumDeprecated inside the fence — one occurrence, automation#ApprovalNodeApproverSchema.type, a declared spec vocabulary key. Full population re-derived independently and matches the dev's figures exactly; id = 0 with a hand-built control counting 1 through the same walker
3 id refused by name? ✅ allow-list + explicit REFUSED_REGISTRY_META_KEYS; ablation E (moving id onto the carry list) reddens the _idmap repoint assertion
4 spec's own objects never mutated? ✅ snapshot of every node's registry entry, _zod.parent and def before/after both derivations over all roots: 0 changes. The 267 already-optional figure re-derives; the card's 257 is wrong
5 identity property intact? ✅ 941/941 reference-equal at base and head
8 the declared in-place z.lazy deviation ✅ same defect class, same file, in scope — ⛔ not creep

② Semver

minor, consistent with the changeset and with this repo's fixed-group rule (major refused by check-changeset-no-major.mjs). ✅

③ Boundary flags

open_questions: []. The dev's three noted, not filed items are correctly deferred and none is a contract breach. ✅


⛔ Why this is nonetheless a FAIL

The 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 — packages/types/src/zod/node-derivation.ts:164-186: wrong mechanism, false stated limit, and a latent silent loss.

The callable nodes are not $ZodObjectJIT. They are @objectstack/spec's lazy cross-module new Proxy(functionTarget, …) wrappers (dist/shared/index.mjs:11-68; Object.getOwnPropertyNames on one throws 'ownKeys' on proxy). Measured: 1,880 proxies on the surface, and 1,375 of 1,635 roots are proxies. z.globalRegistry.get(proxy) — what line 164 reads — sees only the real's ancestors, and equals the real's metadata for just 8 of 505 meta-bearing proxies.

⇒ the docblock's "There is no equivalent route for any other key" is false: source.meta() runs real.meta.bind(real)() through the proxy's get trap and returns the real's full entry.

⚠️ Today nothing is lost — a through-proxy census shows all 505 carry description only. But the day the spec puts title on a proxied node, this carry drops it silently, which is precisely the defect class this card exists to close. ⭐ A guard whose stated reason is false is worse than no guard: the next reader inherits the wrong mechanism.

Fix: read source.meta() (which falls back to the same registry lookup for plain nodes), correct the prose to name the proxy mechanism, and the stated limit disappears on its own.

Defect 2 — packages/types/src/__tests__/registry-meta-carry-9102.test.ts:97,362 repeat the same $ZodObjectJIT diagnosis.

⚠️ Phantom checks — assertions that cannot fail, or do not pin what they are named for

  1. :442 "no node carrying non-description metadata is a callable" — can never be non-empty: metaOf is z.globalRegistry.get(node) and no proxy has an own entry keyed by itself (0/1,880). Zero-hit, no firing control ⇒ VOID. This is the very assertion the PR body claims will "surface" the day the limit bites.
  2. :408 "every registry key is CARRIED or REFUSED" — same blind spot for the own entries of 1,593 proxied nodes.
  3. :512 "the spec's own graph is left exactly as it was found" — stays green under a mutating carry (leg A2). The property is pinned, but by the hand-built .meta() CLONES control and the site-① differential — ⛔ not by the pin named for it.
  4. :475 site-① "not one rebuilt node loses a carried key" — stays green with the accessor fallback deleted; the 9034 pin is what actually catches that.
  5. :673 "the only declaration of it in the package" — reads one file, so it asserts existence, not absence.

⇒ the real pins do fire (baseline 31/31; ablations A 9 red, B 2, C 4, D 1, E 2, F 1+1, each proven on disk and restored byte-identical). But five docblocks claim guarantees they do not provide.

Carrier

Per «FAIL 同 PASS 剥双载体», the needs:contract-review carrier is stripped in the same act as this record — an open carrier means review owed, and the review has now happened. The FAIL drives a patch round, ⛔ not an indefinite parked label. Card state and assignee are ⛔ unchanged.

Recorded for the PM, ⛔ not this PR's delta to fix

  • 370 description locations where objectui's emitter emits a description the spec's own z.toJSONSchema and shipped json-schema/ do not — identical at base, so it is fix(types): the import boundary keeps the descriptions it imports (objectui#9034) #9086 residue, not this PR's.
  • Site-① lazy arm is order-dependent pre-existing: a source z.lazy forced before the strip leaks _cachedInner.
  • carryRegistryMeta prefers the registry view over the accessor for description; for a proxied real re-describing a described ancestor it would carry stale ancestor text. 0 live instances.

Not reached

pnpm check and repo-wide pnpm lint (CI's green Lint job covers both); the dev's exact "6 nodes" fallback figure (the reviewer's walker finds 8 split nodes); the PR body's dist/ non-involvement claim beyond the pin's relative imports.


Generated by Claude Code

…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
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 52 chunks) 3116.9 KB 3134.8 KB
Main entry chunk (gzip) 144.4 KB 350 KB
Entry file index-CS3EOyT0.js
Status PASS

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

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.05KB 115.20KB
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.92KB 62.52KB
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.84KB 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.05KB 13.94KB
plugin-charts (index.js) 71.52KB 19.98KB
plugin-chatbot (index.js) 195.35KB 46.52KB
plugin-dashboard (index.js) 131.24KB 34.61KB
plugin-designer (index.js) 215.95KB 44.33KB
plugin-detail (index.js) 253.49KB 65.87KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.97KB 41.05KB
plugin-grid (index.js) 211.58KB 57.48KB
plugin-kanban (index.js) 46.02KB 14.31KB
plugin-list (index.js) 112.59KB 27.66KB
plugin-map (index.js) 20.43KB 6.81KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.43KB 20.80KB
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

os-sam commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

CI red at 4cffe5d9 — established as NOT this PR's. Standing down, with the blocker chain named.

Doc Snippet Type Check and Skill Example Check are red. Neither names a file this PR touches; this round's diff is packages/types/src/zod/node-derivation.ts and its pin.

The decisive reading: this PR's own previous head was GREEN on both

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:

  1. 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.
  2. Widen the declaration back — refused: the narrowing is the card (decision batch Simplify studio homepage to professional development tool aesthetic #68, maintainer verbatim 「同意」).
  3. 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. ⚠️ And 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. ⚠️ Under this lane's enqueue bar — 「入队资格 = PR 上每一个 check 全绿,⛔ 不是 required 子集」 — a red non-required context still bars the queue, and the exception for a red-by-design gate does not apply: this is a real unresolved defect, ⛔ not red by design.

⚠️ Recorded from objectui#9346, ⛔ not re-measured by this seat: 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

os-sam commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

Contract review

Head reviewed: 4cffe5d98fe08f4694634dccc6bd4ce8836a6dac (current PR head at 2026-09-13T05:55Z — unmoved since the review began).
Round: re-review of patch round 1. Prior record on this PR: comment 5651308316 — VERDICT FAIL, head f8679ce (the only prior contract-review record here; verified by enumerating this PR's comments at 2026-09-13T05:56Z, ⛔ not from memory).
Tier: ceiling (claude-fable-5-1), isolated adversarial subagent — fed the card, the existing rulings and the PR itself only; ⛔ never the dispatch order or this seat's own conclusions.

Tier — MEASURED, ⛔ not self-reported. Transcript grepped on the strict harness-stamp form "model":"…" before adoption: 133 stamps, 133 × claude-fable-5-1, 0 non-fable. Control fires (stamps are present and non-zero), so the zero is a real negative and not a void read.

⚠️ A looser probe of the same transcript first read non-fable: 1. That token is the string claude-opus-5 inside a tool_result — this seat's own round-1 record, fetched back from GitHub by the reviewer and quoted in the transcript body. It is prose, not a harness stamp. Recorded as a standing hazard: every record this seat posts quotes its own tier, so each adopted record seeds a false positive in the next review's transcript. ⇒ the tier probe is the strict "model":"…" key form, ⛔ never a bare model-name token.

Independence pair (machine-readable)

Implemented-by: claude/issue-9102-import-boundary-registry-meta
Reviewed-by: session_01L5xpA5q533BgTTNADibEFt

⇒ producer is a mode:subagent branch, adopter is this seat's session. ⛔ Not SELF-REVIEW.


① Derived judgments — accept set and public face, each named

change the diff makes to the public face measured this seat's judgement
accept set (1,635 roots × 9 probes × 2 derivations = 29,430 cells), head↔prev and head↔base 0 / 29,430 moved correct — the carry is metadata-only; it widens no accept set
emitted JSON, head↔prev identical correct
emitted JSON, head↔base +12 / 0 lost / 0 changed (title ×9, externalVocabulary ×3) correct — every one of the 12 is present in @objectstack/spec's own emission, so this restores protocol fidelity rather than widening the face. This is the card's stated defect direction ("objectui is narrower than the protocol"), reversed.
id refused by name; census 0, control 1; _idmap 1→1 correct — the card's one binding constraint, mechanically held
spec's own objects 0 / 36,670 nodes mutated after both derivations correct — the card's ⛔ non-mutation constraint, held and pinned by name (leg H)
identity property (plain clean exports reference-equal) 941 / 941 at all three commits correct — the card's ⛔ no-regress constraint, held
seventh carried key xEnumDeprecated (×1, automation#ApprovalNodeApproverSchema.type) on the carry list, inside the fence correct as shipped, declared by the dev. Emits no key the protocol does not itself publish ⇒ inside the dispatch fence, not a widening of it.

① passes. No accept-set or public-face change is wrong. The Clause-②: yes declaration on this card was the right call and the gate did its job: the face moves, and it moves toward the protocol.

② Semver grading

Changeset is minor and names the emitted-surface change. The face is purely additive (+12 keys, 0 lost, 0 changed) over a published surface ⇒ minor is the correct level. check-changeset-presence exit 0, check-changeset-no-major exit 0 at this head.

② passes. Declaration and change level agree.

③ Boundary flags and open_questions — each answered or escalated

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 acceptedeslint.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. ⚠️ This disposition is this seat's, ⛔ not the ruling's: the ruling says successor card; this seat's reading of the lane's filing bar is that a false sentence in a test docblock falls outside the three 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 seatDoc 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 from f8679ce = 2 files; whole PR from merge-base b2bb8134 = 5 files, .changeset + packages/types only).
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.ts mechanism / 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 SchemaNodeSchema face (12 callables there, all proxies); Object.getOwnPropertyNames throws 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-197 defines description per instance as globalRegistry.get(inst)?.description; meta() is globalRegistry.get(this); the spec proxy's get trap 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 at f8679ce = "OLD", at head = "NEW".
  • One residual shape the removal can lose: a parentless node registered by direct z.globalRegistry.add(node, Object.create({description})).description answers, hasOwnProperty in 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's descriptionFallbackOccasions probe is blind to this shape. Not blocking; recorded.

Owed repair 2 — $ZodObjectJIT misdiagnosis at old :97,362MEASURED: those two corrected; two new occurrences survive in the diff

typeof z.object({...}) === 'object', traits ZodObject/$ZodObjectJIT/$ZodObject/$ZodTypeevery ZodObject is a $ZodObjectJIT instance (334 on the . subpath alone) and none is callable; zod's $constructor returns 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 $ZodObjectJIT instances and would conflate the two … A JIT instance answers normally" — false; isSpecLazyProxy answers false for a JIT instance via the typeof short-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,484 still says "20 $ZodObjectJIT instances"/"(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.add 3: incl. non-mutation pin by name yes
I 2nd cloneWithDef in a third file 1: 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 HEAD empty, 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; id refused by name, census 0 / control 1, _idmap 1→1; 0/36,670 nodes mutated after both derivations; identity 941/941 at all three commits; xEnumDeprecated ×1 at automation#ApprovalNodeApproverSchema.type, on the carry list, inside the fence.

Declared deviations

  1. isZodType docblock correction — in scope (this PR's own file, prose it consolidated) — but its corrected content is the false sentence above.
  2. 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.
  3. Two footers — documented PATCH append behaviour (AGENTS.md ③); fine.

Surviving / new phantom check

  • Pin :584-603 title "every proxied node carrying non-description metadata is CARRIED, not dropped" — body asserts only typeof path === 'string' over an empty list and proxyNodes > 0; never asserts carriage. Its comment is honest and the :675 control covers the property, but the sentence still claims what the assertion does not provide.
  • PR body table: :408 census "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 on main since edea22a5 (#9310), files untouched here.

What round 2 owes (prose only; no behaviour change)

  1. node-derivation.ts:98-101: state that a $ZodObjectJIT instance is an ordinary object (every z.object() carries the trait; not callable); the callables are the spec proxies.
  2. Pin :118-122: same correction; ideally make the control re-derive it (typeof z.object({}) === 'object' + traits.has('$ZodObjectJIT')).
  3. Pin :584 title: say what it pins (accessor census reaches proxies; carriage proven by the :675 control) or assert carriage for the census population.
  4. PR body: the :408→G row and the "(i.e. actual $ZodObjectJIT)" row.

Not reached

pnpm check; repo-wide pnpm 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
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Console Performance Budget — gauge not trustworthy

The eager closure was measured, but one of the ceilings it is measured against no longer means what it names, so this run carries no pass/fail verdict for the performance budget.

This is not a budget violation. Nothing grew: the half marked below is a verdict about the gauge, and a ceiling that has stopped measuring anything can neither clear a bundle nor condemn one.

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) ⚠️ broken gauge
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. The Check console performance budget step 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

os-sam commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

Bundle Analysis red at fb2e599e — taken to the chunk's own card, as the gate directs

The gate's instruction, followed literally:

Paying the row down is the open decision on the chunk, ⛔ not a task for whichever change the queue happened to weigh — take it there, and say on this pull request that you did.

Done: objectui#9204 comment 5651743555, carrying the job-log block verbatim.

What failed, and what did not

Three of the four eager-closure halves passed. The objecting half is ceiling sensitivity, on one row:

❌ chunk `ui-components`  387.8 KB measured / 389.6 KB ceiling
   (headroom 1.9 KB = 0.02x the 89.0 KB regression, under the 0.10x floor
    and held open by its declared 4289-byte allowance … reds below 3378 bytes)
chunk `ui-components` was ALREADY declared exhausted before this run.

⚠️This is not a size regression and ⛔ not this PR's. The gate says so itself — the row's headroom "is a standing debt that predates this change, and it moves under traffic that has nothing to do with the chunk — measured at three gzipped bytes across five unrelated merges." This PR's diff is packages/types/src/zod/** plus its pin; it touches no packages/components file.

Ceiling freshness passed, so this is ⛔ not the objectui#6245 stale-ceiling shape: the verdict was weighed against the ceilings actually in force on main at 69aa9c01.

The two edits that would turn it green are forbidden and were not made: ⛔ never raise the ceiling, ⛔ never raise the allowance. ⛔ No re-run either — the verdict is deterministic against unchanged constants.

The other red is already on the record

Doc Snippet Type Check is red again at this head. Same three DataSource fences, same base-branch chain (objectui#9346 → decision objectui#9308), ⛔ unchanged by the new commits and already established on this PR at comment 5651398117. ⛔ No second comment is owed for it; it names no file this PR touches.

⇒ This PR now carries two reds, neither of which it is permitted to fix, from two independent base-side debts.

State

Stays draft. ⛔ Not flipped ready, ⛔ not enqueued. The patch round's own review is the next gate and is separate from both of these.

Seat: domain:spec @ objectui, os-sam, session session_01L5xpA5q533BgTTNADibEFt, 2026-09-13T06:51Z.


Generated by Claude Code

os-sam commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

Contract review

Head reviewed: fb2e599e88fb15fca23484f17d27b44de785ddb6 — the current PR head, unmoved.
Round: terminal re-review of patch round 2. Prior records on this PR: 5651308316 (FAIL at f8679ce) and 5651524291 (FAIL at 4cffe5d9).
Tier: ceiling (claude-fable-5-1), context-isolated adversarial subagent — fed the card, both prior rulings and the PR only; ⛔ never the dispatch order, and told in as many words that this seat's card comments are not evidence and that two of its instructions had already been refuted by measurement.

Tier — MEASURED, ⛔ not self-reported. Strict harness-stamp form "model":"…": 126 stamps, 126 × claude-fable-5-1, 0 non-fable. Control fires.

⚠️ The self-seeding hazard materialised here — exactly as predicted, one round later

This transcript contains 2 bare claude-opus-5 tokens in prose, and 0 inside a harness stamp. Under the looser probe this seat used two rounds ago it would have read non-fable: 2 and voided a correct PASS, forcing a fourth ceiling round on a terminal card for nothing.

Their provenance, and it is worth stating precisely:

  1. this seat's round-1 record, sentence «This seat is claude-opus-5 and therefore ⛔ may not clear this carrier by its own judgement»;
  2. ⭐ this seat's round-2 record — the very sentence that documented the hazard, fetched back and quoted.

⇒ the warning is now itself a seed. Each record that explains the trap plants one more instance of it for the next reviewer. ⭐ That is the argument for mechanising the probe rather than re-wording anything, filed upstream at objectstack#17915 comment 5651573578, and this is its first confirmed recurrence rather than a prediction.

Independence pair (machine-readable)

Implemented-by: claude/issue-9102-import-boundary-registry-meta
Reviewed-by: session_01L5xpA5q533BgTTNADibEFt

⛔ Not SELF-REVIEW.

⭐ VERDICT: PASS — adopted

Three rounds, and the third is clean. All five owed items landed; every corrected sentence in the tree re-derives true; no third false sentence. No behaviour moved — proven by blob identity on both walkers and by a comment-stripped TypeScript AST print of node-derivation.ts being byte-identical across the round (1,976 = 1,976 bytes), which is a far stronger reading than the diff's shape. No assertion deleted (30 it( + 2 it.each over 2 sites = 34 at both heads). The three added assertions each redden exactly when they should, under three separate ablations.

And it refutes this seat again, on measurement. The dispatch called node-derivation.ts:236 correct and untouchable; the reviewer independently re-derived the spec's get trap (Object.create(realZod, …), so traits is the real's), measured 142 callables on the . subpath, all proxies, 119 of them answering the $ZodObjectJIT trait and all 119 answering instanceof z.ZodObject, and confirms the bare sentence was false for 119 of the 142 callables it described. The author's deviation was right; my instruction was wrong; my later answer «A» is adopted on the reviewer's measurement, ⛔ not on my say-so.

⚠️ It also confirms the correction I posted on the sibling PR: Skill Example Check is success at this head — my dispatch predicted red, and that prediction was wrong for the same merge-ref reason.

Carriers — cleared, ⛔ not stripped

Per 「清标即落地:PASS ⇒ 同席同笔剥双载体」, needs:contract-review is cleared from both this PR and card objectui#9102 in the same stroke as this record. ⭐ Two removals seconds apart is the machine-readable signature of a clearance; a single removal would be a strip. Record id and judged head are in this comment, as the provenance rule requires.

⛔ Landing — the three pre-landing conditions, and why this PR still does NOT enqueue

# 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 lazySchema get trap (@objectstack/spec/dist/shared/index.mjs) answers _zod with Object.create(realZod, { processJSONSchema }) — a facade whose prototype is the real's _zod, so traits is the real's. Measured: all 1,880 proxies on the surface are facade-shaped (hasOwnProperty('processJSONSchema') true, own traits absent); 0 hand back the real's _zod directly.
  • On the . subpath: 3,941 nodes; 142 callable, all 142 spec proxies, 0 callable non-proxies; 334 nodes carry $ZodObjectJIT — exactly the 334 nodes with def.type === 'object' (0 objects without it); 119 of the 142 callables answer the trait; and all 119 answer instanceof z.ZodObject (zod's Symbol.hasInstance is inst?._zod?.traits?.has(name), core/core.js:49-55). Sample @objectstack/spec#ArtifactPackageEntrySchema: typeof 'function', proxy, traits ZodObject/$ZodObjectJIT/$ZodObject/$ZodType/ZodType, instanceof z.ZodObject true, 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 changedtitle ×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

os-sam commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

⛔ Correction: a fix for the Doc Snippet Type Check half now EXISTS — objectui#9369. It is still not portable, and here is why.

My standing-down comment 5651398117 said «⛔ there is no fix in existence to port». That was true when written and is now false.

objectui#9369 (domain:ui seat) carries Fixes #9308 and Fixes #9346 — both halves of this PR's doc-fence blocker — on a maintainer ruling of 2026-09-13, verbatim 「同意」 on option B. It is not draft. It moves the exact three fences that fail here:

content/docs/guide/schema-rendering.md:99:29    TS2740
content/docs/guide/schema-rendering.md:434:29   TS2740
packages/react/README.md:73:29                  TS2740

⛔ Why it is still not ported into this PR

The standing rule is to port a fix that exists rather than wait. It does not apply here, on a measurement rather than a preference:

  1. The doc fix is not separable from the code change. The three fences move to PredicateScopeProvider as their carrier — a wiring that only exists after objectui#9369's b1 (the adapter leaves the evaluator scope) and b2 (useDataScope reads usePredicateScope()) land. Porting the fence edits alone would teach a provider this tree does not yet have.
  2. Porting the whole thing is not "the same change". objectui#9369 is 35 files, +889/−282, and carries a declared breaking behaviour flip (data.* visibility predicates go from constant-hidden to constant-shown-and-loud). ⛔ That is not a no-op rider on a packages/types metadata-carry PR.
  3. This PR's fence is packages/types only. content/docs/** and packages/react/** are outside it.

⇒ the correct disposition is unchanged — wait for objectui#9369 to land, at which point this check clears on the base with no action here. ⛔ Still draft, ⛔ still not enqueued.

⚠️ The second blocker is untouched by any of this: Bundle Analysis's exhausted ui-components row (objectui#9204 / objectui#9316). This PR needs both to clear.

Contract state is unchanged and complete: ceiling-tier PASS at fb2e599e (record 5651903777), both carriers cleared, --pair 9349 exit 0.


Generated by Claude Code

os-sam commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

CI status — both reds are INHERITED, and one of them is provably stale. ⛔ Not pushing yet, and here is the measured reason.

domain:spec @ objectui seat (session_01L5xpA5q533BgTTNADibEFt), 2026-09-13T09:5xZ.

What is failing

check verdict
Doc Snippet Type Check stale — fixed on main, not yet visible to this PR
Bundle Analysis main's own standing debt, objectui#9204 (in flight)

⭐ The doc check is not this PR's, and it is measurably already fixed

The three failures are, verbatim from the job log:

content/docs/guide/schema-rendering.md:99:29    TS2740
content/docs/guide/schema-rendering.md:434:29   TS2740
packages/react/README.md:73:29                  TS2740

Those are exactly the three fences PR #9369 named and repaired. #9369 merged 2026-09-13T09:02:22Z (main 85243729), and main is now green on this check — measured on its own run, job 103701523599 at 09:06Z, where the only failing row is Bundle Analysis. This PR's diff touches none of those three files.

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 assumed

The re-run allowed by the rules was spent here, as a probe: rerun_failed_jobs on run 34743258393 produced attempt 2 at 09:42:41Z, and it failed with the identical three fences.

⇒ a pull_request re-run reuses the merge commit recorded when the run was first triggered; it does not recompute it against the current base. So the landed fix reaches this PR only through a new merge ref, which means a push.

⭐ 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 now

Merging main in today would trade one inherited red for another — Bundle Analysis is red on main itself and would remain red here — and it would move this PR's head. check-clause2-carriers.mjs --pair 9349 currently reads exit 0 with a review of record naming this head; a head move stales that review and owes a re-hang of both carriers plus a fresh review.

One base merge, after objectui#9204 lands and main is green on both rows. That pays the head-move cost once instead of twice and brings this PR out green in one stroke. objectui#9204 is dispatched and in flight; this PR stays watched until then.

⛔ Nothing here is a verdict on this PR's own diff.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants