Files
portal/tools/router-prehook.test.mjs
T
Дмитрий 90cbe95598 feat(router): UserPromptSubmit hook — classifier wiring (stage 3 task 4)
При каждом 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>
2026-05-24 10:28:31 +03:00

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);
});
});