From e295d68a9ec8651653044ddf02b5a6faab6770b4 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 18 Sep 2026 07:04:23 +0000 Subject: [PATCH] fix(pm): check-widening-tells reports a key re-typed INTO a universal acceptor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #16943's replacement budget confirms that a key was rewritten and never asks whether the rewrite accepts more or less. That direction blindness made a real widening silent: `filter: z.union([A, B])` replaced by `filter: z.unknown()` earned a T1 unit on the removed line and spent it on the added one, so the gate declined while both of the filing card's controls (a brand-new key, an enum widened in place) fired on the same harness. #18234 supplied the one direction fact a line can carry, on the REMOVED side. This is that same fact read on the ADDED side: the added line declares the key as a universal acceptor by `declaresUniversalAcceptorKey` — the same reading, so the two ends of the budget cannot drift apart — and a removed line names the SAME key carrying no acceptor call at all. Absence of evidence still leaves the budget paying, as everywhere else in this file. The widening SPENDS its unit and is reported on top of it. Refusing the spend would hand the unit to the next added line, silencing a genuine new key riding along with the widening — the surplus rule inverted. The `#18234` battery asserted this silence on the stated ground that the case "is what reds the day that lands"; it did, and it is replaced by the case that asserts the inverse now fires while #18234's own direction still declines. Claude-Session: https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2 Co-authored-by: Claude --- scripts/pm/check-widening-tells.mjs | 238 +++++++++++++++++++++++++--- 1 file changed, 219 insertions(+), 19 deletions(-) diff --git a/scripts/pm/check-widening-tells.mjs b/scripts/pm/check-widening-tells.mjs index b147e0bfdcc..31cb9de0f29 100644 --- a/scripts/pm/check-widening-tells.mjs +++ b/scripts/pm/check-widening-tells.mjs @@ -585,14 +585,14 @@ * wraps closes it by reading the block's removed run forward instead of one * line. * - * ⚠️ And the direction this deliberately does NOT touch: a key re-typed INTO a - * universal acceptor (`z.union([…])` → `z.unknown()`) is a real widening and - * goes unreported. ⛔ That silence is #16943's replacement budget and it - * PREDATES this reading — measured identical at 21b7c12b4 with the new-key and - * widened-enum controls firing on the same harness — so it is filed as its own - * finding rather than repaired here: reporting it is a change to the budget for - * every key, not to this class. The battery carries it as an asserted quiet - * direction, which is the case that reds the day it is repaired. + * ⚠️ And the direction this reading deliberately did NOT touch — now CLOSED + * by #18629 rather than left standing: a key re-typed INTO a universal acceptor + * (`z.union([…])` → `z.unknown()`) is a real widening and went unreported. + * ⛔ That silence was #16943's replacement budget and it PREDATED this reading + * — measured identical at 21b7c12b4 with the new-key and widened-enum controls + * firing on the same harness — so it was filed as its own finding rather than + * repaired here. Its section is below; the repair is a second piece of positive + * evidence a BLOCK can carry, not a change to the budget for every key. * * ## The eighth accidental variable #18560 removed — a DECLARING FORM the * vocabulary never learned @@ -942,6 +942,83 @@ * was silent before. One repair, one false positive closed and one false * negative with it. * + * ## The twelfth accidental variable #18629 removed — the budget's DIRECTION + * BLINDNESS, on the one value whose direction a line can carry + * + * #16943's budget is stated in its own section as "replacement-vs-net-addition, + * not spelling", and that arithmetic is deliberately DIRECTIONLESS: it confirms + * that the same key was rewritten and ⛔ never asks whether the rewrite accepts + * MORE or LESS. For nearly every key that is the right instrument — a line + * cannot say what `FilterSchema` admits — and #18234's section is the reading + * that supplied the one exception on the REMOVED side: a removed value that + * accepted EVERYTHING proves the replacement is a subset by construction. + * + * ⭐ #18629 is that same fact read on the ADDED side, and the two are one + * predicate seen from either end. `- filter: z.union([A, B]),` replaced by + * `+ filter: z.unknown().optional(),` is a REAL widening — the accept set goes + * from two shapes to the universe — and the budget paid for it silently: the + * removed `filter:` line is T1-shaped, buys one T1 unit, and the added line is + * no closed-set opener, so nothing even asked for evidence. Measured on this + * tree before the repair, with both of the filing card's controls lit on the + * same harness (a brand-new key and an enum widened in place both FIRE): the + * inverse patch read exit 0. + * + * ⛔ This is NOT a change to the budget for every key, which is what the + * filing card and #18234's section both said it would take. It is a third way + * for a BLOCK to carry positive evidence about direction, in the same place the + * other two are read and subject to the same default: absence of evidence + * leaves the budget paying, exactly as absence of evidence leaves a tell firing + * everywhere else in this file. Two facts, both on lines the block shows: + * + * ① the ADDED line declares the key as a universal acceptor — + * {@link declaresUniversalAcceptorKey}, the SAME reading #18234 certifies a + * removed value with, so the two ends of the budget cannot disagree about + * what "accepts everything" means; and + * ② the same block removed the SAME key, on a line that carries NO universal + * acceptor call at all. + * + * Fact ② is the precision, and it is written as a mention rather than as + * "⛔ not certified by ①" on purpose. A removed acceptor whose chain WRAPS onto + * a second line is not certifiable — 33 of this tree's 132 acceptor key lines + * do not terminate on their own line — so a negated ① would fire on a pure + * REFORMAT of an already-universal key, a false positive on a diff that changes + * no accept set at all. Reading the mention declines there instead. + * + * ⭐ And the widening still SPENDS its unit before it is reported, which is the + * half that is easy to get wrong. Refusing to spend would hand the unit to the + * next added line in the block, so a genuinely new key riding along with the + * widening would go silent — this file's surplus rule inverted, buying one + * report at the price of another. The spend is unchanged and the row is + * reported on top of it: `- filter: z.union([A, B]),` `+ filter: z.unknown(),` + * `+ other: z.string(),` reports BOTH lines, each with its own file:line. + * + * ⚠️ The quiet directions this buys, stated rather than left to be discovered, + * and all three are the SAME default — no evidence, no report: + * + * • a removed value that merely MENTIONS an acceptor is not read as narrower, + * so `z.array(z.unknown())` → `z.unknown()` and `z.unknown().refine(f)` → + * `z.unknown()` both decline. Both are real widenings; both would need a + * reading of what the removed value did with the acceptor it names, which is + * a shape reading this file does not have. + * • an added acceptor whose own chain wraps onto a second line is not + * certified by ① and declines, the mirror of #18234's residual and closed by + * the same overturn condition. + * • a key re-typed into an acceptor with NO removed line naming it is not this + * row at all, and what becomes of it is #16943's business, unchanged. A + * block that removed some OTHER key still PAYS for it — the one-for-one + * rename silence that section already states — and a block that removed + * nothing reports it as the ordinary new key it is. ⚠️ The first of those + * two is a real remaining silence on this very transition; it is #16943's + * direction blindness on a DIFFERENT key, which no line in the block can + * speak to, and ⛔ not something fact ② could be widened to reach without + * giving up the key match that makes it evidence at all. Both measured, + * both pinned below. + * + * ⭐ The OVERTURN CONDITION, written down so it needs no second discussion: the + * first landed pair whose removed value mentions an acceptor inside a narrower + * one closes the first bullet by reading the removed value's shape rather than + * its text. + * * ## The remedy with no reader — #17848, and a pin the shape never had * * #17848 filed two halves against this family. Re-measuring both on the tree @@ -1211,6 +1288,7 @@ const SELF_TEST_BATTERIES = Object.freeze({ '#17955 — a `retiredKey()` tombstone declares a key UNWRITABLE, and never adds a spelling': 37, '#17848 — a key RE-DECLARED with a zod `error` param is not a key ADDED': 8, '#18234 — a key narrowed OUT of a universal acceptor is not a set that gained a value': 33, + '#18629 — a key re-typed INTO a universal acceptor is a real widening the budget must not pay for': 27, 'T3 — a new row in a published entry point': 8, 'T4 — a new registration in a registry': 10, '#16448 acceptance: the four positive controls, each with its file:line': 8, @@ -2415,6 +2493,15 @@ const INERT_CHAIN_STEP = /^\.(?:optional|describe|meta)\(/; /** What may follow the value when it TERMINATES on this line: a comma, nothing else. */ const UNIVERSAL_ACCEPTOR_TAIL = /^[ \t]*,[ \t]*$/; +/** + * A universal-acceptor CALL anywhere in a value — the negative half of #18629's + * fact ②, and ⛔ never a certification. It answers "this removed value had + * something to do with the universe", which is all that is needed to DECLINE; + * certifying that a value IS the universe stays {@link declaresUniversalAcceptorKey}'s + * one reading. + */ +const UNIVERSAL_ACCEPTOR_MENTION = /z\.(?:unknown|any)\(/; + /** * The key this line declares as a UNIVERSAL ACCEPTOR, or `null` (#18234). * @@ -2495,6 +2582,48 @@ export function replacesUniversalAcceptorKey(text, removedTexts) { return removedTexts.some((r) => declaresUniversalAcceptorKey(r) === key); } +/** + * Does this added line re-type a key the same change block removed INTO a + * universal acceptor — a real WIDENING the #16943 budget would otherwise pay + * for in silence? (#18629) + * + * The mirror of {@link replacesUniversalAcceptorKey}, read on the ADDED side, + * and the one direction fact a line can carry about a value this file cannot + * otherwise shape-read. Two facts, both on lines the BLOCK shows: + * + * ① the added line declares the key as a universal acceptor, by the SAME + * reading #18234 certifies a removed one with — so the two ends of the + * budget cannot drift apart about what "accepts everything" means; and + * ② a removed line names the SAME key and carries NO universal-acceptor call + * at all. + * + * ⛔ Fact ② is a MENTION, deliberately, and not "⛔ not certified by ①". A + * removed acceptor whose chain wraps onto a second line cannot be certified — + * 33 of this tree's 132 acceptor key lines do not terminate on their own line — + * so negating ① would fire on a pure REFORMAT of an already-universal key, + * which changes no accept set. Reading the mention declines there instead, and + * pays for that with the quiet direction the header states: a removed value that + * merely mentions an acceptor inside a narrower one (`z.array(z.unknown())`) + * buys the silence too. + * + * ⛔ The KEY must match, for the reason it must in #18234: a removed + * `other: z.string()` says nothing about what `filter` now accepts. + * + * @param {string} text — one ADDED patch line's text, with its `+` stripped + * @param {string[]} removedTexts — the lines this change block REMOVED + * @returns {boolean} true when the block carries positive evidence that this + * key's accept set became the universe + */ +export function widensKeyIntoUniversalAcceptor(text, removedTexts) { + const key = declaresUniversalAcceptorKey(text); + if (key === null || !Array.isArray(removedTexts)) return false; + return removedTexts.some( + (r) => + keyedPropertyName(r) === key && + !UNIVERSAL_ACCEPTOR_MENTION.test(withoutComments(String(r ?? ''))), + ); +} + /** * Does this line DECLARE a key unwritable? (#17955) * @@ -3456,12 +3585,23 @@ export function tellsInFile( !declaresClosedSet || respellsExistingClosedSetKey(text, removedHere) || replacesUniversalAcceptorKey(text, removedHere); + // #18629 — … and the SAME fact read on the added side, which is the one + // direction the budget was blind to. A key re-typed INTO a universal + // acceptor is a real widening: the block removed that key carrying no + // acceptor at all and put the universe on it. + // + // ⭐ It still SPENDS. Refusing the spend would hand the unit to the next + // added line in the block, so a genuinely new key riding along with the + // widening would go silent — the surplus rule inverted, one report bought + // at the price of another. The unit is consumed exactly as before and the + // row is reported on top of it. + const widensIntoAcceptor = widensKeyIntoUniversalAcceptor(text, removedHere); if (kind !== null && spendable) { const budget = budgetOfLine.get(i); const paid = budget?.get(kind) ?? 0; if (paid > 0) { budget.set(kind, paid - 1); - continue; + if (!widensIntoAcceptor) continue; } } // #17300 — a retirement-ledger row this DIFF mints the licence for is a @@ -3493,7 +3633,13 @@ export function tellsInFile( // object-level refinement, so without this reading every diff that adds a // cross-field REFUSAL raised a widening tell for the refusal itself. if (inParameterList(newFile, newAt.get(i))) continue; - rows.push({ tell: 'T1', ...at, why: 'a new key on a Zod object schema — the accept set gains a spelling an author may now write' }); + rows.push({ + tell: 'T1', + ...at, + why: widensIntoAcceptor + ? 'a key re-typed INTO a universal acceptor (`z.unknown()` / `z.any()`) — the accept set for a key the same block removed becomes every value an author may write' + : 'a new key on a Zod object schema — the accept set gains a spelling an author may now write', + }); continue; } // #16822 — an opener that re-declares a set the same hunk removed adds no @@ -4683,15 +4829,68 @@ export function selfTest() { t('⭐ the vocabulary is INTACT — `memberTellKind` still classifies a universal-acceptor key line as T1, so both sides of the budget read one question', memberTellKind(' filter: z.unknown().optional(),', { onContractSource: true }) === 'T1'); t('⭐ …and the narrowed key SPENDS the budget rather than being exempt from it: two removed acceptors pay for two narrowed keys, and a third key fires', tells(uaHere('@@ -30,2 +30,3 @@\n- a: z.unknown(),\n- b: z.unknown(),\n+ a: z.union([X]),\n+ b: z.union([Y]),\n+ c: z.union([Z]),')).length === 1); - // ⚠️ The direction this reading deliberately does NOT touch, asserted rather - // than described: a key re-typed INTO a universal acceptor — `z.union([…])` - // → `z.unknown()` — is a real WIDENING and goes unreported. ⛔ That silence - // is #16943's replacement budget and it predates this reading: measured - // identical at 21b7c12b4, with the new-key and widened-enum controls firing - // on the same harness. It is filed as its own finding rather than repaired - // here, because reporting it is a change to the budget for EVERY key rather - // than to this one class, and this case is what reds the day that lands. - t('⚠️ QUIET — the inverse direction (a key re-typed INTO `z.unknown()`) is unreported, unchanged by this reading and filed as its own card', tells(uaHere('@@ -30,1 +30,1 @@\n- filter: z.union([A, B]),\n+ filter: z.unknown().optional(),')).length === 0); + // ⭐ The direction this reading deliberately did NOT touch was asserted here + // as a QUIET one, on the stated ground that "this case is what reds the day + // that lands". #18629 is that day, and its own battery is below. What stays + // here is the half that must NOT have moved: repairing the inverse must not + // be bought by breaking this reading's own direction, so the live pair is + // re-asserted in the same case that asserts the inverse now fires. + t('⭐ #18629 closed the inverse direction (a key re-typed INTO `z.unknown()` now FIRES) — and this reading\'s own direction is UNCHANGED by it', tells(uaHere('@@ -30,1 +30,1 @@\n- filter: z.union([A, B]),\n+ filter: z.unknown().optional(),')).length === 1 && uaTells(UNIVERSAL_ACCEPTOR_NARROWED).length === 0); + + // -- #18629: the same fact read on the ADDED side -------------------------- + // + // #16943's budget confirms that a key was REWRITTEN and never asks whether the + // rewrite accepts more or less. #18234 supplied the one direction fact a line + // can carry, on the REMOVED side; this battery is that fact on the ADDED side, + // and the two are one predicate seen from either end. + // + // ⭐ Read the FIRING half first, as every battery above it is ordered: a + // reading that can only suppress is untestable in the direction that matters, + // and this one can only REPORT, so the decline is bracketed on every side — + // an acceptor re-spelled as an acceptor, a wrapped acceptor pulled onto one + // line, an acceptor mentioned inside a narrower value, an added acceptor whose + // own chain wraps, and a rename onto a key no removed line names. + battery('#18629 — a key re-typed INTO a universal acceptor is a real widening the budget must not pay for'); + const inv = (...lines) => uaHere(['@@ -30,4 +30,4 @@ export const S = z.object({', ...lines, ' '].join('\n')); + const invTells = (...lines) => tells(inv(...lines)); + const invAt = (...lines) => at(inv(...lines)); + const INV_REMOVED = '- filter: z.union([A, B]),'; + const INV_ADDED = '+ filter: z.unknown().optional(),'; + const INV_WHY = 'a key re-typed INTO a universal acceptor'; + + // -- the firing half: what the budget must no longer pay for --------------- + t('⭐ THE FINDING — the filing card\'s own patch: a key re-typed from `z.union([A, B])` into `z.unknown()` FIRES, where the replacement budget paid for it in silence', invTells(INV_REMOVED, INV_ADDED).length === 1); + t('…at the line that declares the key, which is the row an author has to answer for', invAt(INV_REMOVED, INV_ADDED)[0] === 'packages/spec/src/a.zod.ts:30'); + t('⭐ …and the row NAMES the direction rather than reporting a key that was never added', invTells(INV_REMOVED, INV_ADDED)[0]?.why.startsWith(INV_WHY)); + t('⭐ `z.any()` is in the class on the SAME measurement #18234 read it by — both accept every value', invTells(INV_REMOVED, '+ filter: z.any(),').length === 1); + t('⛔ a removed value that was never a closed set is evidence too — `z.string()` is not the universe either', invTells('- filter: z.string(),', '+ filter: z.unknown(),').length === 1); + t('⛔ …and so is a removed multi-line OPENER on the same key, whose own list never closes on its line', invTells('- filter: z.union([', '- A,', '- ]),', '+ filter: z.unknown(),').length === 1); + const INV_SURPLUS = ['- filter: z.union([A, B]),', '+ filter: z.unknown(),', '+ other: z.string(),']; + t('⭐ SPEND CONTROL — the widening SPENDS its unit and is reported ON TOP of it, so a genuine new key riding along still fires', invTells(...INV_SURPLUS).length === 2); + t('…and the two rows are the widened key and the new key, each with its own file:line — refusing the spend would have silenced the second', invAt(...INV_SURPLUS).join(' ') === 'packages/spec/src/a.zod.ts:30 packages/spec/src/a.zod.ts:31'); + t('…and only the first carries the direction wording; the new key is reported as the new key it is', invTells(...INV_SURPLUS)[0]?.why.startsWith(INV_WHY) && !invTells(...INV_SURPLUS)[1]?.why.startsWith(INV_WHY)); + t('⛔ a removal in a DIFFERENT change block is a different edit — the acceptor fires there as the ordinary new key it is', invTells(INV_REMOVED, ' ctx', '+ filter: z.unknown(),').length === 1); + t('⛔ DARK CONTROL — the same added acceptor with NOTHING removed still fires: the row is bought by the removal, never by the added shape', invTells('+ filter: z.unknown(),').length === 1); + t('…and THAT row is the plain new-key reading, which is the whole difference this reading makes', invTells('+ filter: z.unknown(),')[0]?.why.startsWith(INV_WHY) === false); + t('⛔ a trailing COMMENT mentioning the acceptor is not the value — comments are stripped before the mention is read', invTells('- filter: z.string(), // was z.unknown()', '+ filter: z.unknown(),').length === 1); + t('⭐ the reading is about the BLOCK, never the board — the same shape on the objectui mirror reads the same', uaTells({ filename: UA_MIRROR, status: 'modified', patch: uaPatch(INV_REMOVED, INV_ADDED) }).length === 1); + t('⛔ …and nothing at all fires off the contract source surface', tellsInFile({ filename: 'packages/core/src/a.ts', status: 'modified', patch: uaPatch(INV_REMOVED, INV_ADDED) }, {}).length === 0); + + // -- the declining half: what is NOT a widening ---------------------------- + t('⛔ NEUTRAL CONTROL — an acceptor re-spelled as an acceptor gained nothing and still declines', invTells('- filter: z.unknown().optional(),', '+ filter: z.unknown(),').length === 0); + t('⛔ REFORMAT CONTROL — a removed acceptor whose chain WRAPS is read as a MENTION, so pulling it onto one line is not read as a widening', invTells('- filter: z.unknown()', "- .describe('x'),", "+ filter: z.unknown().describe('x'),").length === 0); + t('⚠️ QUIET — a removed value that merely MENTIONS an acceptor inside a narrower one declines; the header states it with its overturn condition', invTells('- filter: z.array(z.unknown()),', '+ filter: z.unknown(),').length === 0); + t('⚠️ QUIET — an added acceptor whose own chain wraps onto a second line is not certified, the mirror of #18234\'s residual and closed by the same condition', invTells(INV_REMOVED, '+ filter: z.unknown().optional()', "+ .describe('x'),").length === 0); + t('⚠️ QUIET — a rename ONTO a key no removed line names is not this row: #16943\'s budget pays for it exactly as before', invTells('- other: z.string(),', '+ filter: z.unknown(),').length === 0); + t('⭐ SIBLING INTACT — #18234\'s live pair, a key narrowed OUT of an acceptor, still declines under this reading', uaTells(UNIVERSAL_ACCEPTOR_NARROWED).length === 0); + + // -- the reader itself ----------------------------------------------------- + t('`widensKeyIntoUniversalAcceptor` needs the SAME key — a removed `other` says nothing about what `filter` now accepts', widensKeyIntoUniversalAcceptor(' filter: z.unknown(),', [' filter: z.union([A, B]),']) === true && widensKeyIntoUniversalAcceptor(' filter: z.unknown(),', [' other: z.union([A, B]),']) === false); + t('⛔ …and declines when the removed line carries an acceptor CALL anywhere on it, certified or not', widensKeyIntoUniversalAcceptor(' filter: z.unknown(),', [' filter: z.unknown().optional(),']) === false && widensKeyIntoUniversalAcceptor(' filter: z.unknown(),', [' filter: z.array(z.unknown()),']) === false); + t('⛔ …and when the ADDED line is not a CERTIFIED acceptor — unterminated, or carrying a narrowing step', widensKeyIntoUniversalAcceptor(' filter: z.unknown().optional()', [INV_REMOVED.slice(1)]) === false && widensKeyIntoUniversalAcceptor(' filter: z.unknown().refine(f),', [INV_REMOVED.slice(1)]) === false); + t('…and reads the key in every spelling `declaresUniversalAcceptorKey` admits — quoted, and optional-marked', widensKeyIntoUniversalAcceptor(" 'a.b': z.unknown(),", [" 'a.b': z.union([A]),"]) === true && widensKeyIntoUniversalAcceptor(' filter?: z.unknown(),', [' filter?: z.union([A]),']) === true); + t('⛔ …and a `removedTexts` that is not an array is not evidence', widensKeyIntoUniversalAcceptor(' filter: z.unknown(),', null) === false); + t('⭐ …and it is the MIRROR of `replacesUniversalAcceptorKey`: one pair of lines, read from either end, and the neutral pair is a replacement to one and a widening to neither', widensKeyIntoUniversalAcceptor(' filter: z.unknown(),', [' filter: z.union([A, B]),']) === true && replacesUniversalAcceptorKey(' filter: z.union([A, B]),', [' filter: z.unknown(),']) === true && widensKeyIntoUniversalAcceptor(' filter: z.unknown(),', [' filter: z.unknown(),']) === false); // -- T3 -------------------------------------------------------------------- battery('T3 — a new row in a published entry point'); @@ -5577,6 +5776,7 @@ export function selfTest() { '#17955\'s tombstone decline — read before the budget so a rename is still paid for, and requiring the value to BE the call — with the un-retiring control, the two chained-arm controls that fire, and the multi-line chained close pinned as the residual quiet direction, ' + "#17848's re-declared key with a zod `error` param — declined by #16943's budget, bracketed by the dark control that fires when nothing paid and the surplus control that fires on a real new key beside it, " + "#18234's key narrowed out of a universal acceptor — certified by the REMOVED value's own semantics rather than by the added value's spelling, with the dark, different-key, never-universal, narrowing-step and surplus controls that still fire, " + + "#18629's key re-typed INTO one — the same fact read on the ADDED side, closing the direction #18234 asserted as quiet, spending its unit and reporting on top of it so a genuine new key beside it still fires, bracketed by the neutral, reformat, mention, wrapped-chain and rename declines, " + "#18640's inline closed set re-spelled at the same binding — bounded by the control set that IS the finding, the same edit spelled one member per line and at a keyed property, with the added-arm, different-binding, brand-new, widened-enum and new-key controls that still fire, " + "#18702's FILE-LOCAL declaring factory, resolved through its own definition at the head BLOB and classified by what its body returns — every factory the filing card names pinned against its own arm, the refusal arm read off a `z.never` definition rather than a name with its chained-arm control, the counterfactual bracketed by the same fixture with the resolver blind, and both boundaries (an imported factory, an unclassifiable body) pinned as a STATED silence the reader prints, " + "#18721's hunk LEADING CONTEXT — an underflowing closer drops and the walk goes on, so #17618's parameter decline reaches a real diff: PR #18720's own hunk silent at its reported line, bracketed by the same file's true-positive control that fires, by a new key behind the same underflowing context, by a key added after the parameter list closes, and by the removed side where a phantom budget disappearing makes a genuine key fire, " +