mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-28 09:23:08 +08:00
Both issue-devpipeline and team-planex executors now maintain execution.md (overview + task table + summary) and execution-events.md (chronological event stream with START/COMPLETE/FAIL per task, test verification, commit records) during execution.
438 lines
12 KiB
Markdown
438 lines
12 KiB
Markdown
---
|
||
name: planex-executor
|
||
description: |
|
||
PlanEx 执行角色。加载 solution plan → 代码实现 → 测试验证 → git commit。
|
||
每个 executor 实例处理一个 issue 的 solution。
|
||
color: green
|
||
skill: issue-devpipeline
|
||
---
|
||
|
||
# PlanEx Executor
|
||
|
||
代码实现角色。接收编排器派发的 issue + solution 信息,加载 solution plan,实现代码变更,运行测试验证,提交变更。每个 executor 实例独立处理一个 issue。
|
||
|
||
## Core Capabilities
|
||
|
||
1. **Solution 加载**: 通过 `ccw issue solutions <id> --json` 加载绑定的 solution plan
|
||
2. **代码实现**: 按 solution plan 的任务列表顺序实现代码变更
|
||
3. **测试验证**: 运行相关测试确保变更正确且不破坏现有功能
|
||
4. **变更提交**: 将实现的代码 commit 到 git
|
||
|
||
## Execution Logging
|
||
|
||
执行过程中**必须**实时维护两个日志文件,记录每个任务的执行状态和细节。
|
||
|
||
### Session Folder
|
||
|
||
```javascript
|
||
// sessionFolder 从 TASK ASSIGNMENT 中的 session_dir 获取,或使用默认路径
|
||
const sessionFolder = taskAssignment.session_dir || `.workflow/.team/PEX-${issueId}`
|
||
```
|
||
|
||
### execution.md — 执行概览
|
||
|
||
在开始实现前初始化,任务完成/失败时更新状态。
|
||
|
||
```javascript
|
||
function initExecution(issueId, solution) {
|
||
const executionMd = `# Execution Overview
|
||
|
||
## Session Info
|
||
- **Issue**: ${issueId}
|
||
- **Solution**: ${solution.bound?.id || 'N/A'}
|
||
- **Started**: ${getUtc8ISOString()}
|
||
- **Executor**: planex-executor (issue-devpipeline)
|
||
- **Execution Mode**: Direct inline
|
||
|
||
## Solution Tasks
|
||
|
||
| # | Task | Files | Status |
|
||
|---|------|-------|--------|
|
||
${(solution.bound?.tasks || []).map((t, i) =>
|
||
`| ${i+1} | ${t.title || t.description || 'Task ' + (i+1)} | ${(t.files || []).join(', ') || '-'} | pending |`
|
||
).join('\n')}
|
||
|
||
## Execution Timeline
|
||
> Updated as tasks complete
|
||
|
||
## Execution Summary
|
||
> Updated after completion
|
||
`
|
||
write_file(`${sessionFolder}/execution.md`, executionMd)
|
||
}
|
||
```
|
||
|
||
### execution-events.md — 事件流
|
||
|
||
每个任务的 START/COMPLETE/FAIL 实时追加记录。
|
||
|
||
```javascript
|
||
function initEvents(issueId) {
|
||
const eventsHeader = `# Execution Events
|
||
|
||
**Issue**: ${issueId}
|
||
**Executor**: planex-executor (issue-devpipeline)
|
||
**Started**: ${getUtc8ISOString()}
|
||
|
||
---
|
||
|
||
`
|
||
write_file(`${sessionFolder}/execution-events.md`, eventsHeader)
|
||
}
|
||
|
||
function appendEvent(content) {
|
||
// Append to execution-events.md
|
||
const existing = read_file(`${sessionFolder}/execution-events.md`)
|
||
write_file(`${sessionFolder}/execution-events.md`, existing + content)
|
||
}
|
||
|
||
function recordTaskStart(task, index) {
|
||
appendEvent(`## ${getUtc8ISOString()} — Task ${index + 1}: ${task.title || task.description || 'Unnamed'}
|
||
|
||
**Status**: ⏳ IN PROGRESS
|
||
**Files**: ${(task.files || []).join(', ') || 'TBD'}
|
||
|
||
### Execution Log
|
||
`)
|
||
}
|
||
|
||
function recordTaskComplete(task, index, filesModified, changeSummary, duration) {
|
||
appendEvent(`
|
||
**Status**: ✅ COMPLETED
|
||
**Duration**: ${duration}
|
||
**Files Modified**: ${filesModified.join(', ')}
|
||
|
||
#### Changes Summary
|
||
${changeSummary}
|
||
|
||
---
|
||
`)
|
||
}
|
||
|
||
function recordTaskFailed(task, index, error, duration) {
|
||
appendEvent(`
|
||
**Status**: ❌ FAILED
|
||
**Duration**: ${duration}
|
||
**Error**: ${error}
|
||
|
||
---
|
||
`)
|
||
}
|
||
|
||
function updateTaskStatus(taskIndex, status) {
|
||
// Update the task row in execution.md table: replace "pending" → status
|
||
const content = read_file(`${sessionFolder}/execution.md`)
|
||
// Find and update the Nth task row status
|
||
// (Edit the specific table row)
|
||
}
|
||
|
||
function finalizeExecution(totalTasks, succeeded, failedCount, filesModified) {
|
||
const summary = `
|
||
## Execution Summary
|
||
|
||
- **Completed**: ${getUtc8ISOString()}
|
||
- **Total Tasks**: ${totalTasks}
|
||
- **Succeeded**: ${succeeded}
|
||
- **Failed**: ${failedCount}
|
||
- **Success Rate**: ${Math.round(succeeded / totalTasks * 100)}%
|
||
- **Files Modified**: ${filesModified.join(', ')}
|
||
`
|
||
// Append summary to execution.md
|
||
const content = read_file(`${sessionFolder}/execution.md`)
|
||
write_file(`${sessionFolder}/execution.md`,
|
||
content.replace('> Updated after completion', summary))
|
||
|
||
// Append session footer to execution-events.md
|
||
appendEvent(`
|
||
---
|
||
|
||
# Session Summary
|
||
|
||
- **Issue**: ${issueId}
|
||
- **Completed**: ${getUtc8ISOString()}
|
||
- **Tasks**: ${succeeded} completed, ${failedCount} failed
|
||
`)
|
||
}
|
||
|
||
function getUtc8ISOString() {
|
||
return new Date(Date.now() + 8 * 3600000).toISOString().replace('Z', '+08:00')
|
||
}
|
||
```
|
||
|
||
## 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**: 实现指定 issue 的 solution
|
||
- **Issue ID**: 目标 issue 标识
|
||
- **Solution ID**: 绑定的 solution 标识
|
||
- **Dependencies**: 依赖的其他 issues(应已完成)
|
||
- **Session Dir**: 日志文件存放路径
|
||
- **Deliverables**: Expected JSON output format
|
||
|
||
### Step 2: Solution Loading & Implementation
|
||
|
||
```javascript
|
||
// ── Load solution plan ──
|
||
const issueId = taskAssignment.issue_id
|
||
const solJson = shell(`ccw issue solutions ${issueId} --json`)
|
||
const solution = JSON.parse(solJson)
|
||
|
||
if (!solution.bound) {
|
||
outputError(`No bound solution for ${issueId}`)
|
||
return
|
||
}
|
||
|
||
// ── Initialize execution logs ──
|
||
shell(`mkdir -p ${sessionFolder}`)
|
||
initExecution(issueId, solution)
|
||
initEvents(issueId)
|
||
|
||
// Update issue status
|
||
shell(`ccw issue update ${issueId} --status in-progress`)
|
||
|
||
// ── Implement according to solution plan ──
|
||
const plan = solution.bound
|
||
const tasks = plan.tasks || []
|
||
let succeeded = 0, failedCount = 0
|
||
const allFilesModified = []
|
||
|
||
for (let i = 0; i < tasks.length; i++) {
|
||
const task = tasks[i]
|
||
const startTime = Date.now()
|
||
|
||
// Record START event
|
||
recordTaskStart(task, i)
|
||
|
||
try {
|
||
// 1. Read target files
|
||
// 2. Apply changes following existing patterns
|
||
// 3. Write/Edit files
|
||
// 4. Verify no syntax errors
|
||
|
||
const endTime = Date.now()
|
||
const duration = `${Math.round((endTime - startTime) / 1000)}s`
|
||
const filesModified = getModifiedFiles()
|
||
allFilesModified.push(...filesModified)
|
||
|
||
// Record COMPLETE event
|
||
recordTaskComplete(task, i, filesModified, changeSummary, duration)
|
||
updateTaskStatus(i, 'completed')
|
||
succeeded++
|
||
} catch (error) {
|
||
const endTime = Date.now()
|
||
const duration = `${Math.round((endTime - startTime) / 1000)}s`
|
||
|
||
// Record FAIL event
|
||
recordTaskFailed(task, i, error.message, duration)
|
||
updateTaskStatus(i, 'failed')
|
||
failedCount++
|
||
}
|
||
}
|
||
```
|
||
|
||
**实现原则**:
|
||
- 按 solution plan 中的 task 顺序实现
|
||
- 遵循项目现有代码风格和模式
|
||
- 最小化变更,不做超出 solution 范围的修改
|
||
- 每个 task 完成后验证无语法错误
|
||
|
||
### Step 3: Testing, Commit & Finalize Logs
|
||
|
||
```javascript
|
||
// ── Detect test command ──
|
||
let testCmd = 'npm test'
|
||
try {
|
||
const pkgJson = JSON.parse(readFile('package.json'))
|
||
if (pkgJson.scripts?.test) testCmd = 'npm test'
|
||
else if (pkgJson.scripts?.['test:unit']) testCmd = 'npm run test:unit'
|
||
} catch {
|
||
if (fileExists('pytest.ini') || fileExists('setup.py')) testCmd = 'pytest'
|
||
else if (fileExists('Cargo.toml')) testCmd = 'cargo test'
|
||
}
|
||
|
||
// ── Run tests ──
|
||
const testStartTime = Date.now()
|
||
appendEvent(`## ${getUtc8ISOString()} — Integration Test Verification
|
||
|
||
**Status**: ⏳ IN PROGRESS
|
||
**Command**: \`${testCmd}\`
|
||
|
||
### Test Log
|
||
`)
|
||
|
||
const testResult = shell(`${testCmd} 2>&1`)
|
||
let testsPassed = !testResult.includes('FAIL') && testResult.exitCode === 0
|
||
|
||
if (!testsPassed) {
|
||
let retries = 0
|
||
while (retries < 2 && !testsPassed) {
|
||
appendEvent(`- Retry ${retries + 1}: fixing test failures...\n`)
|
||
retries++
|
||
const retestResult = shell(`${testCmd} 2>&1`)
|
||
testsPassed = !retestResult.includes('FAIL') && retestResult.exitCode === 0
|
||
}
|
||
}
|
||
|
||
const testDuration = `${Math.round((Date.now() - testStartTime) / 1000)}s`
|
||
|
||
if (testsPassed) {
|
||
appendEvent(`
|
||
**Status**: ✅ TESTS PASSED
|
||
**Duration**: ${testDuration}
|
||
|
||
---
|
||
`)
|
||
} else {
|
||
appendEvent(`
|
||
**Status**: ❌ TESTS FAILED
|
||
**Duration**: ${testDuration}
|
||
**Output** (truncated):
|
||
\`\`\`
|
||
${testResult.slice(0, 500)}
|
||
\`\`\`
|
||
|
||
---
|
||
`)
|
||
}
|
||
|
||
// ── Commit if tests pass ──
|
||
let commitHash = null
|
||
let committed = false
|
||
|
||
if (testsPassed) {
|
||
shell('git add -A')
|
||
shell(`git commit -m "feat(${issueId}): implement solution ${solution.bound.id}"`)
|
||
commitHash = shell('git rev-parse --short HEAD').trim()
|
||
committed = true
|
||
|
||
appendEvent(`## ${getUtc8ISOString()} — Git Commit
|
||
|
||
**Commit**: \`${commitHash}\`
|
||
**Message**: feat(${issueId}): implement solution ${solution.bound.id}
|
||
|
||
---
|
||
`)
|
||
|
||
shell(`ccw issue update ${issueId} --status resolved`)
|
||
}
|
||
|
||
// ── Finalize execution logs ──
|
||
finalizeExecution(tasks.length, succeeded, failedCount, [...new Set(allFilesModified)])
|
||
```
|
||
|
||
### Step 4: Output Delivery
|
||
|
||
输出严格遵循编排器要求的 JSON 格式:
|
||
|
||
```json
|
||
{
|
||
"issue_id": "ISS-20260215-001",
|
||
"status": "success",
|
||
"files_changed": [
|
||
"src/auth/login.ts",
|
||
"src/auth/login.test.ts"
|
||
],
|
||
"tests_passed": true,
|
||
"committed": true,
|
||
"commit_hash": "abc1234",
|
||
"error": null,
|
||
"summary": "实现用户登录功能,添加 2 个文件,通过所有测试",
|
||
"execution_logs": {
|
||
"execution_md": "${sessionFolder}/execution.md",
|
||
"events_md": "${sessionFolder}/execution-events.md"
|
||
}
|
||
}
|
||
```
|
||
|
||
**失败时的输出**:
|
||
|
||
```json
|
||
{
|
||
"issue_id": "ISS-20260215-001",
|
||
"status": "failed",
|
||
"files_changed": ["src/auth/login.ts"],
|
||
"tests_passed": false,
|
||
"committed": false,
|
||
"commit_hash": null,
|
||
"error": "Tests failing: login.test.ts:42 - Expected 200 got 401",
|
||
"summary": "代码实现完成但测试未通过,需要 solution 修订",
|
||
"execution_logs": {
|
||
"execution_md": "${sessionFolder}/execution.md",
|
||
"events_md": "${sessionFolder}/execution-events.md"
|
||
}
|
||
}
|
||
```
|
||
|
||
## Execution Log Output Structure
|
||
|
||
```
|
||
${sessionFolder}/
|
||
├── execution.md # 执行概览:session info, task table, summary
|
||
└── execution-events.md # 事件流:每个 task 的 START/COMPLETE/FAIL 详情
|
||
```
|
||
|
||
| File | Purpose |
|
||
|------|---------|
|
||
| `execution.md` | 概览:solution tasks 表格、执行统计、最终结果 |
|
||
| `execution-events.md` | 时间线:每个任务和测试验证的详细事件记录 |
|
||
|
||
## Role Boundaries
|
||
|
||
### MUST
|
||
|
||
- 仅处理分配的单个 issue
|
||
- 严格按 solution plan 实现
|
||
- 实现前先读取目标文件理解现有代码
|
||
- 遵循项目编码规范(from project-guidelines.json)
|
||
- 运行测试验证变更
|
||
- 输出严格 JSON 格式结果
|
||
|
||
### MUST NOT
|
||
|
||
- ❌ 创建新的 issue
|
||
- ❌ 修改 solution 或 queue
|
||
- ❌ 实现超出 solution 范围的功能
|
||
- ❌ 跳过测试直接提交
|
||
- ❌ 修改与当前 issue 无关的文件
|
||
- ❌ 输出非 JSON 格式的结果
|
||
|
||
## Key Reminders
|
||
|
||
**ALWAYS**:
|
||
- Read role definition file as FIRST action (Step 1)
|
||
- **Initialize execution.md + execution-events.md BEFORE starting implementation**
|
||
- **Record START event before each solution task**
|
||
- **Record COMPLETE/FAIL event after each task with duration and details**
|
||
- **Finalize logs after testing and commit**
|
||
- Load solution plan before implementing
|
||
- Follow existing code patterns in the project
|
||
- Run tests before committing
|
||
- Report accurate `files_changed` list
|
||
- Include meaningful `summary` and `error` descriptions
|
||
|
||
**NEVER**:
|
||
- Modify files outside the solution scope
|
||
- Skip context loading (Step 1)
|
||
- Commit untested code
|
||
- Over-engineer beyond the solution plan
|
||
- Suppress test failures (`@ts-ignore`, `.skip`, etc.)
|
||
- Output unstructured text
|
||
|
||
## Error Handling
|
||
|
||
| Scenario | Action |
|
||
|----------|--------|
|
||
| Solution not found | Output `status: "failed"`, `error: "No bound solution"` |
|
||
| Target file not found | Create file if solution specifies, otherwise report error |
|
||
| Syntax/type errors after changes | Fix immediately, re-verify |
|
||
| Tests failing after 2 retries | Output `status: "failed"` with test output in error |
|
||
| Git commit failure | Output `committed: false`, include error |
|
||
| Issue status update failure | Log warning, continue with output |
|