mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-10 02:24:35 +08:00
Refactor API calls to use csrfFetch for enhanced security across multiple views, including loop-monitor, mcp-manager, memory, prompt-history, rules-manager, session-detail, and skills-manager. Additionally, add Phase 1 and Phase 2 documentation for session initialization and orchestration loop in the ccw-loop-b skill.
This commit is contained in:
@@ -1,38 +1,60 @@
|
||||
---
|
||||
name: CCW Loop-B
|
||||
description: Hybrid orchestrator pattern for iterative development. Coordinator + specialized workers with batch wait support. Triggers on "ccw-loop-b".
|
||||
argument-hint: TASK="<task description>" [--loop-id=<id>] [--mode=<interactive|auto|parallel>]
|
||||
name: ccw-loop-b
|
||||
description: Hybrid orchestrator pattern for iterative development. Coordinator + specialized workers with batch wait, parallel split, and two-phase clarification. Triggers on "ccw-loop-b".
|
||||
allowed-tools: Task, AskUserQuestion, TodoWrite, Read, Write, Edit, Bash, Glob, Grep
|
||||
---
|
||||
|
||||
# CCW Loop-B - Hybrid Orchestrator Pattern
|
||||
|
||||
协调器 + 专用 worker 的迭代开发工作流。支持单 agent 深度交互、多 agent 并行、混合模式灵活切换。
|
||||
协调器 + 专用 worker 的迭代开发工作流。支持三种执行模式(Interactive / Auto / Parallel),每个 action 由独立 worker agent 执行,协调器负责调度、状态管理和结果汇聚。
|
||||
|
||||
## Arguments
|
||||
|
||||
| Arg | Required | Description |
|
||||
|-----|----------|-------------|
|
||||
| TASK | No | Task description (for new loop) |
|
||||
| --loop-id | No | Existing loop ID to continue |
|
||||
| --mode | No | `interactive` (default) / `auto` / `parallel` |
|
||||
|
||||
## Architecture
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
+------------------------------------------------------------+
|
||||
| Main Coordinator |
|
||||
| 职责: 状态管理 + worker 调度 + 结果汇聚 + 用户交互 |
|
||||
+------------------------------------------------------------+
|
||||
|
|
||||
+--------------------+--------------------+
|
||||
| | |
|
||||
v v v
|
||||
| | |
|
||||
v v v
|
||||
+----------------+ +----------------+ +----------------+
|
||||
| Worker-Develop | | Worker-Debug | | Worker-Validate|
|
||||
| 专注: 代码实现 | | 专注: 问题诊断 | | 专注: 测试验证 |
|
||||
+----------------+ +----------------+ +----------------+
|
||||
| | |
|
||||
v v v
|
||||
.workers/ .workers/ .workers/
|
||||
develop.output.json debug.output.json validate.output.json
|
||||
```
|
||||
|
||||
### Subagent API
|
||||
|
||||
| API | 作用 | 注意事项 |
|
||||
|-----|------|----------|
|
||||
| `spawn_agent({ message })` | 创建 worker,返回 `agent_id` | 首条 message 加载角色 |
|
||||
| `wait({ ids, timeout_ms })` | 等待结果 | **唯一取结果入口**,非 close |
|
||||
| `send_input({ id, message })` | 继续交互/追问 | `interrupt=true` 慎用 |
|
||||
| `close_agent({ id })` | 关闭回收 | 不可逆,确认不再交互后才关闭 |
|
||||
|
||||
## Key Design Principles
|
||||
|
||||
1. **协调器保持轻量**: 只做调度和状态管理,具体工作交给 worker
|
||||
2. **Worker 职责单一**: 每个 worker 专注一个领域(develop/debug/validate)
|
||||
3. **角色路径传递**: Worker 自己读取角色文件,主流程不传递内容
|
||||
4. **延迟 close_agent**: 确认不再需要交互后才关闭 worker
|
||||
5. **两阶段工作流**: 复杂任务先澄清后执行,减少返工
|
||||
6. **批量等待优化**: 并行模式用 `wait({ ids: [...] })` 批量等待
|
||||
7. **结果标准化**: Worker 输出遵循统一 WORKER_RESULT 格式
|
||||
8. **灵活模式切换**: 根据任务复杂度选择 interactive/auto/parallel
|
||||
|
||||
## Arguments
|
||||
|
||||
| Arg | Required | Description |
|
||||
|-----|----------|-------------|
|
||||
| TASK | One of TASK or --loop-id | Task description (for new loop) |
|
||||
| --loop-id | One of TASK or --loop-id | Existing loop ID to continue |
|
||||
| --mode | No | `interactive` (default) / `auto` / `parallel` |
|
||||
|
||||
## Execution Modes
|
||||
|
||||
### Mode: Interactive (default)
|
||||
@@ -45,7 +67,7 @@ Coordinator -> Show menu -> User selects -> spawn worker -> wait -> Display resu
|
||||
|
||||
### Mode: Auto
|
||||
|
||||
自动按预设顺序执行,worker 完成后自动切换到下一阶段。
|
||||
自动按预设顺序执行,worker 完成后协调器决定下一步。
|
||||
|
||||
```
|
||||
Init -> Develop -> [if issues] Debug -> Validate -> [if fail] Loop back -> Complete
|
||||
@@ -53,241 +75,342 @@ Init -> Develop -> [if issues] Debug -> Validate -> [if fail] Loop back -> Compl
|
||||
|
||||
### Mode: Parallel
|
||||
|
||||
并行 spawn 多个 worker 分析不同维度,batch wait 汇聚结果。
|
||||
并行 spawn 多个 worker,batch wait 汇聚结果,协调器综合决策。
|
||||
|
||||
```
|
||||
Coordinator -> spawn [develop, debug, validate] in parallel -> wait({ ids: all }) -> Merge -> Decide
|
||||
```
|
||||
|
||||
## Execution Flow
|
||||
|
||||
```
|
||||
Input Parsing:
|
||||
└─ Parse arguments (TASK | --loop-id + --mode)
|
||||
└─ Convert to structured context (loopId, state, mode)
|
||||
|
||||
Phase 1: Session Initialization
|
||||
└─ Ref: phases/01-session-init.md
|
||||
├─ Create new loop OR resume existing loop
|
||||
├─ Initialize state file and directory structure
|
||||
└─ Output: loopId, state, progressDir, mode
|
||||
|
||||
Phase 2: Orchestration Loop
|
||||
└─ Ref: phases/02-orchestration-loop.md
|
||||
├─ Mode dispatch: interactive / auto / parallel
|
||||
├─ Worker spawn with structured prompt (Goal/Scope/Context/Deliverables)
|
||||
├─ Wait + timeout handling + result parsing
|
||||
├─ State update per iteration
|
||||
└─ close_agent on loop exit
|
||||
```
|
||||
|
||||
**Phase Reference Documents** (read on-demand when phase executes):
|
||||
|
||||
| Phase | Document | Purpose |
|
||||
|-------|----------|---------|
|
||||
| 1 | [phases/01-session-init.md](phases/01-session-init.md) | Argument parsing, state creation/resume, directory init |
|
||||
| 2 | [phases/02-orchestration-loop.md](phases/02-orchestration-loop.md) | 3-mode orchestration, worker spawn, batch wait, result merge |
|
||||
|
||||
## Data Flow
|
||||
|
||||
```
|
||||
User Input (TASK | --loop-id + --mode)
|
||||
↓
|
||||
[Parse Arguments]
|
||||
↓ loopId, state, mode
|
||||
|
||||
Phase 1: Session Initialization
|
||||
↓ loopId, state (initialized/resumed), progressDir
|
||||
|
||||
Phase 2: Orchestration Loop
|
||||
↓
|
||||
┌─── Interactive Mode ──────────────────────────────────┐
|
||||
│ showMenu → user selects → spawn worker → wait → │
|
||||
│ parseResult → updateState → close worker → loop │
|
||||
└───────────────────────────────────────────────────────┘
|
||||
┌─── Auto Mode ─────────────────────────────────────────┐
|
||||
│ selectNext → spawn worker → wait → parseResult → │
|
||||
│ updateState → close worker → [loop_back?] → next │
|
||||
└───────────────────────────────────────────────────────┘
|
||||
┌─── Parallel Mode ─────────────────────────────────────┐
|
||||
│ spawn [develop, debug, validate] → batch wait → │
|
||||
│ mergeOutputs → coordinator decides → close all │
|
||||
└───────────────────────────────────────────────────────┘
|
||||
↓
|
||||
return finalState
|
||||
```
|
||||
|
||||
## Session Structure
|
||||
|
||||
```
|
||||
.workflow/.loop/
|
||||
+-- {loopId}.json # Master state
|
||||
+-- {loopId}.workers/ # Worker outputs
|
||||
| +-- develop.output.json
|
||||
| +-- debug.output.json
|
||||
| +-- validate.output.json
|
||||
+-- {loopId}.progress/ # Human-readable progress
|
||||
+-- develop.md
|
||||
+-- debug.md
|
||||
+-- validate.md
|
||||
+-- summary.md
|
||||
├── {loopId}.json # Master state (API + Skill shared)
|
||||
├── {loopId}.workers/ # Worker structured outputs
|
||||
│ ├── init.output.json
|
||||
│ ├── develop.output.json
|
||||
│ ├── debug.output.json
|
||||
│ ├── validate.output.json
|
||||
│ └── complete.output.json
|
||||
└── {loopId}.progress/ # Human-readable progress
|
||||
├── develop.md
|
||||
├── debug.md
|
||||
├── validate.md
|
||||
└── summary.md
|
||||
```
|
||||
|
||||
## Subagent API
|
||||
## State Management
|
||||
|
||||
| API | 作用 |
|
||||
|-----|------|
|
||||
| `spawn_agent({ message })` | 创建 agent,返回 `agent_id` |
|
||||
| `wait({ ids, timeout_ms })` | 等待结果(唯一取结果入口) |
|
||||
| `send_input({ id, message })` | 继续交互 |
|
||||
| `close_agent({ id })` | 关闭回收 |
|
||||
Master state file: `.workflow/.loop/{loopId}.json`
|
||||
|
||||
## Implementation
|
||||
```json
|
||||
{
|
||||
"loop_id": "loop-b-20260122-abc123",
|
||||
"title": "Task title",
|
||||
"description": "Full task description",
|
||||
"mode": "interactive | auto | parallel",
|
||||
"status": "running | paused | completed | failed",
|
||||
"current_iteration": 0,
|
||||
"max_iterations": 10,
|
||||
"created_at": "ISO8601",
|
||||
"updated_at": "ISO8601",
|
||||
|
||||
### Coordinator Logic
|
||||
|
||||
```javascript
|
||||
// ==================== HYBRID ORCHESTRATOR ====================
|
||||
|
||||
// 1. Initialize
|
||||
const loopId = args['--loop-id'] || generateLoopId()
|
||||
const mode = args['--mode'] || 'interactive'
|
||||
let state = readOrCreateState(loopId, taskDescription)
|
||||
|
||||
// 2. Mode selection
|
||||
switch (mode) {
|
||||
case 'interactive':
|
||||
await runInteractiveMode(loopId, state)
|
||||
break
|
||||
|
||||
case 'auto':
|
||||
await runAutoMode(loopId, state)
|
||||
break
|
||||
|
||||
case 'parallel':
|
||||
await runParallelMode(loopId, state)
|
||||
break
|
||||
}
|
||||
```
|
||||
|
||||
### Interactive Mode (单 agent 交互或按需 spawn worker)
|
||||
|
||||
```javascript
|
||||
async function runInteractiveMode(loopId, state) {
|
||||
while (state.status === 'running') {
|
||||
// Show menu, get user choice
|
||||
const action = await showMenuAndGetChoice(state)
|
||||
|
||||
if (action === 'exit') break
|
||||
|
||||
// Spawn specialized worker for the action
|
||||
const workerId = spawn_agent({
|
||||
message: buildWorkerPrompt(action, loopId, state)
|
||||
})
|
||||
|
||||
// Wait for worker completion
|
||||
const result = wait({ ids: [workerId], timeout_ms: 600000 })
|
||||
const output = result.status[workerId].completed
|
||||
|
||||
// Update state and display result
|
||||
state = updateState(loopId, action, output)
|
||||
displayResult(output)
|
||||
|
||||
// Cleanup worker
|
||||
close_agent({ id: workerId })
|
||||
"skill_state": {
|
||||
"phase": "init | develop | debug | validate | complete",
|
||||
"action_index": 0,
|
||||
"workers_completed": [],
|
||||
"parallel_results": null,
|
||||
"pending_tasks": [],
|
||||
"completed_tasks": [],
|
||||
"findings": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Auto Mode (顺序执行 worker 链)
|
||||
**Control Signal Checking**: 协调器在每次 spawn worker 前检查 `state.status`:
|
||||
- `running` → continue
|
||||
- `paused` → exit gracefully, wait for resume
|
||||
- `failed` → terminate
|
||||
|
||||
```javascript
|
||||
async function runAutoMode(loopId, state) {
|
||||
const actionSequence = ['init', 'develop', 'debug', 'validate', 'complete']
|
||||
let currentIndex = state.skill_state?.action_index || 0
|
||||
**Recovery**: If state corrupted, rebuild from `.progress/` markdown files and `.workers/*.output.json`.
|
||||
|
||||
while (currentIndex < actionSequence.length && state.status === 'running') {
|
||||
const action = actionSequence[currentIndex]
|
||||
## Worker Catalog
|
||||
|
||||
// Spawn worker
|
||||
const workerId = spawn_agent({
|
||||
message: buildWorkerPrompt(action, loopId, state)
|
||||
})
|
||||
| Worker | Role File | Purpose | Output Files |
|
||||
|--------|-----------|---------|--------------|
|
||||
| [init](workers/worker-init.md) | ccw-loop-b-init.md | 会话初始化、任务解析 | init.output.json |
|
||||
| [develop](workers/worker-develop.md) | ccw-loop-b-develop.md | 代码实现、重构 | develop.output.json, develop.md |
|
||||
| [debug](workers/worker-debug.md) | ccw-loop-b-debug.md | 问题诊断、假设验证 | debug.output.json, debug.md |
|
||||
| [validate](workers/worker-validate.md) | ccw-loop-b-validate.md | 测试执行、覆盖率 | validate.output.json, validate.md |
|
||||
| [complete](workers/worker-complete.md) | ccw-loop-b-complete.md | 总结收尾 | complete.output.json, summary.md |
|
||||
|
||||
const result = wait({ ids: [workerId], timeout_ms: 600000 })
|
||||
const output = result.status[workerId].completed
|
||||
### Worker Dependencies
|
||||
|
||||
// Parse worker result to determine next step
|
||||
const workerResult = parseWorkerResult(output)
|
||||
| Worker | Depends On | Leads To |
|
||||
|--------|------------|----------|
|
||||
| init | - | develop (auto) / menu (interactive) |
|
||||
| develop | init | validate / debug |
|
||||
| debug | init | develop / validate |
|
||||
| validate | develop or debug | complete / develop (if fail) |
|
||||
| complete | - | Terminal |
|
||||
|
||||
// Update state
|
||||
state = updateState(loopId, action, output)
|
||||
### Worker Sequences
|
||||
|
||||
close_agent({ id: workerId })
|
||||
|
||||
// Determine next action
|
||||
if (workerResult.needs_loop_back) {
|
||||
// Loop back to develop or debug
|
||||
currentIndex = actionSequence.indexOf(workerResult.loop_back_to)
|
||||
} else if (workerResult.status === 'failed') {
|
||||
// Stop on failure
|
||||
break
|
||||
} else {
|
||||
currentIndex++
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Simple Task (Auto): init → develop → validate → complete
|
||||
Complex Task (Auto): init → develop → validate (fail) → debug → develop → validate → complete
|
||||
Bug Fix (Auto): init → debug → develop → validate → complete
|
||||
Analysis (Parallel): init → [develop ‖ debug ‖ validate] → complete
|
||||
Interactive: init → menu → user selects → worker → menu → ...
|
||||
```
|
||||
|
||||
### Parallel Mode (批量 spawn + wait)
|
||||
## Worker Prompt Protocol
|
||||
|
||||
```javascript
|
||||
async function runParallelMode(loopId, state) {
|
||||
// Spawn multiple workers in parallel
|
||||
const workers = {
|
||||
develop: spawn_agent({ message: buildWorkerPrompt('develop', loopId, state) }),
|
||||
debug: spawn_agent({ message: buildWorkerPrompt('debug', loopId, state) }),
|
||||
validate: spawn_agent({ message: buildWorkerPrompt('validate', loopId, state) })
|
||||
}
|
||||
|
||||
// Batch wait for all workers
|
||||
const results = wait({
|
||||
ids: Object.values(workers),
|
||||
timeout_ms: 900000 // 15 minutes for all
|
||||
})
|
||||
|
||||
// Collect outputs
|
||||
const outputs = {}
|
||||
for (const [role, workerId] of Object.entries(workers)) {
|
||||
outputs[role] = results.status[workerId].completed
|
||||
close_agent({ id: workerId })
|
||||
}
|
||||
|
||||
// Merge and analyze results
|
||||
const mergedAnalysis = mergeWorkerOutputs(outputs)
|
||||
|
||||
// Update state with merged results
|
||||
updateState(loopId, 'parallel-analysis', mergedAnalysis)
|
||||
|
||||
// Coordinator decides next action based on merged results
|
||||
const decision = decideNextAction(mergedAnalysis)
|
||||
return decision
|
||||
}
|
||||
```
|
||||
|
||||
### Worker Prompt Builder
|
||||
### Spawn Message Structure (§7.1)
|
||||
|
||||
```javascript
|
||||
function buildWorkerPrompt(action, loopId, state) {
|
||||
const workerRoles = {
|
||||
develop: '~/.codex/agents/ccw-loop-b-develop.md',
|
||||
debug: '~/.codex/agents/ccw-loop-b-debug.md',
|
||||
validate: '~/.codex/agents/ccw-loop-b-validate.md',
|
||||
init: '~/.codex/agents/ccw-loop-b-init.md',
|
||||
complete: '~/.codex/agents/ccw-loop-b-complete.md'
|
||||
}
|
||||
|
||||
return `
|
||||
## TASK ASSIGNMENT
|
||||
|
||||
### MANDATORY FIRST STEPS (Agent Execute)
|
||||
1. **Read role definition**: ${workerRoles[action]} (MUST read first)
|
||||
1. **Read role definition**: ~/.codex/agents/ccw-loop-b-${action}.md (MUST read first)
|
||||
2. Read: .workflow/project-tech.json
|
||||
3. Read: .workflow/project-guidelines.json
|
||||
|
||||
---
|
||||
|
||||
## LOOP CONTEXT
|
||||
Goal: ${goalForAction(action, state)}
|
||||
|
||||
- **Loop ID**: ${loopId}
|
||||
- **Action**: ${action}
|
||||
- **State File**: .workflow/.loop/${loopId}.json
|
||||
- **Output File**: .workflow/.loop/${loopId}.workers/${action}.output.json
|
||||
- **Progress File**: .workflow/.loop/${loopId}.progress/${action}.md
|
||||
Scope:
|
||||
- 可做: ${allowedScope(action)}
|
||||
- 不可做: ${forbiddenScope(action)}
|
||||
- 目录限制: ${directoryScope(action, state)}
|
||||
|
||||
## CURRENT STATE
|
||||
Context:
|
||||
- Loop ID: ${loopId}
|
||||
- State: .workflow/.loop/${loopId}.json
|
||||
- Output: .workflow/.loop/${loopId}.workers/${action}.output.json
|
||||
- Progress: .workflow/.loop/${loopId}.progress/${action}.md
|
||||
|
||||
${JSON.stringify(state, null, 2)}
|
||||
Deliverables:
|
||||
- 按 WORKER_RESULT 格式输出
|
||||
- 写入 output.json 和 progress.md
|
||||
|
||||
## TASK DESCRIPTION
|
||||
|
||||
${state.description}
|
||||
|
||||
## EXPECTED OUTPUT
|
||||
|
||||
\`\`\`
|
||||
WORKER_RESULT:
|
||||
- action: ${action}
|
||||
- status: success | failed | needs_input
|
||||
- summary: <brief summary>
|
||||
- files_changed: [list]
|
||||
- next_suggestion: <suggested next action>
|
||||
- loop_back_to: <action name if needs loop back>
|
||||
|
||||
DETAILED_OUTPUT:
|
||||
<structured output specific to action type>
|
||||
\`\`\`
|
||||
|
||||
Execute the ${action} action now.
|
||||
Quality bar:
|
||||
- ${qualityCriteria(action)}
|
||||
`
|
||||
}
|
||||
```
|
||||
|
||||
## Worker Roles
|
||||
**关键**: 角色文件由 worker 自己读取,主流程只传递路径。不嵌入角色内容。
|
||||
|
||||
| Worker | Role File | 专注领域 |
|
||||
|--------|-----------|----------|
|
||||
| init | ccw-loop-b-init.md | 会话初始化、任务解析 |
|
||||
| develop | ccw-loop-b-develop.md | 代码实现、重构 |
|
||||
| debug | ccw-loop-b-debug.md | 问题诊断、假设验证 |
|
||||
| validate | ccw-loop-b-validate.md | 测试执行、覆盖率 |
|
||||
| complete | ccw-loop-b-complete.md | 总结收尾 |
|
||||
### Worker Output Format (WORKER_RESULT)
|
||||
|
||||
## State Schema
|
||||
```
|
||||
WORKER_RESULT:
|
||||
- action: {action_name}
|
||||
- status: success | failed | needs_input
|
||||
- summary: <brief summary>
|
||||
- files_changed: [list]
|
||||
- next_suggestion: <suggested next action>
|
||||
- loop_back_to: <action name if needs loop back, or null>
|
||||
|
||||
See [phases/state-schema.md](phases/state-schema.md)
|
||||
DETAILED_OUTPUT:
|
||||
<action-specific structured output>
|
||||
```
|
||||
|
||||
### Two-Phase Clarification (§5.2)
|
||||
|
||||
Worker 遇到模糊需求时采用两阶段模式:
|
||||
|
||||
```
|
||||
阶段 1: Worker 输出 CLARIFICATION_NEEDED + Open questions
|
||||
阶段 2: 协调器收集用户回答 → send_input → Worker 继续执行
|
||||
```
|
||||
|
||||
```javascript
|
||||
// 解析 worker 是否需要澄清
|
||||
if (output.includes('CLARIFICATION_NEEDED')) {
|
||||
const userAnswers = await collectUserAnswers(output)
|
||||
send_input({
|
||||
id: workerId,
|
||||
message: `## CLARIFICATION ANSWERS\n${userAnswers}\n\n## CONTINUE EXECUTION`
|
||||
})
|
||||
const finalResult = wait({ ids: [workerId], timeout_ms: 600000 })
|
||||
}
|
||||
```
|
||||
|
||||
## Parallel Split Strategy (§6)
|
||||
|
||||
### Strategy 1: 按职责域拆分(推荐)
|
||||
|
||||
| Worker | 职责 | 交付物 | 禁止事项 |
|
||||
|--------|------|--------|----------|
|
||||
| develop | 定位入口、调用链、实现方案 | 变更点清单 | 不做测试 |
|
||||
| debug | 问题诊断、风险评估 | 问题清单+修复建议 | 不写代码 |
|
||||
| validate | 测试策略、覆盖分析 | 测试结果+质量报告 | 不改实现 |
|
||||
|
||||
### Strategy 2: 按模块域拆分
|
||||
|
||||
```
|
||||
Worker 1: src/auth/** → 认证模块变更
|
||||
Worker 2: src/api/** → API 层变更
|
||||
Worker 3: src/database/** → 数据层变更
|
||||
```
|
||||
|
||||
### 拆分原则
|
||||
|
||||
1. **文件隔离**: 避免多个 worker 同时修改同一文件
|
||||
2. **职责单一**: 每个 worker 只做一件事
|
||||
3. **边界清晰**: 超出范围用 `CLARIFICATION_NEEDED` 请求确认
|
||||
4. **最小上下文**: 只传递完成任务所需的最小信息
|
||||
|
||||
## Result Merge (Parallel Mode)
|
||||
|
||||
```javascript
|
||||
function mergeWorkerOutputs(outputs) {
|
||||
return {
|
||||
develop: parseWorkerResult(outputs.develop),
|
||||
debug: parseWorkerResult(outputs.debug),
|
||||
validate: parseWorkerResult(outputs.validate),
|
||||
conflicts: detectConflicts(outputs), // 检查 worker 间建议冲突
|
||||
merged_at: getUtc8ISOString()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**冲突检测**: 当多个 worker 建议修改同一文件时,协调器标记冲突,由用户决定。
|
||||
|
||||
## TodoWrite Pattern
|
||||
|
||||
### Phase-Level Tracking (Attached)
|
||||
|
||||
```json
|
||||
[
|
||||
{"content": "Phase 1: Session Initialization", "status": "completed"},
|
||||
{"content": "Phase 2: Orchestration Loop (auto mode)", "status": "in_progress"},
|
||||
{"content": " → Worker: init", "status": "completed"},
|
||||
{"content": " → Worker: develop (task 2/5)", "status": "in_progress"},
|
||||
{"content": " → Worker: validate", "status": "pending"},
|
||||
{"content": " → Worker: complete", "status": "pending"}
|
||||
]
|
||||
```
|
||||
|
||||
### Parallel Mode Tracking
|
||||
|
||||
```json
|
||||
[
|
||||
{"content": "Phase 1: Session Initialization", "status": "completed"},
|
||||
{"content": "Phase 2: Parallel Analysis", "status": "in_progress"},
|
||||
{"content": " → Worker: develop (parallel)", "status": "in_progress"},
|
||||
{"content": " → Worker: debug (parallel)", "status": "in_progress"},
|
||||
{"content": " → Worker: validate (parallel)", "status": "in_progress"},
|
||||
{"content": " → Merge results", "status": "pending"}
|
||||
]
|
||||
```
|
||||
|
||||
## Core Rules
|
||||
|
||||
1. **Start Immediately**: First action is TodoWrite initialization, then Phase 1 execution
|
||||
2. **Progressive Phase Loading**: Read phase docs ONLY when that phase is about to execute
|
||||
3. **Parse Every Output**: Extract WORKER_RESULT from worker output for next decision
|
||||
4. **Worker 生命周期**: spawn → wait → [send_input if needed] → close,不长期保留 worker
|
||||
5. **结果持久化**: Worker 输出写入 `.workflow/.loop/{loopId}.workers/`
|
||||
6. **状态同步**: 每次 worker 完成后更新 master state
|
||||
7. **超时处理**: send_input 请求收敛,再超时则使用已有结果继续
|
||||
8. **DO NOT STOP**: Continuous execution until completed, paused, or max iterations
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error Type | Recovery |
|
||||
|------------|----------|
|
||||
| Worker timeout | send_input 请求收敛 → 再超时则跳过 |
|
||||
| Worker failed | Log error, 协调器决策是否重试 |
|
||||
| Batch wait partial timeout | 使用已完成结果继续 |
|
||||
| State corrupted | 从 progress 文件和 worker output 重建 |
|
||||
| Conflicting worker results | 标记冲突,由用户决定 |
|
||||
| Max iterations reached | 生成总结,记录未完成项 |
|
||||
|
||||
## Coordinator Checklist
|
||||
|
||||
### Before Each Phase
|
||||
|
||||
- [ ] Read phase reference document
|
||||
- [ ] Check current state and control signals
|
||||
- [ ] Update TodoWrite with phase tasks
|
||||
|
||||
### After Each Worker
|
||||
|
||||
- [ ] Parse WORKER_RESULT from output
|
||||
- [ ] Persist output to `.workers/{action}.output.json`
|
||||
- [ ] Update master state file
|
||||
- [ ] close_agent (确认不再需要交互)
|
||||
- [ ] Determine next action (continue / loop back / complete)
|
||||
|
||||
## Reference Documents
|
||||
|
||||
| Document | Purpose |
|
||||
|----------|---------|
|
||||
| [workers/](workers/) | Worker 定义 (init, develop, debug, validate, complete) |
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -304,20 +427,3 @@ See [phases/state-schema.md](phases/state-schema.md)
|
||||
# Resume existing loop
|
||||
/ccw-loop-b --loop-id=loop-b-20260122-abc123
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Situation | Action |
|
||||
|-----------|--------|
|
||||
| Worker timeout | send_input 请求收敛 |
|
||||
| Worker failed | Log error, 协调器决策是否重试 |
|
||||
| Batch wait partial timeout | 使用已完成结果继续 |
|
||||
| State corrupted | 从 progress 文件重建 |
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **协调器保持轻量**: 只做调度和状态管理,具体工作交给 worker
|
||||
2. **Worker 职责单一**: 每个 worker 专注一个领域
|
||||
3. **结果标准化**: Worker 输出遵循统一 WORKER_RESULT 格式
|
||||
4. **灵活模式切换**: 根据任务复杂度选择合适模式
|
||||
5. **及时清理**: Worker 完成后 close_agent 释放资源
|
||||
|
||||
156
.codex/skills/ccw-loop-b/phases/01-session-init.md
Normal file
156
.codex/skills/ccw-loop-b/phases/01-session-init.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# Phase 1: Session Initialization
|
||||
|
||||
Create or resume a development loop, initialize state file and directory structure, detect execution mode.
|
||||
|
||||
## Objective
|
||||
|
||||
- Parse user arguments (TASK, --loop-id, --mode)
|
||||
- Create new loop with unique ID OR resume existing loop
|
||||
- Initialize directory structure (progress + workers)
|
||||
- Create master state file
|
||||
- Output: loopId, state, progressDir, mode
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1.1: Parse Arguments
|
||||
|
||||
```javascript
|
||||
const { loopId: existingLoopId, task, mode = 'interactive' } = options
|
||||
|
||||
// Validate mutual exclusivity
|
||||
if (!existingLoopId && !task) {
|
||||
console.error('Either --loop-id or task description is required')
|
||||
return { status: 'error', message: 'Missing loopId or task' }
|
||||
}
|
||||
|
||||
// Validate mode
|
||||
const validModes = ['interactive', 'auto', 'parallel']
|
||||
if (!validModes.includes(mode)) {
|
||||
console.error(`Invalid mode: ${mode}. Use: ${validModes.join(', ')}`)
|
||||
return { status: 'error', message: 'Invalid mode' }
|
||||
}
|
||||
```
|
||||
|
||||
### Step 1.2: Utility Functions
|
||||
|
||||
```javascript
|
||||
const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()
|
||||
|
||||
function readState(loopId) {
|
||||
const stateFile = `.workflow/.loop/${loopId}.json`
|
||||
if (!fs.existsSync(stateFile)) return null
|
||||
return JSON.parse(Read(stateFile))
|
||||
}
|
||||
|
||||
function saveState(loopId, state) {
|
||||
state.updated_at = getUtc8ISOString()
|
||||
Write(`.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2))
|
||||
}
|
||||
```
|
||||
|
||||
### Step 1.3: New Loop Creation
|
||||
|
||||
When `TASK` is provided (no `--loop-id`):
|
||||
|
||||
```javascript
|
||||
const timestamp = getUtc8ISOString().replace(/[-:]/g, '').split('.')[0]
|
||||
const random = Math.random().toString(36).substring(2, 10)
|
||||
const loopId = `loop-b-${timestamp}-${random}`
|
||||
|
||||
console.log(`Creating new loop: ${loopId}`)
|
||||
```
|
||||
|
||||
#### Create Directory Structure
|
||||
|
||||
```bash
|
||||
mkdir -p .workflow/.loop/${loopId}.workers
|
||||
mkdir -p .workflow/.loop/${loopId}.progress
|
||||
```
|
||||
|
||||
#### Initialize State File
|
||||
|
||||
```javascript
|
||||
function createState(loopId, taskDescription, mode) {
|
||||
const now = getUtc8ISOString()
|
||||
|
||||
const state = {
|
||||
loop_id: loopId,
|
||||
title: taskDescription.substring(0, 100),
|
||||
description: taskDescription,
|
||||
mode: mode,
|
||||
status: 'running',
|
||||
current_iteration: 0,
|
||||
max_iterations: 10,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
|
||||
skill_state: {
|
||||
phase: 'init',
|
||||
action_index: 0,
|
||||
workers_completed: [],
|
||||
parallel_results: null,
|
||||
pending_tasks: [],
|
||||
completed_tasks: [],
|
||||
findings: []
|
||||
}
|
||||
}
|
||||
|
||||
Write(`.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2))
|
||||
return state
|
||||
}
|
||||
```
|
||||
|
||||
### Step 1.4: Resume Existing Loop
|
||||
|
||||
When `--loop-id` is provided:
|
||||
|
||||
```javascript
|
||||
const loopId = existingLoopId
|
||||
const state = readState(loopId)
|
||||
|
||||
if (!state) {
|
||||
console.error(`Loop not found: ${loopId}`)
|
||||
return { status: 'error', message: 'Loop not found' }
|
||||
}
|
||||
|
||||
console.log(`Resuming loop: ${loopId}`)
|
||||
console.log(`Mode: ${state.mode}, Status: ${state.status}`)
|
||||
|
||||
// Override mode if provided
|
||||
if (options['--mode']) {
|
||||
state.mode = options['--mode']
|
||||
saveState(loopId, state)
|
||||
}
|
||||
```
|
||||
|
||||
### Step 1.5: Control Signal Check
|
||||
|
||||
```javascript
|
||||
function checkControlSignals(loopId) {
|
||||
const state = readState(loopId)
|
||||
|
||||
switch (state?.status) {
|
||||
case 'paused':
|
||||
return { continue: false, action: 'pause_exit' }
|
||||
case 'failed':
|
||||
return { continue: false, action: 'stop_exit' }
|
||||
case 'running':
|
||||
return { continue: true, action: 'continue' }
|
||||
default:
|
||||
return { continue: false, action: 'stop_exit' }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
- **Variable**: `loopId` - Unique loop identifier
|
||||
- **Variable**: `state` - Initialized or resumed loop state object
|
||||
- **Variable**: `progressDir` - `.workflow/.loop/${loopId}.progress`
|
||||
- **Variable**: `workersDir` - `.workflow/.loop/${loopId}.workers`
|
||||
- **Variable**: `mode` - `'interactive'` / `'auto'` / `'parallel'`
|
||||
- **TodoWrite**: Mark Phase 1 completed, Phase 2 in_progress
|
||||
|
||||
## Next Phase
|
||||
|
||||
Return to orchestrator, then auto-continue to [Phase 2: Orchestration Loop](02-orchestration-loop.md).
|
||||
453
.codex/skills/ccw-loop-b/phases/02-orchestration-loop.md
Normal file
453
.codex/skills/ccw-loop-b/phases/02-orchestration-loop.md
Normal file
@@ -0,0 +1,453 @@
|
||||
# Phase 2: Orchestration Loop
|
||||
|
||||
Run main orchestration loop with 3-mode dispatch: Interactive, Auto, Parallel.
|
||||
|
||||
## Objective
|
||||
|
||||
- Dispatch to appropriate mode handler based on `state.mode`
|
||||
- Spawn workers with structured prompts (Goal/Scope/Context/Deliverables)
|
||||
- Handle batch wait, timeout, two-phase clarification
|
||||
- Parse WORKER_RESULT, update state per iteration
|
||||
- close_agent after confirming no more interaction needed
|
||||
- Exit on completion, pause, stop, or max iterations
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 2.1: Mode Dispatch
|
||||
|
||||
```javascript
|
||||
const mode = state.mode || 'interactive'
|
||||
|
||||
console.log(`=== CCW Loop-B Orchestrator (${mode} mode) ===`)
|
||||
|
||||
switch (mode) {
|
||||
case 'interactive':
|
||||
return await runInteractiveMode(loopId, state)
|
||||
|
||||
case 'auto':
|
||||
return await runAutoMode(loopId, state)
|
||||
|
||||
case 'parallel':
|
||||
return await runParallelMode(loopId, state)
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2.2: Interactive Mode
|
||||
|
||||
```javascript
|
||||
async function runInteractiveMode(loopId, state) {
|
||||
while (state.status === 'running') {
|
||||
// 1. Check control signals
|
||||
const signal = checkControlSignals(loopId)
|
||||
if (!signal.continue) break
|
||||
|
||||
// 2. Show menu, get user choice
|
||||
const action = await showMenuAndGetChoice(state)
|
||||
if (action === 'exit') {
|
||||
state.status = 'user_exit'
|
||||
saveState(loopId, state)
|
||||
break
|
||||
}
|
||||
|
||||
// 3. Spawn worker
|
||||
const workerId = spawn_agent({
|
||||
message: buildWorkerPrompt(action, loopId, state)
|
||||
})
|
||||
|
||||
// 4. Wait for result (with two-phase clarification support)
|
||||
let output = await waitWithClarification(workerId, action)
|
||||
|
||||
// 5. Process and persist output
|
||||
const workerResult = parseWorkerResult(output)
|
||||
persistWorkerOutput(loopId, action, workerResult)
|
||||
state = processWorkerOutput(loopId, action, workerResult, state)
|
||||
|
||||
// 6. Cleanup worker
|
||||
close_agent({ id: workerId })
|
||||
|
||||
// 7. Display result
|
||||
displayResult(workerResult)
|
||||
|
||||
// 8. Update iteration
|
||||
state.current_iteration++
|
||||
saveState(loopId, state)
|
||||
}
|
||||
|
||||
return { status: state.status, loop_id: loopId, iterations: state.current_iteration }
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2.3: Auto Mode
|
||||
|
||||
```javascript
|
||||
async function runAutoMode(loopId, state) {
|
||||
const sequence = ['init', 'develop', 'debug', 'validate', 'complete']
|
||||
let idx = state.skill_state?.action_index || 0
|
||||
|
||||
while (idx < sequence.length && state.status === 'running') {
|
||||
// Check control signals
|
||||
const signal = checkControlSignals(loopId)
|
||||
if (!signal.continue) break
|
||||
|
||||
// Check iteration limit
|
||||
if (state.current_iteration >= state.max_iterations) {
|
||||
console.log(`Max iterations (${state.max_iterations}) reached`)
|
||||
break
|
||||
}
|
||||
|
||||
const action = sequence[idx]
|
||||
|
||||
// Spawn worker
|
||||
const workerId = spawn_agent({
|
||||
message: buildWorkerPrompt(action, loopId, state)
|
||||
})
|
||||
|
||||
// Wait with two-phase clarification
|
||||
let output = await waitWithClarification(workerId, action)
|
||||
|
||||
// Parse and persist
|
||||
const workerResult = parseWorkerResult(output)
|
||||
persistWorkerOutput(loopId, action, workerResult)
|
||||
state = processWorkerOutput(loopId, action, workerResult, state)
|
||||
|
||||
close_agent({ id: workerId })
|
||||
|
||||
// Determine next step
|
||||
if (workerResult.loop_back_to && workerResult.loop_back_to !== 'null') {
|
||||
idx = sequence.indexOf(workerResult.loop_back_to)
|
||||
if (idx === -1) idx = sequence.indexOf('develop') // fallback
|
||||
} else if (workerResult.status === 'failed') {
|
||||
console.log(`Worker ${action} failed: ${workerResult.summary}`)
|
||||
break
|
||||
} else {
|
||||
idx++
|
||||
}
|
||||
|
||||
// Update state
|
||||
state.skill_state.action_index = idx
|
||||
state.current_iteration++
|
||||
saveState(loopId, state)
|
||||
}
|
||||
|
||||
return { status: state.status, loop_id: loopId, iterations: state.current_iteration }
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2.4: Parallel Mode
|
||||
|
||||
```javascript
|
||||
async function runParallelMode(loopId, state) {
|
||||
// 1. Run init worker first (sequential)
|
||||
const initWorker = spawn_agent({
|
||||
message: buildWorkerPrompt('init', loopId, state)
|
||||
})
|
||||
const initResult = wait({ ids: [initWorker], timeout_ms: 300000 })
|
||||
const initOutput = parseWorkerResult(initResult.status[initWorker].completed)
|
||||
persistWorkerOutput(loopId, 'init', initOutput)
|
||||
state = processWorkerOutput(loopId, 'init', initOutput, state)
|
||||
close_agent({ id: initWorker })
|
||||
|
||||
// 2. Spawn analysis workers in parallel
|
||||
const workers = {
|
||||
develop: spawn_agent({ message: buildWorkerPrompt('develop', loopId, state) }),
|
||||
debug: spawn_agent({ message: buildWorkerPrompt('debug', loopId, state) }),
|
||||
validate: spawn_agent({ message: buildWorkerPrompt('validate', loopId, state) })
|
||||
}
|
||||
|
||||
// 3. Batch wait for all workers
|
||||
const results = wait({
|
||||
ids: Object.values(workers),
|
||||
timeout_ms: 900000 // 15 minutes for all
|
||||
})
|
||||
|
||||
// 4. Handle partial timeout
|
||||
if (results.timed_out) {
|
||||
console.log('Partial timeout - using completed results')
|
||||
// Send convergence request to timed-out workers
|
||||
for (const [role, workerId] of Object.entries(workers)) {
|
||||
if (!results.status[workerId]?.completed) {
|
||||
send_input({
|
||||
id: workerId,
|
||||
message: '## TIMEOUT\nPlease output WORKER_RESULT with current progress immediately.'
|
||||
})
|
||||
}
|
||||
}
|
||||
// Brief second wait for convergence
|
||||
const retryResults = wait({ ids: Object.values(workers), timeout_ms: 60000 })
|
||||
Object.assign(results.status, retryResults.status)
|
||||
}
|
||||
|
||||
// 5. Collect and merge outputs
|
||||
const outputs = {}
|
||||
for (const [role, workerId] of Object.entries(workers)) {
|
||||
const completed = results.status[workerId]?.completed
|
||||
if (completed) {
|
||||
outputs[role] = parseWorkerResult(completed)
|
||||
persistWorkerOutput(loopId, role, outputs[role])
|
||||
}
|
||||
close_agent({ id: workerId })
|
||||
}
|
||||
|
||||
// 6. Merge analysis
|
||||
const mergedResults = mergeWorkerOutputs(outputs)
|
||||
state.skill_state.parallel_results = mergedResults
|
||||
state.current_iteration++
|
||||
saveState(loopId, state)
|
||||
|
||||
// 7. Run complete worker
|
||||
const completeWorker = spawn_agent({
|
||||
message: buildWorkerPrompt('complete', loopId, state)
|
||||
})
|
||||
const completeResult = wait({ ids: [completeWorker], timeout_ms: 300000 })
|
||||
const completeOutput = parseWorkerResult(completeResult.status[completeWorker].completed)
|
||||
persistWorkerOutput(loopId, 'complete', completeOutput)
|
||||
state = processWorkerOutput(loopId, 'complete', completeOutput, state)
|
||||
close_agent({ id: completeWorker })
|
||||
|
||||
return { status: state.status, loop_id: loopId, iterations: state.current_iteration }
|
||||
}
|
||||
```
|
||||
|
||||
## Helper Functions
|
||||
|
||||
### buildWorkerPrompt
|
||||
|
||||
```javascript
|
||||
function buildWorkerPrompt(action, loopId, state) {
|
||||
const roleFiles = {
|
||||
init: '~/.codex/agents/ccw-loop-b-init.md',
|
||||
develop: '~/.codex/agents/ccw-loop-b-develop.md',
|
||||
debug: '~/.codex/agents/ccw-loop-b-debug.md',
|
||||
validate: '~/.codex/agents/ccw-loop-b-validate.md',
|
||||
complete: '~/.codex/agents/ccw-loop-b-complete.md'
|
||||
}
|
||||
|
||||
return `
|
||||
## TASK ASSIGNMENT
|
||||
|
||||
### MANDATORY FIRST STEPS (Agent Execute)
|
||||
1. **Read role definition**: ${roleFiles[action]} (MUST read first)
|
||||
2. Read: .workflow/project-tech.json
|
||||
3. Read: .workflow/project-guidelines.json
|
||||
|
||||
---
|
||||
|
||||
Goal: Execute ${action} action for loop ${loopId}
|
||||
|
||||
Scope:
|
||||
- 可做: ${action} 相关的所有操作
|
||||
- 不可做: 其他 action 的操作
|
||||
- 目录限制: 项目根目录
|
||||
|
||||
Context:
|
||||
- Loop ID: ${loopId}
|
||||
- Action: ${action}
|
||||
- State File: .workflow/.loop/${loopId}.json
|
||||
- Output File: .workflow/.loop/${loopId}.workers/${action}.output.json
|
||||
- Progress File: .workflow/.loop/${loopId}.progress/${action}.md
|
||||
|
||||
Deliverables:
|
||||
- WORKER_RESULT 格式输出
|
||||
- 写入 output.json 和 progress.md
|
||||
|
||||
## CURRENT STATE
|
||||
|
||||
${JSON.stringify(state, null, 2)}
|
||||
|
||||
## TASK DESCRIPTION
|
||||
|
||||
${state.description}
|
||||
|
||||
## EXPECTED OUTPUT
|
||||
|
||||
\`\`\`
|
||||
WORKER_RESULT:
|
||||
- action: ${action}
|
||||
- status: success | failed | needs_input
|
||||
- summary: <brief summary>
|
||||
- files_changed: [list]
|
||||
- next_suggestion: <suggested next action>
|
||||
- loop_back_to: <action name if needs loop back, or null>
|
||||
|
||||
DETAILED_OUTPUT:
|
||||
<action-specific structured output>
|
||||
\`\`\`
|
||||
|
||||
Execute the ${action} action now.
|
||||
`
|
||||
}
|
||||
```
|
||||
|
||||
### waitWithClarification (Two-Phase Workflow)
|
||||
|
||||
```javascript
|
||||
async function waitWithClarification(workerId, action) {
|
||||
const result = wait({ ids: [workerId], timeout_ms: 600000 })
|
||||
|
||||
// Handle timeout
|
||||
if (result.timed_out) {
|
||||
send_input({
|
||||
id: workerId,
|
||||
message: '## TIMEOUT\nPlease converge and output WORKER_RESULT with current progress.'
|
||||
})
|
||||
const retry = wait({ ids: [workerId], timeout_ms: 300000 })
|
||||
if (retry.timed_out) {
|
||||
return `WORKER_RESULT:\n- action: ${action}\n- status: failed\n- summary: Worker timeout\n\nNEXT_ACTION_NEEDED: NONE`
|
||||
}
|
||||
return retry.status[workerId].completed
|
||||
}
|
||||
|
||||
const output = result.status[workerId].completed
|
||||
|
||||
// Check if worker needs clarification (two-phase)
|
||||
if (output.includes('CLARIFICATION_NEEDED')) {
|
||||
// Collect user answers
|
||||
const questions = parseClarificationQuestions(output)
|
||||
const userAnswers = await collectUserAnswers(questions)
|
||||
|
||||
// Send answers back to worker
|
||||
send_input({
|
||||
id: workerId,
|
||||
message: `
|
||||
## CLARIFICATION ANSWERS
|
||||
|
||||
${userAnswers.map(a => `Q: ${a.question}\nA: ${a.answer}`).join('\n\n')}
|
||||
|
||||
## CONTINUE EXECUTION
|
||||
|
||||
Based on clarification answers, continue with the ${action} action.
|
||||
Output WORKER_RESULT when complete.
|
||||
`
|
||||
})
|
||||
|
||||
// Wait for final result
|
||||
const finalResult = wait({ ids: [workerId], timeout_ms: 600000 })
|
||||
return finalResult.status[workerId]?.completed || output
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
```
|
||||
|
||||
### parseWorkerResult
|
||||
|
||||
```javascript
|
||||
function parseWorkerResult(output) {
|
||||
const result = {
|
||||
action: 'unknown',
|
||||
status: 'unknown',
|
||||
summary: '',
|
||||
files_changed: [],
|
||||
next_suggestion: null,
|
||||
loop_back_to: null,
|
||||
detailed_output: ''
|
||||
}
|
||||
|
||||
// Parse WORKER_RESULT block
|
||||
const match = output.match(/WORKER_RESULT:\s*([\s\S]*?)(?:DETAILED_OUTPUT:|$)/)
|
||||
if (match) {
|
||||
const lines = match[1].split('\n')
|
||||
for (const line of lines) {
|
||||
const m = line.match(/^-\s*(\w[\w_]*):\s*(.+)$/)
|
||||
if (m) {
|
||||
const [, key, value] = m
|
||||
if (key === 'files_changed') {
|
||||
try { result.files_changed = JSON.parse(value) } catch {}
|
||||
} else {
|
||||
result[key] = value.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse DETAILED_OUTPUT
|
||||
const detailMatch = output.match(/DETAILED_OUTPUT:\s*([\s\S]*)$/)
|
||||
if (detailMatch) {
|
||||
result.detailed_output = detailMatch[1].trim()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
```
|
||||
|
||||
### mergeWorkerOutputs (Parallel Mode)
|
||||
|
||||
```javascript
|
||||
function mergeWorkerOutputs(outputs) {
|
||||
const merged = {
|
||||
develop: outputs.develop || null,
|
||||
debug: outputs.debug || null,
|
||||
validate: outputs.validate || null,
|
||||
conflicts: [],
|
||||
merged_at: getUtc8ISOString()
|
||||
}
|
||||
|
||||
// Detect file conflicts: multiple workers suggest modifying same file
|
||||
const allFiles = {}
|
||||
for (const [role, output] of Object.entries(outputs)) {
|
||||
if (output?.files_changed) {
|
||||
for (const file of output.files_changed) {
|
||||
if (allFiles[file]) {
|
||||
merged.conflicts.push({
|
||||
file,
|
||||
workers: [allFiles[file], role],
|
||||
resolution: 'manual'
|
||||
})
|
||||
} else {
|
||||
allFiles[file] = role
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return merged
|
||||
}
|
||||
```
|
||||
|
||||
### showMenuAndGetChoice
|
||||
|
||||
```javascript
|
||||
async function showMenuAndGetChoice(state) {
|
||||
const ss = state.skill_state
|
||||
const pendingCount = ss?.pending_tasks?.length || 0
|
||||
const completedCount = ss?.completed_tasks?.length || 0
|
||||
|
||||
const response = await AskUserQuestion({
|
||||
questions: [{
|
||||
question: `Select next action (completed: ${completedCount}, pending: ${pendingCount}):`,
|
||||
header: "Action",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "develop", description: `Continue development (${pendingCount} pending)` },
|
||||
{ label: "debug", description: "Start debugging / diagnosis" },
|
||||
{ label: "validate", description: "Run tests and validation" },
|
||||
{ label: "complete", description: "Complete loop and generate summary" },
|
||||
{ label: "exit", description: "Exit and save progress" }
|
||||
]
|
||||
}]
|
||||
})
|
||||
|
||||
return response["Action"]
|
||||
}
|
||||
```
|
||||
|
||||
### persistWorkerOutput
|
||||
|
||||
```javascript
|
||||
function persistWorkerOutput(loopId, action, workerResult) {
|
||||
const outputPath = `.workflow/.loop/${loopId}.workers/${action}.output.json`
|
||||
Write(outputPath, JSON.stringify({
|
||||
...workerResult,
|
||||
timestamp: getUtc8ISOString()
|
||||
}, null, 2))
|
||||
}
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
- **Return**: `{ status, loop_id, iterations }`
|
||||
- **TodoWrite**: Mark Phase 2 completed
|
||||
|
||||
## Next Phase
|
||||
|
||||
None. Phase 2 is the terminal phase of the orchestrator.
|
||||
168
.codex/skills/ccw-loop-b/workers/worker-complete.md
Normal file
168
.codex/skills/ccw-loop-b/workers/worker-complete.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# Worker: COMPLETE
|
||||
|
||||
Session finalization worker. Aggregate results, generate summary, cleanup.
|
||||
|
||||
## Purpose
|
||||
|
||||
- Aggregate all worker results into comprehensive summary
|
||||
- Verify completeness of tasks
|
||||
- Generate commit message suggestion
|
||||
- Offer expansion options
|
||||
- Mark loop as completed
|
||||
|
||||
## Preconditions
|
||||
|
||||
- `state.status === 'running'`
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Read All Worker Outputs
|
||||
|
||||
```javascript
|
||||
const workerOutputs = {}
|
||||
for (const action of ['init', 'develop', 'debug', 'validate']) {
|
||||
const outputPath = `${workersDir}/${action}.output.json`
|
||||
if (fs.existsSync(outputPath)) {
|
||||
workerOutputs[action] = JSON.parse(Read(outputPath))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Aggregate Statistics
|
||||
|
||||
```javascript
|
||||
const stats = {
|
||||
duration: Date.now() - new Date(state.created_at).getTime(),
|
||||
iterations: state.current_iteration,
|
||||
tasks_completed: state.skill_state.completed_tasks.length,
|
||||
tasks_total: state.skill_state.completed_tasks.length + state.skill_state.pending_tasks.length,
|
||||
files_changed: collectAllFilesChanged(workerOutputs),
|
||||
test_passed: workerOutputs.validate?.summary?.passed || 0,
|
||||
test_total: workerOutputs.validate?.summary?.total || 0,
|
||||
coverage: workerOutputs.validate?.coverage || 'N/A'
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Generate Summary
|
||||
|
||||
```javascript
|
||||
Write(`${progressDir}/summary.md`, `# CCW Loop-B Session Summary
|
||||
|
||||
**Loop ID**: ${loopId}
|
||||
**Task**: ${state.description}
|
||||
**Mode**: ${state.mode}
|
||||
**Started**: ${state.created_at}
|
||||
**Completed**: ${getUtc8ISOString()}
|
||||
**Duration**: ${formatDuration(stats.duration)}
|
||||
|
||||
---
|
||||
|
||||
## Results
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Iterations | ${stats.iterations} |
|
||||
| Tasks Completed | ${stats.tasks_completed}/${stats.tasks_total} |
|
||||
| Tests | ${stats.test_passed}/${stats.test_total} |
|
||||
| Coverage | ${stats.coverage} |
|
||||
| Files Changed | ${stats.files_changed.length} |
|
||||
|
||||
## Files Changed
|
||||
|
||||
${stats.files_changed.map(f => `- \`${f}\``).join('\n') || '- None'}
|
||||
|
||||
## Worker Summary
|
||||
|
||||
${Object.entries(workerOutputs).map(([action, output]) => `
|
||||
### ${action}
|
||||
- Status: ${output.status}
|
||||
- Summary: ${output.summary}
|
||||
`).join('\n')}
|
||||
|
||||
## Recommendations
|
||||
|
||||
${generateRecommendations(stats, state)}
|
||||
|
||||
---
|
||||
|
||||
*Generated by CCW Loop-B at ${getUtc8ISOString()}*
|
||||
`)
|
||||
```
|
||||
|
||||
### Step 4: Generate Commit Suggestion
|
||||
|
||||
```javascript
|
||||
const commitSuggestion = {
|
||||
message: generateCommitMessage(state.description, stats),
|
||||
files: stats.files_changed,
|
||||
ready_for_pr: stats.test_passed > 0 && stats.tasks_completed === stats.tasks_total
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: Update State
|
||||
|
||||
```javascript
|
||||
state.status = 'completed'
|
||||
state.completed_at = getUtc8ISOString()
|
||||
state.skill_state.phase = 'complete'
|
||||
state.skill_state.workers_completed.push('complete')
|
||||
saveState(loopId, state)
|
||||
```
|
||||
|
||||
## Output Format
|
||||
|
||||
```
|
||||
WORKER_RESULT:
|
||||
- action: complete
|
||||
- status: success
|
||||
- summary: Loop completed. {tasks_completed} tasks, {test_passed} tests pass
|
||||
- files_changed: []
|
||||
- next_suggestion: null
|
||||
- loop_back_to: null
|
||||
|
||||
DETAILED_OUTPUT:
|
||||
SESSION_SUMMARY:
|
||||
achievements: [...]
|
||||
files_changed: [...]
|
||||
test_results: { passed: N, total: N }
|
||||
|
||||
COMMIT_SUGGESTION:
|
||||
message: "feat: ..."
|
||||
files: [...]
|
||||
ready_for_pr: true
|
||||
|
||||
EXPANSION_OPTIONS:
|
||||
1. [test] Add more test cases
|
||||
2. [enhance] Feature enhancements
|
||||
3. [refactor] Code refactoring
|
||||
4. [doc] Documentation updates
|
||||
```
|
||||
|
||||
## Helper Functions
|
||||
|
||||
```javascript
|
||||
function formatDuration(ms) {
|
||||
const seconds = Math.floor(ms / 1000)
|
||||
const minutes = Math.floor(seconds / 60)
|
||||
const hours = Math.floor(minutes / 60)
|
||||
if (hours > 0) return `${hours}h ${minutes % 60}m`
|
||||
if (minutes > 0) return `${minutes}m ${seconds % 60}s`
|
||||
return `${seconds}s`
|
||||
}
|
||||
|
||||
function generateRecommendations(stats, state) {
|
||||
const recs = []
|
||||
if (stats.tasks_completed < stats.tasks_total) recs.push('- Complete remaining tasks')
|
||||
if (stats.test_passed < stats.test_total) recs.push('- Fix failing tests')
|
||||
if (stats.coverage !== 'N/A' && parseFloat(stats.coverage) < 80) recs.push(`- Improve coverage (${stats.coverage}%)`)
|
||||
if (recs.length === 0) recs.push('- Consider code review', '- Update documentation')
|
||||
return recs.join('\n')
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error | Recovery |
|
||||
|-------|----------|
|
||||
| Missing worker outputs | Generate partial summary |
|
||||
| State write failed | Retry, then report |
|
||||
148
.codex/skills/ccw-loop-b/workers/worker-debug.md
Normal file
148
.codex/skills/ccw-loop-b/workers/worker-debug.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# Worker: DEBUG
|
||||
|
||||
Problem diagnosis worker. Hypothesis-driven debugging with evidence tracking.
|
||||
|
||||
## Purpose
|
||||
|
||||
- Locate error source and understand failure mechanism
|
||||
- Generate testable hypotheses ranked by likelihood
|
||||
- Collect evidence and evaluate against criteria
|
||||
- Document root cause and fix recommendations
|
||||
|
||||
## Preconditions
|
||||
|
||||
- Issue exists (test failure, bug report, blocked task)
|
||||
- `state.status === 'running'`
|
||||
|
||||
## Mode Detection
|
||||
|
||||
```javascript
|
||||
const debugPath = `${progressDir}/debug.md`
|
||||
const debugExists = fs.existsSync(debugPath)
|
||||
|
||||
const debugMode = debugExists ? 'continue' : 'explore'
|
||||
```
|
||||
|
||||
## Execution
|
||||
|
||||
### Mode: Explore (First Debug)
|
||||
|
||||
#### Step E1: Understand Problem
|
||||
|
||||
```javascript
|
||||
// From test failures, blocked tasks, or user description
|
||||
const bugDescription = state.skill_state.findings?.[0]
|
||||
|| state.description
|
||||
```
|
||||
|
||||
#### Step E2: Search Codebase
|
||||
|
||||
```javascript
|
||||
const searchResults = mcp__ace_tool__search_context({
|
||||
project_root_path: '.',
|
||||
query: `code related to: ${bugDescription}`
|
||||
})
|
||||
```
|
||||
|
||||
#### Step E3: Generate Hypotheses
|
||||
|
||||
```javascript
|
||||
const hypotheses = [
|
||||
{
|
||||
id: 'H1',
|
||||
description: 'Most likely cause',
|
||||
testable_condition: 'What to check',
|
||||
confidence: 'high | medium | low',
|
||||
evidence: [],
|
||||
mechanism: 'Detailed explanation of how this causes the bug'
|
||||
},
|
||||
// H2, H3...
|
||||
]
|
||||
```
|
||||
|
||||
#### Step E4: Create Understanding Document
|
||||
|
||||
```javascript
|
||||
Write(`${progressDir}/debug.md`, `# Debug Understanding
|
||||
|
||||
**Loop ID**: ${loopId}
|
||||
**Bug**: ${bugDescription}
|
||||
**Started**: ${getUtc8ISOString()}
|
||||
|
||||
---
|
||||
|
||||
## Hypotheses
|
||||
|
||||
${hypotheses.map(h => `
|
||||
### ${h.id}: ${h.description}
|
||||
- Confidence: ${h.confidence}
|
||||
- Testable: ${h.testable_condition}
|
||||
- Mechanism: ${h.mechanism}
|
||||
`).join('\n')}
|
||||
|
||||
## Evidence
|
||||
|
||||
[To be collected]
|
||||
|
||||
## Root Cause
|
||||
|
||||
[Pending investigation]
|
||||
`)
|
||||
```
|
||||
|
||||
### Mode: Continue (Previous Debug Exists)
|
||||
|
||||
#### Step C1: Review Previous Findings
|
||||
|
||||
```javascript
|
||||
const previousDebug = Read(`${progressDir}/debug.md`)
|
||||
// Continue investigation based on previous findings
|
||||
```
|
||||
|
||||
#### Step C2: Apply Fix and Verify
|
||||
|
||||
```javascript
|
||||
// If root cause identified, apply fix
|
||||
// Record fix in progress document
|
||||
```
|
||||
|
||||
## Output Format
|
||||
|
||||
```
|
||||
WORKER_RESULT:
|
||||
- action: debug
|
||||
- status: success
|
||||
- summary: Root cause: {description}
|
||||
- files_changed: []
|
||||
- next_suggestion: develop
|
||||
- loop_back_to: develop
|
||||
|
||||
DETAILED_OUTPUT:
|
||||
ROOT_CAUSE_ANALYSIS:
|
||||
hypothesis: "H1: {description}"
|
||||
confidence: high
|
||||
evidence: [...]
|
||||
mechanism: "Detailed explanation"
|
||||
|
||||
FIX_RECOMMENDATIONS:
|
||||
1. {specific fix action}
|
||||
2. {verification step}
|
||||
```
|
||||
|
||||
## Clarification Mode
|
||||
|
||||
If insufficient information:
|
||||
|
||||
```
|
||||
CLARIFICATION_NEEDED:
|
||||
Q1: Can you reproduce the issue? | Options: [Yes, No, Sometimes] | Recommended: [Yes]
|
||||
Q2: When did this start? | Options: [Recent change, Always, Unknown] | Recommended: [Recent change]
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error | Recovery |
|
||||
|-------|----------|
|
||||
| Insufficient info | Output CLARIFICATION_NEEDED |
|
||||
| All hypotheses rejected | Generate new hypotheses |
|
||||
| >5 iterations | Suggest escalation |
|
||||
123
.codex/skills/ccw-loop-b/workers/worker-develop.md
Normal file
123
.codex/skills/ccw-loop-b/workers/worker-develop.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# Worker: DEVELOP
|
||||
|
||||
Code implementation worker. Execute pending tasks, record changes.
|
||||
|
||||
## Purpose
|
||||
|
||||
- Execute next pending development task
|
||||
- Implement code changes following project conventions
|
||||
- Record progress to markdown and NDJSON log
|
||||
- Update task status in state
|
||||
|
||||
## Preconditions
|
||||
|
||||
- `state.skill_state.pending_tasks.length > 0`
|
||||
- `state.status === 'running'`
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Find Pending Task
|
||||
|
||||
```javascript
|
||||
const tasks = state.skill_state.pending_tasks
|
||||
const currentTask = tasks.find(t => t.status === 'pending')
|
||||
|
||||
if (!currentTask) {
|
||||
// All tasks done
|
||||
return WORKER_RESULT with next_suggestion: 'validate'
|
||||
}
|
||||
|
||||
currentTask.status = 'in_progress'
|
||||
```
|
||||
|
||||
### Step 2: Find Existing Patterns
|
||||
|
||||
```javascript
|
||||
// Use ACE search_context to find similar implementations
|
||||
const patterns = mcp__ace_tool__search_context({
|
||||
project_root_path: '.',
|
||||
query: `implementation patterns for: ${currentTask.description}`
|
||||
})
|
||||
|
||||
// Study 3+ similar features/components
|
||||
// Follow existing conventions
|
||||
```
|
||||
|
||||
### Step 3: Implement Task
|
||||
|
||||
```javascript
|
||||
// Use appropriate tools:
|
||||
// - ACE search_context for finding patterns
|
||||
// - Read for loading files
|
||||
// - Edit/Write for making changes
|
||||
|
||||
const filesChanged = []
|
||||
// ... implementation logic ...
|
||||
```
|
||||
|
||||
### Step 4: Record Changes
|
||||
|
||||
```javascript
|
||||
// Append to progress document
|
||||
const progressEntry = `
|
||||
### Task ${currentTask.id} - ${currentTask.description} (${getUtc8ISOString()})
|
||||
|
||||
**Files Changed**:
|
||||
${filesChanged.map(f => `- \`${f}\``).join('\n')}
|
||||
|
||||
**Summary**: [implementation description]
|
||||
|
||||
**Status**: COMPLETED
|
||||
|
||||
---
|
||||
`
|
||||
|
||||
const existingProgress = Read(`${progressDir}/develop.md`)
|
||||
Write(`${progressDir}/develop.md`, existingProgress + progressEntry)
|
||||
```
|
||||
|
||||
### Step 5: Update State
|
||||
|
||||
```javascript
|
||||
currentTask.status = 'completed'
|
||||
state.skill_state.completed_tasks.push(currentTask)
|
||||
state.skill_state.pending_tasks = tasks.filter(t => t.status === 'pending')
|
||||
saveState(loopId, state)
|
||||
```
|
||||
|
||||
## Output Format
|
||||
|
||||
```
|
||||
WORKER_RESULT:
|
||||
- action: develop
|
||||
- status: success
|
||||
- summary: Implemented: {task_description}
|
||||
- files_changed: ["file1.ts", "file2.ts"]
|
||||
- next_suggestion: develop | validate
|
||||
- loop_back_to: null
|
||||
|
||||
DETAILED_OUTPUT:
|
||||
tasks_completed: [T1]
|
||||
tasks_remaining: [T2, T3]
|
||||
metrics:
|
||||
lines_added: 180
|
||||
lines_removed: 15
|
||||
```
|
||||
|
||||
## Clarification Mode
|
||||
|
||||
If task is ambiguous, output:
|
||||
|
||||
```
|
||||
CLARIFICATION_NEEDED:
|
||||
Q1: [question about implementation approach] | Options: [A, B] | Recommended: [A]
|
||||
Q2: [question about scope] | Options: [A, B, C] | Recommended: [B]
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error | Recovery |
|
||||
|-------|----------|
|
||||
| Pattern unclear | Output CLARIFICATION_NEEDED |
|
||||
| Task blocked | Mark blocked, suggest debug |
|
||||
| Partial completion | Set loop_back_to: "develop" |
|
||||
115
.codex/skills/ccw-loop-b/workers/worker-init.md
Normal file
115
.codex/skills/ccw-loop-b/workers/worker-init.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# Worker: INIT
|
||||
|
||||
Session initialization worker. Parse requirements, create execution plan.
|
||||
|
||||
## Purpose
|
||||
|
||||
- Parse task description and project context
|
||||
- Break task into development phases
|
||||
- Generate initial task list
|
||||
- Create progress document structure
|
||||
|
||||
## Preconditions
|
||||
|
||||
- `state.status === 'running'`
|
||||
- `state.skill_state.phase === 'init'` or first run
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Read Project Context
|
||||
|
||||
```javascript
|
||||
// MANDATORY FIRST STEPS (already in prompt)
|
||||
// 1. Read role definition
|
||||
// 2. Read .workflow/project-tech.json
|
||||
// 3. Read .workflow/project-guidelines.json
|
||||
```
|
||||
|
||||
### Step 2: Analyze Task
|
||||
|
||||
```javascript
|
||||
// Use ACE search_context to find relevant patterns
|
||||
const searchResults = mcp__ace_tool__search_context({
|
||||
project_root_path: '.',
|
||||
query: `code related to: ${state.description}`
|
||||
})
|
||||
|
||||
// Parse task into 3-7 development tasks
|
||||
const tasks = analyzeAndDecompose(state.description, searchResults)
|
||||
```
|
||||
|
||||
### Step 3: Create Task Breakdown
|
||||
|
||||
```javascript
|
||||
const breakdown = tasks.map((t, i) => ({
|
||||
id: `T${i + 1}`,
|
||||
description: t.description,
|
||||
priority: t.priority || i + 1,
|
||||
status: 'pending',
|
||||
files: t.relatedFiles || []
|
||||
}))
|
||||
```
|
||||
|
||||
### Step 4: Initialize Progress Document
|
||||
|
||||
```javascript
|
||||
const progressPath = `${progressDir}/develop.md`
|
||||
|
||||
Write(progressPath, `# Development Progress
|
||||
|
||||
**Loop ID**: ${loopId}
|
||||
**Task**: ${state.description}
|
||||
**Started**: ${getUtc8ISOString()}
|
||||
|
||||
---
|
||||
|
||||
## Task List
|
||||
|
||||
${breakdown.map((t, i) => `${i + 1}. [ ] ${t.description}`).join('\n')}
|
||||
|
||||
---
|
||||
|
||||
## Progress Timeline
|
||||
|
||||
`)
|
||||
```
|
||||
|
||||
### Step 5: Update State
|
||||
|
||||
```javascript
|
||||
state.skill_state.pending_tasks = breakdown
|
||||
state.skill_state.phase = 'init'
|
||||
state.skill_state.workers_completed.push('init')
|
||||
saveState(loopId, state)
|
||||
```
|
||||
|
||||
## Output Format
|
||||
|
||||
```
|
||||
WORKER_RESULT:
|
||||
- action: init
|
||||
- status: success
|
||||
- summary: Initialized with {N} development tasks
|
||||
- files_changed: []
|
||||
- next_suggestion: develop
|
||||
- loop_back_to: null
|
||||
|
||||
DETAILED_OUTPUT:
|
||||
TASK_BREAKDOWN:
|
||||
- T1: {description}
|
||||
- T2: {description}
|
||||
...
|
||||
|
||||
EXECUTION_PLAN:
|
||||
1. Develop (T1-T2)
|
||||
2. Validate
|
||||
3. Complete
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error | Recovery |
|
||||
|-------|----------|
|
||||
| Task analysis failed | Create single generic task |
|
||||
| Project context missing | Proceed without context |
|
||||
| State write failed | Retry once, then report |
|
||||
132
.codex/skills/ccw-loop-b/workers/worker-validate.md
Normal file
132
.codex/skills/ccw-loop-b/workers/worker-validate.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# Worker: VALIDATE
|
||||
|
||||
Testing and verification worker. Run tests, check coverage, quality gates.
|
||||
|
||||
## Purpose
|
||||
|
||||
- Detect test framework and run tests
|
||||
- Measure code coverage
|
||||
- Check quality gates (lint, types, security)
|
||||
- Generate validation report
|
||||
- Determine pass/fail status
|
||||
|
||||
## Preconditions
|
||||
|
||||
- Code exists to validate
|
||||
- `state.status === 'running'`
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Detect Test Framework
|
||||
|
||||
```javascript
|
||||
const packageJson = JSON.parse(Read('package.json') || '{}')
|
||||
const testScript = packageJson.scripts?.test || 'npm test'
|
||||
const coverageScript = packageJson.scripts?.['test:coverage']
|
||||
```
|
||||
|
||||
### Step 2: Run Tests
|
||||
|
||||
```javascript
|
||||
const testResult = await Bash({
|
||||
command: testScript,
|
||||
timeout: 300000 // 5 minutes
|
||||
})
|
||||
|
||||
const testResults = parseTestOutput(testResult.stdout, testResult.stderr)
|
||||
```
|
||||
|
||||
### Step 3: Run Coverage (if available)
|
||||
|
||||
```javascript
|
||||
let coverageData = null
|
||||
if (coverageScript) {
|
||||
const coverageResult = await Bash({ command: coverageScript, timeout: 300000 })
|
||||
coverageData = parseCoverageReport(coverageResult.stdout)
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Quality Checks
|
||||
|
||||
```javascript
|
||||
// Lint check
|
||||
const lintResult = await Bash({ command: 'npm run lint 2>&1 || true' })
|
||||
|
||||
// Type check
|
||||
const typeResult = await Bash({ command: 'npx tsc --noEmit 2>&1 || true' })
|
||||
```
|
||||
|
||||
### Step 5: Generate Validation Report
|
||||
|
||||
```javascript
|
||||
Write(`${progressDir}/validate.md`, `# Validation Report
|
||||
|
||||
**Loop ID**: ${loopId}
|
||||
**Validated**: ${getUtc8ISOString()}
|
||||
|
||||
## Test Results
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Total | ${testResults.total} |
|
||||
| Passed | ${testResults.passed} |
|
||||
| Failed | ${testResults.failed} |
|
||||
| Pass Rate | ${((testResults.passed / testResults.total) * 100).toFixed(1)}% |
|
||||
|
||||
## Coverage
|
||||
|
||||
${coverageData ? `Overall: ${coverageData.overall}%` : 'N/A'}
|
||||
|
||||
## Quality Checks
|
||||
|
||||
- Lint: ${lintResult.exitCode === 0 ? 'PASS' : 'FAIL'}
|
||||
- Types: ${typeResult.exitCode === 0 ? 'PASS' : 'FAIL'}
|
||||
|
||||
## Failed Tests
|
||||
|
||||
${testResults.failures?.map(f => `- ${f.name}: ${f.error}`).join('\n') || 'None'}
|
||||
`)
|
||||
```
|
||||
|
||||
### Step 6: Save Structured Results
|
||||
|
||||
```javascript
|
||||
Write(`${workersDir}/validate.output.json`, JSON.stringify({
|
||||
action: 'validate',
|
||||
timestamp: getUtc8ISOString(),
|
||||
summary: { total: testResults.total, passed: testResults.passed, failed: testResults.failed },
|
||||
coverage: coverageData?.overall || null,
|
||||
quality: { lint: lintResult.exitCode === 0, types: typeResult.exitCode === 0 }
|
||||
}, null, 2))
|
||||
```
|
||||
|
||||
## Output Format
|
||||
|
||||
```
|
||||
WORKER_RESULT:
|
||||
- action: validate
|
||||
- status: success
|
||||
- summary: {passed}/{total} tests pass, coverage {N}%
|
||||
- files_changed: []
|
||||
- next_suggestion: complete | develop
|
||||
- loop_back_to: develop (if tests fail)
|
||||
|
||||
DETAILED_OUTPUT:
|
||||
TEST_RESULTS:
|
||||
unit_tests: { passed: 98, failed: 0 }
|
||||
integration_tests: { passed: 15, failed: 0 }
|
||||
coverage: "95%"
|
||||
|
||||
QUALITY_CHECKS:
|
||||
lint: PASS
|
||||
types: PASS
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error | Recovery |
|
||||
|-------|----------|
|
||||
| Tests don't run | Check config, report error |
|
||||
| All tests fail | Suggest debug action |
|
||||
| Coverage tool missing | Skip coverage, tests only |
|
||||
| Timeout | Increase timeout or split tests |
|
||||
Reference in New Issue
Block a user