Skip to content

Commit ea840ec

Browse files
qq9340100claude
andauthored
fix(scripts): make the three refusal-shape gates state their registry's nature in author-facing text (#8576) (#8583)
All three refuse the ratchet-weakening remedy outright — the stronger shape under the #8435 convention — but each stated WHY only in a comment. An author who trips the gate was told "no" without being told "because this only ever shrinks". Mirrors one clause of each gate's own comment into its existing refusal string; no new message, no verdict changed, no ratchet touched. Two of the three consequently flip in the #8540 detector (PR #8575) from `excluded` to `refused`, which is the intended effect: the refusal limb's sample grows from one gate to three. The detector's hand-classified control corpus is re-read and updated to match, per its own C2/C3 audit cases. check-driver-memory-census.mjs does NOT flip, and is left exactly as it is: its refusal sentence carries no target inside the detector's 200-char offer window, and its governance wording sits outside the GOVERN vocabulary. That is a finding about the detector's grammar, not a defect in a correct gate. Claude-Session: https://claude.ai/code/session_01Jqe56GnYFddggeAyfkZFVz Co-authored-by: Claude <noreply@anthropic.com>
1 parent 3dede58 commit ea840ec

4 files changed

Lines changed: 62 additions & 16 deletions

File tree

scripts/check-driver-memory-census.mjs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,12 @@ export function reconcile(scan, ledger, read = (f) => readFileSync(join(ROOT, f)
376376
: '')
377377
+ ' A new arrival is not a bookkeeping chore: it is the #6664 defect itself. Do NOT add an entry to make '
378378
+ 'this green if the answer is "this should have been migrated" — take the disposition through the '
379-
+ 'process #5704 / #6664 record (rule it, migrate it, or file it), then write down what was decided.',
379+
+ 'process #5704 / #6664 record (rule it, migrate it, or file it), then write down what was decided.'
380+
// The REASON for that refusal, in the text the author actually reads (#8576).
381+
// Mirrors this file's own header verbatim rather than restating it: one rule in
382+
// two voices becomes two rules by the next reading.
383+
+ ' "Is this consumer legitimate?" is a maintainer ruling (#5704 Q2, #6664 A) and this gate is the '
384+
+ 'bookkeeping under it, not a second opinion on it.',
380385
);
381386
}
382387
// LIVE — bindings
@@ -580,6 +585,11 @@ function selfTest() {
580585
arrival.bindings.push({ file: 'p/new.test.ts', kind: 'import', line: 2, specifier: SPECIFIER });
581586
let ps = reconcile(arrival, okLedger(), readFrom({ [ruledFile]: textOk(), 'p/new.test.ts': '' }));
582587
expect('an unledgered arrival is a finding', ps.some((p) => p.startsWith('LEDGERED:') && p.includes('p/new.test.ts')));
588+
// #8576. The refusal above turns the bookkeeping remedy down; this pins that it
589+
// also says WHOSE call the disposition is, in the text the author reads.
590+
// Asserted on the planted arrival — the string only ever prints on failure.
591+
expect('the LEDGERED refusal states whose call the disposition is, not just that it is refused',
592+
ps.some((p) => p.startsWith('LEDGERED:') && p.includes('is a maintainer ruling')));
583593

584594
// The mirror: a ledger entry whose consumer was migrated away.
585595
ps = reconcile({ bindings: [], mentions: [], manifests: [] }, okLedger(), readFrom({ [ruledFile]: textOk() }));

scripts/check-ratchet-remedy-authority.mjs

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -606,16 +606,24 @@ const CONTROL = {
606606
expect: 'refused',
607607
why: 'Refuses by binding a negation to the verb, over a shrink-only registry.',
608608
},
609-
// Refuses by predication (the widening act named as subject and denied), and
610-
// the refusal predicate does fire on that exact sentence — self-test (12) pins
611-
// it. It still lands in `excluded` because stage 2 declines it FIRST: the
612-
// sentence names its target as a bare noun and carries no shrink testimony in
613-
// that message, so the target is never established as a ratchet. Recorded as
614-
// `excluded` rather than `refused` because that is what is measured, not what
615-
// the shape deserves — a control that flattered the detector would be worthless.
609+
// The two gates below refuse by PREDICATION (the act named as subject and
610+
// denied) — self-test (12) pins that predicate on their exact sentence.
611+
//
612+
// Until #8576 both were recorded here as `excluded`, and that was the honest
613+
// reading: stage 2 declined them FIRST, because each named its target in a
614+
// message carrying no testimony about the registry's nature — the testimony
615+
// sat in a comment, where no author and no detector reads it. #8576 mirrored
616+
// one clause of each gate's own shrink-only comment into that same message, so
617+
// the target is now established as a ratchet and the refusal limb is reached.
618+
// Growing the refusal limb's sample from one gate to three was the point: a
619+
// regression in that limb used to be measured against a sample of one.
616620
'check-test-source-alias.mjs': {
617-
expect: 'excluded',
618-
why: 'A refusal-shaped gate that stage 2 declines before refusal is consulted. Not a violation by either route.',
621+
expect: 'refused',
622+
why: 'Refuses by predication. Its registry states its own nature in the same message since #8576, so stage 2 reaches it and the refusal limb is consulted.',
623+
},
624+
'check-type-source-resolution.mjs': {
625+
expect: 'refused',
626+
why: 'The other refusal precedent, refusing by the same predication shape. Its registry states its own nature in the same message since #8576, so stage 2 reaches it rather than declining on a path target.',
619627
},
620628

621629
// ── Declaration registries and near-misses: recording the fact IS the fix ──
@@ -643,13 +651,15 @@ const CONTROL = {
643651
expect: 'excluded',
644652
why: 'Carries shrink-only registries, but its author-facing remedy names a declaration registry instead. Reached only when two unrelated diagnostics are allowed to merge, which is why messages are bounded.',
645653
},
654+
// The third gate #8576 made reader-visible, and the one that did NOT move. Its
655+
// testimony is governance, not shrink, and it now states that governance in
656+
// author-facing text — yet the verdict is unchanged, for two reasons that are
657+
// each about THIS DETECTOR rather than about the gate. Recorded rather than
658+
// engineered around: the gate's wording is correct, and bending it to satisfy a
659+
// grammar would be the control flattering the detector.
646660
'check-driver-memory-census.mjs': {
647661
expect: 'excluded',
648-
why: 'Named in the #8540 ruling as a refusal precedent, and it does refuse. It lands here rather than in refused because its ledger carries no shrink or governance testimony, so stage 2 declines it first. Not a violation either way; recorded so the distinction stays measured rather than assumed.',
649-
},
650-
'check-type-source-resolution.mjs': {
651-
expect: 'excluded',
652-
why: 'The other refusal precedent. Its remedy points at a package tsconfig, which is not a ratchet, so stage 2 declines it. Reached only when a path target is allowed file-wide scope, which is why it is not.',
662+
why: 'Named in the #8540 ruling as a refusal precedent, and it does refuse. Since #8576 it states its ledger governance in author-facing text too, yet it still lands here for two independent reasons: stage 1 finds no target inside the offer window of its refusal sentence, and its wording — a maintainer ruling — sits outside the governance vocabulary. Not a violation by any route; recorded so the distinction stays measured rather than assumed.',
653663
},
654664
'regen-artifacts.mjs': {
655665
expect: 'excluded',

scripts/check-test-source-alias.mjs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,6 +1370,11 @@ function check(root, registry) {
13701370
failures.push(
13711371
`${name}: NEW unaliased artifact import(s) since this entry was measured: ${added.join(', ')}.\n` +
13721372
" Alias them in the package's vitest.config.* — widening the registry entry is not the fix.\n" +
1373+
// The REASON for that refusal, in the text the author actually reads (#8576).
1374+
// Mirrors `KNOWN_UNALIASED_TEST_IMPORTS`'s own words verbatim rather than
1375+
// restating them: one rule in two voices becomes two rules by the next reading.
1376+
' That registry is ⛔ SHRINK-ONLY: entries are audited in both directions, so one that is no\n' +
1377+
' longer needed fails the gate and names itself for deletion.\n' +
13731378
// Same defect, same fix: this branch also named bare packages and left
13741379
// the reader to guess the specifier shape (#8256).
13751380
remediationHint(
@@ -1824,6 +1829,14 @@ function selfTest() {
18241829
});
18251830
const grown = check(root, { '@fx/violator': ['@fx/core'] });
18261831
expect(has(grown.failures, 'NEW unaliased artifact import'), 'a new unaliased import under an existing entry did not fail');
1832+
// #8576. The refusal above turns the registry remedy down; this pins that it
1833+
// also says WHY, in the text the author reads. Asserted on the planted
1834+
// violation, never on a green run — the string only ever prints on failure.
1835+
expect(
1836+
has(grown.failures, '⛔ SHRINK-ONLY'),
1837+
'the refusal no longer states WHY it refuses — the registry\'s shrink-only nature is back to being '
1838+
+ 'comment-only, which tells the maintainer reading the script and not the author tripping the gate',
1839+
);
18271840

18281841
// Shrink: an entry wider than the measurement must fail too — no headroom.
18291842
const wide = check(root, { '@fx/violator': ['@fx/core', '@fx/other', '@fx/gone'] });

scripts/check-type-source-resolution.mjs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -878,7 +878,12 @@ function check(root, registry) {
878878
if (added.length > 0)
879879
failures.push(
880880
`${name}: NEW dist-resolved type import(s) since this entry was measured: ${added.join(', ')}.\n` +
881-
" Add the `paths` rules to the package's tsconfig.json — widening the registry entry is not the fix.",
881+
" Add the `paths` rules to the package's tsconfig.json — widening the registry entry is not the fix.\n" +
882+
// The REASON for that refusal, in the text the author actually reads (#8576).
883+
// Mirrors `KNOWN_DIST_RESOLVED_TYPE_IMPORTS`'s own words verbatim rather than
884+
// restating them: one rule in two voices becomes two rules by the next reading.
885+
' That registry is ⛔ SHRINK-ONLY: entries are audited in both directions, so one that is no\n' +
886+
' longer needed fails the gate and names itself for deletion.',
882887
);
883888
if (gone.length > 0)
884889
failures.push(
@@ -1283,6 +1288,14 @@ function selfTest() {
12831288
});
12841289
const grown = check(root, measuredNames);
12851290
expect(has(grown.failures, 'NEW dist-resolved type import'), 'a new dist-resolved import under an entry did not fail');
1291+
// #8576. The refusal above turns the registry remedy down; this pins that it
1292+
// also says WHY, in the text the author reads. Asserted on the planted
1293+
// violation, never on a green run — the string only ever prints on failure.
1294+
expect(
1295+
has(grown.failures, '⛔ SHRINK-ONLY'),
1296+
'the refusal no longer states WHY it refuses — the registry\'s shrink-only nature is back to being '
1297+
+ 'comment-only, which tells the maintainer reading the script and not the author tripping the gate',
1298+
);
12861299

12871300
const wide = check(root, { ...measuredNames, '@fx/violator': ['@fx/spec', '@fx/other', '@fx/gone'] });
12881301
expect(has(wide.failures, 'STALE'), 'a registry entry listing a dep that is no longer dist-resolved did not fail');

0 commit comments

Comments
 (0)