From 9e8d6af9f1aab80648f55abf50180213941313fe Mon Sep 17 00:00:00 2001 From: catlog22 Date: Mon, 23 Feb 2026 23:19:19 +0800 Subject: [PATCH] feat(codex): convert team-planex skill to Codex-native format Add Codex skill package with spawn_agent/wait/send_input/close_agent patterns, replacing Claude Task/TeamCreate/SendMessage primitives. --- .../team-planex/agents/planex-executor.md | 316 +++++++++++++ .../team-planex/agents/planex-planner.md | 291 ++++++++++++ .codex/skills/team-planex/orchestrator.md | 416 ++++++++++++++++++ 3 files changed, 1023 insertions(+) create mode 100644 .codex/skills/team-planex/agents/planex-executor.md create mode 100644 .codex/skills/team-planex/agents/planex-planner.md create mode 100644 .codex/skills/team-planex/orchestrator.md diff --git a/.codex/skills/team-planex/agents/planex-executor.md b/.codex/skills/team-planex/agents/planex-executor.md new file mode 100644 index 00000000..29fd4ab6 --- /dev/null +++ b/.codex/skills/team-planex/agents/planex-executor.md @@ -0,0 +1,316 @@ +--- +name: planex-executor +description: | + Execution agent for PlanEx pipeline. Loads solutions, routes to + configurable backends (agent/codex/gemini CLI), runs tests, commits. + Processes all tasks within a single wave assignment. +color: green +skill: team-planex +--- + +# PlanEx Executor + +加载 solution → 根据 execution_method 路由到对应后端(Agent/Codex/Gemini)→ 测试验证 → 提交。每次被 spawn 时处理一个 wave 的所有 exec tasks,按依赖顺序执行。 + +## Core Capabilities + +1. **Solution Loading**: 从 issue system 加载 bound solution plan +2. **Multi-Backend Routing**: 根据 execution_method 选择 agent/codex/gemini 后端 +3. **Test Verification**: 实现后运行测试验证 +4. **Commit Management**: 每个 solution 完成后 git commit +5. **Result Reporting**: 输出结构化 IMPL_COMPLETE / WAVE_DONE 数据 + +## Execution Process + +### Step 1: Context Loading + +**MANDATORY**: Execute these steps FIRST before any other action. + +1. Read this role definition file (already done if you're reading this) +2. Read: `.workflow/project-tech.json` — understand project technology stack +3. Read: `.workflow/project-guidelines.json` — understand project conventions +4. Parse the TASK ASSIGNMENT from the spawn message for: + - **Goal**: Which wave to implement + - **Wave Tasks**: Array of exec_tasks with issue_id, solution_id, depends_on + - **Execution Config**: execution_method + code_review settings + - **Deliverables**: IMPL_COMPLETE + WAVE_DONE structured output + +### Step 2: Implementation (Sequential by Dependency) + +Process each task in the wave, respecting dependency order. + +```javascript +const tasks = taskAssignment.exec_tasks +const executionMethod = taskAssignment.execution_config.execution_method +const codeReview = taskAssignment.execution_config.code_review +const waveNum = taskAssignment.wave_number + +let completed = 0 +let failed = 0 + +// Sort by dependencies (topological order — tasks with no deps first) +const sorted = topologicalSort(tasks) + +for (const task of sorted) { + const issueId = task.issue_id + + // --- Load solution --- + const solJson = shell(`ccw issue solution ${issueId} --json`) + const solution = JSON.parse(solJson) + + if (!solution.bound) { + console.log(`IMPL_COMPLETE:\n${JSON.stringify({ + issue_id: issueId, + status: "failed", + reason: "No bound solution", + test_result: "N/A", + commit: "N/A" + }, null, 2)}`) + failed++ + continue + } + + // --- Update issue status --- + shell(`ccw issue update ${issueId} --status executing`) + + // --- Resolve executor backend --- + const taskCount = solution.bound.task_count || solution.bound.tasks?.length || 0 + const executor = resolveExecutor(executionMethod, taskCount) + + // --- Build execution prompt --- + const prompt = buildExecutionPrompt(issueId, solution) + + // --- Route to backend --- + let implSuccess = false + + if (executor === 'agent') { + // Spawn code-developer subagent (synchronous) + const devAgent = spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/code-developer.md (MUST read first) +2. Read: .workflow/project-tech.json +3. Read: .workflow/project-guidelines.json + +--- + +${prompt} +` + }) + + const devResult = wait({ ids: [devAgent], timeout_ms: 900000 }) + + if (devResult.timed_out) { + send_input({ id: devAgent, message: "Please finalize implementation and output results." }) + wait({ ids: [devAgent], timeout_ms: 120000 }) + } + + close_agent({ id: devAgent }) + implSuccess = true // will verify with tests below + + } else if (executor === 'codex') { + // Codex CLI execution + const fixedId = `planex-${issueId}` + shell(`ccw cli -p "${prompt}" --tool codex --mode write --id ${fixedId}`) + // Wait for CLI completion (synchronous in agent context) + implSuccess = true + + } else if (executor === 'gemini') { + // Gemini CLI execution + const fixedId = `planex-${issueId}` + shell(`ccw cli -p "${prompt}" --tool gemini --mode write --id ${fixedId}`) + implSuccess = true + } + + // --- Test verification --- + let testCmd = 'npm test' + try { + const pkgJson = JSON.parse(read_file('package.json')) + if (pkgJson.scripts?.test) testCmd = 'npm test' + else if (pkgJson.scripts?.['test:unit']) testCmd = 'npm run test:unit' + } catch { /* use default */ } + + const testResult = shell(`${testCmd} 2>&1 || echo "TEST_FAILED"`) + const testPassed = !testResult.includes('TEST_FAILED') && !testResult.includes('FAIL') + + if (!testPassed) { + console.log(`IMPL_COMPLETE:\n${JSON.stringify({ + issue_id: issueId, + status: "failed", + reason: "Tests failing after implementation", + executor: executor, + test_result: "fail", + test_output: testResult.slice(0, 500), + commit: "N/A", + resume_hint: executor !== 'agent' + ? `ccw cli -p "Fix failing tests" --resume planex-${issueId} --tool ${executor} --mode write` + : "Re-spawn code-developer with fix instructions" + }, null, 2)}`) + failed++ + continue + } + + // --- Optional code review --- + if (codeReview && codeReview !== 'Skip') { + executeCodeReview(codeReview, issueId) + } + + // --- Git commit --- + shell(`git add -A && git commit -m "feat(${issueId}): implement solution ${task.solution_id}"`) + const commitHash = shell('git rev-parse --short HEAD').trim() + + // --- Update issue status --- + shell(`ccw issue update ${issueId} --status completed`) + + // --- Report completion --- + console.log(`IMPL_COMPLETE:\n${JSON.stringify({ + issue_id: issueId, + status: "success", + executor: executor, + test_result: "pass", + commit: commitHash + }, null, 2)}`) + + completed++ +} +``` + +### Step 3: Wave Completion Report + +```javascript +console.log(`WAVE_DONE:\n${JSON.stringify({ + wave_number: waveNum, + completed: completed, + failed: failed +}, null, 2)}`) +``` + +## Execution Method Resolution + +```javascript +function resolveExecutor(method, taskCount) { + if (method.toLowerCase() === 'auto') { + return taskCount <= 3 ? 'agent' : 'codex' + } + return method.toLowerCase() // 'agent' | 'codex' | 'gemini' +} +``` + +## Execution Prompt Builder + +```javascript +function buildExecutionPrompt(issueId, solution) { + return ` +## Issue +ID: ${issueId} +Title: ${solution.bound.title || 'N/A'} + +## Solution Plan +${JSON.stringify(solution.bound, null, 2)} + +## Implementation Requirements +1. Follow the solution plan tasks in order +2. Write clean, minimal code following existing patterns +3. Run tests after each significant change +4. Ensure all existing tests still pass +5. Do NOT over-engineer — implement exactly what the solution specifies + +## Quality Checklist +- [ ] All solution tasks implemented +- [ ] No TypeScript/linting errors +- [ ] Existing tests pass +- [ ] New tests added where appropriate +- [ ] No security vulnerabilities introduced +` +} +``` + +## Code Review (Optional) + +```javascript +function executeCodeReview(reviewTool, issueId) { + if (reviewTool === 'Gemini Review') { + shell(`ccw cli -p "PURPOSE: Code review for ${issueId} implementation +TASK: Verify solution convergence, check test coverage, analyze quality +MODE: analysis +CONTEXT: @**/* +EXPECTED: Quality assessment with issue identification +CONSTRAINTS: Focus on solution adherence" --tool gemini --mode analysis`) + } else if (reviewTool === 'Codex Review') { + shell(`ccw cli --tool codex --mode review --uncommitted`) + } + // Agent Review: perform inline review (read diff, analyze) +} +``` + +## Role Boundaries + +### MUST + +- 仅处理被分配的 wave 中的 exec tasks +- 按依赖顺序(topological sort)执行任务 +- 每个 task 完成后输出 IMPL_COMPLETE +- 所有 tasks 完成后输出 WAVE_DONE +- 通过 spawn_agent 调用 code-developer(agent 后端) +- 运行测试验证实现 + +### MUST NOT + +- ❌ 创建 issue(planner 职责) +- ❌ 修改 solution 或 queue(planner 职责) +- ❌ Spawn issue-plan-agent 或 issue-queue-agent +- ❌ 处理非当前 wave 的任务 +- ❌ 跳过测试验证直接 commit + +## Topological Sort + +```javascript +function topologicalSort(tasks) { + const taskMap = new Map(tasks.map(t => [t.issue_id, t])) + const visited = new Set() + const result = [] + + function visit(id) { + if (visited.has(id)) return + visited.add(id) + const task = taskMap.get(id) + if (task?.depends_on) { + task.depends_on.forEach(dep => visit(dep)) + } + result.push(task) + } + + tasks.forEach(t => visit(t.issue_id)) + return result.filter(Boolean) +} +``` + +## Key Reminders + +**ALWAYS**: +- Read role definition file as FIRST action (Step 1) +- Follow structured output template (IMPL_COMPLETE / WAVE_DONE) +- Verify tests pass before committing +- Respect dependency ordering within the wave +- Include executor backend info and commit hash in reports + +**NEVER**: +- Skip test verification before commit +- Modify files outside of the assigned solution scope +- Produce unstructured output +- Continue to next task if current has unresolved blockers +- Create new issues or modify planning artifacts + +## Error Handling + +| Scenario | Action | +|----------|--------| +| Solution not found | Report IMPL_COMPLETE with status=failed, reason | +| code-developer timeout | Urge convergence via send_input, close and report | +| CLI execution failure | Include resume_hint in IMPL_COMPLETE output | +| Tests failing | Report with test_output excerpt and resume_hint | +| Git commit failure | Retry once, then report in IMPL_COMPLETE | +| Unknown execution_method | Fallback to 'agent' with warning | +| Dependency task failed | Skip dependent tasks, report as failed with reason | diff --git a/.codex/skills/team-planex/agents/planex-planner.md b/.codex/skills/team-planex/agents/planex-planner.md new file mode 100644 index 00000000..fdb41816 --- /dev/null +++ b/.codex/skills/team-planex/agents/planex-planner.md @@ -0,0 +1,291 @@ +--- +name: planex-planner +description: | + Planning lead for PlanEx pipeline. Decomposes requirements into issues, + generates solutions via issue-plan-agent, forms execution queues via + issue-queue-agent, outputs wave-structured data for orchestrator dispatch. +color: blue +skill: team-planex +--- + +# PlanEx Planner + +需求拆解 → issue 创建 → 方案设计 → 队列编排 → 输出 wave 数据。内部 spawn issue-plan-agent 和 issue-queue-agent 子代理,通过 Wave Pipeline 持续推进。每完成一个 wave 立即输出 WAVE_READY,等待 orchestrator send_input 继续下一 wave。 + +## Core Capabilities + +1. **Requirement Decomposition**: 将需求文本/plan 文件拆解为独立 issues +2. **Solution Planning**: 通过 issue-plan-agent 为每个 issue 生成 solution +3. **Queue Formation**: 通过 issue-queue-agent 排序 solutions 并检测冲突 +4. **Wave Output**: 每个 wave 完成后输出结构化 WAVE_READY 数据 + +## Execution Process + +### Step 1: Context Loading + +**MANDATORY**: Execute these steps FIRST before any other action. + +1. Read this role definition file (already done if you're reading this) +2. Read: `.workflow/project-tech.json` — understand project technology stack +3. Read: `.workflow/project-guidelines.json` — understand project conventions +4. Parse the TASK ASSIGNMENT from the spawn message for: + - **Goal**: What to achieve + - **Input**: Issue IDs / text / plan file + - **Execution Config**: execution_method + code_review settings + - **Deliverables**: WAVE_READY + ALL_PLANNED structured output + +### Step 2: Input Parsing & Issue Creation + +Parse the input from TASK ASSIGNMENT and create issues as needed. + +```javascript +const input = taskAssignment.input + +// 1) 已有 Issue IDs +const issueIds = input.match(/ISS-\d{8}-\d{6}/g) || [] + +// 2) 文本输入 → 创建 issue +const textMatch = input.match(/text:\s*(.+)/) +if (textMatch && issueIds.length === 0) { + // Use ccw issue create CLI to create issue from text + const result = shell(`ccw issue create --data '{"title":"${textMatch[1]}","description":"${textMatch[1]}"}' --json`) + const newIssue = JSON.parse(result) + issueIds.push(newIssue.id) +} + +// 3) Plan 文件 → 解析并批量创建 issues +const planMatch = input.match(/plan_file:\s*(\S+)/) +if (planMatch && issueIds.length === 0) { + const planContent = read_file(planMatch[1]) + + // Check if execution-plan.json from req-plan-with-file + try { + const content = JSON.parse(planContent) + if (content.waves && content.issue_ids) { + // execution-plan format: use wave structure directly + executionPlan = content + issueIds = content.issue_ids + } + } catch { + // Regular plan file: parse phases and create issues + const phases = parsePlanPhases(planContent) + for (const phase of phases) { + const result = shell(`ccw issue create --data '{"title":"${phase.title}","description":"${phase.description}"}' --json`) + issueIds.push(JSON.parse(result).id) + } + } +} +``` + +### Step 3: Wave-Based Solution Planning + +Group issues into waves, spawn sub-agents for each wave. + +```javascript +const projectRoot = shell('cd . && pwd').trim() + +// Group into waves (max 5 per wave, or use execution-plan wave structure) +const WAVE_SIZE = 5 +let waves +if (executionPlan) { + waves = executionPlan.waves.map(w => w.issue_ids) +} else { + waves = [] + for (let i = 0; i < issueIds.length; i += WAVE_SIZE) { + waves.push(issueIds.slice(i, i + WAVE_SIZE)) + } +} + +let waveNum = 0 +for (const waveIssues of waves) { + waveNum++ + + // --- Step 3a: Spawn issue-plan-agent for solutions --- + const planAgent = spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/issue-plan-agent.md (MUST read first) +2. Read: .workflow/project-tech.json +3. Read: .workflow/project-guidelines.json + +--- + +Goal: Generate solutions for Wave ${waveNum} issues + +issue_ids: ${JSON.stringify(waveIssues)} +project_root: "${projectRoot}" + +## Requirements +- Generate solutions for each issue +- Auto-bind single solutions +- For multiple solutions, select the most pragmatic one + +## Deliverables +Structured output with solution bindings per issue. +` + }) + + const planResult = wait({ ids: [planAgent], timeout_ms: 600000 }) + + if (planResult.timed_out) { + send_input({ id: planAgent, message: "Please finalize solutions and output current results." }) + wait({ ids: [planAgent], timeout_ms: 120000 }) + } + + close_agent({ id: planAgent }) + + // --- Step 3b: Spawn issue-queue-agent for ordering --- + const queueAgent = spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/issue-queue-agent.md (MUST read first) +2. Read: .workflow/project-tech.json + +--- + +Goal: Form execution queue for Wave ${waveNum} + +issue_ids: ${JSON.stringify(waveIssues)} +project_root: "${projectRoot}" + +## Requirements +- Order solutions by dependency (DAG) +- Detect conflicts between solutions +- Output execution queue to .workflow/issues/queue/execution-queue.json + +## Deliverables +Structured execution queue with dependency ordering. +` + }) + + const queueResult = wait({ ids: [queueAgent], timeout_ms: 300000 }) + + if (queueResult.timed_out) { + send_input({ id: queueAgent, message: "Please finalize queue and output results." }) + wait({ ids: [queueAgent], timeout_ms: 60000 }) + } + + close_agent({ id: queueAgent }) + + // --- Step 3c: Read queue and output WAVE_READY --- + const queuePath = `.workflow/issues/queue/execution-queue.json` + const queue = JSON.parse(read_file(queuePath)) + + const execTasks = queue.queue.map(entry => ({ + issue_id: entry.issue_id, + solution_id: entry.solution_id, + title: entry.title || entry.issue_id, + priority: entry.priority || "normal", + depends_on: entry.depends_on || [] + })) + + // Output structured wave data for orchestrator + console.log(` +WAVE_READY: +${JSON.stringify({ + wave_number: waveNum, + issue_ids: waveIssues, + queue_path: queuePath, + exec_tasks: execTasks +}, null, 2)} +`) + + // Wait for orchestrator send_input before continuing to next wave + // (orchestrator will send: "Wave N dispatched. Continue to Wave N+1.") +} +``` + +### Step 4: Finalization + +After all waves are planned, output ALL_PLANNED signal. + +```javascript +console.log(` +ALL_PLANNED: +${JSON.stringify({ + total_waves: waveNum, + total_issues: issueIds.length +}, null, 2)} +`) +``` + +## Role Boundaries + +### MUST + +- 仅执行规划和拆解工作 +- 每个 wave 完成后输出 WAVE_READY 结构化数据 +- 所有 wave 完成后输出 ALL_PLANNED +- 通过 spawn_agent 调用 issue-plan-agent 和 issue-queue-agent +- 等待 orchestrator send_input 才继续下一 wave + +### MUST NOT + +- ❌ 直接编写/修改业务代码(executor 职责) +- ❌ Spawn code-developer agent(executor 职责) +- ❌ 运行项目测试 +- ❌ git commit 代码变更 +- ❌ 直接修改 solution 内容(issue-plan-agent 负责) + +## Plan File Parsing + +```javascript +function parsePlanPhases(planContent) { + const phases = [] + const phaseRegex = /^#{2,3}\s+(?:Phase|Step|阶段)\s*\d*[:.:]\s*(.+?)$/gm + let match, lastIndex = 0, lastTitle = null + + while ((match = phaseRegex.exec(planContent)) !== null) { + if (lastTitle !== null) { + phases.push({ title: lastTitle, description: planContent.slice(lastIndex, match.index).trim() }) + } + lastTitle = match[1].trim() + lastIndex = match.index + match[0].length + } + + if (lastTitle !== null) { + phases.push({ title: lastTitle, description: planContent.slice(lastIndex).trim() }) + } + + if (phases.length === 0) { + const titleMatch = planContent.match(/^#\s+(.+)$/m) + phases.push({ + title: titleMatch ? titleMatch[1] : 'Plan Implementation', + description: planContent.slice(0, 500) + }) + } + + return phases +} +``` + +## Key Reminders + +**ALWAYS**: +- Read role definition file as FIRST action (Step 1) +- Follow structured output template (WAVE_READY / ALL_PLANNED) +- Stay within planning boundaries (no code implementation) +- Spawn issue-plan-agent and issue-queue-agent for each wave +- Include all issue IDs and solution references in wave data + +**NEVER**: +- Modify source code files +- Skip context loading (Step 1) +- Produce unstructured or free-form output +- Continue to next wave without outputting WAVE_READY +- Close without outputting ALL_PLANNED + +## Error Handling + +| Scenario | Action | +|----------|--------| +| Issue creation failure | Retry once with simplified text, report in output | +| issue-plan-agent timeout | Urge convergence via send_input, close and report partial | +| issue-queue-agent failure | Create exec tasks without DAG ordering | +| Plan file not found | Report error in output with CLARIFICATION_NEEDED | +| Empty input (no issues, no text) | Output CLARIFICATION_NEEDED asking for requirements | +| Sub-agent produces invalid output | Report error, continue with available data | diff --git a/.codex/skills/team-planex/orchestrator.md b/.codex/skills/team-planex/orchestrator.md new file mode 100644 index 00000000..273a10ca --- /dev/null +++ b/.codex/skills/team-planex/orchestrator.md @@ -0,0 +1,416 @@ +--- +name: team-planex +description: | + 2-member plan-and-execute pipeline with Wave Pipeline for concurrent planning and execution. + Planner decomposes requirements into issues, generates solutions, forms execution queues. + Executor implements solutions via configurable backends (agent/codex/gemini). +agents: 2 +phases: 4 +--- + +# Team PlanEx + +2 成员边规划边执行团队。通过 Wave Pipeline(波次流水线)实现 planner 和 executor 并行工作:planner 完成一个 wave 的 queue 后,orchestrator 立即 spawn executor agent 处理该 wave,同时 send_input 让 planner 继续下一 wave。 + +## Architecture Overview + +``` +┌──────────────────────────────────────────────┐ +│ Orchestrator (this file) │ +│ → Parse input → Spawn planner → Spawn exec │ +└────────────────┬─────────────────────────────┘ + │ Wave Pipeline + ┌───────┴───────┐ + ↓ ↓ + ┌─────────┐ ┌──────────┐ + │ planner │ │ executor │ + │ (plan) │ │ (impl) │ + └─────────┘ └──────────┘ + │ │ + issue-plan-agent code-developer + issue-queue-agent (or codex/gemini CLI) +``` + +## Agent Registry + +| Agent | Role File | Responsibility | New/Existing | +|-------|-----------|----------------|--------------| +| `planex-planner` | `.codex/skills/team-planex/agents/planex-planner.md` | 需求拆解 → issue 创建 → 方案设计 → 队列编排 | New (skill-specific) | +| `planex-executor` | `.codex/skills/team-planex/agents/planex-executor.md` | 加载 solution → 代码实现 → 测试 → 提交 | New (skill-specific) | +| `issue-plan-agent` | `~/.codex/agents/issue-plan-agent.md` | ACE exploration + solution generation + binding | Existing | +| `issue-queue-agent` | `~/.codex/agents/issue-queue-agent.md` | Solution ordering + conflict detection | Existing | +| `code-developer` | `~/.codex/agents/code-developer.md` | Code implementation (agent backend) | Existing | + +## Input Types + +支持 3 种输入方式(通过 orchestrator message 传入): + +| 输入类型 | 格式 | 示例 | +|----------|------|------| +| Issue IDs | 直接传入 ID | `ISS-20260215-001 ISS-20260215-002` | +| 需求文本 | `--text '...'` | `--text '实现用户认证模块'` | +| Plan 文件 | `--plan path` | `--plan plan/2026-02-15-auth.md` | + +## Execution Method Selection + +支持 3 种执行后端: + +| Executor | 后端 | 适用场景 | +|----------|------|----------| +| `agent` | code-developer subagent | 简单任务、同步执行 | +| `codex` | `ccw cli --tool codex --mode write` | 复杂任务、后台执行 | +| `gemini` | `ccw cli --tool gemini --mode write` | 分析类任务、后台执行 | + +## Phase Execution + +### Phase 1: Input Parsing & Preference Collection + +Parse user arguments and determine execution configuration. + +```javascript +// Parse input from orchestrator message +const args = orchestratorMessage +const issueIds = args.match(/ISS-\d{8}-\d{6}/g) || [] +const textMatch = args.match(/--text\s+['"]([^'"]+)['"]/) +const planMatch = args.match(/--plan\s+(\S+)/) +const autoYes = /\b(-y|--yes)\b/.test(args) +const explicitExec = args.match(/--exec[=\s]+(agent|codex|gemini|auto)/i)?.[1] + +let executionConfig + +if (explicitExec) { + executionConfig = { + executionMethod: explicitExec.charAt(0).toUpperCase() + explicitExec.slice(1), + codeReviewTool: "Skip" + } +} else if (autoYes) { + executionConfig = { executionMethod: "Auto", codeReviewTool: "Skip" } +} else { + // Interactive: ask user for preferences + // (orchestrator handles user interaction directly) +} +``` + +### Phase 2: Planning (Planner Agent — Deep Interaction) + +Spawn planner agent for wave-based planning. Uses send_input for multi-wave progression. + +```javascript +// Build planner input context +let plannerInput = "" +if (issueIds.length > 0) plannerInput = `issue_ids: ${JSON.stringify(issueIds)}` +else if (textMatch) plannerInput = `text: ${textMatch[1]}` +else if (planMatch) plannerInput = `plan_file: ${planMatch[1]}` + +const planner = spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: .codex/skills/team-planex/agents/planex-planner.md (MUST read first) +2. Read: .workflow/project-tech.json +3. Read: .workflow/project-guidelines.json + +--- + +Goal: Decompose requirements into waves of executable solutions + +## Input +${plannerInput} + +## Execution Config +execution_method: ${executionConfig.executionMethod} +code_review: ${executionConfig.codeReviewTool} + +## Deliverables +For EACH wave, output structured wave data: + +\`\`\` +WAVE_READY: +wave_number: N +issue_ids: [ISS-xxx, ...] +queue_path: .workflow/issues/queue/execution-queue.json +exec_tasks: [ + { issue_id: "ISS-xxx", solution_id: "SOL-xxx", title: "...", priority: "normal", depends_on: [] }, + ... +] +\`\`\` + +After ALL waves planned, output: +\`\`\` +ALL_PLANNED: +total_waves: N +total_issues: N +\`\`\` + +## Quality bar +- Every issue has a bound solution +- Queue respects dependency DAG +- Wave boundaries are logical groupings +` +}) + +// Wait for Wave 1 +const wave1 = wait({ ids: [planner], timeout_ms: 600000 }) + +if (wave1.timed_out) { + send_input({ id: planner, message: "Please finalize current wave and output WAVE_READY." }) + const retry = wait({ ids: [planner], timeout_ms: 120000 }) +} + +// Parse wave data from planner output +const wave1Data = parseWaveReady(wave1.status[planner].completed) +``` + +### Phase 3: Wave Pipeline (Planning + Execution Interleaved) + +Pipeline: spawn executor for current wave while planner continues next wave. + +```javascript +const allAgentIds = [planner] +const executorAgents = [] +let waveNum = 1 +let allPlanned = false + +while (!allPlanned) { + // --- Spawn executor for current wave --- + const waveData = parseWaveReady(currentWaveOutput) + + if (waveData && waveData.exec_tasks.length > 0) { + const executor = spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: .codex/skills/team-planex/agents/planex-executor.md (MUST read first) +2. Read: .workflow/project-tech.json +3. Read: .workflow/project-guidelines.json + +--- + +Goal: Implement all solutions in Wave ${waveNum} + +## Wave ${waveNum} Tasks +${JSON.stringify(waveData.exec_tasks, null, 2)} + +## Execution Config +execution_method: ${executionConfig.executionMethod} +code_review: ${executionConfig.codeReviewTool} + +## Deliverables +For each task, output: +\`\`\` +IMPL_COMPLETE: +issue_id: ISS-xxx +status: success|failed +test_result: pass|fail +commit: +\`\`\` + +After all wave tasks done: +\`\`\` +WAVE_DONE: +wave_number: ${waveNum} +completed: N +failed: N +\`\`\` + +## Quality bar +- All existing tests pass after each implementation +- Code follows project conventions +- One commit per solution +` + }) + allAgentIds.push(executor) + executorAgents.push({ id: executor, wave: waveNum }) + } + + // --- Tell planner to continue next wave --- + if (!allPlanned) { + send_input({ id: planner, message: `Wave ${waveNum} dispatched to executor. Continue to Wave ${waveNum + 1}.` }) + + // Wait for both: planner (next wave) + current executor + const activeIds = [planner] + if (executorAgents.length > 0) { + activeIds.push(executorAgents[executorAgents.length - 1].id) + } + + const results = wait({ ids: activeIds, timeout_ms: 600000 }) + + // Check planner output + const plannerOutput = results.status[planner]?.completed || "" + if (plannerOutput.includes("ALL_PLANNED")) { + allPlanned = true + } else if (plannerOutput.includes("WAVE_READY")) { + waveNum++ + currentWaveOutput = plannerOutput + } + } +} + +// Wait for remaining executor agents +const pendingExecutors = executorAgents + .map(e => e.id) + .filter(id => !completedIds.includes(id)) + +if (pendingExecutors.length > 0) { + const finalResults = wait({ ids: pendingExecutors, timeout_ms: 900000 }) + + // Handle timeout + if (finalResults.timed_out) { + const pending = pendingExecutors.filter(id => !finalResults.status[id]?.completed) + pending.forEach(id => { + send_input({ id, message: "Please finalize current task and output results." }) + }) + wait({ ids: pending, timeout_ms: 120000 }) + } +} +``` + +### Phase 4: Result Aggregation & Cleanup + +```javascript +// Collect results from all executors +const pipelineResults = { + waves: [], + totalCompleted: 0, + totalFailed: 0 +} + +executorAgents.forEach(({ id, wave }) => { + const output = results.status[id]?.completed || "" + const waveDone = parseWaveDone(output) + pipelineResults.waves.push({ + wave, + completed: waveDone?.completed || 0, + failed: waveDone?.failed || 0 + }) + pipelineResults.totalCompleted += waveDone?.completed || 0 + pipelineResults.totalFailed += waveDone?.failed || 0 +}) + +// Output final summary +console.log(` +## PlanEx Pipeline Complete + +### Summary +- Total Waves: ${waveNum} +- Total Completed: ${pipelineResults.totalCompleted} +- Total Failed: ${pipelineResults.totalFailed} + +### Wave Details +${pipelineResults.waves.map(w => + `- Wave ${w.wave}: ${w.completed} completed, ${w.failed} failed` +).join('\n')} +`) + +// Cleanup ALL agents +allAgentIds.forEach(id => { + try { close_agent({ id }) } catch { /* already closed */ } +}) +``` + +## Coordination Protocol + +### File-Based Communication + +Since Codex agents have isolated contexts, use file-based coordination: + +| File | Purpose | Writer | Reader | +|------|---------|--------|--------| +| `.workflow/.team/PEX-{slug}-{date}/wave-{N}.json` | Wave plan data | planner | orchestrator | +| `.workflow/.team/PEX-{slug}-{date}/exec-{issueId}.json` | Execution result | executor | orchestrator | +| `.workflow/.team/PEX-{slug}-{date}/pipeline-log.ndjson` | Event log | both | orchestrator | +| `.workflow/issues/queue/execution-queue.json` | Execution queue | planner (via issue-queue-agent) | executor | + +### Wave Data Format + +```json +{ + "wave_number": 1, + "issue_ids": ["ISS-20260215-001", "ISS-20260215-002"], + "queue_path": ".workflow/issues/queue/execution-queue.json", + "exec_tasks": [ + { + "issue_id": "ISS-20260215-001", + "solution_id": "SOL-001", + "title": "Implement auth module", + "priority": "high", + "depends_on": [] + } + ] +} +``` + +### Execution Result Format + +```json +{ + "issue_id": "ISS-20260215-001", + "status": "success", + "executor": "agent", + "test_result": "pass", + "commit": "abc123", + "files_changed": ["src/auth/login.ts", "src/auth/login.test.ts"] +} +``` + +## Lifecycle Management + +### Timeout Handling + +| Timeout Scenario | Action | +|-----------------|--------| +| Planner wave timeout | send_input to urge convergence, retry wait | +| Executor impl timeout | send_input to finalize, record partial result | +| All agents timeout | Log error, abort with partial state | + +### Cleanup Protocol + +```javascript +// Track all agents created during execution +const allAgentIds = [] + +// ... (agents added during phase execution) ... + +// Final cleanup (end of orchestrator or on error) +allAgentIds.forEach(id => { + try { close_agent({ id }) } catch { /* already closed */ } +}) +``` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| Planner wave failure | Retry once via send_input, then abort pipeline | +| Executor impl failure | Record failure, continue with next wave tasks | +| No issues created from text | Report to user, abort | +| Solution generation failure | Skip issue, continue with remaining | +| Queue formation failure | Create exec tasks without DAG ordering | +| Pipeline stall (no progress) | Timeout handling → urge convergence → abort | +| Missing role file | Log error, use inline fallback instructions | + +## Helper Functions + +```javascript +function parseWaveReady(output) { + const match = output.match(/WAVE_READY:\s*\n([\s\S]*?)(?=\n```|$)/) + if (!match) return null + // Parse structured wave data + return JSON.parse(match[1]) +} + +function parseWaveDone(output) { + const match = output.match(/WAVE_DONE:\s*\n([\s\S]*?)(?=\n```|$)/) + if (!match) return null + return JSON.parse(match[1]) +} + +function resolveExecutor(method, taskCount) { + if (method.toLowerCase() === 'auto') { + return taskCount <= 3 ? 'agent' : 'codex' + } + return method.toLowerCase() +} +```