You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit f8ca2fd
Browse filesBrowse the repository at this point in the historyBrowse files
fix(pm): the widening refusal names the two doors that move its exit code, and pins the re-declared key
`REFUSAL_SENTENCE` offered "re-declare `yes` or explain in the claim why this
addition does not widen". The second branch has no reader: `c5WideningTell()`
compares the declaration against the diff's tells and stops, so an author who
followed the instruction got the identical exit 4 and no way to learn the
remedy was never implemented. That left one working door, `Clause-②: no` ->
`yes`, which on a FALSE tell is the widening-that-did-not-happen this family
exists against. The sentence now names the two real doors -- re-declare when
the diff widens, repair the MATCHER when the tell is false -- and says
outright that an explanation moves no exit code.
Giving the explanation a reader was refused rather than overlooked: an
author-written sentence that clears the author's own gate is self-clearance,
and it needs the claim-line syntax #16448 forbids.
The other half of the filing did not reproduce. Re-run against all three PR
diffs it measured, the matcher reports no tell -- and neither does it at the
card's own filing commit (758ac40), because #16943's replacement budget had
landed three days earlier. No matcher change was made for it; what it did
leave was a missing case, so the `{ error: ... }` re-declaration is now pinned
with its dark control (same added lines, nothing removed -> still fires) and a
surplus control (a real new key beside it -> still fires).
No exit code moves. The self-test grew from 298 to 309 cases -- 11 added in
both directions, none removed.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KB5PFtxuy1x3dcR5gxudx6
@@ -2823,6 +2897,71 @@ export function selfTest() {
2823
2897
// that reds when it does.
2824
2898
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);
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
+
2826
2965
// -- T3 --------------------------------------------------------------------
2827
2966
battery('T3 — a new row in a published entry point');
2828
2967
t('a new export row is a tell',tells(FILE_API_SURFACE)[0]?.tell==='T3');
@@ -2882,7 +3021,10 @@ export function selfTest() {
2882
3021
2883
3022
// -- the sentence and the prohibitions ------------------------------------
2884
3023
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'));
2886
3028
t('…and quotes the declaration in the spelling the reader uses',says(REFUSAL_SENTENCE,'`Clause-②: no`'));
2887
3029
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:')));
2888
3030
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() {
3093
3235
'each bracketed by the control that still fires, '+
3094
3236
'#17300\'s retirement-ledger licence with the firing controls that bracket it on every side, '+
3095
3237
'#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, "+
3096
3239
"#16448's four positive controls each with its file:line, its negative controls — "+
3097
3240
'the same diffs with `yes`, and a removal-only diff with `no` — the local path composed end '+
3098
3241
'to end so a binary change to a tell surface cannot read as clean, #17112\'s split count with '+
0 commit comments