diff --git a/tools/enforce-classifier-match.mjs b/tools/enforce-classifier-match.mjs index 8f1a2af70..d7aaa4e13 100644 --- a/tools/enforce-classifier-match.mjs +++ b/tools/enforce-classifier-match.mjs @@ -28,7 +28,7 @@ import { const RULE_KEY = 'classifier-mismatch'; const CONFIDENCE_THRESHOLD = 0.7; -const MUTATING_TOOLS = new Set(['Edit', 'Write', 'MultiEdit', 'NotebookEdit', 'Bash']); +const MUTATING_TOOLS = new Set(['Edit', 'Write', 'MultiEdit', 'NotebookEdit', 'Bash', 'Task', 'Agent']); /** Normalize a node id: strip "superpowers:" / "skill:" prefix; allow #ID. */ function normalizeNode(s) { diff --git a/tools/enforce-classifier-match.test.mjs b/tools/enforce-classifier-match.test.mjs index 0dea6c4b1..1a6e90af4 100644 --- a/tools/enforce-classifier-match.test.mjs +++ b/tools/enforce-classifier-match.test.mjs @@ -103,4 +103,26 @@ describe('enforce-classifier-match / decide', () => { }); expect(r.block).toBe(false); }); + + it('blocks when Task subagent is spawned without matching recommendation (hole 2)', () => { + const r = decide({ + toolUses: [{ name: 'Task', input: { subagent_type: 'general-purpose', prompt: 'do stuff' } }], + recommendation: 'superpowers:writing-plans', + confidence: 0.9, + assistantText: '', + override: null, + }); + expect(r.block).toBe(true); + }); + + it('does NOT block when Task subagent matches recommendation (regression — Task should count as match when right type)', () => { + const r = decide({ + toolUses: [{ name: 'Task', input: { subagent_type: 'writing-plans', prompt: '...' } }], + recommendation: 'writing-plans', + confidence: 0.9, + assistantText: '', + override: null, + }); + expect(r.block).toBe(false); + }); });