|
295 | 295 | * EVERY diff that adds a cross-field refusal raised a widening tell for the |
296 | 296 | * refusal itself: the instrument read the tightening direction as the |
297 | 297 | * widening one, which is the inverse of what clause ② exists to catch. |
| 298 | + * ⚠️ …and the decline written here did NOT reach a real diff of that shape |
| 299 | + * until #18721 below: it was abandoned on the hunk's LEADING CONTEXT, and |
| 300 | + * the synthetic hunk that pins it carries none. |
298 | 301 | * - PR #17638 — `+ strategy: z.enum(['eager', 'lazy'], {`, an in-shape key |
299 | 302 | * the same change block removed as `- strategy: z.enum(['eager', 'lazy', |
300 | 303 | * 'scheduled']).default('lazy')`. The same key, one member FEWER. This one |
|
857 | 860 | * fact about where it is DECLARED; whether it belongs to EITHER is a fact about |
858 | 861 | * what it RETURNS. Two questions, measured separately, ⛔ never one heuristic. |
859 | 862 | * |
| 863 | + * ## The eleventh accidental variable #18721 removed — a hunk's LEADING |
| 864 | + * CONTEXT |
| 865 | + * |
| 866 | + * #17618 taught T1 that a typed PARAMETER is not a key on a shape, and this |
| 867 | + * file's header names PR #17616's `+ ctx: z.RefinementCtx,` — the SECOND |
| 868 | + * PARAMETER of an exported object-level refinement, this repo's own prescribed |
| 869 | + * `#16489` signature — as the measured case that decline was written for. The |
| 870 | + * decline was real and it was pinned. It still never fired on a real diff. |
| 871 | + * |
| 872 | + * ⭐ The variable is WHERE THE HUNK STARTS. `enclosingDelimiter` walks from the |
| 873 | + * first line of the line's own hunk, and it abandoned the walk — answering |
| 874 | + * `null`, which every caller reads as "keep the tell firing" — the first time a |
| 875 | + * closer arrived with an empty stack. A real hunk opens on CONTEXT lines, and |
| 876 | + * on this repo's spec files that context is the tail of the previous |
| 877 | + * declaration: ` });`. Two closers, no opener above them, and the reading was |
| 878 | + * over before the hunk reached the `export function …(` head it went on to show |
| 879 | + * 108 lines later. The three-line synthetic the pin drives |
| 880 | + * (`+export const refine = (\n+ ctx: z.RefinementCtx,\n+) => ctx;`) has no |
| 881 | + * context line at all, so the pin stayed green through every diff it was |
| 882 | + * written to protect. |
| 883 | + * |
| 884 | + * ⭐ RE-DERIVED here rather than inherited from the card: `git diff |
| 885 | + * 72dd95fa5a..09e16a5745 -- packages/spec/src/ui/dashboard.zod.ts` (PR #18720's |
| 886 | + * own hunk, 202 lines, ONE hunk) with `--declaration no` exited 4 on a T1 row |
| 887 | + * against the `ctx` parameter of |
| 888 | + * `packages/spec/src/ui/dashboard.zod.ts#checkDashboardWidgetMetricMeasureArity` |
| 889 | + * — new-file line 628 — and the same file's true-positive control, |
| 890 | + * `+ brandNewAuthorableKey: z.string().optional(),` added to |
| 891 | + * `#DashboardWidgetSchema` as a real `git diff`, fired on line 701 of the same |
| 892 | + * run of the same matcher. ⇒ a FALSE POSITIVE, ⛔ not a dead instrument. The |
| 893 | + * proving line is the hunk's own first line, ` });`, a CONTEXT line. |
| 894 | + * |
| 895 | + * ⛔ The repair is at that branch and nowhere else — ⛔ NOT a `z.RefinementCtx` |
| 896 | + * type-name exception, which one differently-named parameter type walks past. |
| 897 | + * An underflow DROPS the closer and the walk continues. The argument is a stack |
| 898 | + * one: everything the hunk opens is strictly INSIDE everything it did not show, |
| 899 | + * so the shown stack is a SUFFIX of the real one and its top — whenever it has |
| 900 | + * one — IS the innermost open delimiter, whatever sits below. An empty shown |
| 901 | + * stack still answers `null`, so the reading remains positive evidence only. |
| 902 | + * |
| 903 | + * ⭐ The direction is provable both ways, and BOTH are pinned. On the added |
| 904 | + * side the decline reaches diffs it never reached, which is the false positive |
| 905 | + * this round removes. On the REMOVED side — where #17618 reads the same decline |
| 906 | + * so a deleted parameter cannot buy an added key the right to go unreported — |
| 907 | + * it makes a phantom #16943 budget disappear: a block that removes a parameter |
| 908 | + * behind leading context and adds a genuine key now FIRES on that key, where it |
| 909 | + * was silent before. One repair, one false positive closed and one false |
| 910 | + * negative with it. |
| 911 | + * |
860 | 912 | * ## The remedy with no reader — #17848, and a pin the shape never had |
861 | 913 | * |
862 | 914 | * #17848 filed two halves against this family. Re-measuring both on the tree |
@@ -1138,6 +1190,7 @@ const SELF_TEST_BATTERIES = Object.freeze({ |
1138 | 1190 | '#18560 — the declaring vocabulary is a NAMED list, every form pinned by a counterfactual fixture': 32, |
1139 | 1191 | '#18640 — an inline closed set RE-SPELLED at the same binding is not a set that gained a value': 20, |
1140 | 1192 | '#18702 — a declaring factory PRIVATE to one file, resolved through its own DEFINITION': 54, |
| 1193 | + "#18721 — a hunk's LEADING CONTEXT is not a reason to abandon the parameter reading": 14, |
1141 | 1194 | }); |
1142 | 1195 |
|
1143 | 1196 | // DELETING an entry silences that battery's floor exactly as effectively as |
@@ -1942,10 +1995,23 @@ function topLevelMembers(s, open, close) { |
1942 | 1995 | * |
1943 | 1996 | * ⭐ Positive evidence only, and `null` is the whole safety property. The scan |
1944 | 1997 | * starts at the first line of the line's OWN hunk, so a construct opened before |
1945 | | - * the hunk is never guessed at: a closer arriving with an empty stack means the |
1946 | | - * hunk began inside something it was never shown, and a string literal that |
1947 | | - * does not close on its line means the state cannot be carried across it — |
1948 | | - * both answer `null`, and both callers read `null` as "keep the tell firing". |
| 1998 | + * the hunk is never guessed at, and a string literal that does not close on its |
| 1999 | + * line means the state cannot be carried across it — that answers `null`, and |
| 2000 | + * both callers read `null` as "keep the tell firing". The answer is ALWAYS an |
| 2001 | + * opener this hunk showed, never one inferred from a closer. |
| 2002 | + * |
| 2003 | + * ⭐ #18721 — a closer arriving with an EMPTY stack closes an opener the hunk |
| 2004 | + * never showed, and that is NOT a reason to abandon the reading. The openers |
| 2005 | + * the hunk DOES show are strictly INSIDE the ones it did not, so the shown |
| 2006 | + * stack is a SUFFIX of the real one: whenever it is non-empty its top IS the |
| 2007 | + * innermost open delimiter, whatever sits below it. So an underflow drops the |
| 2008 | + * closer and the walk continues, and the answer is still `null` for exactly the |
| 2009 | + * state that has no positive evidence — a shown stack that is empty where the |
| 2010 | + * line sits. ⛔ The reading this replaces abandoned the walk at the FIRST |
| 2011 | + * underflow, which a real hunk reaches on its LEADING CONTEXT LINES: a hunk |
| 2012 | + * whose context opens on the tail of the previous declaration (` });`) said |
| 2013 | + * `null` for every line after it, however plainly the hunk went on to show the |
| 2014 | + * `(` the line sits in. |
1949 | 2015 | * |
1950 | 2016 | * ⛔ This is NOT the depth-aware `z.object({ … })` reader T1's own comment |
1951 | 2017 | * refuses, and ⛔ it must never be grown into one. It answers exactly one |
@@ -1982,7 +2048,12 @@ export function enclosingDelimiter(side, index) { |
1982 | 2048 | } |
1983 | 2049 | if (BRACKET_CLOSERS[ch] !== undefined) { stack.push({ opener: ch, head: s.slice(0, k) }); continue; } |
1984 | 2050 | if (ch === ')' || ch === ']' || ch === '}') { |
1985 | | - if (stack.length === 0) return null; |
| 2051 | + // #18721 — UNDERFLOW: this closes an opener the hunk never showed. Drop |
| 2052 | + // it and keep walking. The shown stack is a suffix of the real one, so |
| 2053 | + // nothing below it can ever be the innermost open delimiter; an empty |
| 2054 | + // shown stack still answers `null` at the end, which is the same "no |
| 2055 | + // positive evidence" this reader has always reported. |
| 2056 | + if (stack.length === 0) continue; |
1986 | 2057 | stack.pop(); |
1987 | 2058 | } |
1988 | 2059 | } |
@@ -5213,6 +5284,104 @@ export function selfTest() { |
5213 | 5284 | t('⛔ …a COMMENT carrying a local-factory key line is still not a key line', localRun(BLUEPRINT, "+ // snapshotObject: strictIdent('x'),", FACTORY_FIXTURES.strictIdent.definition).rows.length === 0); |
5214 | 5285 | t('⛔ …and a file OFF the contract source surface reads no blob at all, whatever its lines say', localRun('README.md', FACTORY_FIXTURES.strictIdent.line, FACTORY_FIXTURES.strictIdent.definition).unresolved.length === 0); |
5215 | 5286 |
|
| 5287 | + // -- #18721: a hunk's LEADING CONTEXT is not a reason to abandon the walk --- |
| 5288 | + // |
| 5289 | + // The live pair is PR #18720 (card #17779): `git diff 72dd95fa5a..09e16a5745 |
| 5290 | + // -- packages/spec/src/ui/dashboard.zod.ts` — 202 lines, ONE hunk — exited 4 |
| 5291 | + // on `+ ctx: z.RefinementCtx,` at `dashboard.zod.ts:628`, the SECOND |
| 5292 | + // PARAMETER of an exported object-level refinement and the very line #17618's |
| 5293 | + // decline was written for. The decline did not fire because |
| 5294 | + // `enclosingDelimiter` abandoned its walk at the hunk's FIRST LINE: a real |
| 5295 | + // hunk opens on CONTEXT, and this one's context is the tail of the previous |
| 5296 | + // declaration — ` });` — whose closers underflow a stack that has seen no |
| 5297 | + // opener. The three-line synthetic the #18560 battery drives shows no context |
| 5298 | + // at all, so the pin held while every real diff of this shape told. |
| 5299 | + // |
| 5300 | + // ⭐ Read the FIRING half beside the decline, the way every battery above is |
| 5301 | + // ordered: the card's own TRUE-POSITIVE control on the SAME file, and the two |
| 5302 | + // shapes that prove the drop cannot silence a real key — a genuine new key |
| 5303 | + // behind the same underflowing context, and one added after the parameter |
| 5304 | + // list closes. ⚠️ The filing card's first control read 0 and was its own |
| 5305 | + // mis-build (a synthetic path off the declared surface is judged by nothing); |
| 5306 | + // both fixtures here sit on the real path the probe was taken from. |
| 5307 | + battery("#18721 — a hunk's LEADING CONTEXT is not a reason to abandon the parameter reading"); |
| 5308 | + const DASHBOARD = 'packages/spec/src/ui/dashboard.zod.ts'; |
| 5309 | + // PR #18720's own hunk, reduced to exactly what the failing branch needs: the |
| 5310 | + // leading CONTEXT that closes the previous declaration, the function head, |
| 5311 | + // the object-literal-typed FIRST parameter, and the `ctx` line — at the line |
| 5312 | + // the card reported. ⛔ Not the three-line synthetic: the context is the case. |
| 5313 | + const PROBE_18720 = { |
| 5314 | + filename: DASHBOARD, |
| 5315 | + status: 'modified', |
| 5316 | + patch: [ |
| 5317 | + '@@ -623,3 +623,7 @@ export function checkDashboardWidgetStageOrder(', |
| 5318 | + ' });', |
| 5319 | + ' }', |
| 5320 | + ' ', |
| 5321 | + '+export function checkDashboardWidgetMetricMeasureArity(', |
| 5322 | + '+ widget: { id?: unknown; type?: unknown; values?: unknown },', |
| 5323 | + '+ ctx: z.RefinementCtx,', |
| 5324 | + '+): void {', |
| 5325 | + ].join('\n'), |
| 5326 | + }; |
| 5327 | + t('⭐ THE FINDING — PR #18720\'s real hunk: `ctx: z.RefinementCtx,` behind three leading context lines reads NO tell', tells(PROBE_18720).length === 0); |
| 5328 | + t('…at the line the card reported, which is the line that told — the fixture is the probe, not a shape like it', patchLines(PROBE_18720.patch).find((r) => r.kind === 'added' && r.text.includes('z.RefinementCtx'))?.line === 628); |
| 5329 | + t('…and the whole verdict is CLEAN, which is the exit code the live pair could not reach', wideningRefusal({ declaration: 'no', files: [PROBE_18720] }).state === 'clean'); |
| 5330 | + t('⛔ …and an object-literal TYPE on the first parameter is not what confused it: the `{` closes on its own line', enclosingDelimiter([{ text: 'export function check(', hunk: 0 }, { text: ' widget: { id?: unknown },', hunk: 0 }, { text: ' ctx: z.RefinementCtx,', hunk: 0 }], 2)?.opener === '('); |
| 5331 | + // ⭐ THE TRUE-POSITIVE CONTROL, on the SAME file the probe was taken from — |
| 5332 | + // the card's own, re-derived here as a real `git diff` in a worktree. |
| 5333 | + const NEW_KEY_ON_DASHBOARD = { |
| 5334 | + filename: DASHBOARD, |
| 5335 | + status: 'modified', |
| 5336 | + patch: [ |
| 5337 | + '@@ -698,3 +698,4 @@ export const DashboardWidgetSchema = lazySchema(() => strictObject({', |
| 5338 | + ' ', |
| 5339 | + ' /** Widget Description (displayed below the title) */', |
| 5340 | + " description: I18nLabelSchema.optional().describe('Widget description text below the header').meta({ title: 'Description' }),", |
| 5341 | + '+ brandNewAuthorableKey: z.string().optional(),', |
| 5342 | + ].join('\n'), |
| 5343 | + }; |
| 5344 | + t('⛔ CONTROL — a genuinely new key on the SAME file still FIRES: the matcher was never dead, the reading was false', tells(NEW_KEY_ON_DASHBOARD)[0]?.tell === 'T1'); |
| 5345 | + t('…with its own file:line, the one an author can open', at(NEW_KEY_ON_DASHBOARD)[0] === 'packages/spec/src/ui/dashboard.zod.ts:701'); |
| 5346 | + t('⛔ CONTROL — a new key behind the SAME underflowing context still tells: the hunk shows no opener, so there is no positive evidence to read', tells({ filename: DASHBOARD, status: 'modified', patch: ['@@ -30,1 +30,2 @@', ' });', '+ brandNewAuthorableKey: z.string().optional(),'].join('\n') }).length === 1); |
| 5347 | + t('⛔ CONTROL — a real key added AFTER the parameter list closes still tells, underflowing context and all', tells({ filename: DASHBOARD, status: 'modified', patch: ['@@ -30,1 +30,7 @@', ' });', '+export function check(', '+ ctx: z.RefinementCtx,', '+): void {}', '+export const S = z.object({', '+ extra: z.string(),', '+});'].join('\n') }).map((r) => r.text).join('|') === 'extra: z.string(),'); |
| 5348 | + // ⭐ The reading itself, at the branch: an underflow DROPS the closer and the |
| 5349 | + // walk goes on, because the openers a hunk shows are strictly inside the ones |
| 5350 | + // it did not — so a non-empty shown stack is the innermost open delimiter |
| 5351 | + // whatever sits below it, and an empty one is still `null`. |
| 5352 | + const AFTER_UNDERFLOW = [ |
| 5353 | + { text: ' });', hunk: 0 }, |
| 5354 | + { text: 'export function checkThing(', hunk: 0 }, |
| 5355 | + { text: ' ctx: z.RefinementCtx,', hunk: 0 }, |
| 5356 | + ]; |
| 5357 | + t('⭐ an opener the hunk shows AFTER an underflow is the answer — the shown stack is a suffix of the real one', enclosingDelimiter(AFTER_UNDERFLOW, 2)?.opener === '(' && inParameterList(AFTER_UNDERFLOW, 2) === true); |
| 5358 | + t('⛔ …while an underflow with NO opener after it still answers `null` — positive evidence only, never a guess', enclosingDelimiter([{ text: ' });', hunk: 0 }, { text: ' extra: z.string(),', hunk: 0 }], 1) === null); |
| 5359 | + t('⛔ …and the drop does not leak past the parameter list\'s own close: the body\'s `{` is innermost there', inParameterList([{ text: ' });', hunk: 0 }, { text: 'export function check(', hunk: 0 }, { text: ' ctx: z.RefinementCtx,', hunk: 0 }, { text: '): void {', hunk: 0 }, { text: ' extra: z.string(),', hunk: 0 }], 4) === false); |
| 5360 | + t('⛔ …and no reading crosses a HUNK boundary, underflow or not', inParameterList([{ text: ' });', hunk: 0 }, { text: 'export function checkThing(', hunk: 0 }, { text: ' ctx: z.RefinementCtx,', hunk: 1 }], 2) === false); |
| 5361 | + // ⭐ The OLD side moves with it, and that direction is LOUD: #17618 reads the |
| 5362 | + // decline on the removed side too, so a removed parameter behind leading |
| 5363 | + // context now buys no #16943 budget — and the key added in the same block, |
| 5364 | + // which that phantom budget used to pay for, fires. |
| 5365 | + const REMOVED_PARAM_PAYS_NOTHING = { |
| 5366 | + filename: DASHBOARD, |
| 5367 | + status: 'modified', |
| 5368 | + patch: [ |
| 5369 | + '@@ -40,7 +40,7 @@', |
| 5370 | + ' });', |
| 5371 | + ' }', |
| 5372 | + ' export function check(', |
| 5373 | + '- ctx: z.RefinementCtx,', |
| 5374 | + '-): void {}', |
| 5375 | + '-const S = z.object({', |
| 5376 | + '+): void {}', |
| 5377 | + '+const S = z.object({', |
| 5378 | + '+ extra: z.string(),', |
| 5379 | + ' });', |
| 5380 | + ].join('\n'), |
| 5381 | + }; |
| 5382 | + t('⭐ the OLD side moves too — a REMOVED parameter behind leading context is still not a key, so it buys no budget', inParameterList(AFTER_UNDERFLOW, 2) === true && tells(REMOVED_PARAM_PAYS_NOTHING).length === 1); |
| 5383 | + t('…and the row that fires is the genuine new key the phantom budget used to pay for', tells(REMOVED_PARAM_PAYS_NOTHING)[0]?.text === 'extra: z.string(),'); |
| 5384 | + |
5216 | 5385 | // -- the floor ------------------------------------------------------------- |
5217 | 5386 | const floorFailures = []; |
5218 | 5387 | const floorFailure = (text) => { |
@@ -5268,6 +5437,7 @@ export function selfTest() { |
5268 | 5437 | "#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, " + |
5269 | 5438 | "#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, " + |
5270 | 5439 | "#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, " + |
| 5440 | + "#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, " + |
5271 | 5441 | "#16448's four positive controls each with its file:line, its negative controls — " + |
5272 | 5442 | 'the same diffs with `yes`, and a removal-only diff with `no` — the local path composed end ' + |
5273 | 5443 | 'to end so a binary change to a tell surface cannot read as clean, #17112\'s split count with ' + |
|
0 commit comments