diff --git a/tools/enforce-classifier-match.mjs b/tools/enforce-classifier-match.mjs index 4b9551a60..8f1a2af70 100644 --- a/tools/enforce-classifier-match.mjs +++ b/tools/enforce-classifier-match.mjs @@ -1,4 +1,4 @@ -#!/usr/bin/env node +#!/usr/bin/env node /** * Rule #8 — Classifier-mismatch enforce. * @@ -63,8 +63,8 @@ export function decide({ toolUses, recommendation, confidence, assistantText, ov const matched = toolUses.some((u) => nodeMatches(recommendation, u)); if (matched) return { block: false }; - // Allow explicit override: lines like "override: " in assistant text. - if (assistantText && /\boverride:\s+\S/i.test(assistantText)) return { block: false }; + // NOTE: prior \ self-bypass removed (retro #5 hole 1) - assistant + // cannot grant itself an override. User must use a vocabulary phrase. return { block: true, diff --git a/tools/enforce-classifier-match.test.mjs b/tools/enforce-classifier-match.test.mjs index ccbe57248..0dea6c4b1 100644 --- a/tools/enforce-classifier-match.test.mjs +++ b/tools/enforce-classifier-match.test.mjs @@ -72,14 +72,26 @@ describe('enforce-classifier-match / decide', () => { expect(r.block).toBe(false); }); - it('allows when explicit "override:" in assistant text', () => { + it('blocks (not allows) when only "override:" in assistant text — self-override removed (hole 1)', () => { const r = decide({ toolUses: [{ name: 'Edit', input: {} }], recommendation: 'foo:bar', confidence: 0.9, assistantText: 'override: simpler direct edit, foo:bar overkill here\n', + override: null, }); - expect(r.block).toBe(false); + expect(r.block).toBe(true); + }); + + it('blocks when assistant text has "override: reason" but user prompt has no override phrase (hole 1)', () => { + const r = decide({ + toolUses: [{ name: 'Edit', input: {} }], + recommendation: 'superpowers:writing-plans', + confidence: 0.9, + assistantText: 'override: just doing it quick', + override: null, + }); + expect(r.block).toBe(true); }); it('allows when override phrase present', () => {