Skip to content

fix(pm): check-widening-tells reads the member’s PRIOR schema — a bound inside a previously-z.unknown() bag is not a new key - #19153

Draft
os-steve wants to merge 7 commits into
mainfrom
claude/issue-19099-widening-tell-prior-schema
Draft

os-steve wants to merge 7 commits into
mainfrom
claude/issue-19099-widening-tell-prior-schema

Conversation

@os-steve

@os-steve os-steve commented Sep 19, 2026

Copy link
Copy Markdown
Collaborator

Fixes #19099

Clause-②: no — one file under scripts/pm/**. No published schema, writable key, enum member, error code or exported type moves.

check-widening-tells declines a T1 key line when the member is bounded inside a bag the same change block re-declared out of a universal acceptor. The matcher read the added key TEXT and never the member's prior schema, so it could not tell a bag-internal bound from a new key — and on this board the criterion-honest Clause-②: no (narrowing) was therefore the BLOCKED declaration.


⛔ Body replaced by the seat at 2026-09-19T02:53Z, for head 306f2020be — the THIRD version, and the two it replaces were both proved wrong

This PR has been through two at-tier FAILs. Each time the body asserted a justification the next review falsified, so it is rewritten rather than patched; ⛔ what each version claimed is preserved as HISTORY at the end. The delivering dev writes a body once and ⛔ does not patch it — the rewrite is the seat's act.

The arc is worth stating, because it is the finding: the decline needs to know that two lines name the same place, and the first two answers were both proxies for identity that are not identity.

round what "same place" meant how it broke
1 the same NAME removed anywhere in the block a same-named acceptor removed elsewhere certified a bag at a different path
2 the same opener + head TEXT + indent two keyless parents spell their opener identically — discriminatedUnion arms, tuple members, .or()/.and() arguments — so they read as one place
3 frames carry the KIND of the line they stand on, and samePlace requires CONTEXT on both sides

Why round 3's answer is identity rather than another proxy: a CONTEXT line is one line shown twice, so a frame standing on it is the same construct on both sides by construction. An ADDED line against a REMOVED line is two lines, and ⛔ no amount of shared text makes them one construct. The re-declared bag itself never reaches that test, because framePlace already drops the frame's own line.

The unreadable flag — two SOUND discriminations, ⛔ neither needing a regex lexer

Round 2's second blocking finding was that the flag's dominant real trigger was undisclosed: not the division operator the round-2 body named — which occurs 0 times in a 250-diff corpus — but */ when a hunk begins inside a JSDoc. That regressed a landed #18234 decline and made 21 of 88 readable stacks unreadable.

Both are now decided rather than flagged:

  • A * / pair outside a block comment, on a walk that has opened none of its own and raised no flag, can only mean the hunk BEGAN inside a comment ⇒ the frames its body pushed are discarded and the walk restarts after it.
  • A lone / with no second / left on its line cannot be a regex literal — regex literals never span lines ⇒ it is arithmetic and raises nothing.

One residual is deliberately KEPT and named in the file header: a real regex literal carrying a bracket on a line the walk crosses still refuses, so an honest #18234 narrowing under such a line keeps telling. It is 0 of 250 on the corpus. ⭐ Closing it would need the preceding token — a guess inside a reader whose whole safety property is that it makes none — so this is the honest remainder, ⛔ not a shortcut.

Measured through FOUR gates

BASE 1047fe1016 · ROUND0 b3944e3865 · ROUND1 fa8321b2fa (the head round 2 failed) · FIXED 306f2020be. Every probe is a git-emitted diff from real files in throwaway repos — ⛔ never a hand-assembled hunk. Exit codes captured before any pipe.

