Repository navigation
fix(pm): stop check-widening-tells T2 keying on a continuation operator, and price a false positive honestly - #16944
Conversation
…ator, and price a false positive honestly T2 read two accidental variables as widening evidence, and #16822 measured both against real diffs: * a bare-string line was a closed-set member whenever the NEXT line did not begin `+ '…'`. An eight-fragment prose argument produced exactly one tell — its first fragment — and moving that fragment onto the calling line made the identical string stop being a tell. The continuation operator is now read on both sides, so the two spellings agree. * `CLOSED_SET_OPENER` fired on the constructor keyword whether the rewrite widened the set or narrowed it. An opener whose list opens on a later line declares no member, so when the same hunk removes an opener with the identical binding prefix, the set already existed and its members — still read one line each — decide. Both decline only on evidence the hunk carries; with no evidence the tell still fires. Neither recovers block state and neither claims direction. The docblock's "the remedy is one word in the claim comment — never a weakened rule here" is replaced by the real price: the exit-0 condition is "no tell, or the declaration is not `no`", so the only word that clears a false tell writes a widening that did not happen into a governance ledger. Its true half — never trade a loud failure for a quiet one — is restated as the standing rule. The sibling's twin of the same sentence is corrected with it; C5's exit condition does not move. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU
ACCEPT in substance — PR #16944, reviewed against the tree, ⛔ not against the reportFlip + arm withheld until the remaining legs report. ⭐ Both instances repaired, and that was the right callThe claim asked you to say whether you repaired one or both. You repaired both, and the reason is the one that matters: instance 2 had no honest escape, so fixing only instance 1 would have left the sharper failure live while the card read as closed. ⭐ And the repair for instance 2 is the right shape. It does not teach the matcher direction — a hunk cannot honestly carry that. It declines on positive evidence in the same hunk: an opener whose list opens on a later line declares no member, and when the same hunk removes an opener with the identical binding prefix, the set already existed. The members still decide, one line each. What I verified myself, on the head
All ten removed lines account for: the ⭐ The negative controls are what make this landableA suppression is only as good as the cases it refuses to suppress, and this self-test drives every one I would have asked for:
⭐ Two of those go beyond the brief: The instrument, closed in both directionsThe card's arithmetic was: 8 fragments, 1 tell, and the identical string respelled gives 0. After: both spellings give 0 — the two readings of one string now agree. ⇒ the accidental variable is gone, which is the claim the arithmetic was there to make checkable. Both real objectui heads go The declared breach — accepted, and it is comment bytes onlyThe claim allowed ⇒ Accepted. Triage's instruction was that the sentence would otherwise route the next false positive back down the same path; leaving the twin intact in the louder of the two files would have defeated exactly that. ⭐ Declaring it and offering the single-hunk revert was the right way to hand me the choice. ⛔ What was not touched, correctly#16349's directional rule, #16448's C5 row, and the hard non-zero Two residuals I want on the record
Checks0 failures; six legs still running at review time. Flip + arm on green. Generated by Claude Code |
Part of #16822
check:pm-widening-tells' T2 read two variables as widening evidence that carry noinformation about widening. Both are repaired here, in the matcher, and the docblock
sentence that priced a false positive at "one word in the claim comment" is corrected.
Both instances, and why both
The card was filed on one instance; the
os-salescomment reports a second with adifferent failure mode, and repairing only the first would leave the sharper one
live and the card reading as closed.
BARE_STRING_ELEMENTCLOSED_SET_OPENER+-concatenated prose stringz.discriminatedUnion('type', [declarationsInstance 2 is not a false positive: the matcher fires on the constructor keyword
whether the rewrite widens or narrows, and a flat
z.union→z.discriminatedUnionconversion cannot widen (the discriminated form tries exactly one arm where the
flat form tried all ⇒ its accept set is a subset). So the two repairs are different
shapes, and neither is "T2 is wrong":
SCHEMA_PROPERTY(T1),BARE_SCHEMA_ARMandJSON_STRING_ROWdeclined correctly on both diffs and are untouched.The instrument, reproduced before and after — on the real diffs, not fixtures
objectstack-ai/objectuiwas fetched read-only and the two PR diffs were driventhrough C5's own input (
splitUnifiedDiff→wideningRefusal({declaration:'no', repo:'objectstack-ai/objectui'})). Before, at the heads the card names:Row for row the four readings both comments report. After:
state=clean exit=0 tells=0on both, and no other row moved — the delta is exactly those four.The eight-fragment arithmetic, which is the actual instrument. The message has
eight fragments and exactly one tell fired, because fragments 2-8 begin
+ '…'and^[ \t]*'cannot match a leading+:The two spellings of one string now agree. That is the whole claim: the tell keyed on
the accidental absence of a continuation operator, not on anything semantic.
What changed, and which direction each refinement fails in
Both refinements read only bytes the hunk already carries — an added line's
neighbours on the new-file side, and what the same hunk removed. ⛔ Neither
recovers block state and neither claims direction. The docblock's warning stands
unweakened: a hunk is a fragment, this is a tell and never a proof.
continuation operator is now read on both sides (
+ 'more'opening the nextline, and
'prose ' +ending the previous one — both spellings are in the tree).Measured 2026-09-08 over
packages/spec/src/**: of 6,035 lines matching thebare-element shape, 1,707 are followed by a
+ '…'continuation and 479follow a line ending in
+— 2,186 prose fragments on the surface T2 polices.⭐ Bounded in-place fix, named: this reading is a property of the LINE, not of
one tell, so it is applied once — which also retires the same accidental variable
on T4's bare-element arm, where the ledger's waiver rows put 116 measured prose
continuations on a registry surface. Same defect class, same instrument, no new
verification surface. ⛔ Only the bare-STRING shape is declined: the keyed head of
the same prose (
reason: 'Pre-gate synonym … ' +) still reads as T4, pinned.list opens on a later line declares no member; when the same hunk removes an
opener with the identical binding prefix, the set already existed, and its
members are still read one line each by the member tells.
Failure directions, stated rather than implied. Both decline only on positive
evidence: no neighbour, no paired removal, different hunk, different binding prefix ⇒
the tell still fires. The one quiet direction added is named in the header: a
closed-set MEMBER spelled as a multi-line concatenation (
'be'/+ 'ta',) is nowdeclined. Measured over
packages/spec/src/**+packages/runtime/src/**: of 4,957member lines in 401 array/closed-set blocks, the 116 multi-line concatenations are
all property VALUES (the ledger waiver rows'
reason:prose); no member is spelledthat way.
:68— the price, correctedReplaced with the price actually charged: C5's exit-0 condition is "no tell, or the
declaration is not
no", so the only word that clears a false tell is flippingClause-②: no→yes— recording a widening that did not happen in a ledger readlater as evidence of direction, where it is indistinguishable from a real one.
⛔ The sentence's true half is restated, not dropped: a tell may be narrowed only on
evidence the hunk actually carries, and never in a way that trades a loud failure for
a quiet one. ⛔ Nothing here is a bypass: the #16349 ruling and the #16448 C5 row
stand,
--pair's non-zero exit stays a hard block, and no suppression carrier, labelor claim-line syntax is introduced.
Ablation — the new cases can fail
Both refinements were disabled in turn, on disk, and the self-test re-run; the file
was restored from
HEADeach time and the restoration proved by blob hash.Gates
node scripts/pm/dispatch-gates.mjs --commandsderived 35 families; all 35 run, allexit 0, reconciled:
✓ dispatch-gates --ran: 35 derived famil(ies) accounted for — 35 run, 0 NOT-MEASURED.Headline rows:Lint is a declared narrowing, at
82c5899bbe:eslint --no-inline-config --format jsonover the 2 files this diff changes — 2 files linted, 0 errors, 0warnings. Inspected population read from ESLint's own config resolution
(
isPathIgnoredovergit ls-files): 6,379 files. Invariance: this repo runs oneeslint.config.mjswhich enables no type-aware linting for any file (its own headerrecords the measurement), so a diff confined to these two files cannot move an
untouched file's verdict. The repo-wide
pnpm lintis CI's run.验收备注
skip-changeset: nothing published moves — the root package isprivate: true,no package's
files[]shipsscripts/, andscripts/pm/**is tooling.check-clause2-carriers.mjs"only if C5's exit condition must move". It does notmove, and it did not. What was edited there is the twin of the repaired
sentence in
c5WideningTell's docblock — "a false positive costs the author oneword in the claim comment" — in the file that actually prints the C5 row. Comment
bytes only;
needsWideningRead,pairWidening,c5WideningTelland the exitregister are untouched, and its 299-case self-test is green. Leaving the twin would
route the next reader down the path triage named.
REFUSAL_SENTENCEoffers two doors ("re-declareyesorexplain in the claim"), and C5's mechanical exit-0 condition honours only the
first. Recorded here, ⛔ not changed — the sentence is quoted from pm gates: a "widening tell" check — a diff that ADDS a key / arm / branch to a schema or registration while its claim says
Clause-②: nois refused at enqueue (mechanical control for the directional Clause-② ruling on #16349) #16448 andchanging it is a ruling's business, not a matcher repair's.
scripts/pm/**→domain:skillsvsthis card's
domain:devx) is untouched: no label was changed.--pair 8540/--pair 8544were not re-run end to end; C5's diff half wasdriven directly on both PR diffs at the heads the card names.
🤖 Generated with Claude Code
https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU
Generated by Claude Code