fix(enforce): hole 2 — Task/Agent count as mutating actions
Brain-retro #5 candidate C, hole 2: enforce-classifier-match.mjs's MUTATING_TOOLS set missed Task/Agent, so delegating mutations via Task() bypassed the rule. Added Task and Agent to the set; nodeMatches already handles Task.subagent_type matching. Regression test asserts Task with matching subagent_type does NOT block (keeps the existing nodeMatches Task path intact). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user