probe BASE ROUND1 FIXED
A1ddiscriminatedUnion; arm a is a CONTEXT strictObject({ losing the acceptor, arm b respelled to the SAME text on an ADDED line and gaining filter: strictObject({ newKey }) 4, 1 row 0 4, same row
A1 — arm b respelled by a trailing comment alone, head text unchanged 4 0 4
A1b CONTROL — arm b untouched ⇒ two blocks 4 (2 rows) 4 (2) 4 (2)
A1c CONTROL — .or() chain, head text differs 4 (1) 4 (1) 4 (1)
J1 — an honest #18234 narrowing under a 4-line JSDoc 0 4 ⟵ the regression 0
J3 — division in the leading context (round 2's disclosed trigger) 0 4 0
J4 — JSDoc opened AND closed inside the hunk 0 4 0
J2 CONTROL — the same narrowing with no comment at all 0 0 0
J5 — the DISCLOSED RESIDUAL: a real regex carrying a brace in the leading context 0 4 4
⭐ THE LIVE PAIR — #19095's real diff 4 at component.zod.ts:2689, :2692 0 0

Every round-1 pin also holds on FIXED (V1, V3, indent-only, frames-only, the regex controls, the two-block control, spend-at-another-place 4/4/4 and spend-at-the-same-place still declined).

14 new --self-test cases carry those git-emitted hunk bodies verbatim. Self-test 563 → 577; the #19099 battery floor 53 → 67.

Blast radius — REVERSED, not merely bounded

Both walkers imported directly, 250 real commit diffs, packages/spec/src non-test .ts:

reading value
T1-shaped added lines 96
readable under ROUND1 68
of those, newly unreadable under FIXED 0
stacks unreadable under ROUND1 that are readable again 21 — exactly the 21 round 2 measured

Answer differential for the firing-only readers enclosingDelimiter and inParameterList, over 431,750 side-lines (1,881 sides of the 250 diffs): 0 disagreements, both ROUND1→FIXED and ROUND0→FIXED. ⇒ no removed parameter earns budget it did not earn. ⚠️ The word is answer-identical, ⛔ not byte-identical — round 2's F6 was right that the earlier body overstated it, and this is the measurement behind the corrected word.

No other tell moved. Four-way sweep of the same 250: BASE 85 rows / ROUND0 83 / ROUND1 83 / FIXED 83. FIXED is identical row-for-row to BOTH prior heads on all 250, and differs from BASE on exactly one diff — 9bb059dbfe, which is #19095. ⚠️ The 21 recovered stacks moved no row because none sits in a block carrying a removed universal acceptor: the J1 regression was real and corpus-invisible, exactly as round 2 said.

⚠️ INSTRUMENT REACH for those zeros: 250 commits in a shallow clone (1,314 commits, 2026-09-06 onward), packages/spec/src non-test only. Known targets outside it: commits before that date, merge commits, objectui's mirror packages/types/src/zod/**, and the keyless-arm and JSDoc-start shapes themselves — 0 of 250 exhibit either, so the corpus bounds the NOISE this repair could add and says nothing about the frequency of the leak it closes.

Ablation — eight legs, ⛔ all red

Same-path half dropped 10/577 · the bag reading's unreadable refusal 2/577 · the indent half 2/577 · the absent-evidence guard 1/577 · the CONTEXT requirement 3/577 (the two keyless-arm pins plus the reader's own case) · the leading-comment reset 2/577 · the division discrimination 2/577 · the frame discard 1/577. Each restore proven by blob == HEAD and an empty git diff HEAD.

⛔ Two incidents and one green leg, reported rather than tidied away

⭐ These are in the body on purpose. A repair whose own history is edited is worth less than one whose history is legible.

  1. Commit d603731b4f does not parse. Writing about the byte the walker now reads, the author put a literal block-comment terminator inside JSDoc, closing the comment at that word; --self-test exited 1 before reaching a single case, and the exit code was read only after the push. Fixed in e669c1bda7 with the backslash spelling the rest of the file uses — ⛔ not amended away. Independently confirmed: node --check on that commit's file exits 1 at line 1116; on this head it exits 0.
  2. One ablation leg ran while its new pin was still UNCOMMITTED. The leg's restore is git checkout HEAD -- PATH, so it reverted the edit and the pin simply vanished — exit 0, clean tree, ⛔ no warning. Re-applied, committed, re-run from the committed state. AGENTS.md prescribes committing the fix first for exactly this reason.
  3. One leg came back GREEN, and that is how a missing pin was found. After a VOID leg (the mutation tool refused it — the replacement text was a substring of the anchor, so its count could not rise) was re-spelled, dropping the !unreadable guard on the leading-comment reset broke nothing ⇒ the guard was pinned by NOTHING. Recorded loudly rather than quietly re-run; a case reading exactly that was added in 306f2020be (+2 / −1, one new t(), and the leg now fails 1/577.

Verification

dispatch-gates --commands29 families, all exit 0, each code recorded before any pipe, reconciled with --ran: 29 derived, 29 run, 0 NOT-MEASURED, 0 UNRUN. check:pm-clause2-carriers exit 0; check-clause2-carriers --pair 19153 exit 0. eslint . --no-inline-config at this head: 6890 files, 0 errors, 0 warnings. Control bytes over the edited file: no match; check:nul-bytes exit 0.

STALE TREE discharged with a ROSTER, not a count. The gate-family roster derived on the branch tree and on origin/maindiff exit 0, identical 29-line roster. ⛔ origin/main was deliberately NOT merged: the reviews' readings are anchored at the base, and no family was added for this path in that window.

NOT MEASURED, declared

objectui's mirror under any matcher · the historical frequency of the keyless-arm shape and of JSDoc-start acceptor pairs (0 of 250 for both) · the place limits of rewritesExistingOpener and respellsExistingClosedSetBinding — ⛔ probed by nobody in any round, stated as a gap rather than a verdict · merge-queue convergence.

Attribution (prose, because the edit side of a PR-body write always appends its own footer): this body was written by the domain:spec PM seat in session session_01AmH9bKvGoLjiY86Q4Z3og2; the change was implemented by the dispatched dev on branch claude/issue-19099-widening-tell-prior-schema.


HISTORY — the two justifications this body used to carry, both REFUTED

  1. Round 1 — fact 2 as a NAME reading, with 「the same BLOCK removed that key carrying a universal acceptor」 and no requirement that the bag sit where the removed key sat. Refuted by three git-emitted diffs.
  2. Round 1 — 「this one leaves no 'but this member widens' case open」. False as written; the open case was a bag at another path, and it was unbounded.
  3. Round 2 — the place as opener + head TEXT. Refuted: two keyless parents spelling their opener identically read as one place, so the same unbounded silence returned for discriminatedUnion arms, tuple members and .or()/.and() arguments.
  4. Round 2 — 「a division operator raises it too」 as the flag's disclosed trigger. Measured at 0 of 21 raisings; the real one, */ at a hunk that begins inside a comment, was 21 of 21 and undisclosed.
  5. Round 2 — 「byte-identical」 for the firing-only readers. Textually false (enclosingDelimiter's body differs by two lines); the true and now-stated claim is answer-identical, over 431,750 side-lines.

Generated by Claude Code

…that was the universe

A key declared inside a bag whose removed value was `z.unknown()` adds no
spelling: that bag admitted every value under every key, so whatever the
replacement admits it admitted before. The tell read the added key TEXT and
never the member's prior schema, so it could not tell a bag-internal bound
from a new key — and because the bag members SPENT #16943's replacement
budget in patch order, the rows it reported were the bag's own re-declared
keys.

Two facts, both carried by the block: a `{` the hunk showed open whose own
line names a key, and a removed line declaring that key as a universal
acceptor. Read before the budget, so a bag member neither fires nor spends.
`declaresUniversalAcceptorKey` now reads the rest of its own removed run for
the end of the value, which is #18234's stated overturn condition.

Claude-Session: https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2
Co-authored-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

Contract review — the gate-loosening half

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: b3944e3865b96a9b1905f7741e7aa7c0940ed30a

No gate mechanically required this review. Clause-② is no, check-clause2-carriers --pair 19153 exits 0, and scripts/pm/** is not on the governed register. The seat ordered it because this PR loosens the very control that stops a seat from under-declaring Clause-②, and because a gate that refuses too little is the dangerous direction. That judgement is now paid for.

Reviewer worktrees at head and at the merge base, removed afterwards; shared checkout untouched. Lineage verified independently (git log --format='%h %p'); the API head.sha matches; head blob 1a416f894c36… equals the diff's own index line.

VERDICT: FAIL

The arithmetic is right; the identity model behind fact ② is what leaks.

the new decline CAN swallow a real widening. Three git-emitted diffs (⛔ not hand-assembled hunks) where the BASE gate's CLI exits 4 and the HEAD gate's CLI exits 0 with 「1 judged against a declared surface (no widening tell)」 — each adding a spelling to a strictObject an author could not write before.

⛔ F1 — BLOCKING — fact ② is NAME identity inside the block, not PATH identity

replacesUniversalAcceptorKey(stack[f].head, removedTexts) compares only keyedPropertyName. ⇒ a same-named acceptor removed ANYWHERE in the block certifies a bag at a DIFFERENT path. The stated justification — 「every value the new bag admits was admitted before」 — is false the moment the bag does not sit where the removed key sat.

probe base head what the author gains
V1filter: z.unknown() moved INTO its options: strictObject({…}) bag as filter: strictObject({ newKey }), the bag's closer respelled so git emits ONE block exit 4 (T1 x.zod.ts:7 newKey) exit 0 options.filter.newKey — a spelling a strict object gained
V3 — sibling bag meta loses filter: z.unknown(), TOP level gains filter: strictObject({ newKey }), one block exit 4 (:8) exit 0 a top-level strict-object spelling
V2 — CONTROL exit 4, 2 rows exit 4, 2 rows closer untouched ⇒ git emits TWO blocks ⇒ loud on both

Plus harness variants X5/X5s (boundary rewrite across two schemas), X6, X9 (two keys on one line), X22 — all base ≥1 row, head 0 rows.

⇒ the hole needs the removal and the differently-placed bag in one block, which happens whenever the bag's closer line changes in the same edit — an ordinary refactor shape, not a contrivance.

Why this is worse than the #16943 quiet direction it resembles: that budget is bounded — one unit per removed line, and V1's base still fired because the unit ran out. This decline silences every member inside the bag, unbounded.

⛔ F2 — BLOCKING — a regex literal can hold a closed { open, and the decline then swallows a genuinely NEW OUTER key

enclosingDelimiters lexes no regex literal and pops type-blind (a ) pops a {). A regex with net ≥ +2 unmatched openers inside the re-declared bag consumes the bag's real closers.

  • V4 (git-emitted): removed filter: z.unknown(); added filter: strictObject({ name: z.string().regex(/^\{\{/) }) and then brandNew: z.string() at the OUTER level. BASE exit 4 (:5, :7); HEAD exit 0. Harness X23 (/^\(\{/) identical.
  • Controls that stay loud on head: one unmatched { (X8b), and a balanced token regex /\{\{\w+\}\}/ (X24) — brandNew fires in both.

Reach on the tree: every one-line .regex(/…/) literal in non-test .ts under packages/spec/src at 1047fe1016115 literals, 0 bracket-unbalanced as the walker reads them. ⚠️ INSTRUMENT REACH: .regex( call sites only. Known targets outside it: regex literals bound to consts, ones used inside .refine, multi-line regexes, and objectui's mirror.

⚠️ #18488 already names regex blindness for readToCloser — which has a type check and an unreadable flag. enclosingDelimiters has neither. The blindness is pre-existing; what this PR changes is its consequence: before, a decline needed a ( frame with a function-head text; now { + a key-naming head + a same-named removal suffices.

F3 — noted, disclosed, sound, but a real second behaviour move

The run reading also feeds #18234's spend path. X17: removed filter: z.unknown() / .optional(), wrapped, added filter: z.union([A, B]).optional(), → base 1 row / exit 4, head 0 rows / exit 0. Subset-of-universe, same key, budget-bounded (one unit) ⇒ adds no unbounded silence, and it is subject to the same name-identity limit as F1. Not present in the corpus.

What VERIFIED — and it is most of the PR

  • F4 — the dangerous direction of the run reading is CLOSED on every probe: .refine, .brand(), .transform(), .catch(), .pipe(), a narrowing step skipped over on the way to .optional(),, .describe( whose argument wraps, last member with no comma, a block comment hiding the narrowing on its continuation, a second key line, empty run, never-terminating value — all answer null, members keep telling.
  • F5 — 「read BEFORE the budget」 is pinned. The reviewer's own ablation (make a declined member also spend) fails 4/542.
  • F6 — all eight controls reproduce, and the real fix(spec): the object-grid arm takes the page-size accept set the view arm rules (#19046) #19095 bytes: BASE exit 4 at :2689/:2692, HEAD exit 0. ⚠️ One case-name nit: the T2 case reports 2 on head but 3 on base — the head also declines the T1 opener line mode: z.enum([ inside the bag, which the case name does not say.
  • F7 — both author legs reproduce exactly (2/542 and 6/542), plus six reviewer legs; every leg reds, restore proven each time. ⭐ One reviewer leg first crashed on an empty stack (exit 1, no failure line) — recorded as void, re-spelled, re-run, not counted as a pass.
  • F8 — no other tell moved: base vs head over 248 real commit diffs, 19,530 vs 19,528 rows, and exactly one diff differs9bb059dbfe (fix(spec): the object-grid arm takes the page-size accept set the view arm rules (#19046) #19095), the two live rows. ⚠️ REACH: shallow clone, 1,313 commits, 2026-09-06 → 2026-09-19; the decline's preconditions reach 5 real diffs and changed 1. Outside: commits before 2026-09-06, objectui's mirror.
  • F9 — the tree numbers are right (130 / 99 / 31 / 24 / 7), identical with and without test files. Prose nit: the header's 「.describe( opening on the key line」 holds for 4 of those 5automation/flow.zod.ts:211 opens .describe( on its continuation line.
  • F10 — self-test 542 head / 510 base, 32 = the battery floor, no case tautological.
  • F11 — the PR's own diff through the gate it edits exits 0 with 「1 NOT MEASURED — no declared surface covers it」. The framing is right: that zero is evidence about no surface, and the script says so itself.

NOT MEASURED, declared

How many historical Clause-②: yes declarations this tell forced · objectui's mirror under either matcher · regex literals outside one-line .regex( calls · real-world frequency of F1's block shape beyond the 5 corpus diffs that meet the preconditions (0 of 5 exhibit it).

The repair, in one line

Facts ① and ② as implemented are necessary but not sufficient for 「the removed value was the universe of THIS bag」: ② needs a same-path reading (at minimum the removed key line and the frame's line at the same indentation/nesting inside the block), and the walker feeding ① needs the regex / type-blind-pop limit either fixed or turned into a null the way readToCloser already does.

⛔ The PR stays a draft and ⛔ does not enter the queue. F1 and F2 go back to the delivering dev. ⛔ needs:contract-review was never hung on this pair and is ⛔ not being hung now just to strip it — this record is the act.

Implemented-by: claude/issue-19099-widening-tell-prior-schema
Reviewed-by: session_01AmH9bKvGoLjiY86Q4Z3og2


Generated by Claude Code

…says when it guessed

The at-tier review failed the first cut on two blocking findings, both
reproduced here from git-emitted diffs before anything was written:

F1 — `replacesUniversalAcceptorKey` compared `keyedPropertyName` alone, so a
same-named acceptor removed ANYWHERE in the change block certified a bag at a
different PATH. A `filter: z.unknown()` lifted into a sibling
`options: strictObject({…})` bag — one change block whenever the bag's closer
is respelled in the same edit — silenced `options.filter.newKey`, a spelling a
strict object had just gained. Unlike #16943's bounded budget this decline
silences every member inside the bag at once. The identity is now a PLACE: the
frames the hunk shows open where each line begins, compared opener for opener
and head text for head text, plus the two lines' indentation. #18234's SPEND
path reads the same predicate and takes the same place.

F2 — `enclosingDelimiters` lexes no regex literal and pops type-blind, so
`.regex(/^\{\{/)` inside the re-declared bag ate the bag's own closers and a
genuinely new OUTER key read as a member inside it. The walk now reports
`unreadable` the way `readToCloser` already does, and every reader that
SUPPRESSES a tell refuses when it is set; the firing-only readers are unmoved.

Also: the T2 control's case name now states what it measures (3 rows on the
prior matcher, 2 here), and the header's gloss on the 5 refused wrapped
acceptors no longer claims all five open `.describe(` on the key line.

Claude-Session: https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/l and removed size/m labels Sep 19, 2026
The lift-into-a-sibling and indent-only shapes both differ in indentation, so
neither pinned the frameset comparison on its own. This git-emitted pair puts
the removal and the new bag at the same indent inside different parents, so the
frames the hunk shows open are the only evidence that tells them apart.

Claude-Session: https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2
Co-authored-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

Contract review — round 2, on the reworked head

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: fa8321b2faa181693f740fa55626e32b5040d16b

Lineage verified independently (139f7ae13f → b3944e3865 → 027cba0918 → fa8321b2fa); API head.sha matches; four detached worktrees (head / merge base / prior head / origin/main), all removed; shared checkout untouched before and after. Probe diffs are git-emitted from real files in throwaway repos — ⛔ never hand-assembled hunks. Every exit code captured before any pipe.

VERDICT: FAIL

The place predicate is TEXTUAL, so two keyless parents whose opener lines read identically are one place — a real widening is still declined, unbounded. And the unreadable flag's dominant real-world trigger is not the disclosed division operator but the */ of a hunk that begins inside a JSDoc, which regresses a LANDED #18234 decline in the commonest hunk shape on this tree.

⭐ Both findings are new. They were found by attacking the repaired reading, ⛔ not by re-running round 1's probes — those all now behave correctly (§F3).

⛔ F1 — BLOCKING — the FRAMES half compares opener+head TEXT, never PROVENANCE

Round 1 closed name identity. What replaced it compares opener + head text + indentation — so two keyless parents whose opener lines read identically are ONE place: union / discriminatedUnion arms, tuple members, .or() / .and() arguments.

probe (ONE git-emitted change block) BASE round-1 head FIXED
A1ddiscriminatedUnion; filter: z.unknown() removed from arm a; arm b tightened z.object({strictObject({ and gains filter: strictObject({ newKey }); arm a's closer gains .describe('arm a') 4, 1 row newKey 0 0, 0 rows
A1 — same, arm b's opener respelled by a trailing comment instead 4 0 0
A1b CONTROL — arm b's opener untouched ⇒ git emits TWO blocks 4 (2 rows) 4 (2) 4 (2)
A1c CONTROL — .or(strictObject({ chain, head text differs 4 (1) 0 4 (1)

Mechanism, dumped through the API on A1d. The removed filter line's frames both come from a CONTEXT line (arm a). newKey's enclosing frames carry a strictObject( pair from an ADDED line (arm b). framePlace drops the bag's own-line pair; samePlace then compares opener + head + indent (' ' on both) ⇒ they agree ⇒ the decline fires.

arm b is a strictObject that could not accept filter before and accepts filter.newKey now. The bag's own key line spends the removal's unit and every member inside is declined — exactly the unbounded silence round 1 named, moved from the keyed-parent class to the keyless one.

Repair direction (the reviewer states it, ⛔ does not prescribe an implementation): a frame from a CONTEXT line is the same construct on both sides; a frame from a removed line versus an added line is never the same construct unless it IS the re-declared bag. The head text cannot carry that distinction — the line's kind can.

⛔ F2 — BLOCKING — the flag's real trigger is undisclosed, and it REGRESSES a landed decline

J1, an honest #18234 narrowing: pagination: z.unknown().optional()pagination: z.union([A, B]).optional(), with a 4-line JSDoc above the key so the hunk's leading context is * Doc line two. / */ / name: z.string(),.

BASE round-1 head FIXED
J1 exit 0 exit 0 exit 4, 1 row

The */ is read as a bare / because inBlockComment starts false at the hunk head. The #18234 decline that BASE certified is gone, because the bag reading now routes through samePlace, which refuses on unreadable. J4 (JSDoc opened AND closed inside the hunk) declines correctly; J3 (division in context — the disclosed trigger) fires.

Blast radius, measured on the real corpus (PRIOR and FIXED walkers imported directly; 250 diffs, packages/spec/src/**/*.ts non-test):

reading value
T1-shaped added lines 95
had a readable stack under the round-1 head 88
unreadable under FIXED 21 of those 88 — 24%, across 7 diffs
first raising byte 21 / 21 = */ · 0 regex · 0 division
hunks carrying ≥1 flagged added line 70 of 261

the PR body discloses only the division operator — a trigger that occurs ZERO times in the corpus — while the trigger that fires 21 times out of 21 is undisclosed. And the round-2 「no other tell moved」 reading is true only because none of the 250 carries an acceptor pair inside a JSDoc-start hunk.

⛔ The seat's call on F2, stated rather than left open

The reviewer offered the seat a downgrade: 「Direction is loud, so not a silent hole … Seat may downgrade to noted if loud-direction regressions are acceptable; the verdict is FAIL on F1 regardless.」

⛔ F2 stays BLOCKING. This card exists because a false positive forced an honest Clause-②: no (narrowing) into an over-declaration. A repair that introduces a new false positive — in the commonest hunk shape on this tree, at 24% of readable stacks, with the only exit-code door again being noyes — trades one forced over-declaration for another at greater frequency. ⭐ That is the exact defect #19099 was filed against, reproduced by its own fix. The reviewer also records that the remedy is small and needs no regex lexer: a */ seen while not in a block comment and before any other bare / on the hunk is unambiguous — it means the hunk began inside a comment.

VERIFIED, and standing

  • F3 — round 1's findings are genuinely closed. V1, V3, V4, V4d, FRAMES-ALONE, INDENT-ALONE and the F3 spend path all read BASE 4 → round-1 head 0 → FIXED 4; the same-place spend pair stays declined (0); the live pair 9bb059dbfe reads 4 → 0 → 0. Controls unmoved.
  • F4 — no other tell moved on the corpus. Own sweep, 250 newest non-merge commits, 750 gate runs: BASE 18 exit-4 / 86 rows, round-1 head 17 / 84, FIXED 17 / 84; BASE vs FIXED differ on exactly one diff (9bb059dbfe); round-1 head vs FIXED identical row-for-row on all 250. ⚠️ REACH: 250 commits, 2 days, shallow clone; 0 of 250 exhibit the A1 or J1 shapes, so it bounds noise and says nothing about leak frequency.
  • F5 — all nine author legs reproduce exactly, plus five reviewer legs, none green. ⭐ The author's disclosure that legs 7 and 8 do not individually free the gate-level shape is confirmed complete. ⭐ And the reviewer's own leg R14 came back GREEN and it reported it as VOID — its own error (the re-insertion point was still before the spend), re-spelled as R14′ which fails 4/563. Same discipline, both directions.
  • F7 — the gate-family ROSTER, not just the count. dispatch-gates --commands scripts/pm/check-widening-tells.mjs on the branch tree → 29 lines; on origin/main 8b8258d910 → 29 lines; diff exit 0 — identical roster. ⇒ this confirms and strengthens the seat's earlier count-only reading, and the STALE TREE warning is discharged with a roster comparison rather than a count.
  • F7 (cont.) — self-test BASE 510 / round-1 542 / FIXED 563, #19099 floor 53; the 21 new cases read adversarially, none tautological. T2 control case-name claim confirmed on a real diff. automation/flow.zod.ts:211 confirmed. Both call sites pass the place evidence; no call site omits it. CI on this head: Lint & Repo Gates, TypeScript Type Check, Test Core, Dogfood Regression Gate, Governed Surface Queue Guard all success; Build Core and Temporal Conformance path-filtered.

NOTED

  • F6 — 「byte-identical」 is TEXTUALLY FALSE and should read 「answer-identical」. enclosingDelimiter's body differs by two lines (a destructuring change); enclosingDelimiters differs substantially. ⛔ But the behavioural differential over 101,447 side-lines (1,234 sides across the 250 diffs) is 0 disagreements for enclosingDelimiter and inParameterList, both PRIOR→FIXED and BASE→FIXED. ⇒ the substance holds — no removed parameter earns budget it did not earn — and the wording is the seat's to correct in the body.
  • F8 — the same name-only limit exists in respellsExistingClosedSetKey, measured (A9: BASE 0 / round-1 0 / FIXED 0). ⛔ Pre-existing at BASE and bounded by one unit per removed line ⇒ not this PR's regression. rewritesExistingOpener and respellsExistingClosedSetBinding were not probed.
  • F9 — no leak found from dropping the frame's own-line openers, and the honest narrowings the place predicate needlessly blocks are all disclosed and loud (re-indented bag, parent opener respelled, tabs→spaces).
  • The branch is now 10 commits behind origin/main.

NOT MEASURED, declared

objectui's mirror under either matcher · the 130/99/31/24/7 re-derivation (round 1 took it) · historical frequency of the A1 keyless-parent shape and of JSDoc-start acceptor pairs (0 of 250 in the corpus for both) · rewritesExistingOpener / respellsExistingClosedSetBinding place limits · merge-queue convergence.

⛔ The PR stays a draft and ⛔ does not enter the queue. F1 and F2 go back to the delivering dev.

Implemented-by: claude/issue-19099-widening-tell-prior-schema
Reviewed-by: session_01AmH9bKvGoLjiY86Q4Z3og2


Generated by Claude Code

…inside a comment

Round 2 of the at-tier review failed the repaired reading on two NEW findings,
both reproduced here from git-emitted diffs before anything was written.

F1 — the place predicate compared opener + head TEXT, which is identity only
while the two lines are the same line. Two KEYLESS parents can spell their
opener identically: discriminatedUnion arms, tuple members, `.or()`/`.and()`
arguments. A `filter: z.unknown()` removed from arm a (a context line) and a
`filter: strictObject({ newKey })` added to arm b (an added line respelled to
the same text — a trailing comment is enough) compared EQUAL, and arm b is a
strict object that could not accept `filter` before. Frames now carry the KIND
of the line they stand on and `samePlace` requires CONTEXT on both sides: a
context line is one line shown twice; an added line against a removed line is
two lines. The re-declared bag itself never reaches the test, because
`framePlace` drops the frame's own line.

F2 — the flag's dominant trigger was undisclosed and regressed a landed
#18234 decline. A walk starts at its hunk's first line with no comment state,
so a JSDoc opened above the hunk had its body read as code and the ` */` that
ends it was taken for a bare slash: 21 of 88 readable T1 stacks on the corpus,
21/21 raised by ` */`, zero by a regex or a division. A `*/` outside a block
comment, on a walk that has opened none and raised no flag, is now read as what
it unambiguously is. And a LONE `/` no longer raises anything: a regex literal
cannot span lines, so a `/` with no second `/` on its line is arithmetic. Both
discriminations are sound and neither needs a regex lexer. The header now names
every trigger the flag has.

Claude-Session: https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2
Co-authored-by: Claude <noreply@anthropic.com>
The previous commit wrote a literal star-slash inside JSDoc while describing
the byte the walker now reads. It closed the comment at that word: the module
stopped parsing and `--self-test` exited 1 before reaching a single case. The
sequence is spelled with a backslash here, which is what the rest of this file
already does, and the push that carried the broken parse is recorded in the
report rather than amended away.

Claude-Session: https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2
Co-authored-by: Claude <noreply@anthropic.com>
Ablation leg B4 — dropping the `!unreadable` guard on the leading-comment reset
— came back GREEN, so the guard was pinned by nothing. Recorded rather than
quietly re-run: every suppressing reader has already refused on the flag by
then, so the guard's only observable effect is on `enclosingDelimiter`, which
ignores the flag and would lose a frame the hunk really showed. This case reads
that, and the re-spelled leg now fails 1 of 577.

Claude-Session: https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2
Co-authored-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

Contract review — round 3, on head 306f2020be

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 306f2020be33e12b3b794a765e31f207819f671f

Five detached worktrees (BASE 1047fe1016 · round-0 b3944e3865 · round-1 fa8321b2fa · d603731b4f · fixed), all removed afterwards; shared checkout untouched before and after. Lineage re-verified: 7 commits, each parent the previous, merge-base = 1047fe1016. Every probe is a git-emitted diff from a throwaway repo, judged through the CLI at four gates; exit codes captured before any pipe.

VERDICT: FAIL

Every shape rounds 1 and 2 named is genuinely closed at the reading, both self-reported incidents are exactly as described and handled correctly, and the eight-leg claim holds. But the place predicate is a HUNK-LOCAL suffix, and one ordinary shape — the parent boundary moved by an EARLIER hunk of the same file — still certifies a bag at a different real path.

⛔ F1 — BLOCKING — an earlier hunk can move the parent under the reading

samePlace compares frames from the hunk's own context lines plus indent. Everything opened ABOVE the hunk is invisible on both sides equally — ⛔ unless an earlier hunk of the same file changed it.

X1: schema P ends with filter: z.unknown(); Q = strictObject({ x }) pre-exists. Hunk 1 closes P early and opens Q there, leaving the key run as context. Hunk 2 converts filter: z.unknown()filter: strictObject({ newKey }). Hunk 2 shows no opener at all.

probe BASE round-0 round-1 fixed
X1Q pre-exists as strict, boundary moved in hunk 1 4, 1 row newKey 0 0 0, 0 rows
X1b — same, Q brand new 4, 1 row 0 0 0
X3 — same move inside a shown context frame 4 (2 rows) 4 (1) 4 (1) 4 (1) — newKey is declined; the diff stays loud only because the moved opener is itself a keyed T1 line
X1s CONTROL — the identical move in ONE hunk 4 (3 rows) 4 (3) 4 (3) 4 (3) — the added parent opener is visible and refused

Mechanism dumped through the fixed head's own API: the removed acceptor's shownPlace frames are [] at indent " "; the bag frame's framePlace frames are [] at indent " " ⇒ they agree ⇒ the decline fires.

Q is a strict object that could not accept filter at all, and now accepts filter.newKey. The decline's justification — 「the removed value was the universe of THIS bag」 — is false: P.filter was the universe, ⛔ not Q.filter. Unbounded, exactly as in round 1: every member of the bag is silenced.

This is not a new class — it is the F1 class, reached from outside the hunk. Rounds 1 and 2 attacked only in-hunk shapes, so all three heads leak identically. The author's claim 「F1 closed inside the predicate」 is TRUE for the in-hunk class (A1d, A1, and X4 at depth 2 all fire correctly on the fixed head) and FALSE as a statement about the different-path class as a whole. The file header already says 「never a resolved path」 and 「when a hunk shows no opener at all … the indent is the only nesting evidence left」 — ⛔ it does not say an earlier hunk defeats it, and nothing measures it.

⚠️ The reviewer states the direction and ⛔ not an implementation: the DIFF carries the evidence the hunk does not — another hunk of the same file adding or removing a line that opens or closes a construct at indent ≤ the removed key's.

⛔ F2 — the round-2 class, recurring at a lower frequency

The leading-comment reset never reaches a JSDoc whose prose carries an apostrophe or a path: endOfStringLiteral returns −1 on an unclosed ' and the walk returns unreadable before the */ line is read.

probe (the JSDoc prose above the key) BASE round-1 fixed
J1 plain prose 0 4 0 ✓ recovered
J6 * Doesn't span lines. 0 4 4
J7 * See packages/spec/src/ui/x.zod.ts for the shape. 0 4 4
J12 {@link A} · J13 a URL · J14 backticks · J3 division · J4 · J8 a */ inside a string 0 4 / 0 0

Blast radius re-measured independently: 95 T1-shaped added lines; readable round-1 67 → fixed 88; REGRESS 0; RECOVER 21 across 7 diffs ⇒ ⭐ claim 5 confirmed. ⚠️ But 7 stay unreadable, and 7 of 7 are raised by an unclosed ' in doc prose (value's, can't, action's), 0 by a regex.

⇒ the header's 「What remains loud, and is the whole residual: a real regex literal carrying a bracket … 0 of the 250 diffs」 names a residual with zero occurrences, while the one that occurs seven times is described only as 「a string literal that never closes on its line」 — ⛔ a reader cannot tell that means doc prose.

⭐ The seat's call, and it is the same ruling as round 2: F2 is the same class, and consistency binds. In round 2 I refused the offered downgrade on the grounds that this card exists because a false positive forced an honest Clause-②: no (narrowing) into an over-declaration, so introducing a NEW one reproduces the card's own defect. J6 and J7 are declines BASE certified that the fixed head fires on — a new forced over-declaration, at 7 of 95 rather than 24%. ⛔ Lower frequency does not change the class. It is carried into the escalation rather than into a fourth attempt.

F3 — NOTED — one conjunct of the reset guard is pinned by NOTHING

Leg M1: !openedBlockComment && !leadingCommentClosed && !unreadable!unreadable && !leadingCommentClosedself-test 577/577 GREEN, exit 0, restore proven. ⭐ This is the sibling of the !unreadable conjunct the author itself found unpinned and pinned in 306f2020be. Behaviourally it is unreachable in valid TS on this surface (0 */ in code across 1,010 files / 203,811 lines) ⇒ a self-test gap, ⛔ not a leak. One t( of the shape closes it.

⭐ VERIFIED — and most of this PR is genuinely good work

  • Rounds 1 and 2 are closed at the reading. A1d 4/0/0/4 · A1 4/0/0/4 · X4, keyless arms nested at DEPTH 2 (tuple inside union) 4/0/0/4 · V1, V3, V4 (the outer brandNew fires while the bag member stays declined) · every control unmoved · the live pair 9bb059dbfe 4 → 0/0/0.
  • Within one hunk the kind requirement is sound BY CONSTRUCTION — both compared frame lists are prefixes of the frames the block's preceding context lines pushed (a push on an added or removed line is non-context and refuses; only pops happen inside the block) ⇒ no in-hunk keyless-parent shape at any depth can agree. Hence F1 lives only across hunks.
  • Both incidents confirmed independently. node --check on d603731b4fexit 1 at line 1116; on e669c1bda7 and 306f2020beexit 0; d603731b4f remains in the lineage, ⛔ not amended away. 306f2020be is +2/−1: floor 66→67 and exactly one new t(.
  • The green-leg remedy is REAL, re-run by the reviewer: leg G → 1 of 577 fails, and it is that case, restore proven.
  • The 14 new cases: none tautological. Nine were each made to fail by a reviewer leg; one is a deliberate pin of the disclosed residual; four are bracketing controls. The self-test's keyless-arms hunk matches the reviewer's own git-emitted hunk body line for line.
  • Answer differential over 2,084 sides / 435,043 side-lines: 0 disagreements at every pairing. ⚠️ The reviewer discloses that a FIRST run showed 87,063 「disagreements」 — base and round-0 frames carry no line field at all, so that was its own key, not the reader; discarded and re-taken. ⇒ 「answer-identical」 holds on what the firing-only callers read.
  • Four-way sweep, 1,000 CLI runs: BASE 18 exit-4 / 86 rows; the other three 17 / 84 each; fixed identical row-for-row to BOTH prior heads on all 250, and differs from BASE on exactly one diff (fix(spec): the object-grid arm takes the page-size accept set the view arm rules (#19046) #19095's).
  • Discrimination (b) is sound in the suppressing direction — a regex literal cannot span lines, so a / with no second / cannot open one. Discrimination (a) can discard real frames only on a */ in code (x*/re/), of which there are 0 occurrences on the surface ⇒ theoretical, ⛔ not blocking.

NOT MEASURED, declared

objectui's mirror under any gate · the author's eight legs by their exact anchors (the reviewer's seven legs plus G cover the same conjuncts) · the frequency of the X1 shape in history — 0 of 250 corpus diffs carry a boundary move plus an acceptor conversion in one file, and the sweep would have shown a row difference · commits before 2026-09-17 (shallow clone) · CI on this head · the 130/99/31/24/7 re-derivation · rewritesExistingOpener / respellsExistingClosedSetBinding under the place limit.

⛔ This card ESCALATES — ⛔ there is no fourth attempt

The lane allows two rework rounds and both are spent (round 1 → fa8321b2fa, round 2 → 306f2020be). I said so in the round-2 dispatch before this work began, so it is a rule applied, ⛔ not a rule invented at the failure.

⭐ And the escalation is on the merits, not on the count: the remedy F1 now needs is structural — the reading must move from hunk-local to diff-wide — which is a different design of this matcher, not a patch to the current one. ⛔ That is not a dev's call to make inside a rework round.

The card goes to needs-user-decision with its options and measurements. ⛔ The PR stays a draft, ⛔ does not enter the queue, and ⛔ is not closed — the F2/F3 work in it recovers 21 stacks and fixes a landed regression, and whether that half lands separately is part of the decision.

On the disclosed residual, the reviewer's verdict, quoted because the seat asked for it plainly: the regex-literal residual is an honest remainder — pinned as still telling, named in the header, loud, and the alternative genuinely needs the preceding token. ⛔ It is not a shortcut. But it is not 「the whole residual」 on this tree: the one that actually occurs is the apostrophe in doc prose, and the one that is silent is the cross-hunk parent move, which the reading cannot see by construction.

Implemented-by: claude/issue-19099-widening-tell-prior-schema
Reviewed-by: session_01AmH9bKvGoLjiY86Q4Z3og2


Generated by Claude Code

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

Labels

size/l skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants