import { describe, expect, test } from 'bun:test'; import { createBuiltinPatterns, resolvePatternGraph, syncPatternGraph, validatePatternDefinition, } from '@shared/domain/pattern'; const BUILTIN_TIMESTAMP = '2026-03-22T00:00:00.000Z'; describe('pattern validation', () => { test('builtin patterns are valid except explicitly unavailable modes', () => { const patterns = createBuiltinPatterns(BUILTIN_TIMESTAMP); const validPatterns = patterns.filter((pattern) => pattern.availability !== 'unavailable'); for (const pattern of validPatterns) { expect(validatePatternDefinition(pattern)).toEqual([]); } }); test('magentic pattern is marked unavailable', () => { const magentic = createBuiltinPatterns(BUILTIN_TIMESTAMP).find( (pattern) => pattern.mode === 'magentic', ); expect(magentic).toBeDefined(); expect(validatePatternDefinition(magentic!)[0]?.message).toContain('unsupported'); }); test('single-agent mode reports agent count, warning, and model issues together', () => { const singlePattern = createBuiltinPatterns(BUILTIN_TIMESTAMP).find( (pattern) => pattern.mode === 'single', ); expect(singlePattern).toBeDefined(); const issues = validatePatternDefinition({ ...singlePattern!, agents: [ { ...singlePattern!.agents[0], instructions: ' ', }, { ...singlePattern!.agents[0], id: 'agent-reviewer', name: 'Reviewer', model: '', }, ], }); expect(issues.find((issue) => issue.field === 'agents')?.message).toBe( 'Single-agent chat requires exactly one agent.', ); expect(issues.find((issue) => issue.field === 'agents.instructions')?.level).toBe('warning'); expect(issues.find((issue) => issue.field === 'agents.instructions')?.message).toBe( 'Agent "Primary Agent" should have instructions.', ); expect(issues.find((issue) => issue.field === 'agents.model')?.message).toBe( 'Agent "Reviewer" requires a model identifier.', ); }); test('multi-agent orchestration modes reject single-agent configurations', () => { const patterns = createBuiltinPatterns(BUILTIN_TIMESTAMP); const handoff = patterns.find((pattern) => pattern.mode === 'handoff'); const groupChat = patterns.find((pattern) => pattern.mode === 'group-chat'); expect(handoff).toBeDefined(); expect(groupChat).toBeDefined(); expect( validatePatternDefinition({ ...handoff!, agents: handoff!.agents.slice(0, 1), }).find((issue) => issue.field === 'agents')?.message, ).toBe('Handoff orchestration requires at least two agents.'); expect( validatePatternDefinition({ ...groupChat!, agents: groupChat!.agents.slice(0, 1), }).find((issue) => issue.field === 'agents')?.message, ).toBe('Group chat requires at least two agents.'); }); test('handoff builtin instructions clearly separate triage and specialist ownership', () => { const handoff = createBuiltinPatterns(BUILTIN_TIMESTAMP).find((pattern) => pattern.mode === 'handoff'); expect(handoff).toBeDefined(); expect(handoff?.agents[0].instructions).toContain('hand off before inspecting files'); expect(handoff?.agents[0].instructions).toContain('Do not claim that you delegated'); expect(handoff?.agents[1].instructions).toContain('own the substantive answer'); expect(handoff?.agents[2].instructions).toContain('own the substantive answer'); }); test('group chat builtin instructions frame iterative drafting and review', () => { const groupChat = createBuiltinPatterns(BUILTIN_TIMESTAMP).find( (pattern) => pattern.mode === 'group-chat', ); expect(groupChat).toBeDefined(); expect(groupChat?.agents[0].instructions).toContain('refine your earlier draft'); expect(groupChat?.agents[1].instructions).toContain('specific improvements'); expect(groupChat?.agents[1].instructions).toContain('instead of restarting the conversation'); }); test('approval policy rejects unknown agent references', () => { const singlePattern = createBuiltinPatterns(BUILTIN_TIMESTAMP).find( (pattern) => pattern.mode === 'single', ); expect(singlePattern).toBeDefined(); const issues = validatePatternDefinition({ ...singlePattern!, approvalPolicy: { rules: [ { kind: 'tool-call', agentIds: ['agent-missing'], }, ], }, }); expect(issues.find((issue) => issue.field === 'approvalPolicy')?.message).toBe( 'Approval checkpoint "tool-call" references unknown agent "agent-missing".', ); }); test('approval policy rejects unknown auto-approved tool references when tool names are provided', () => { const singlePattern = createBuiltinPatterns(BUILTIN_TIMESTAMP).find( (pattern) => pattern.mode === 'single', ); expect(singlePattern).toBeDefined(); const issues = validatePatternDefinition({ ...singlePattern!, approvalPolicy: { rules: [{ kind: 'tool-call' }], autoApprovedToolNames: ['web_fetch', 'unknown.tool'], }, }, ['web_fetch']); expect(issues.find((issue) => issue.field === 'approvalPolicy')?.message).toBe( 'Approval auto-approve references unknown tool "unknown.tool".', ); }); test('builtin patterns seed graph topology for each orchestration mode', () => { const patterns = createBuiltinPatterns(BUILTIN_TIMESTAMP); const single = patterns.find((pattern) => pattern.mode === 'single'); const concurrent = patterns.find((pattern) => pattern.mode === 'concurrent'); const handoff = patterns.find((pattern) => pattern.mode === 'handoff'); const groupChat = patterns.find((pattern) => pattern.mode === 'group-chat'); expect(single).toBeDefined(); expect(concurrent).toBeDefined(); expect(handoff).toBeDefined(); expect(groupChat).toBeDefined(); expect(resolvePatternGraph(single!).nodes.map((node) => node.kind)).toEqual([ 'user-input', 'agent', 'user-output', ]); expect(resolvePatternGraph(concurrent!).nodes.map((node) => node.kind)).toEqual([ 'user-input', 'distributor', 'agent', 'agent', 'agent', 'collector', 'user-output', ]); expect(resolvePatternGraph(handoff!).edges).toContainEqual( expect.objectContaining({ source: 'system-user-input', target: 'agent-node-agent-handoff-triage', }), ); expect(resolvePatternGraph(handoff!).edges).toContainEqual( expect.objectContaining({ source: 'agent-node-agent-handoff-triage', target: 'agent-node-agent-handoff-ux', }), ); expect(resolvePatternGraph(groupChat!).nodes.map((node) => node.kind)).toContain('orchestrator'); }); test('syncPatternGraph rebuilds sequential topology from the current agent list', () => { const sequential = createBuiltinPatterns(BUILTIN_TIMESTAMP).find( (pattern) => pattern.mode === 'sequential', ); expect(sequential).toBeDefined(); const updated = syncPatternGraph({ ...sequential!, agents: [ ...sequential!.agents, { id: 'agent-sequential-final', name: 'Final Reviewer', description: 'Adds a final pass.', instructions: 'Do a last review.', model: 'gpt-5.4', reasoningEffort: 'medium', }, ], }); const graph = resolvePatternGraph(updated); expect(graph.nodes.filter((node) => node.kind === 'agent')).toHaveLength(4); expect(graph.edges).toContainEqual( expect.objectContaining({ source: 'agent-node-agent-sequential-reviewer', target: 'agent-node-agent-sequential-final', }), ); expect(graph.edges).toContainEqual( expect.objectContaining({ source: 'agent-node-agent-sequential-final', target: 'system-user-output', }), ); }); test('graph validation rejects branched sequential topology', () => { const sequential = createBuiltinPatterns(BUILTIN_TIMESTAMP).find( (pattern) => pattern.mode === 'sequential', ); expect(sequential).toBeDefined(); const issues = validatePatternDefinition({ ...sequential!, graph: { ...resolvePatternGraph(sequential!), edges: [ ...resolvePatternGraph(sequential!).edges, { id: 'edge-system-user-input-to-agent-node-agent-sequential-builder-duplicate', source: 'system-user-input', target: 'agent-node-agent-sequential-builder', }, ], }, }); expect(issues.find((issue) => issue.field === 'graph')?.message).toContain('single path'); }); });