Skip to content

Commit cbe19dd

Browse files
committed
fix(scripts): compliance is carried in author-facing text, not in commentary
Reverse verification found the hole: stripping the authority token from a real gate left the detector green, because that gate's own header mentions the token in a comment. A source-wide search reads a gate's commentary as compliance, so the gate could go silent for every author who trips it while the farm reported clean — the exact failure the convention exists to prevent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jqe56GnYFddggeAyfkZFVz
1 parent 10cee3f commit cbe19dd

1 file changed

Lines changed: 46 additions & 1 deletion

File tree

scripts/check-ratchet-remedy-authority.mjs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@
5858
// inside its message, so the literal 24-character token appears in the source
5959
// ONLY at the `const` declaration — nowhere near the offer text it governs.
6060
//
61+
// Presence is nonetheless checked in AUTHOR-FACING text, never raw source. A gate
62+
// whose header merely discusses the convention has told the author nothing, and
63+
// counting that as compliance was a real hole: it was found by stripping the token
64+
// from a real gate and watching this detector stay green, because that file's own
65+
// header names the token in a comment. See {@link carriesAuthorityToken}.
66+
//
6167
// ── The control corpus is part of the deliverable ──────────────────────────
6268
//
6369
// A first cut of the prototype behind #8540 spelled its gap class `[^.;]`, which
@@ -479,6 +485,27 @@ export function offerIsRefused(offer) {
479485
return REFUSAL_BOUND.test(offer.context) || REFUSAL_PREDICATION.test(offer.context);
480486
}
481487

488+
/**
489+
* Does this gate actually CARRY the authority token — in the text an author
490+
* reads, not merely somewhere in the file?
491+
*
492+
* ⛔ The distinction is the whole assertion, and a raw `src.includes(…)` gets it
493+
* wrong. Measured: strip the token from check-role-word.mjs's marker const and a
494+
* source-wide search still finds it, because the file's own header COMMENT
495+
* mentions the convention by name. The gate would then have gone silent for every
496+
* author who trips it while this detector reported the farm clean — the exact
497+
* shape of failure the convention exists to prevent, reproduced by its detector.
498+
*
499+
* Comments are maintainer-facing. A gate that discusses the token has not told
500+
* the author anything; a gate that puts it in a string has.
501+
*
502+
* @param {string} src
503+
* @returns {boolean}
504+
*/
505+
export function carriesAuthorityToken(src) {
506+
return authorFacingMessages(src).some((m) => m.includes(RATCHET_AUTHORITY_MARKER));
507+
}
508+
482509
// ── Classification ──────────────────────────────────────────────────────────
483510

484511
/**
@@ -510,7 +537,7 @@ export function classify(src) {
510537
return { verdict: refused.length > 0 ? 'refused' : 'excluded', live, refused, anchors };
511538
}
512539
return {
513-
verdict: src.includes(RATCHET_AUTHORITY_MARKER) ? 'marked' : 'unmarked',
540+
verdict: carriesAuthorityToken(src) ? 'marked' : 'unmarked',
514541
live,
515542
refused,
516543
anchors,
@@ -897,6 +924,24 @@ function selfTest() {
897924
+ `longer reaches: ${unreached.join(', ') || '(none)'}`,
898925
unreached.length === 0);
899926

927+
// (18) Compliance is carried in AUTHOR-FACING text, not in commentary. Found by
928+
// reverse verification: stripping the token from a real gate left this detector
929+
// green, because that gate's header mentions the token in a comment.
930+
const commentaryOnly = `// this gate marks the path ${RATCHET_AUTHORITY_MARKER} per #8435\n`
931+
+ `const m = 'Fix it properly. Or ${ADD} a MEASURED entry to the baseline saying why not. That baseline is shrink-only.';`;
932+
expect('compliance — a gate that only MENTIONS the token in a comment does not count as carrying '
933+
+ 'it (a source-wide search reads a gate\'s own commentary as compliance, so a gate could go '
934+
+ 'silent for authors while this detector reported the farm clean)',
935+
classify(commentaryOnly).verdict === 'unmarked');
936+
937+
// (19) …and the mirror: the token in a string literal DOES count. Paired with
938+
// (18) by construction, so exactly one of the two can fire on a broken check.
939+
const inLiteral = `const T = '${RATCHET_AUTHORITY_MARKER}';\n`
940+
+ `const m = 'Fix it properly. Or ${ADD} a MEASURED entry to the baseline saying why not. That baseline is shrink-only.';`;
941+
expect('compliance — the token declared as a string literal DOES count as carrying it, which is '
942+
+ 'how all six instrumented gates spell it',
943+
classify(inLiteral).verdict === 'marked');
944+
900945
// (17) This gate must not be an instance of its own convention.
901946
expect('self-classification — this gate is NOT an instance of the convention it enforces (its '
902947
+ 'control is a declaration registry, and its offer-shaped quotes live in comments)',

0 commit comments

Comments
 (0)