90cbe95598
При каждом prompt'е: classifier → state-файл ~/.claude/runtime/router-state-<session>.json. isEnforcementRequired — guard: micro/question/memory-sync пропускают. Cache per-prompt-hash в runtime/router-classification-cache.json. Любая ошибка прехука — silent fallback, пользовательский поток не ломается. Smoke-test verified: regex-only path работает без ANTHROPIC_API_KEY. Fix: CLI guard использует fileURLToPath для корректного сравнения путей с кириллицей (Windows quirk). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
51 lines
2.3 KiB
JavaScript
51 lines
2.3 KiB
JavaScript
import { describe, it, expect } from 'vitest';
|
|
import { buildStateFromClassification, isEnforcementRequired } from './router-prehook.mjs';
|
|
|
|
describe('buildStateFromClassification', () => {
|
|
it('builds full state object', () => {
|
|
const cls = { taskType: 'feature', micro: false, recommendedNode: '#19', confidence: 0.9, source: 'regex', recommendedChain: 'L1' };
|
|
const s = buildStateFromClassification(cls, { sessionId: 'abc', promptHash: '12345' });
|
|
expect(s.sessionId).toBe('abc');
|
|
expect(s.promptHash).toBe('12345');
|
|
expect(s.classification).toEqual(cls);
|
|
expect(s.skillInvokedThisTurn).toBe(false);
|
|
expect(s.chainProgress).toEqual([]);
|
|
expect(s.enforcementRequired).toBe(true);
|
|
expect(s.timestamp).toBeDefined();
|
|
});
|
|
|
|
it('enforcementRequired false on micro', () => {
|
|
const s = buildStateFromClassification({ taskType: 'bugfix', micro: true, recommendedNode: null }, { sessionId: 'a', promptHash: 'b' });
|
|
expect(s.enforcementRequired).toBe(false);
|
|
});
|
|
|
|
it('enforcementRequired false when no recommendedNode', () => {
|
|
const s = buildStateFromClassification({ taskType: 'question', micro: false, recommendedNode: null }, { sessionId: 'a', promptHash: 'b' });
|
|
expect(s.enforcementRequired).toBe(false);
|
|
});
|
|
|
|
it('enforcementRequired false on excluded taskType', () => {
|
|
const s = buildStateFromClassification({ taskType: 'question', micro: false, recommendedNode: '#60' }, { sessionId: 'a', promptHash: 'b' });
|
|
expect(s.enforcementRequired).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('isEnforcementRequired', () => {
|
|
it('true on feature with node', () => {
|
|
expect(isEnforcementRequired({ taskType: 'feature', micro: false, recommendedNode: '#19' })).toBe(true);
|
|
});
|
|
|
|
it('false on micro', () => {
|
|
expect(isEnforcementRequired({ taskType: 'feature', micro: true, recommendedNode: '#19' })).toBe(false);
|
|
});
|
|
|
|
it('false when no node', () => {
|
|
expect(isEnforcementRequired({ taskType: 'feature', micro: false, recommendedNode: null })).toBe(false);
|
|
});
|
|
|
|
it('false on question/memory-sync (excluded)', () => {
|
|
expect(isEnforcementRequired({ taskType: 'question', micro: false, recommendedNode: '#60' })).toBe(false);
|
|
expect(isEnforcementRequired({ taskType: 'memory-sync', micro: false, recommendedNode: '#33' })).toBe(false);
|
|
});
|
|
});
|