From a39dad4c8ce152836dd9b897f99ad89f0409f764 Mon Sep 17 00:00:00 2001 From: Eyejoker Date: Tue, 31 Mar 2026 09:28:10 +0900 Subject: [PATCH] refactor: add role agent plan ssot --- src/role-agent-plan.test.ts | 63 +++++++++++++++++++++++++++++++++++++ src/role-agent-plan.ts | 41 ++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 src/role-agent-plan.test.ts create mode 100644 src/role-agent-plan.ts diff --git a/src/role-agent-plan.test.ts b/src/role-agent-plan.test.ts new file mode 100644 index 0000000..b5e3de6 --- /dev/null +++ b/src/role-agent-plan.test.ts @@ -0,0 +1,63 @@ +import { describe, expect, it } from 'vitest'; + +import { resolveRoleAgentPlan } from './role-agent-plan.js'; + +describe('resolveRoleAgentPlan', () => { + it('uses the group agent type as owner in single rooms', () => { + expect( + resolveRoleAgentPlan({ + paired: false, + groupAgentType: 'codex', + configuredReviewer: 'claude-code', + configuredArbiter: 'claude-code', + }), + ).toEqual({ + ownerAgentType: 'codex', + reviewerAgentType: null, + arbiterAgentType: null, + }); + }); + + it('falls back to claude-code when group agent type is undefined', () => { + expect( + resolveRoleAgentPlan({ + paired: false, + groupAgentType: undefined, + configuredReviewer: 'codex', + }), + ).toEqual({ + ownerAgentType: 'claude-code', + reviewerAgentType: null, + arbiterAgentType: null, + }); + }); + + it('uses configured reviewer and arbiter when paired room roles differ', () => { + expect( + resolveRoleAgentPlan({ + paired: true, + groupAgentType: 'codex', + configuredReviewer: 'claude-code', + configuredArbiter: 'claude-code', + }), + ).toEqual({ + ownerAgentType: 'codex', + reviewerAgentType: 'claude-code', + arbiterAgentType: 'claude-code', + }); + }); + + it('collapses reviewer to owner and keeps arbiter null when absent', () => { + expect( + resolveRoleAgentPlan({ + paired: true, + groupAgentType: 'claude-code', + configuredReviewer: 'claude-code', + }), + ).toEqual({ + ownerAgentType: 'claude-code', + reviewerAgentType: 'claude-code', + arbiterAgentType: null, + }); + }); +}); diff --git a/src/role-agent-plan.ts b/src/role-agent-plan.ts new file mode 100644 index 0000000..8dce972 --- /dev/null +++ b/src/role-agent-plan.ts @@ -0,0 +1,41 @@ +import type { AgentType } from './types.js'; + +export interface RoleAgentPlan { + ownerAgentType: AgentType; + reviewerAgentType: AgentType | null; + arbiterAgentType: AgentType | null; +} + +export interface ResolveRoleAgentPlanArgs { + paired: boolean; + groupAgentType?: AgentType; + configuredReviewer: AgentType; + configuredArbiter?: AgentType | null; +} + +/** + * Preserve current runtime agent-resolution behavior in one pure helper. + * This intentionally mirrors today's paired inference and does not read room mode yet. + */ +export function resolveRoleAgentPlan( + args: ResolveRoleAgentPlanArgs, +): RoleAgentPlan { + const ownerAgentType: AgentType = args.groupAgentType || 'claude-code'; + + if (!args.paired) { + return { + ownerAgentType, + reviewerAgentType: null, + arbiterAgentType: null, + }; + } + + return { + ownerAgentType, + reviewerAgentType: + args.configuredReviewer !== ownerAgentType + ? args.configuredReviewer + : ownerAgentType, + arbiterAgentType: args.configuredArbiter ?? null, + }; +}