refactor(team-planex): redesign skill with inverted control and beat model

- Delete executor agent (main flow IS the executor now)
- Rewrite SKILL.md: delegated planning + inline execution
- Input accepts issues.jsonl / roadmap session from roadmap-with-file
- Single reusable planner agent via send_input (Pattern 2.3)
- Interleaved plan-execute loop with eager delegation
- Follow codex v3 conventions (decision tables, placeholders)
- Remove complexity assessment and dynamic splitting
This commit is contained in:
catlog22
2026-03-01 15:06:06 +08:00
parent 1f859ae4b9
commit 8799a9c2fd
2 changed files with 425 additions and 393 deletions

View File

@@ -1,261 +1,503 @@
---
name: team-planex
description: |
Inline planning + delegated execution pipeline. Main flow does planning directly,
spawns Codex executor per issue immediately. All execution via Codex CLI only.
Plan-and-execute pipeline with inverted control. Accepts issues.jsonl or roadmap
session from roadmap-with-file. Delegates planning to issue-plan-agent (background),
executes inline (main flow). Interleaved plan-execute loop.
---
# Team PlanEx (Codex)
# Team PlanEx
主流程内联规划 + 委托执行。SKILL.md 自身完成规划(不再 spawn planner agent每完成一个 issue 的 solution 后立即 spawn executor agent 并行实现,无需等待所有规划完成。
接收 `issues.jsonl` 或 roadmap session 作为输入,委托规划 + 内联执行。Spawn issue-plan-agent 后台规划下一个 issue主流程内联执行当前已规划的 issue。交替循环规划 → 执行 → 规划下一个 → 执行 → 直到所有 issue 完成。
## Architecture
```
┌────────────────────────────────────────┐
│ SKILL.md (主流程 = 规划 + 节拍控制) │
┌────────────────────────────────────────────────────
│ SKILL.md (主流程 = 内联执行 + 循环控制)
│ │
│ Phase 1: 解析输入 + 初始化 session │
│ Phase 2: 逐 issue 规划循环 (内联)
│ ├── issue-plan → 写 solution artifact
│ ├── spawn executor agent ────────────┼──> [executor] 实现
└── continue (不等 executor)
Phase 3: 等待所有 executors
Phase 4: 汇总报告
└────────────────────────────────────────┘
│ Phase 1: 加载 issues.jsonl / roadmap session
│ Phase 2: 规划-执行交替循环
│ ├── 取下一个未规划 issue → spawn planner (bg)
│ ├── 取下一个已规划 issue → 内联执行 │
│ ├── 内联实现 (Read/Edit/Write/Bash)
│ ├── 验证测试 + 自修复 (max 3 retries)
│ └── git commit
│ └── 循环直到所有 issue 规划+执行完毕 │
│ Phase 3: 汇总报告 │
└────────────────────────────────────────────────────┘
Planner (single reusable agent, background):
issue-plan-agent spawn once → send_input per issue → write solution JSON → write .ready marker
```
## Beat Model (Interleaved Plan-Execute Loop)
```
Interleaved Loop (Phase 2, single planner reused via send_input):
═══════════════════════════════════════════════════════════════
Beat 1 Beat 2 Beat 3
| | |
spawn+plan(A) send_input(C) (drain)
↓ ↓ ↓
poll → A.ready poll → B.ready poll → C.ready
↓ ↓ ↓
exec(A) exec(B) exec(C)
↓ ↓ ↓
send_input(B) send_input(done) done
(eager delegate) (all delegated)
═══════════════════════════════════════════════════════════════
Planner timeline (never idle between issues):
─────────────────────────────────────────────────────────────
Planner: ├─plan(A)─┤├─plan(B)─┤├─plan(C)─┤ done
Main: 2a(spawn) ├─exec(A)──┤├─exec(B)──┤├─exec(C)──┤
^2f→send(B) ^2f→send(C)
─────────────────────────────────────────────────────────────
Single Beat Detail:
───────────────────────────────────────────────────────────────
1. Delegate next 2. Poll ready 3. Execute 4. Verify + commit
(2a or 2f eager) solutions inline + eager delegate
| | | |
send_input(bg) Glob(*.ready) Read/Edit/Write test → commit
│ → send_input(next)
wait if none ready
───────────────────────────────────────────────────────────────
```
---
## Agent Registry
| Agent | Role File | Responsibility |
|-------|-----------|----------------|
| `executor` | `~/.codex/agents/planex-executor.md` | Codex CLI implementation per issue |
| Agent | Role File | Responsibility | Pattern |
|-------|-----------|----------------|---------|
| `issue-plan-agent` | `~/.codex/agents/issue-plan-agent.md` | Explore codebase + generate solutions, single instance reused via send_input | 2.3 Deep Interaction |
> Executor agent must be deployed to `~/.codex/agents/` before use.
> Source: `.codex/skills/team-planex/agents/`
> **COMPACT PROTECTION**: Agent files are execution documents. When context compression occurs and agent instructions are reduced to summaries, **you MUST immediately `Read` the corresponding agent.md to reload before continuing execution**.
---
## Input Parsing
## Subagent API Reference
Supported input types (parse from `$ARGUMENTS`):
| Type | Detection | Handler |
|------|-----------|---------|
| Issue IDs | `ISS-\d{8}-\d{6}` regex | Use directly for planning |
| Text | `--text '...'` flag | Create issue(s) first via CLI |
| Plan file | `--plan <path>` flag | Read file, parse phases, batch create issues |
### Issue Creation (when needed)
For `--text` input:
```bash
ccw issue create --data '{"title":"<title>","description":"<description>"}' --json
```
For `--plan` input:
- Match `## Phase N: Title`, `## Step N: Title`, or `### N. Title`
- Each match → one issue (title + description from section content)
- Fallback: no structure found → entire file as single issue
---
## Session Setup
Before processing issues, initialize session directory:
### spawn_agent
```javascript
const slug = toSlug(inputDescription).slice(0, 20)
const date = new Date().toISOString().slice(0, 10).replace(/-/g, '')
const sessionDir = `.workflow/.team/PEX-${slug}-${date}`
const artifactsDir = `${sessionDir}/artifacts/solutions`
Bash(`mkdir -p "${artifactsDir}"`)
Write({
file_path: `${sessionDir}/team-session.json`,
content: JSON.stringify({
session_id: `PEX-${slug}-${date}`,
input_type: inputType,
input: rawInput,
status: "running",
started_at: new Date().toISOString(),
executors: []
}, null, 2)
})
```
---
## Phase 1: Parse Input + Initialize
1. Parse `$ARGUMENTS` to determine input type
2. Create issues if needed (--text / --plan)
3. Collect all issue IDs
4. Initialize session directory
---
## Phase 2: Inline Planning Loop
For each issue, execute planning inline (no planner agent):
### 2a. Generate Solution via issue-plan-agent
```javascript
const planAgent = spawn_agent({
const agentId = 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
---
issue_ids: ["${issueId}"]
project_root: "${projectRoot}"
## Requirements
- Generate solution for this issue
- Auto-bind single solution
- Output solution JSON when complete
${taskContext}
`
})
const result = wait({ ids: [planAgent], timeout_ms: 600000 })
close_agent({ id: planAgent })
```
### 2b. Write Solution Artifact
### wait
```javascript
const solution = parseSolution(result)
Write({
file_path: `${artifactsDir}/${issueId}.json`,
content: JSON.stringify({
session_id: sessionId,
issue_id: issueId,
solution: solution,
planned_at: new Date().toISOString()
}, null, 2)
const result = wait({
ids: [agentId],
timeout_ms: 600000 // 10 minutes
})
```
### 2c. Spawn Executor Immediately
### send_input
Continue interaction with active agent (reuse for next issue).
```javascript
const executorId = spawn_agent({
send_input({
id: agentId,
message: `
## NEXT ISSUE
issue_ids: ["<nextIssueId>"]
## Output Requirements
1. Generate solution for this issue
2. Write solution JSON to: <artifactsDir>/<nextIssueId>.json
3. Write ready marker to: <artifactsDir>/<nextIssueId>.ready
`
})
```
### close_agent
```javascript
close_agent({ id: agentId })
```
---
## Input
Accepts output from `roadmap-with-file` or direct `issues.jsonl` path.
Supported input forms (parse from `$ARGUMENTS`):
| Form | Detection | Example |
|------|-----------|---------|
| Roadmap session | `RMAP-` prefix or `--session` flag | `team-planex --session RMAP-auth-20260301` |
| Issues JSONL path | `.jsonl` extension | `team-planex .workflow/issues/issues.jsonl` |
| Issue IDs | `ISS-\d{8}-\d{3,6}` regex | `team-planex ISS-20260301-001 ISS-20260301-002` |
### Input Resolution
| Input Form | Resolution |
|------------|------------|
| `--session RMAP-*` | Read `.workflow/.roadmap/<sessionId>/roadmap.md` → extract issue IDs from Roadmap table → load issue data from `.workflow/issues/issues.jsonl` |
| `.jsonl` path | Read file → parse each line as JSON → collect all issues |
| Issue IDs | Use directly → fetch details via `ccw issue status <id> --json` |
### Issue Record Fields Used
| Field | Usage |
|-------|-------|
| `id` | Issue identifier for planning and execution |
| `title` | Commit message and reporting |
| `status` | Skip if already `completed` |
| `tags` | Wave ordering: `wave-1` before `wave-2` |
| `extended_context.notes.depends_on_issues` | Execution ordering |
### Wave Ordering
Issues are sorted by wave tag for execution order:
| Priority | Rule |
|----------|------|
| 1 | Lower wave number first (`wave-1` before `wave-2`) |
| 2 | Within same wave: issues without `depends_on_issues` first |
| 3 | Within same wave + no deps: original order from JSONL |
---
## Session Setup
Initialize session directory before processing:
| Item | Value |
|------|-------|
| Slug | `toSlug(<first issue title>)` truncated to 20 chars |
| Date | `YYYYMMDD` format |
| Session dir | `.workflow/.team/PEX-<slug>-<date>` |
| Solutions dir | `<sessionDir>/artifacts/solutions` |
Create directories:
```bash
mkdir -p "<sessionDir>/artifacts/solutions"
```
Write `<sessionDir>/team-session.json`:
| Field | Value |
|-------|-------|
| `session_id` | `PEX-<slug>-<date>` |
| `input_type` | `roadmap` / `jsonl` / `issue_ids` |
| `source_session` | Roadmap session ID (if applicable) |
| `issue_ids` | Array of all issue IDs to process (wave-sorted) |
| `status` | `"running"` |
| `started_at` | ISO timestamp |
---
## Phase 1: Load Issues + Initialize
1. Parse `$ARGUMENTS` to determine input form
2. Resolve issues (see Input Resolution table)
3. Filter out issues with `status: completed`
4. Sort by wave ordering
5. Collect into `<issueQueue>` (ordered list of issue IDs to process)
6. Initialize session directory and `team-session.json`
7. Set `<plannedSet>` = {} , `<executedSet>` = {} , `<plannerAgent>` = null (single reusable planner)
---
## Phase 2: Plan-Execute Loop
Interleaved loop that keeps planner agent busy at all times. Each beat: (1) delegate next issue to planner if idle, (2) poll for ready solutions, (3) execute inline, (4) after execution completes, immediately delegate next issue to planner before polling again.
### Loop Entry
Set `<queueIndex>` = 0 (pointer into `<issueQueue>`).
### 2a. Delegate Next Issue to Planner
Single planner agent is spawned once and reused via `send_input` for subsequent issues.
| Condition | Action |
|-----------|--------|
| `<plannerAgent>` is null AND `<queueIndex>` < `<issueQueue>.length` | Spawn planner (first issue), advance `<queueIndex>` |
| `<plannerAgent>` exists, idle AND `<queueIndex>` < `<issueQueue>.length` | `send_input` next issue, advance `<queueIndex>` |
| `<plannerAgent>` is busy | Skip (wait for current planning to finish) |
| `<queueIndex>` >= `<issueQueue>.length` | No more issues to plan |
**First issue — spawn planner**:
```javascript
const plannerAgent = spawn_agent({
message: `
## TASK ASSIGNMENT
### MANDATORY FIRST STEPS (Agent Execute)
1. **Read role definition**: ~/.codex/agents/planex-executor.md (MUST read first)
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
---
## Issue
Issue ID: ${issueId}
Solution file: ${artifactsDir}/${issueId}.json
Session: ${sessionDir}
issue_ids: ["<issueId>"]
project_root: "<projectRoot>"
## Execution
Load solution from file → implement via Codex CLI → verify tests → commit → report.
## Output Requirements
1. Generate solution for this issue
2. Write solution JSON to: <artifactsDir>/<issueId>.json
3. Write ready marker to: <artifactsDir>/<issueId>.ready
- Marker content: {"issue_id":"<issueId>","task_count":<task_count>,"file_count":<file_count>}
## Multi-Issue Mode
You will receive additional issues via follow-up messages. After completing each issue,
output results and wait for next instruction.
`
})
executorIds.push(executorId)
executorIssueMap[executorId] = issueId
```
### 2d. Continue to Next Issue
**Subsequent issues — send_input**:
Do NOT wait for executor. Proceed to next issue immediately.
```javascript
send_input({
id: plannerAgent,
message: `
## NEXT ISSUE
issue_ids: ["<nextIssueId>"]
## Output Requirements
1. Generate solution for this issue
2. Write solution JSON to: <artifactsDir>/<nextIssueId>.json
3. Write ready marker to: <artifactsDir>/<nextIssueId>.ready
- Marker content: {"issue_id":"<nextIssueId>","task_count":<task_count>,"file_count":<file_count>}
`
})
```
Record `<planningIssueId>` = current issue ID.
### 2b. Poll for Ready Solutions
Poll `<artifactsDir>/*.ready` using Glob.
| Condition | Action |
|-----------|--------|
| New `.ready` found (not in `<executedSet>`) | Load `<issueId>.json` solution → proceed to 2c |
| `<plannerAgent>` busy, no `.ready` yet | Check planner: `wait({ ids: [<plannerAgent>], timeout_ms: 30000 })` |
| Planner finished current issue | Mark planner idle, re-poll |
| Planner timed out (30s wait) | Re-poll (planner still working) |
| No `.ready`, planner idle, all issues delegated | Exit loop → Phase 3 |
| Idle >5 minutes total | Exit loop → Phase 3 |
### 2c. Inline Execution
Main flow implements the solution directly. For each task in `solution.tasks`, ordered by `depends_on` sequence:
| Step | Action | Tool |
|------|--------|------|
| 1. Read context | Read all files referenced in current task | Read |
| 2. Identify patterns | Note imports, naming conventions, existing structure | — (inline reasoning) |
| 3. Apply changes | Modify existing files or create new files | Edit (prefer) / Write (new files) |
| 4. Build check | Run project build command if available | Bash |
Build verification:
```bash
npm run build 2>&1 || echo BUILD_FAILED
```
| Build Result | Action |
|--------------|--------|
| Success | Proceed to 2d |
| Failure | Analyze error → fix source → rebuild (max 3 retries) |
| No build command | Skip, proceed to 2d |
### 2d. Verify Tests
Detect test command:
| Priority | Detection |
|----------|-----------|
| 1 | `package.json``scripts.test` |
| 2 | `package.json``scripts.test:unit` |
| 3 | `pytest.ini` / `setup.cfg` (Python) |
| 4 | `Makefile` test target |
Run tests. If tests fail → self-repair loop:
| Attempt | Action |
|---------|--------|
| 13 | Analyze test output → diagnose → fix source code → re-run tests |
| After 3 | Mark issue as failed, log to `<sessionDir>/errors.json`, continue |
### 2e. Git Commit
Stage and commit changes for this issue:
```bash
git add -A
git commit -m "feat(<issueId>): <solution-title>"
```
| Outcome | Action |
|---------|--------|
| Commit succeeds | Record commit hash |
| Commit fails (nothing to commit) | Warn, continue |
| Pre-commit hook fails | Attempt fix once, then warn and continue |
### 2f. Update Status + Eagerly Delegate Next
Update issue status:
```bash
ccw issue update <issueId> --status completed
```
Add `<issueId>` to `<executedSet>`.
**Eager delegation**: Immediately check planner state and delegate next issue before returning to poll:
| Planner State | Action |
|---------------|--------|
| Idle AND more issues in queue | `send_input` next issue → advance `<queueIndex>` |
| Busy (still planning) | Skip — planner already working |
| All issues delegated | Skip — nothing to delegate |
This ensures planner is never idle between beats. Return to 2b for next beat.
---
## Phase 3: Wait All Executors
## Phase 3: Report
### 3a. Cleanup
Close the planner agent. Ignore cleanup failures.
```javascript
if (executorIds.length > 0) {
const execResults = wait({ ids: executorIds, timeout_ms: 1800000 })
if (plannerAgent) {
try { close_agent({ id: plannerAgent }) } catch {}
}
```
if (execResults.timed_out) {
const pending = executorIds.filter(id => !execResults.status[id]?.completed)
if (pending.length > 0) {
const pendingIssues = pending.map(id => executorIssueMap[id])
Write({
file_path: `${sessionDir}/pending-executors.json`,
content: JSON.stringify({ pending_issues: pendingIssues, executor_ids: pending }, null, 2)
})
}
}
### 3b. Generate Report
// Collect summaries
const summaries = executorIds.map(id => ({
issue_id: executorIssueMap[id],
status: execResults.status[id]?.completed ? 'completed' : 'timeout',
output: execResults.status[id]?.completed ?? null
}))
Update `<sessionDir>/team-session.json`:
// Cleanup
executorIds.forEach(id => {
try { close_agent({ id }) } catch { /* already closed */ }
})
| Field | Value |
|-------|-------|
| `status` | `"completed"` |
| `completed_at` | ISO timestamp |
| `results.total` | Total issues in queue |
| `results.completed` | Count in `<executedSet>` |
| `results.failed` | Count of failed issues |
Output summary:
```
## Pipeline Complete
**Total issues**: <total>
**Completed**: <completed>
**Failed**: <failed>
<per-issue status list>
Session: <sessionDir>
```
---
## File-Based Coordination Protocol
| File | Writer | Reader | Purpose |
|------|--------|--------|---------|
| `<artifactsDir>/<issueId>.json` | planner | main flow | Solution data |
| `<artifactsDir>/<issueId>.ready` | planner | main flow | Atomicity signal |
### Ready Marker Format
```json
{
"issue_id": "<issueId>",
"task_count": "<task_count>",
"file_count": "<file_count>"
}
```
---
## Phase 4: Report
## Session Directory
```javascript
const completed = summaries.filter(s => s.status === 'completed').length
const failed = summaries.filter(s => s.status === 'timeout').length
// Update session
Write({
file_path: `${sessionDir}/team-session.json`,
content: JSON.stringify({
...session,
status: "completed",
completed_at: new Date().toISOString(),
results: { total: executorIds.length, completed, failed }
}, null, 2)
})
return `
## Pipeline Complete
**Total issues**: ${executorIds.length}
**Completed**: ${completed}
**Timed out**: ${failed}
${summaries.map(s => `- ${s.issue_id}: ${s.status}`).join('\n')}
Session: ${sessionDir}
`
```
.workflow/.team/PEX-<slug>-<date>/
├── team-session.json
├── artifacts/
│ └── solutions/
│ ├── <issueId>.json
│ └── <issueId>.ready
└── errors.json
```
---
## User Commands
## Lifecycle Management
During execution, the user may issue:
### Timeout Protocol
| Command | Action |
|---------|--------|
| `check` / `status` | Show executor progress summary |
| `resume` / `continue` | Urge stalled executor |
| Phase | Default Timeout | On Timeout |
|-------|-----------------|------------|
| Phase 1 (Load) | 60s | Report error, stop |
| Phase 2 (Planner wait) | 600s per issue | Skip issue, write `.error` marker |
| Phase 2 (Execution) | No timeout | Self-repair up to 3 retries |
| Phase 2 (Loop idle) | 5 min total idle | Break loop → Phase 3 |
### Cleanup Protocol
At workflow end, close the planner agent:
```javascript
if (plannerAgent) {
try { close_agent({ id: plannerAgent }) } catch { /* already closed */ }
}
```
---
## Error Handling
| Scenario | Resolution |
|----------|------------|
| issue-plan-agent timeout (>10 min) | Skip issue, log error, continue to next |
| issue-plan-agent failure | Retry once, then skip with error log |
| Solution file not written | Executor reports error, logs to `${sessionDir}/errors.json` |
| Executor (Codex CLI) failure | Executor handles resume; logs CLI resume command |
| No issues to process | Report error: no issues found |
| Phase | Scenario | Resolution |
|-------|----------|------------|
| 1 | Invalid input (no issues, bad JSONL) | Report error, stop |
| 1 | Roadmap session not found | Report error, stop |
| 1 | Issue fetch fails | Retry once, then skip issue |
| 2 | Planner spawn fails | Retry once, then skip issue |
| 2 | issue-plan-agent timeout (>10 min) | Skip issue, write `.error` marker, continue |
| 2 | Solution file corrupt / unreadable | Skip, log to `errors.json`, continue |
| 2 | Implementation error | Self-repair up to 3 retries per task |
| 2 | Tests failing after 3 retries | Mark issue failed, log, continue |
| 2 | Git commit fails | Warn, mark completed anyway |
| 2 | Polling idle >5 minutes | Break loop → Phase 3 |
| 3 | Agent cleanup fails | Ignore |
---
## User Commands
| Command | Action |
|---------|--------|
| `check` / `status` | Show progress: planned / executing / completed / failed counts |
| `resume` / `continue` | Re-enter loop from Phase 2 |

View File

@@ -1,210 +0,0 @@
---
name: planex-executor
description: |
PlanEx executor agent. Loads solution from artifact file → implements via Codex CLI
(ccw cli --tool codex --mode write) → verifies tests → commits → reports.
Deploy to: ~/.codex/agents/planex-executor.md
color: green
---
# PlanEx Executor
Single-issue implementation agent. Loads solution from JSON artifact, executes
implementation via Codex CLI, verifies with tests, commits, and outputs a structured
completion report.
## Identity
- **Tag**: `[executor]`
- **Backend**: Codex CLI only (`ccw cli --tool codex --mode write`)
- **Granularity**: One issue per agent instance
## Core Responsibilities
| Action | Allowed |
|--------|---------|
| Read solution artifact from disk | Yes |
| Implement via Codex CLI | Yes |
| Run tests for verification | Yes |
| git commit completed work | Yes |
| Create or modify issues | No |
| Spawn subagents | No |
| Interact with user (AskUserQuestion) | No |
---
## Execution Flow
### Step 1: Load Context
After reading role definition:
- Extract issue ID, solution file path, session dir from task message
### Step 2: Load Solution
Read solution artifact:
```javascript
const solutionData = JSON.parse(Read(solutionFile))
const solution = solutionData.solution
```
If file not found or invalid:
- Log error: `[executor] ERROR: Solution file not found: ${solutionFile}`
- Output: `EXEC_FAILED:${issueId}:solution_file_missing`
- Stop execution
Verify solution has required fields:
- `solution.bound.title` or `solution.title`
- `solution.bound.tasks` or `solution.tasks`
### Step 3: Update Issue Status
```bash
ccw issue update ${issueId} --status executing
```
### Step 4: Codex CLI Execution
Build execution prompt and invoke Codex:
```bash
ccw cli -p "$(cat <<'PROMPT_EOF'
## Issue
ID: ${issueId}
Title: ${solution.bound.title}
## 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 (run: npx tsc --noEmit)
- [ ] Existing tests pass
- [ ] New tests added where specified in solution
- [ ] No security vulnerabilities introduced
PROMPT_EOF
)" --tool codex --mode write --id planex-${issueId}
```
**STOP after spawn** — Codex CLI executes in background. Do NOT poll or wait inside this agent. The CLI process handles implementation autonomously.
Wait for CLI completion signal before proceeding to Step 5.
### Step 5: Verify Tests
Detect and run project test command:
```javascript
// Detection priority:
// 1. package.json scripts.test
// 2. package.json scripts.test:unit
// 3. pytest.ini / setup.cfg (Python)
// 4. Makefile test target
const testCmd = detectTestCommand()
if (testCmd) {
const testResult = Bash(`${testCmd} 2>&1 || echo TEST_FAILED`)
if (testResult.includes('TEST_FAILED') || testResult.includes('FAIL')) {
const resumeCmd = `ccw cli -p "Fix failing tests" --resume planex-${issueId} --tool codex --mode write`
Write({
file_path: `${sessionDir}/errors.json`,
content: JSON.stringify({
issue_id: issueId,
type: 'test_failure',
test_output: testResult.slice(0, 2000),
resume_cmd: resumeCmd,
timestamp: new Date().toISOString()
}, null, 2)
})
Output: `EXEC_FAILED:${issueId}:tests_failing`
Stop.
}
}
```
### Step 6: Commit
```bash
git add -A
git commit -m "feat(${issueId}): ${solution.bound.title}"
```
If commit fails (nothing to commit, pre-commit hook error):
- Log warning: `[executor] WARN: Commit failed for ${issueId}, continuing`
- Still proceed to Step 7
### Step 7: Update Issue & Report
```bash
ccw issue update ${issueId} --status completed
```
Output completion report:
```
## [executor] Implementation Complete
**Issue**: ${issueId}
**Title**: ${solution.bound.title}
**Backend**: codex
**Tests**: ${testCmd ? 'passing' : 'skipped (no test command found)'}
**Commit**: ${commitHash}
**Status**: resolved
EXEC_DONE:${issueId}
```
---
## Resume Protocol
If Codex CLI execution fails or times out:
```bash
ccw cli -p "Continue implementation from where stopped" \
--resume planex-${issueId} \
--tool codex --mode write \
--id planex-${issueId}-retry
```
Resume command is always logged to `${sessionDir}/errors.json` on any failure.
---
## Error Handling
| Scenario | Resolution |
|----------|------------|
| Solution file missing | Output `EXEC_FAILED:<id>:solution_file_missing`, stop |
| Solution JSON malformed | Output `EXEC_FAILED:<id>:solution_invalid`, stop |
| Issue status update fails | Log warning, continue |
| Codex CLI failure | Log resume command to errors.json, output `EXEC_FAILED:<id>:codex_failed` |
| Tests failing | Log test output + resume command, output `EXEC_FAILED:<id>:tests_failing` |
| Commit fails | Log warning, still output `EXEC_DONE:<id>` (implementation complete) |
| No test command found | Skip test step, proceed to commit |
## Key Reminders
**ALWAYS**:
- Output `EXEC_DONE:<issueId>` on its own line when implementation succeeds
- Output `EXEC_FAILED:<issueId>:<reason>` on its own line when implementation fails
- Log resume command to errors.json on any failure
- Use `[executor]` prefix in all status messages
**NEVER**:
- Use any execution backend other than Codex CLI
- Create, modify, or read issues beyond the assigned issueId
- Spawn subagents
- Ask the user for clarification (fail fast with structured error)