…nd repair the pin that read its shape (objectui#9659)
The objectui#8344 `superRefine` clause on `defineNodeComponentUnion` could no
longer fire once ruling A on objectui#8572 made `ChatbotSchema.body` an ADR-0049
tombstone: zod skips a check once the schema it wraps has refused, and the arm
refuses every defined `body`, while the clause returns early on `undefined`.
Measured on the issue tree of a nested refusal (the arm's tombstone, no
clause-shaped issue) against a lit control built from the pre-retirement record
arm, which produces the clause's issue and no tombstone.
The accept set does not move: byte-identical verdicts over 432 schema-catalog
documents plus a 60-case sweep of three chatbot faces x ten `body` shapes x both
depths.
The wrapper's other role is kept. The read-back assertion is untouched and the
`fill is LIVE` leg is re-pointed from the wrapper's shape to the installation.
A second leg was vacuous: `names body in the refusal` read the serialized issue
blob for `"body"`, which the parent card slot supplies for any refused child. It
now reads the child's own `body` path and asserts the remedy reaches the author
at depth, with the three documents that made it vacuous pinned as controls.
Also annotates the plain `chatbot` row of the objectui#9256 narrowing table as a
one-sided reading, and amends the two prose sites this change falsified.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UanLVj6xvbS6puBCewLr8L
Fixes #9659
Clause-②: no
Carries the three in-scope non-blocking residuals an isolated at-tier contract review named on PR objectui#9639 (card objectui#8572). ⛔ That PASS is adopted verbatim and is not reopened; none of these changes it.
Every reading below was re-taken on this branch. ⛔ No figure is inherited from the card.
1 — a pin asserted the shape of a clause that can no longer fire
Reviewer's reading reproduced, with a lit control on the same instrument and the same corpus.
Document: a corpus-valid
chatbotseed plusbody: { model, temperature }, nested incard.body[]. Instrument: the flattened issue tree ofAnyComponentSchema.safeParse, discriminating by issuecodeat the child's ownbodypath — the arm's tombstone isinvalid_type/expected: never, the clause re-issuesBaseSchemaCore.shape.body's own refusal, which isinvalid_union.body.0.bodybody.0.bodyThe lit control is a real mutation, not an argument:
ChatbotSchema.bodyrestored toz.record(z.string(), z.unknown()).optional(), proven on disk (marker count 1 to 0, replacement 0 to 1, blob hash moved), rebuilt, proven to have reacheddist/zod/index.zod.js, then restored with the blob hash back to HEAD's andgit diff HEADempty. ⇒ the zero above is a reading, not a broken instrument.Why it cannot fire, enumerated over the guard's own branches. The clause runs only if the wrapped union parse SUCCEEDED,
type === 'chatbot', andbody !== undefined. Measured againstChatbotSchemadirectly:⇒ "reaches the clause" and "has a
bodyto check" are disjoint, with no input in between.Changed.
defineNodeComponentUnioninstalls the component union as it stands. The read-back assertion that announces a zod which stopped keeping its option array by reference is untouched. Thefill is LIVEleg is re-pointed from the wrapper's SHAPE (not.toBethe bare union, pluschecksof length exactly 1) to the INSTALLATION: slot 0 holds the component union and not the pre-objectui#8344 base shape.Clause-②: no, and it is MEASURED
432 schema-catalog documents through
safeValidateSchema, plus a 60-case sweep (three chatbot faces x tenbodyshapes x both depths) throughAnyComponentSchema. Verdict lists before and after:diffexit 0, byte-identical. A clause that cannot fire cannot be narrowing anything, in either direction.2 — a leg that could not fail
Vacuity measured, ⛔ not asserted. The old leg read
JSON.stringify(issues)for the substring"body". The parentcard.body[]slot puts"body"in the issue path for ANY refused child, so:body.0.bodyicon(size: 'huge')metric-cardchatbotmissingmessageschatbotwith a recordbodyThree documents with nothing wrong at
bodysatisfy the old assertion. ⇒ it was a green light wired to nothing.Changed. The leg now reads the child's OWN path, and asserts the remedy reaches the author at DEPTH and not only at the root — a reading no other leg in the file makes. The three vacuity documents are pinned alongside it as CONTROLS that assert nothing at
body.0.bodyAND that the old assertion holds anyway, so the discrimination is tested rather than described in prose.Ablation legs
A — the thing the leg is supposed to catch. The
chatbotbodytombstone stops naming the remedy key (`requestBody`tosome other key). On disk: old phrase 1 to 0, new phrase 0 to 1, blob hash moved; rebuilt; proven indist.B — the fill reverts to the pre-objectui#8344 base shape (
nodeUnionOptions[0]installsBaseSchemaCore, with the read-back's expectation moved with it so the failure is silent rather than thrown). Runtime confirms slot 0 is no longer the component union.Restores proved, both legs. Blob hash equal to HEAD's and
git diff HEADempty for the mutated path; atrap ... EXIT INT TERMrestore hook with an absolute repo root in every script; rebuilt after each restore and the marker re-checked indist, so no mutated byte survives into a later measurement.3 — a narrowing row that lost its same-face control
Measured on the
bodychannel: ACCEPTED onchatbot-enhancedandchatbot-floating, REFUSED onchatbot.The plain
chatbotrow of the objectui#9256 table listschildrenonly. On the two twin faces that still means "bodyheld out and live", with the same-face LIVE CONTROL proving it. On the plain face it no longer does — objectui#9639 had to re-point that control at a twin precisely because the plain face refusesbodynow, for objectui#8572's reason rather than this table's.Changed: a line, ⛔ not a rewrite. A comment above the three chatbot rows records that the row is one-sided, that the absence of
bodymeans "not this card's to assert" rather than "still live here", and that thebodyhalf of the plain face is pinned bynode-recursion-point-8344.test.ts. ⛔ No assertion in that table moved, and adding'body'to the plain row is explicitly warned against: those assertions read objectui#9256's message, and the plain face's tombstone carries objectui#8572's — the row would go red on a true statement.Changeset grade:
patch, MEASUREDpackages/typespublishesdist, so the question is whether published bytes move. Built at this branch's base and at this head, same tree, same command:diff -rqexit 0), so the delta above is this change and not build noise.⇒ published bytes move, so an empty frontmatter would have been a false claim. Graded
patchrather thanminor, because the accept set is measured unmoved. ⛔ Nevermajor(this repo's fixed group).A pending changeset this change falsified, amended in the same pass
check:changeset-claimsnamed 14 pending declarations that describe a file this change touches. Re-read; one had gone false..changeset/8344-node-recursion-point-redirect.mdsays "the arm the recursion point installs carries a check that a nestedchatbotnode'sbodystill fits the node slot", and its objectui#8572 amendment then says "the installed arm's check ... cannot rot". It has now moved, and that body publishes VERBATIM into the CHANGELOG. A second AMENDED note records it and states what did NOT move (the nested verdict, the accept set, "narrows at all 109, widens at none"). The bump level is unchanged (minortominor);check:changeset-overwriteis report-only and flags the edit by design.Two source prose sites this change falsified are amended in place, ⛔ not rewritten:
zod/index.zod.ts's "wraps it rather than replacing it", andzod/base.zod.ts's note on the loose parameter bound (kept for the reason it records, with an AMENDED note that the exclusion set the pin reads is now empty).Gates
Exit codes captured to disk BEFORE any pipe, every one of them.
check-governed-queue-guard --teston this PR's five paths: NOT GOVERNED, 5 paths against 5 governed surfaces, none matched.Self-scan beyond the gate, over every changed file:
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'exit 1, zero hits.git grepfor the chatbot tombstone's own text reads 0 where the truth is 1 — the string wraps across concatenated literals — so every marker count here is taken on the whitespace-flattened, literal-joined text with both a lit and an absent control.Surface increment
The claim comment declared three paths. Two more are in the diff and both are required rather than opportunistic:
content-channel-family-d-9256.test.tsis where item 3 lands (item 3 is in scope; the declared surface under-listed it), andzod/index.zod.tscarries one clause that THIS change falsified.Serial constraint, re-taken
Re-enumerated over the live open-PR set immediately before the first write: 13 open PRs, where the dispatch table listed 6. Intersection with
packages/types/src/zod/base.zod.tsandpackages/types/src/__tests__/node-recursion-point-8344.test.ts: EMPTY. Re-confirmed that PR objectui#9742 holdspackages/types/src/base.tsand this branch holdspackages/types/src/zod/base.zod.ts— different files, disjoint on disk. PR objectui#9735 had already landed (this branch is cut from the commit that merged it).Acceptance notes
@object-ui/core. It was VERIFIED first-hand — see the report on card objectui#9659 — and is handed to the seat to file, with dedupe words.🤖 Generated with Claude Code
https://claude.ai/code/session_01UanLVj6xvbS6puBCewLr8L
Generated by Claude Code