import { describe, it, expect } from 'vitest'; import { updateChainProgress, extractSkillInvocations } from './router-stop-gate.mjs'; const chains = { L1: { name: 'brainstorming chain', sequence: ['brainstorming', 'writing-plans', 'executing-plans'] }, L13: { name: 'finance chain', sequence: ['billing-audit', 'pest', 'ru-tax-accounting'] }, }; describe('extractSkillInvocations', () => { it('extracts skill names from Skill tool invocations', () => { const events = [ { tool_name: 'Skill', tool_input: { skill: 'brainstorming' } }, { tool_name: 'Edit', tool_input: {} }, { tool_name: 'Skill', tool_input: { skill: 'writing-plans' } }, ]; expect(extractSkillInvocations(events)).toEqual(['brainstorming', 'writing-plans']); }); it('returns empty when no Skill invocations', () => { expect(extractSkillInvocations([{ tool_name: 'Edit' }])).toEqual([]); }); it('strips namespace prefix (superpowers:brainstorming → brainstorming)', () => { const events = [{ tool_name: 'Skill', tool_input: { skill: 'superpowers:brainstorming' } }]; expect(extractSkillInvocations(events)).toEqual(['brainstorming']); }); }); describe('updateChainProgress', () => { it('appends matched chain step to chainProgress', () => { const state = { classification: { recommendedChain: 'L1' }, chainProgress: [] }; const updated = updateChainProgress(state, ['brainstorming'], chains); expect(updated.chainProgress).toEqual(['brainstorming']); }); it('appends multiple steps if multiple skills invoked', () => { const state = { classification: { recommendedChain: 'L1' }, chainProgress: [] }; const updated = updateChainProgress(state, ['brainstorming', 'writing-plans'], chains); expect(updated.chainProgress).toEqual(['brainstorming', 'writing-plans']); }); it('ignores skills not in chain sequence', () => { const state = { classification: { recommendedChain: 'L1' }, chainProgress: [] }; const updated = updateChainProgress(state, ['random-skill'], chains); expect(updated.chainProgress).toEqual([]); }); it('marks chainCompleted=true when last step reached', () => { const state = { classification: { recommendedChain: 'L1' }, chainProgress: ['brainstorming', 'writing-plans'] }; const updated = updateChainProgress(state, ['executing-plans'], chains); expect(updated.chainCompleted).toBe(true); }); it('preserves existing chainProgress without duplicates', () => { const state = { classification: { recommendedChain: 'L1' }, chainProgress: ['brainstorming'] }; const updated = updateChainProgress(state, ['brainstorming', 'writing-plans'], chains); expect(updated.chainProgress).toEqual(['brainstorming', 'writing-plans']); }); }); describe('UTF-8 cyrillic stdin (regression — Stage 3 fix 1)', () => { it('module loads with UTF-8 helper wired (smoke)', async () => { const mod = await import('./router-stop-gate.mjs'); expect(typeof mod.updateChainProgress).toBe('function'); }); });