Skip to content

Commit c7fb2c9

Browse files
os-warrenclaude
andauthored
fix(pm): the widening refusal names the two doors that move its exit code, and pins the re-declared key (#18539)
Fixes #17848 Clause-②: no `REFUSAL_SENTENCE` in `scripts/pm/check-widening-tells.mjs` now names the two doors that actually move its exit code, and `--self-test` pins the re-declared-key shape in both directions. One file changed. ⛔ No exit code moves. ## First act: the card's own specimens, re-measured — half one did not reproduce The card's table was taken 2026-09-12T11:5xZ. Re-run today against all three PR diffs it names (fetched as the PR's own diff and fed to `--declaration no --diff`), this matcher reports **no widening tell on any of them**: | specimen | judged files | tells | exit | |:--|--:|--:|--:| | PR #17638 — `system/cache.zod.ts:197` | 3 of 7 (4 NOT MEASURED) | 0 | 0 | | PR #17796 — `ui/view.zod.ts:1615` | 1 of 3 (2 NOT MEASURED) | 0 | 0 | | PR #17846 — seven `filter:` doors | 3 of 5 (2 NOT MEASURED) | 0 | 0 | ⚠️ Every zero above is bracketed by controls, because a zero from a dead invocation reads the same as a zero from a decline: - **Lit control** — a genuinely new key added to `packages/spec/src/ui/view.zod.ts` (the same file as #17796's specimen) fires `T1 … :101` and exits **4**. The surface covers these files and the run is alive. - **Dark control** — the card's sharpest specimen reduced to its hunk, with the removed line deleted and the three added lines byte-identical, fires `T1 packages/spec/src/ui/component.zod.ts:2504` and exits **4**. The silence is bought by the replacement, never by the shape. ⭐ And it did not fire at the card's own filing commit either. Running `check-widening-tells.mjs` **as it stood at `758ac409`** (`origin/main`, 2026-09-12T11:38Z — seventeen minutes before the card was written) against the same five inputs reproduces the same five readings: three specimens exit 0, both controls exit 4. The repair had landed three days earlier, in #16943's per-change-block replacement budget: a key re-declared in place removes a T1 line and adds one, and the removal pays. ⇒ the card's "nine tells" was carried over from the threads that accumulated on closed card #17618; it was not a reading of the gate on the day it was filed. Two of the three PRs have since merged (#17846 on 2026-09-12, #17638 on 2026-09-13) and #17796 was closed unmerged, so the "three PRs parked" cost is also spent. ## Why no matcher change was made for half one ⛔ The shapes the card floated — pairing across a HUNK, or diffing the file's key SET instead of the block's added lines — are the silence `changeBlocks`'s own docblock refuses: a hunk carries three context lines each side and routinely holds an unrelated removal at one end and a real addition at the other, so pairing across it pays for a new key with a removal that has nothing to do with it. Buying that would trade a loud failure for a quiet one to repair a defect that is not there. **A fix that silences T1 generally is worse than the bug**, and this PR does not make one. What half one did leave is a gap in the **instrument**, not in the reader: the `{ error: … }` re-declaration had no case of its own, and it is arithmetically distinct from the `.describe()` pair #16943 pinned — the block removes ONE line and adds THREE, of which exactly one is a key. A budget counting LINES instead of KINDS comes up short right there. It is pinned now, with both controls above and a third: - ⭐ **surplus control** — a genuinely new key (`filterLogic:`) added in the SAME block as the re-declaration still fires at its own file:line. One removal pays for one key; a real widening riding along with a re-declaration is still caught. ## Half two — the remedy with no reader — is what this PR repairs The sentence offered two doors and only one was real: > re-declare `yes` or explain in the claim why this addition does not widen `c5WideningTell()` compares the declaration against the diff's tells and stops there; nothing in either file reads an explanation. So an author who followed the instruction got the identical exit 4 with no way to learn that the remedy was never implemented — and the only door that DID move the number was `Clause-②: no` → `yes`, which on a false tell is the one thing the standing rule forbids outright: 「⛔ 永不把 `no` 翻成 `yes` 去过门」. ⚠️ A gate whose only working door is a lie teaches the lie. ⚠️ This file's header had already recorded that twice — at #16822 and again at #16943 — without ever changing the string a refused author actually reads. So the file knew and the author could not: a declared-but-unenforced remedy, which this repo removes rather than documents. **Three shapes were weighed:** 1. **Delete the second branch.** ⛔ Refused. It leaves `re-declare yes` as the only door, which on a FALSE tell is exactly the forbidden lie — the message would then instruct it. 2. **Give the explanation a reader.** ⛔ Refused. An author-written sentence that clears the author's own gate is 自查放行, and it needs the new claim-line syntax #16448 forbids. 3. **Name the door that was always the right one.** ⭐ Chosen. #16822 already ruled where a demonstrated false positive gets repaired — HERE, in the matcher, with a `--self-test` case pinning the shape — and the sentence now says so, names the file to open, allows filing it as its own card when it is out of the PR's scope, and states outright that an explanation moves no exit code so nobody spends a round rediscovering it. Both doors the sentence now names are doors this file can open. That is the pin. ## Exit-code contract — ⛔ UNCHANGED `EXIT_OK` 0 · `EXIT_USAGE` 1 · `EXIT_INCOMPLETE` 2 · `EXIT_REFUSED` 4, all unmoved, and no verdict state changes. Every tell fires exactly where it fired: the only behavioural surface touched is the TEXT a refusal renders. The seat reading `$?` reads the same table it read yesterday. ## Verification `--self-test`: **309 cases pass**, exit 0 — measured against **298** on `origin/main`'s own copy of this file, so 11 cases were added and none removed. Sibling `check-clause2-carriers.mjs --self-test`: **715 cases pass**, exit 0 — it renders `REFUSAL_SENTENCE` unparaphrased and that pin still holds through the constant. All **31** commands derived by `dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` for this file surface were run; **every one exited 0**, each exit code captured by redirect-then-`$?`, never through a pipe. **Non-vacuity — three ablation legs**, each proving the mutation reached disk by a `grep -c` (LINES) count on the anchored text before and after, each restored under a `trap … EXIT INT TERM`, and each restore proven by `git hash-object` equalling the HEAD blob `9ecddb0c…` with `git diff HEAD` empty: | leg | mutation | on-disk proof | self-test | which cases red | |:--|:--|:--|--:|:--| | budget | the replacement budget never pays | 1 → 0 | exit 1, 26 fail | the specimen case, the surplus control, and #16943's own live pairs | | t1-silent | T1 declines unconditionally | 0 → 1 | exit 1, 43 fail | ⭐ the dark control AND the surplus control — the "silencing T1 generally" direction | | old-sentence | the pre-#17848 wording restored | 0 → 1 | exit 1, 4 fail | all four sentence pins | ⛔ The first attempt at the `t1-silent` leg counted an anchor the mutation does not move (before=1, after=1). It was reported as a void reading and re-run against the injected text rather than quietly retried until something landed. **`skip-changeset`, measured not asserted.** `npm pack --dry-run --json --ignore-scripts` in `packages/spec` packs **271** entries (the lit control) and **0** matching `scripts/pm` or `check-widening-tells`. The root package is `private: true`, and 0 of the 70 publishable workspace packages contain the changed path. Nothing published moves. ## Acceptance notes - **noted, not filed** — the card's own "nine tells, 2026-09-12T11:5xZ" figure was not a reading of the gate on that date; its table's `git grep -oF` counts (which prove the keys are not new) were fresh, the tell count was not. Successor: this PR's body and the report. - **noted, not filed** — `SELF_TEST_BATTERY_FLOOR` is 16 against a roster of 22 declared batteries, so five could be deleted without reddening. That is what a floor IS (AGENTS.md prescribes a minimum, not an equality), and adding batteries must not red — an observation about slack, not a defect. Successor: none. - ⛔ Nothing here attaches, removes or waits on `needs:contract-review`; `scripts/pm/**` is not in `GOVERNED_SURFACES` (`check-governed-merges.mjs`: `docs/adr/**`, `.claude/**`, `skills/**`, `AGENTS.md`, `CLAUDE.md`), so this is an ordinary landing path. - ⛔ The three PRs the card names were not touched. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01KB5PFtxuy1x3dcR5gxudx6 --- _Generated by [Claude Code](https://claude.ai/code/session_01KB5PFtxuy1x3dcR5gxudx6)_ --- ## Landing note (seat, 2026-09-17) Contract review at `CONTRACT_REVIEW_TIER` on head `f8ca2fdddf`: **PASS** — record is comment `5706908992` on this PR. ⭐ The review did not reason about this gate, it **executed** it: it `git archive`d a runnable subset at the head, at `origin/main` and at `758ac409` (main seventeen minutes before the card was filed), ran `--self-test` at each (309 / 298 / 269, all exit 0), re-fetched the three specimen PR heads and fed their real diffs through all three file versions, and ran three ablations with the blob hash re-verified after each restore. That is what makes its central finding trustworthy. **Central finding, which confirms this PR rather than undermining it:** the card's half one — T1 firing on a re-declared key — **was never real on any reachable version of the file**, with a lit control alive at every one. The card's 「nine tells」 figure reproduces nowhere. Half two (a remedy with no reader) **was** real and is what this PR fixes. ⇒ closing #17848 on this PR is supportable. **The direction that mattered — does this weaken the gate?** No. Every predicate (`patchLines`, `changeBlocks`, `memberTellKind`, `tellsInFile` incl. the #16943 budget, `wideningRefusal`, `exitForRefusal`) is byte-for-byte unchanged; the exported symbol list is identical at 46; the exit register stays 0/1/2/4; and the t1-silent ablation reds both the new dark and surplus controls. The only output change is the text of `REFUSAL_SENTENCE`, which no parser outside the file reads (0 hits, control 9). ⚠️ **Two self-narration discrepancies in this body, appended rather than rewritten** (this repo squashes, so the body becomes the permanent commit message): the battery roster is **21** at head, not the 22 the body states (20 at main); and the budget-ablation failure count measured **24**, not 26 — the direction is confirmed, the exact figure is not as stated. ⚠️ **One pin label overclaims, recorded not fixed:** 「the row it reports is the new key, never the re-declared one」 holds for the fixture's ordering only. With the new key written *before* the re-declaration in the same block the gate still refuses (1 tell) but reports the re-declared line. That is #16943's pre-existing patch-order budget and it is the loud direction, so nothing is weakened — the word 「never」 is simply too strong. ⛔ Not fixed in-branch: that moves the head and voids an otherwise complete review record, for a word in a test label. **Pre-landing checks:** ① review PASS on record ✅ · ② `check-clause2-carriers --pair 18539` exit 0; ⛔ no carriers were hung on this pair (`Clause-②: no`, no declared surface) so there is nothing to strip ✅ · ③ re-taken at landing time, latest-run-per-check-name ✅. Governed-surface predicate: **0 of 1 path hits the register** ⇒ ordinary queue landing. --- _Generated by [Claude Code](https://claude.ai/code)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 3ba5648 commit c7fb2c9

1 file changed

Lines changed: 152 additions & 9 deletions

File tree

‎scripts/pm/check-widening-tells.mjs‎

Lines changed: 152 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,58 @@
480480
* which `surfaceFlags` puts off the contract source surface. It is a different
481481
* reading's card on the day that population is not zero.
482482
*
483+
* ## The remedy with no reader — #17848, and a pin the shape never had
484+
*
485+
* #17848 filed two halves against this family. Re-measuring both on the tree
486+
* that answers today is the whole of what this round changed.
487+
*
488+
* ⭐ HALF ONE DID NOT REPRODUCE. The card measured three parked PRs and read
489+
* nine T1 tells on keys that had existed for releases — a key re-declared
490+
* because the diff hung a zod `{ error: … }` param on it (#17846, seven doors),
491+
* or rewrote its `.describe()` (#17796), or moved beside a retirement (#17638).
492+
* Re-run against all three PR diffs, this matcher reports NO tell on any of
493+
* them — and neither does it AS IT STOOD AT THE CARD'S OWN FILING COMMIT
494+
* (`758ac409`, seventeen minutes before the card was written). The repair had
495+
* landed three days EARLIER, in #16943's replacement budget above: a key
496+
* re-declared in place removes a T1 line and adds one, and the removal pays.
497+
*
498+
* ⛔ So no matcher change was made for half one, and the reason is not that the
499+
* change would have been small. The shapes the card floated — pairing across a
500+
* HUNK, or diffing the file's key SET instead of the block's lines — are the
501+
* silence `changeBlocks`'s own docblock refuses, and buying them would have
502+
* traded a loud failure for a quiet one to repair a defect that was not there.
503+
*
504+
* What half one did leave is a gap in the INSTRUMENT rather than in the reader:
505+
* the `{ error: … }` shape had no case of its own, and it is arithmetically
506+
* distinct from the `.describe()` pair #16943 pinned — one removed line against
507+
* THREE added, of which exactly one is a key. A budget counting LINES instead
508+
* of KINDS comes up short precisely there. Its battery is below, carrying the
509+
* dark control that shows the silence is bought by the removal and never by the
510+
* shape, and the surplus control that shows a real new key riding along still
511+
* fires.
512+
*
513+
* ⭐ HALF TWO REPRODUCED EXACTLY, and it is what this round repairs. The
514+
* refusal sentence offered two doors and only one was real: "explain in the
515+
* claim why this addition does not widen" has NO READER — `c5WideningTell()`
516+
* compares the declaration against the diff's tells and stops there. The
517+
* sections above had recorded that twice, at #16822 and again at #16943,
518+
* without ever changing the string a refused author actually reads; so this
519+
* file knew, and the author could not. ⇒ the only door that moved the exit code
520+
* was `Clause-②: no` → `yes`, which on a FALSE tell is the one thing the
521+
* standing rule forbids outright: 「⛔ 永不把 `no` 翻成 `yes` 去过门」.
522+
*
523+
* A declared-but-unenforced remedy is a shape this repo removes rather than
524+
* documents, so the sentence now names the two doors that work — re-declare
525+
* when the diff really widens, and repair the MATCHER when the tell is false —
526+
* and states the explanation's uselessness outright, so nobody spends a round
527+
* rediscovering it. ⛔ Giving the explanation a READER was refused rather than
528+
* overlooked: an author-written sentence that clears the author's own gate is
529+
* 自查放行, and it would need exactly the new claim-line syntax #16448 forbids.
530+
*
531+
* ⛔ No exit code moves for either half. The register below is unchanged, every
532+
* tell fires where it fired, and `--self-test` GREW cases in both directions
533+
* rather than losing any.
534+
*
483535
* ## Where the surfaces come from — imported, never hand-copied
484536
*
485537
* The contract SOURCE surface is `SUSPECT_TIER_GLOBS`, imported from
@@ -694,11 +746,12 @@ const SELF_TEST_BATTERIES = Object.freeze({
694746
'#17618 — a PARAMETER is not a key, and a closed set RE-SPELLED around fewer values is not a new one': 24,
695747
'#17300 — the retirement ledger is a record of REMOVALS, not a set that gained a value': 27,
696748
'#17955 — a `retiredKey()` tombstone declares a key UNWRITABLE, and never adds a spelling': 29,
749+
'#17848 — a key RE-DECLARED with a zod `error` param is not a key ADDED': 8,
697750
'T3 — a new row in a published entry point': 8,
698751
'T4 — a new registration in a registry': 10,
699752
'#16448 acceptance: the four positive controls, each with its file:line': 8,
700753
'#16448 acceptance: the negative controls a widening gate must let through': 10,
701-
'the refusal sentence, and the two prohibitions it must keep': 8,
754+
'the refusal sentence, and the two prohibitions it must keep': 11,
702755
'the exit register is distinct in every direction it must be': 6,
703756
'the declared registry rows still exist in this tree': 4,
704757
'#17112 — the count is split: examined is not examinable': 23,
@@ -721,15 +774,36 @@ export const EXIT_INCOMPLETE = 2;
721774
export const EXIT_REFUSED = 4;
722775

723776
/**
724-
* The sentence #16448 fixes, quoted from the card and NOT paraphrased.
725-
*
726-
* It is a constant because the whole point of the refusal is that the author
727-
* knows the two ways out of it — re-declare, or explain — without reading this
728-
* file. A row renders it once; ⛔ never a second wording per tell.
777+
* The sentence a refused author reads — the two doors that MOVE THE EXIT CODE,
778+
* and the one that never did (#17848).
779+
*
780+
* ⚠️ It is no longer #16448's wording, and the change is measured rather than
781+
* editorial. That wording read "re-declare `yes` or explain in the claim why
782+
* this addition does not widen", and the header above has twice recorded that
783+
* its second branch has NO READER: `c5WideningTell()` compares the declaration
784+
* against the diff's tells and nothing else, so an author who followed the
785+
* instruction got the identical exit 4 with no way to learn that the remedy was
786+
* never implemented. What that left standing was the branch the standing rule
787+
* forbids outright — 「⛔ 永不把 `no` 翻成 `yes` 去过门」 — a widening written
788+
* into a governance ledger that did not happen and afterwards indistinguishable
789+
* from one that did. ⛔ A gate whose only working door is a lie teaches the lie.
790+
*
791+
* ⛔ The repair is NOT to give the explanation a reader. An author-written
792+
* sentence that clears the author's own gate is 自查放行, and it would need the
793+
* new claim-line syntax #16448 forbids. The repair is to name the door this
794+
* file has said was the right one since #16822: a DEMONSTRATED false positive
795+
* is repaired HERE, in the matcher, with a `--self-test` case pinning the
796+
* shape — and to say plainly that the claim is not where it gets repaired.
797+
*
798+
* It stays a constant for #16448's reason: the author knows the ways out
799+
* without opening this file. A row renders it once; ⛔ never a second wording
800+
* per tell.
729801
*/
730802
export const REFUSAL_SENTENCE =
731-
'a widening tell with `Clause-②: no` — re-declare `yes` or explain in the claim why this ' +
732-
'addition does not widen';
803+
'a widening tell with `Clause-②: no` — re-declare `yes` if the diff really widens; if the tell ' +
804+
'is FALSE, repair it here in the matcher (`scripts/pm/check-widening-tells.mjs`, with a ' +
805+
'`--self-test` case pinning the shape), or file that repair as its own card when it is out of ' +
806+
"this PR's scope. ⛔ An explanation in the claim moves no exit code — nothing reads one";
733807

734808
// ---------------------------------------------------------------------------
735809
// The surfaces
@@ -2823,6 +2897,71 @@ export function selfTest() {
28232897
// that reds when it does.
28242898
t('⚠️ QUIET — a multi-line tombstone whose CLOSING line chains a live arm is not reported; population 0, and the header carries the overturn condition', tells({ filename: TOMBSTONE_FILE, status: 'modified', patch: '@@ -30,0 +30,3 @@\n+ legacy: retiredKey(\n+ LEGACY_PRESCRIPTION,\n+ ).or(z.string()),' }).length === 0);
28252899

2900+
// -- #17848 -----------------------------------------------------------------
2901+
//
2902+
// The card behind this battery read nine T1 tells across three PRs on keys
2903+
// that had existed for releases. Re-measured, none of the three fires — and
2904+
// none fired at the card's own filing commit either, because #16943's
2905+
// replacement budget above had already landed. ⭐ These cases therefore pin a
2906+
// repair that was ALREADY HERE rather than one this round made, which is
2907+
// exactly why they earn their lines: the shape had no case of its own, so
2908+
// nothing would have reported the day it stopped being paid for.
2909+
//
2910+
// ⚠️ It is arithmetically distinct from the `.describe()` pair #16943 pinned.
2911+
// The block removes ONE line and adds THREE, of which exactly one is a key —
2912+
// a budget counting LINES instead of KINDS comes up short right here. The
2913+
// specimen is #17846's, written seven times in one diff.
2914+
battery("#17848 — a key RE-DECLARED with a zod `error` param is not a key ADDED");
2915+
const REDECLARED_WITH_ERROR_PARAM = {
2916+
filename: 'packages/spec/src/ui/component.zod.ts',
2917+
status: 'modified',
2918+
patch: [
2919+
'@@ -2491,7 +2502,12 @@ export const ObjectGridPropsSchema = lazySchema(() => strictObject({',
2920+
' dataSource: ElementDataSourceSchema.optional(),',
2921+
' ',
2922+
'- filter: z.array(ViewFilterRuleSchema).optional()',
2923+
'+ filter: z.array(ViewFilterRuleSchema, {',
2924+
"+ error: ruleArrayFilterError({ surface: 'object_grid', migration: 'rule-array' }),",
2925+
'+ }).optional()',
2926+
" .describe('Filter rules'),",
2927+
].join('\n'),
2928+
};
2929+
// The SAME added lines with nothing removed to pay for them. ⛔ Without this
2930+
// control the case above proves only that something declined, never that the
2931+
// REPLACEMENT is what declined it.
2932+
const ERROR_PARAM_UNPAID = {
2933+
...REDECLARED_WITH_ERROR_PARAM,
2934+
patch: REDECLARED_WITH_ERROR_PARAM.patch
2935+
.split('\n')
2936+
.filter((l) => !l.startsWith('-'))
2937+
.join('\n'),
2938+
};
2939+
// One genuine new key riding along with the re-declaration: the removal pays
2940+
// for the key it replaced and has nothing left for this one.
2941+
const ERROR_PARAM_PLUS_NEW_KEY = {
2942+
...REDECLARED_WITH_ERROR_PARAM,
2943+
patch: [
2944+
'@@ -2491,7 +2502,13 @@ export const ObjectGridPropsSchema = lazySchema(() => strictObject({',
2945+
' dataSource: ElementDataSourceSchema.optional(),',
2946+
' ',
2947+
'- filter: z.array(ViewFilterRuleSchema).optional()',
2948+
'+ filter: z.array(ViewFilterRuleSchema, {',
2949+
"+ error: ruleArrayFilterError({ surface: 'object_grid', migration: 'rule-array' }),",
2950+
'+ }).optional()',
2951+
"+ .describe('Filter rules'),",
2952+
'+ filterLogic: z.string().optional(),',
2953+
' ',
2954+
].join('\n'),
2955+
};
2956+
t('⭐ the card’s specimen — key, optionality and element schema byte-identical, an `error` param added — is not a new key', tells(REDECLARED_WITH_ERROR_PARAM).length === 0);
2957+
t('…and the pair reads CLEAN end to end, which is the exit code the card reported as unreachable', wideningRefusal({ declaration: 'no', files: [REDECLARED_WITH_ERROR_PARAM] }).state === 'clean');
2958+
t('⛔ DARK CONTROL — the same three added lines with NO removal still fire: the silence is bought by the replacement, never by the shape', tells(ERROR_PARAM_UNPAID).length === 1 && tells(ERROR_PARAM_UNPAID)[0]?.tell === 'T1');
2959+
t('…at the line that declares the key, not at the param that chooses a refusal message', at(ERROR_PARAM_UNPAID)[0] === 'packages/spec/src/ui/component.zod.ts:2504');
2960+
t('⛔ an `error:` param is not a key on a shape — its value is not schema-shaped, so it neither fires nor SPENDS the budget', memberTellKind(" error: ruleArrayFilterError({ surface: 'object_grid' }),", { onContractSource: true }) === null);
2961+
t('⭐ SURPLUS CONTROL — a genuinely new key added in the SAME block still fires: one removal pays for one key', tells(ERROR_PARAM_PLUS_NEW_KEY).length === 1 && tells(ERROR_PARAM_PLUS_NEW_KEY)[0]?.tell === 'T1');
2962+
t('…and the row it reports is the new key, never the re-declared one', at(ERROR_PARAM_PLUS_NEW_KEY)[0] === 'packages/spec/src/ui/component.zod.ts:2508');
2963+
t('⛔ and the re-declaration does not license the block: a THIRD key with no removal behind it is reported too', tells({ ...REDECLARED_WITH_ERROR_PARAM, patch: `${REDECLARED_WITH_ERROR_PARAM.patch}\n+ extra: z.string(),` }).length === 1);
2964+
28262965
// -- T3 --------------------------------------------------------------------
28272966
battery('T3 — a new row in a published entry point');
28282967
t('a new export row is a tell', tells(FILE_API_SURFACE)[0]?.tell === 'T3');
@@ -2882,7 +3021,10 @@ export function selfTest() {
28823021

28833022
// -- the sentence and the prohibitions ------------------------------------
28843023
battery('the refusal sentence, and the two prohibitions it must keep');
2885-
t('the sentence names both ways out', says(REFUSAL_SENTENCE, 're-declare `yes`') && says(REFUSAL_SENTENCE, 'explain in the claim'));
3024+
t('the sentence names both ways out', says(REFUSAL_SENTENCE, 're-declare `yes`') && says(REFUSAL_SENTENCE, 'repair it here in the matcher'));
3025+
t('⛔ #17848 — and BOTH are doors this file can open: the one with no reader is gone', !says(REFUSAL_SENTENCE, 'explain in the claim'));
3026+
t('…and its uselessness is stated outright, so no author spends a round rediscovering it', says(REFUSAL_SENTENCE, 'moves no exit code'));
3027+
t('…while the matcher door names the file to open and the case that must come with it', says(REFUSAL_SENTENCE, 'scripts/pm/check-widening-tells.mjs') && says(REFUSAL_SENTENCE, '`--self-test` case pinning the shape'));
28863028
t('…and quotes the declaration in the spelling the reader uses', says(REFUSAL_SENTENCE, '`Clause-②: no`'));
28873029
t('⛔ no label name appears anywhere in this file\'s outputs — a checker that hung one would be issuing the verdict', !says(REFUSAL_SENTENCE, 'needs:') && refusalLines(refusedAll).every((l) => !l.includes('needs:')));
28883030
t('⛔ no new claim-line syntax is invented: the two values are the sibling\'s two', wideningRefusal({ declaration: 'maybe', files: positives }).state === 'not-applicable');
@@ -3093,6 +3235,7 @@ export function selfTest() {
30933235
'each bracketed by the control that still fires, ' +
30943236
'#17300\'s retirement-ledger licence with the firing controls that bracket it on every side, ' +
30953237
'#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, ' +
3238+
"#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, " +
30963239
"#16448's four positive controls each with its file:line, its negative controls — " +
30973240
'the same diffs with `yes`, and a removal-only diff with `no` — the local path composed end ' +
30983241
'to end so a binary change to a tell surface cannot read as clean, #17112\'s split count with ' +

0 commit comments

Comments
 (0)