Add orchestration loop phase and workflow execution skill

- Implemented Phase 2: Orchestration Loop for CCW Loop, including executor agent spawning, main loop execution, and iteration management.
- Introduced helper functions for action result parsing and user interaction.
- Created new skill: workflow-execute, coordinating agent execution for workflow tasks with automatic session discovery, parallel task processing, and status tracking.
- Defined execution flow, key design principles, and error handling strategies for the workflow execution process.
This commit is contained in:
catlog22
2026-02-06 23:58:18 +08:00
parent 5b48bcff64
commit a4ea817a5f
19 changed files with 1633 additions and 2876 deletions

View File

@@ -1,171 +0,0 @@
# CCW Loop Skill (Codex Version)
Stateless iterative development loop workflow using Codex subagent pattern.
## Overview
CCW Loop is an autonomous development workflow that supports:
- **Develop**: Task decomposition -> Code implementation -> Progress tracking
- **Debug**: Hypothesis generation -> Evidence collection -> Root cause analysis
- **Validate**: Test execution -> Coverage check -> Quality assessment
## Subagent 机制
核心 API: `spawn_agent` / `wait` / `send_input` / `close_agent`
可用模式: 单 agent 深度交互 / 多 agent 并行 / 混合模式
## Installation
Files are in `.codex/skills/ccw-loop/`:
```
.codex/skills/ccw-loop/
+-- SKILL.md # Main skill definition
+-- README.md # This file
+-- phases/
| +-- orchestrator.md # Orchestration logic
| +-- state-schema.md # State structure
| +-- actions/
| +-- action-init.md # Initialize session
| +-- action-develop.md # Development task
| +-- action-debug.md # Hypothesis debugging
| +-- action-validate.md # Test validation
| +-- action-complete.md # Complete loop
| +-- action-menu.md # Interactive menu
+-- specs/
| +-- action-catalog.md # Action catalog
+-- templates/
+-- (templates)
.codex/agents/
+-- ccw-loop-executor.md # Executor agent role
```
## Usage
### Start New Loop
```bash
# Direct call with task description
/ccw-loop TASK="Implement user authentication"
# Auto-cycle mode
/ccw-loop --auto TASK="Fix login bug and add tests"
```
### Continue Existing Loop
```bash
# Resume from loop ID
/ccw-loop --loop-id=loop-v2-20260122-abc123
# API triggered (from Dashboard)
/ccw-loop --loop-id=loop-v2-20260122-abc123 --auto
```
## Execution Flow
```
1. Parse arguments (task or --loop-id)
2. Create/read state from .workflow/.loop/{loopId}.json
3. spawn_agent with ccw-loop-executor role
4. Main loop:
a. wait() for agent output
b. Parse ACTION_RESULT
c. Handle outcome:
- COMPLETED/PAUSED/STOPPED: exit loop
- WAITING_INPUT: collect user input, send_input
- Next action: send_input to continue
d. Update state file
5. close_agent when done
```
## Session Files
```
.workflow/.loop/
+-- {loopId}.json # Master state (API + Skill)
+-- {loopId}.progress/
+-- develop.md # Development timeline
+-- debug.md # Understanding evolution
+-- validate.md # Validation report
+-- changes.log # Code changes (NDJSON)
+-- debug.log # Debug log (NDJSON)
+-- summary.md # Completion summary
```
## Codex Pattern Highlights
### Single Agent Deep Interaction
Instead of creating multiple agents, use `send_input` for multi-phase:
```javascript
const agent = spawn_agent({ message: role + task })
// Phase 1: INIT
const initResult = wait({ ids: [agent] })
// Phase 2: DEVELOP (via send_input, same agent)
send_input({ id: agent, message: 'Execute DEVELOP' })
const devResult = wait({ ids: [agent] })
// Phase 3: VALIDATE (via send_input, same agent)
send_input({ id: agent, message: 'Execute VALIDATE' })
const valResult = wait({ ids: [agent] })
// Only close when all done
close_agent({ id: agent })
```
### Role Path Passing
Agent reads role file itself (no content embedding):
```javascript
spawn_agent({
message: `
### MANDATORY FIRST STEPS
1. **Read role definition**: ~/.codex/agents/ccw-loop-executor.md
2. Read: .workflow/project-tech.json
...
`
})
```
### Explicit Lifecycle Management
- Always use `wait({ ids })` to get results
- Never assume `close_agent` returns results
- Only `close_agent` when confirming no more interaction needed
## Error Handling
| Situation | Action |
|-----------|--------|
| Agent timeout | `send_input` requesting convergence |
| Session not found | Create new session |
| State corrupted | Rebuild from progress files |
| Tests fail | Loop back to DEBUG |
| >10 iterations | Warn and suggest break |
## Integration
### Dashboard Integration
Works with CCW Dashboard Loop Monitor:
- Dashboard creates loop via API
- API triggers this skill with `--loop-id`
- Skill reads/writes `.workflow/.loop/{loopId}.json`
- Dashboard polls state for real-time updates
### Control Signals
- `paused`: Skill exits gracefully, waits for resume
- `failed`: Skill terminates
- `running`: Skill continues execution
## License
MIT

View File

@@ -1,309 +1,436 @@
---
name: CCW Loop
description: Stateless iterative development loop workflow with documented progress. Supports develop, debug, and validate phases with file-based state tracking. Triggers on "ccw-loop", "dev loop", "development loop", "开发循环", "迭代开发".
argument-hint: TASK="<task description>" [--loop-id=<id>] [--auto]
name: ccw-loop
description: Stateless iterative development loop with single-agent deep interaction. Supports develop, debug, validate phases with file-based state tracking. Triggers on "ccw-loop", "dev loop", "development loop", "开发循环", "迭代开发".
allowed-tools: Task, AskUserQuestion, TodoWrite, Read, Write, Edit, Bash, Glob, Grep
---
# CCW Loop - Codex Stateless Iterative Development Workflow
# CCW Loop - Stateless Iterative Development Workflow
Stateless iterative development loop using Codex subagent pattern. Supports develop, debug, and validate phases with file-based state tracking.
Stateless iterative development loop using Codex single-agent deep interaction pattern. One agent handles all phases (develop debug validate → complete) via `send_input`, with file-based state tracking and Dashboard integration.
## Arguments
| Arg | Required | Description |
|-----|----------|-------------|
| TASK | No | Task description (for new loop, mutually exclusive with --loop-id) |
| --loop-id | No | Existing loop ID to continue (from API or previous session) |
| --auto | No | Auto-cycle mode (develop -> debug -> validate -> complete) |
## Unified Architecture (Codex Subagent Pattern)
## Architecture Overview
```
+-------------------------------------------------------------+
| Dashboard (UI) |
| [Create] [Start] [Pause] [Resume] [Stop] [View Progress] |
| [Create] [Start] [Pause] [Resume] [Stop] [View Progress] |
+-------------------------------------------------------------+
|
v
+-------------------------------------------------------------+
| loop-v2-routes.ts (Control Plane) |
| |
| State: .workflow/.loop/{loopId}.json (MASTER) |
| Tasks: .workflow/.loop/{loopId}.tasks.jsonl |
| State: .workflow/.loop/{loopId}.json (MASTER) |
| Tasks: .workflow/.loop/{loopId}.tasks.jsonl |
| |
| /start -> Trigger ccw-loop skill with --loop-id |
| /pause -> Set status='paused' (skill checks before action) |
| /stop -> Set status='failed' (skill terminates) |
| /resume -> Set status='running' (skill continues) |
| /start -> Trigger ccw-loop skill with --loop-id |
| /pause -> Set status='paused' (skill checks before action) |
| /stop -> Set status='failed' (skill terminates) |
| /resume -> Set status='running' (skill continues) |
+-------------------------------------------------------------+
|
v
+-------------------------------------------------------------+
| ccw-loop Skill (Execution Plane) |
| |
| Codex Pattern: spawn_agent -> wait -> send_input -> close |
| Single Agent Deep Interaction: |
| spawn_agent -> wait -> send_input -> ... -> close_agent |
| |
| Reads/Writes: .workflow/.loop/{loopId}.json (unified state) |
| Writes: .workflow/.loop/{loopId}.progress/* (progress files) |
| |
| BEFORE each action: |
| -> Check status: paused/stopped -> exit gracefully |
| -> running -> continue with action |
| |
| Actions: init -> develop -> debug -> validate -> complete |
| Actions: INIT -> DEVELOP -> DEBUG -> VALIDATE -> COMPLETE |
+-------------------------------------------------------------+
```
## Key Design Principles (Codex Adaptation)
## Key Design Principles
1. **Unified State**: API and Skill share `.workflow/.loop/{loopId}.json` state file
2. **Control Signals**: Skill checks status field before each action (paused/stopped)
3. **File-Driven**: All progress documented in `.workflow/.loop/{loopId}.progress/`
4. **Resumable**: Continue any loop with `--loop-id`
5. **Dual Trigger**: Supports API trigger (`--loop-id`) and direct call (task description)
6. **Single Agent Deep Interaction**: Use send_input for multi-phase execution instead of multiple agents
1. **Single Agent Deep Interaction**: One agent handles entire loop lifecycle via `send_input` (no multi-agent overhead)
2. **Unified State**: API and Skill share `.workflow/.loop/{loopId}.json` state file
3. **Control Signals**: Skill checks `status` field before each action (paused/stopped → graceful exit)
4. **File-Driven Progress**: All progress documented in `.workflow/.loop/{loopId}.progress/`
5. **Resumable**: Continue any loop with `--loop-id`
6. **Dual Trigger**: Supports API trigger (`--loop-id`) and direct call (task description)
## Subagent 机制
## Arguments
### 核心 API
| API | 作用 |
|-----|------|
| `spawn_agent({ message })` | 创建 subagent返回 `agent_id` |
| `wait({ ids, timeout_ms })` | 等待结果(唯一取结果入口) |
| `send_input({ id, message })` | 继续交互/追问 |
| `close_agent({ id })` | 关闭回收(不可逆) |
### 可用模式
- **单 Agent 深度交互**: 一个 agent 多阶段,`send_input` 继续
- **多 Agent 并行**: 主协调器 + 多 worker`wait({ ids: [...] })` 批量等待
- **混合模式**: 按需组合
| 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 |
| --auto | No | Auto-cycle mode (develop → debug → validate → complete) |
## Execution Modes
### Mode 1: Interactive
User manually selects each action, suitable for complex tasks.
User manually selects each action via menu.
```
User -> Select action -> Execute -> View results -> Select next action
User -> MENU -> Select action -> Execute -> View results -> MENU -> ...
```
### Mode 2: Auto-Loop
Automatic execution in preset order, suitable for standard development flow.
Automatic execution using `selectNextAction` logic.
```
Develop -> Debug -> Validate -> (if issues) -> Develop -> ...
INIT -> DEVELOP -> ... -> VALIDATE -> (if fail) -> DEBUG -> VALIDATE -> COMPLETE
```
## Session Structure (Unified Location)
## Execution Flow
```
Input Parsing:
└─ Parse arguments (TASK | --loop-id + --auto)
└─ Convert to structured context (loopId, state, progressDir)
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
Phase 2: Orchestration Loop
└─ Ref: phases/02-orchestration-loop.md
├─ Spawn single executor agent
├─ Main while loop: wait → parse → dispatch → send_input
├─ Handle: COMPLETED / PAUSED / STOPPED / WAITING_INPUT / next action
├─ Update iteration count per cycle
└─ close_agent on 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) | Agent spawn, main loop, result parsing, send_input dispatch |
## Data Flow
```
User Input (TASK | --loop-id + --auto)
[Parse Arguments]
↓ loopId, state, progressDir
Phase 1: Session Initialization
↓ loopId, state (initialized/resumed), progressDir
Phase 2: Orchestration Loop
↓ spawn agent → [INIT] → wait → parse
┌─── Main Loop (while iteration < max) ──────────┐
│ wait() → parseActionResult(output) │
│ ├─ COMPLETED → close_agent, return │
│ ├─ PAUSED → close_agent, return │
│ ├─ STOPPED → close_agent, return │
│ ├─ WAITING_INPUT → collect input → send_input │
│ └─ next_action → send_input(continue) │
│ Update iteration in state file │
└──────────────────────────────────────────────────┘
close_agent → return finalState
```
## Session Structure
```
.workflow/.loop/
+-- {loopId}.json # Master state file (API + Skill shared)
+-- {loopId}.tasks.jsonl # Task list (API managed)
+-- {loopId}.progress/ # Skill progress files
+-- develop.md # Development progress timeline
+-- debug.md # Understanding evolution document
+-- validate.md # Validation report
+-- changes.log # Code changes log (NDJSON)
+-- debug.log # Debug log (NDJSON)
├── {loopId}.json # Master state file (API + Skill shared)
├── {loopId}.tasks.jsonl # Task list (API managed)
└── {loopId}.progress/ # Skill progress files
├── develop.md # Development progress timeline
├── debug.md # Understanding evolution document
├── validate.md # Validation report
├── changes.log # Code changes log (NDJSON)
├── debug.log # Debug log (NDJSON)
├── hypotheses.json # Debug hypotheses tracking
├── test-results.json # Test execution results
├── coverage.json # Coverage data
└── summary.md # Completion summary
```
## Implementation (Codex Subagent Pattern)
## State Management
### Session Setup
Master state file: `.workflow/.loop/{loopId}.json`
```javascript
// Helper: Get UTC+8 (China Standard Time) ISO string
const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()
```json
{
"loop_id": "loop-v2-20260122T100000-abc123",
"title": "Task title",
"description": "Full task description",
"max_iterations": 10,
"status": "created | running | paused | completed | failed | user_exit",
"current_iteration": 0,
"created_at": "ISO8601",
"updated_at": "ISO8601",
"completed_at": "ISO8601 (optional)",
"failure_reason": "string (optional)",
// loopId source:
// 1. API trigger: from --loop-id parameter
// 2. Direct call: generate new loop-v2-{timestamp}-{random}
"skill_state": {
"current_action": "init | develop | debug | validate | complete | null",
"last_action": "string | null",
"completed_actions": [],
"mode": "interactive | auto",
const loopId = args['--loop-id'] || (() => {
const timestamp = getUtc8ISOString().replace(/[-:]/g, '').split('.')[0]
const random = Math.random().toString(36).substring(2, 10)
return `loop-v2-${timestamp}-${random}`
})()
"develop": {
"total": 0, "completed": 0, "current_task": null,
"tasks": [{ "id": "task-001", "description": "...", "tool": "gemini", "mode": "write", "status": "pending", "files_changed": [], "created_at": "ISO8601", "completed_at": null }],
"last_progress_at": null
},
const loopFile = `.workflow/.loop/${loopId}.json`
const progressDir = `.workflow/.loop/${loopId}.progress`
"debug": {
"active_bug": null, "hypotheses_count": 0,
"hypotheses": [{ "id": "H1", "description": "...", "testable_condition": "...", "logging_point": "file:func:line", "evidence_criteria": { "confirm": "...", "reject": "..." }, "likelihood": 1, "status": "pending", "evidence": null, "verdict_reason": null }],
"confirmed_hypothesis": null, "iteration": 0, "last_analysis_at": null
},
// Create progress directory
mkdir -p "${progressDir}"
```
"validate": {
"pass_rate": 0, "coverage": 0,
"test_results": [{ "test_name": "...", "suite": "...", "status": "passed | failed | skipped", "duration_ms": 0, "error_message": null, "stack_trace": null }],
"passed": false, "failed_tests": [], "last_run_at": null
},
### Main Execution Flow (Single Agent Deep Interaction)
"errors": [{ "action": "...", "message": "...", "timestamp": "ISO8601" }],
```javascript
// ==================== CODEX CCW-LOOP: SINGLE AGENT ORCHESTRATOR ====================
// Step 1: Read or create initial state
let state = null
if (existingLoopId) {
state = JSON.parse(Read(`.workflow/.loop/${loopId}.json`))
if (!state) {
console.error(`Loop not found: ${loopId}`)
return
"summary": { "duration": 0, "iterations": 0, "develop": {}, "debug": {}, "validate": {} }
}
} else {
state = createInitialState(loopId, taskDescription)
Write(`.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2))
}
// Step 2: Create orchestrator agent (single agent handles all phases)
const agent = spawn_agent({
message: `
## TASK ASSIGNMENT
### MANDATORY FIRST STEPS (Agent Execute)
1. **Read role definition**: ~/.codex/agents/ccw-loop-executor.md (MUST read first)
2. Read: .workflow/project-tech.json
3. Read: .workflow/project-guidelines.json
---
## LOOP CONTEXT
- **Loop ID**: ${loopId}
- **State File**: .workflow/.loop/${loopId}.json
- **Progress Dir**: ${progressDir}
- **Mode**: ${mode} // 'interactive' or 'auto'
## CURRENT STATE
${JSON.stringify(state, null, 2)}
## TASK DESCRIPTION
${taskDescription}
## EXECUTION INSTRUCTIONS
You are executing CCW Loop orchestrator. Your job:
1. **Check Control Signals**
- Read .workflow/.loop/${loopId}.json
- If status === 'paused' -> Output "PAUSED" and stop
- If status === 'failed' -> Output "STOPPED" and stop
- If status === 'running' -> Continue
2. **Select Next Action**
Based on skill_state:
- If not initialized -> Execute INIT
- If mode === 'interactive' -> Output MENU and wait for input
- If mode === 'auto' -> Auto-select based on state
3. **Execute Action**
- Follow action instructions from ~/.codex/skills/ccw-loop/phases/actions/
- Update progress files in ${progressDir}/
- Update state in .workflow/.loop/${loopId}.json
4. **Output Format**
\`\`\`
ACTION_RESULT:
- action: {action_name}
- status: success | failed | needs_input
- message: {user message}
- state_updates: {JSON of skill_state updates}
NEXT_ACTION_NEEDED: {action_name} | WAITING_INPUT | COMPLETED | PAUSED
\`\`\`
## FIRST ACTION
${!state.skill_state ? 'Execute: INIT' : mode === 'auto' ? 'Auto-select next action' : 'Show MENU'}
`
})
// Step 3: Main orchestration loop
let iteration = 0
const maxIterations = state.max_iterations || 10
while (iteration < maxIterations) {
iteration++
// Wait for agent output
const result = wait({ ids: [agent], timeout_ms: 600000 })
const output = result.status[agent].completed
// Parse action result
const actionResult = parseActionResult(output)
// Handle different outcomes
switch (actionResult.next_action) {
case 'COMPLETED':
case 'PAUSED':
case 'STOPPED':
close_agent({ id: agent })
return actionResult
case 'WAITING_INPUT':
// Interactive mode: display menu, get user choice
const userChoice = await displayMenuAndGetChoice(actionResult)
// Send user choice back to agent
send_input({
id: agent,
message: `
## USER INPUT RECEIVED
Action selected: ${userChoice.action}
${userChoice.data ? `Additional data: ${JSON.stringify(userChoice.data)}` : ''}
## EXECUTE SELECTED ACTION
Follow instructions for: ${userChoice.action}
Update state and progress files accordingly.
`
})
break
default:
// Auto mode: agent continues to next action
// Check if we need to prompt for continuation
if (actionResult.next_action && actionResult.next_action !== 'NONE') {
send_input({
id: agent,
message: `
## CONTINUE EXECUTION
Previous action completed: ${actionResult.action}
Result: ${actionResult.status}
## EXECUTE NEXT ACTION
Continue with: ${actionResult.next_action}
`
})
}
}
// Update iteration count in state
const currentState = JSON.parse(Read(`.workflow/.loop/${loopId}.json`))
currentState.current_iteration = iteration
currentState.updated_at = getUtc8ISOString()
Write(`.workflow/.loop/${loopId}.json`, JSON.stringify(currentState, null, 2))
}
// Step 4: Cleanup
close_agent({ id: agent })
```
**Control Signal Checking**: Agent checks `state.status` before every action:
- `running` → continue
- `paused` → exit gracefully, wait for resume
- `failed` → terminate
- Other → stop
**Recovery**: If state corrupted, rebuild `skill_state` from `.progress/` markdown files and logs.
## Action Catalog
| Action | Purpose | Output Files | Trigger |
|--------|---------|--------------|---------|
| [action-init](phases/actions/action-init.md) | Initialize loop session | meta.json, state.json | First run |
| [action-develop](phases/actions/action-develop.md) | Execute development task | progress.md, tasks.json | Has pending tasks |
| [action-debug](phases/actions/action-debug.md) | Hypothesis-driven debug | understanding.md, hypotheses.json | Needs debugging |
| [action-validate](phases/actions/action-validate.md) | Test and validate | validation.md, test-results.json | Needs validation |
| [action-complete](phases/actions/action-complete.md) | Complete loop | summary.md | All done |
| [action-menu](phases/actions/action-menu.md) | Display action menu | - | Interactive mode |
| Action | Purpose | Preconditions | Output Files | Trigger |
|--------|---------|---------------|--------------|---------|
| [INIT](actions/action-init.md) | Initialize session | status=running, skill_state=null | develop.md, state.json | First run |
| [DEVELOP](actions/action-develop.md) | Execute dev task | pending tasks > 0 | develop.md, changes.log | Has pending tasks |
| [DEBUG](actions/action-debug.md) | Hypothesis debug | needs debugging | debug.md, debug.log | Test failures |
| [VALIDATE](actions/action-validate.md) | Run tests | needs validation | validate.md, test-results.json | After develop/debug |
| [COMPLETE](actions/action-complete.md) | Finish loop | all done | summary.md | All tasks done |
| [MENU](actions/action-menu.md) | Display menu | interactive mode | - | Interactive mode |
### Action Flow
```
spawn_agent (ccw-loop-executor)
|
v
+-------+
| INIT | (if skill_state is null)
+-------+
|
v
+-------+ send_input
| MENU | <------------- (user selection in interactive mode)
+-------+
|
+---+---+---+---+
| | | | |
v v v v v
DEV DBG VAL CMP EXIT
|
v
wait() -> get result
|
v
[Loop continues via send_input]
|
v
close_agent()
```
### Action Dependencies
| Action | Depends On | Leads To |
|--------|------------|----------|
| INIT | - | MENU or DEVELOP |
| MENU | INIT | User selection |
| DEVELOP | INIT | DEVELOP, DEBUG, VALIDATE |
| DEBUG | INIT | DEVELOP, VALIDATE |
| VALIDATE | DEVELOP or DEBUG | COMPLETE, DEBUG, DEVELOP |
| COMPLETE | - | Terminal |
### Action Sequences
```
Happy Path (Auto): INIT → DEVELOP → DEVELOP → VALIDATE (pass) → COMPLETE
Debug Iteration: INIT → DEVELOP → VALIDATE (fail) → DEBUG → VALIDATE (pass) → COMPLETE
Interactive Path: INIT → MENU → DEVELOP → MENU → VALIDATE → MENU → COMPLETE
```
## Auto Mode Selection Logic
```javascript
function selectNextAction(state) {
const skillState = state.skill_state
// 1. Terminal conditions
if (state.status === 'completed') return null
if (state.status === 'failed') return null
if (state.current_iteration >= state.max_iterations) return 'COMPLETE'
// 2. Initialization check
if (!skillState) return 'INIT'
// 3. Auto selection based on state
const hasPendingDevelop = skillState.develop.tasks.some(t => t.status === 'pending')
if (hasPendingDevelop) return 'DEVELOP'
if (skillState.last_action === 'DEVELOP') {
if (skillState.develop.completed < skillState.develop.total) return 'DEBUG'
}
if (skillState.last_action === 'DEBUG' || skillState.debug.confirmed_hypothesis) {
return 'VALIDATE'
}
if (skillState.last_action === 'VALIDATE') {
if (!skillState.validate.passed) return 'DEVELOP'
}
if (skillState.validate.passed && !hasPendingDevelop) return 'COMPLETE'
return 'DEVELOP'
}
```
## Coordination Protocol
### Agent → Orchestrator (ACTION_RESULT)
Every action MUST output:
```
ACTION_RESULT:
- action: {ACTION_NAME}
- status: success | failed | needs_input
- message: {user-facing message}
- state_updates: { ... }
FILES_UPDATED:
- {file_path}: {description}
NEXT_ACTION_NEEDED: {ACTION_NAME} | WAITING_INPUT | COMPLETED | PAUSED
```
### Orchestrator → Agent (send_input)
Auto mode continuation:
```
## CONTINUE EXECUTION
Previous action completed: {action}
Result: {status}
## EXECUTE NEXT ACTION
Continue with: {next_action}
Read action instructions and execute.
Output ACTION_RESULT when complete.
```
Interactive mode user input:
```
## USER INPUT RECEIVED
Action selected: {action}
## EXECUTE SELECTED ACTION
Read action instructions and execute: {action}
Update state and progress files accordingly.
Output ACTION_RESULT when complete.
```
### Codex Subagent API
| API | Purpose |
|-----|---------|
| `spawn_agent({ message })` | Create subagent, returns `agent_id` |
| `wait({ ids, timeout_ms })` | Wait for results (only way to get output) |
| `send_input({ id, message })` | Continue interaction / follow-up |
| `close_agent({ id })` | Close and reclaim (irreversible) |
**Rules**: Single agent for entire loop. `send_input` for multi-phase. `close_agent` only after confirming no more interaction needed.
## TodoWrite Pattern
### Phase-Level Tracking (Attached)
```json
[
{"content": "Phase 1: Session Initialization", "status": "completed"},
{"content": "Phase 2: Orchestration Loop", "status": "in_progress"},
{"content": " → Action: INIT", "status": "completed"},
{"content": " → Action: DEVELOP (task 1/3)", "status": "in_progress"},
{"content": " → Action: VALIDATE", "status": "pending"},
{"content": " → Action: COMPLETE", "status": "pending"}
]
```
### Iteration Tracking (Collapsed)
```json
[
{"content": "Phase 1: Session Initialization", "status": "completed"},
{"content": "Iteration 1: DEVELOP x3 + VALIDATE (pass)", "status": "completed"},
{"content": "Phase 2: COMPLETE", "status": "in_progress"}
]
```
## 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 ACTION_RESULT from agent output for next decision
4. **Auto-Continue**: After each action, execute next pending action automatically (auto mode)
5. **Track Progress**: Update TodoWrite dynamically with attachment/collapse pattern
6. **Single Writer**: Orchestrator updates master state file; agent writes to progress files
7. **File References**: Pass file paths between orchestrator and agent, not content
8. **DO NOT STOP**: Continuous execution until COMPLETED, PAUSED, STOPPED, or max iterations
## Error Handling
| Error Type | Recovery |
|------------|----------|
| Agent timeout | send_input requesting convergence, then retry |
| State corrupted | Rebuild from progress markdown files and logs |
| Agent closed unexpectedly | Re-spawn with previous output in message |
| Action failed | Log error, continue or prompt user |
| Tests fail | Loop back to DEVELOP or DEBUG |
| Max iterations reached | Generate summary with remaining issues documented |
| Session not found | Create new session |
## Coordinator Checklist
### Before Each Phase
- [ ] Read phase reference document
- [ ] Check current state for dependencies
- [ ] Update TodoWrite with phase tasks
### After Each Phase
- [ ] Parse agent outputs (ACTION_RESULT)
- [ ] Update master state file (iteration count, updated_at)
- [ ] Collapse TodoWrite sub-tasks
- [ ] Determine next action (continue / iterate / complete)
## Reference Documents
| Document | Purpose |
|----------|---------|
| [actions/](actions/) | Action definitions (INIT, DEVELOP, DEBUG, VALIDATE, COMPLETE, MENU) |
## Usage
@@ -311,40 +438,12 @@ close_agent({ id: agent })
# Start new loop (direct call)
/ccw-loop TASK="Implement user authentication"
# Continue existing loop (API trigger or manual resume)
/ccw-loop --loop-id=loop-v2-20260122-abc123
# Auto-cycle mode
/ccw-loop --auto TASK="Fix login bug and add tests"
# Continue existing loop
/ccw-loop --loop-id=loop-v2-20260122-abc123
# API triggered auto-cycle
/ccw-loop --loop-id=loop-v2-20260122-abc123 --auto
```
## Reference Documents
| Document | Purpose |
|----------|---------|
| [phases/orchestrator.md](phases/orchestrator.md) | Orchestrator: state reading + action selection |
| [phases/state-schema.md](phases/state-schema.md) | State structure definition |
| [specs/loop-requirements.md](specs/loop-requirements.md) | Loop requirements specification |
| [specs/action-catalog.md](specs/action-catalog.md) | Action catalog |
## Error Handling
| Situation | Action |
|-----------|--------|
| Session not found | Create new session |
| State file corrupted | Rebuild from file contents |
| Agent timeout | send_input to request convergence |
| Agent unexpectedly closed | Re-spawn, paste previous output |
| Tests fail | Loop back to develop/debug |
| >10 iterations | Warn user, suggest break |
## Codex Best Practices Applied
1. **Role Path Passing**: Agent reads role file itself (no content embedding)
2. **Single Agent Deep Interaction**: Use send_input for multi-phase instead of multiple agents
3. **Delayed close_agent**: Only close after confirming no more interaction needed
4. **Context Reuse**: Same agent maintains all exploration context automatically
5. **Explicit wait()**: Always use wait({ ids }) to get results, not close_agent

View File

@@ -0,0 +1,138 @@
# Phase 1: Session Initialization
Create or resume a development loop, initialize state file and directory structure.
## Objective
- Parse user arguments (TASK, --loop-id, --auto)
- Create new loop with unique ID OR resume existing loop
- Initialize directory structure for progress files
- Create master state file
- Output: loopId, state, progressDir
## 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' }
}
// Determine mode
const executionMode = options['--auto'] ? 'auto' : 'interactive'
```
### Step 1.2: Utility Functions
```javascript
const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()
function readLoopState(loopId) {
const stateFile = `.workflow/.loop/${loopId}.json`
if (!fs.existsSync(stateFile)) {
return null
}
return JSON.parse(Read(stateFile))
}
```
### Step 1.3: New Loop Creation
When `TASK` is provided (no `--loop-id`):
```javascript
// Generate unique loop ID
const timestamp = getUtc8ISOString().replace(/[-:]/g, '').split('.')[0]
const random = Math.random().toString(36).substring(2, 10)
const loopId = `loop-v2-${timestamp}-${random}`
console.log(`Creating new loop: ${loopId}`)
```
#### Create Directory Structure
```bash
mkdir -p .workflow/.loop/${loopId}.progress
```
#### Initialize State File
```javascript
function createLoopState(loopId, taskDescription) {
const stateFile = `.workflow/.loop/${loopId}.json`
const now = getUtc8ISOString()
const state = {
// API compatible fields
loop_id: loopId,
title: taskDescription.substring(0, 100),
description: taskDescription,
max_iterations: 10,
status: 'running',
current_iteration: 0,
created_at: now,
updated_at: now,
// Skill extension fields (initialized by INIT action)
skill_state: null
}
Write(stateFile, JSON.stringify(state, null, 2))
return state
}
```
### Step 1.4: Resume Existing Loop
When `--loop-id` is provided:
```javascript
const loopId = existingLoopId
const state = readLoopState(loopId)
if (!state) {
console.error(`Loop not found: ${loopId}`)
return { status: 'error', message: 'Loop not found' }
}
console.log(`Resuming loop: ${loopId}`)
console.log(`Status: ${state.status}`)
```
### Step 1.5: Control Signal Check
Before proceeding, verify loop status allows continuation:
```javascript
function checkControlSignals(loopId) {
const state = readLoopState(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**: `mode` - `'interactive'` or `'auto'`
- **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).

View File

@@ -0,0 +1,312 @@
# Phase 2: Orchestration Loop
Spawn single executor agent and run main orchestration loop until completion, pause, or max iterations.
## Objective
- Spawn single executor agent with loop context
- Run main while loop: wait → parse → dispatch → send_input
- Handle terminal conditions (COMPLETED, PAUSED, STOPPED)
- Handle interactive mode (WAITING_INPUT → user choice → send_input)
- Handle auto mode (next action → send_input)
- Update iteration count per cycle
- Close agent on exit
## Execution
### Step 2.1: Spawn Executor Agent
```javascript
const agent = spawn_agent({
message: `
## TASK ASSIGNMENT
### MANDATORY FIRST STEPS (Agent Execute)
1. **Read role definition**: ~/.codex/agents/ccw-loop-executor.md (MUST read first)
2. Read: .workflow/project-tech.json (if exists)
3. Read: .workflow/project-guidelines.json (if exists)
---
## LOOP CONTEXT
- **Loop ID**: ${loopId}
- **State File**: .workflow/.loop/${loopId}.json
- **Progress Dir**: ${progressDir}
- **Mode**: ${mode}
## CURRENT STATE
${JSON.stringify(state, null, 2)}
## TASK DESCRIPTION
${state.description || task}
## EXECUTION INSTRUCTIONS
You are executing CCW Loop orchestrator. Your job:
1. **Check Control Signals**
- Read .workflow/.loop/${loopId}.json
- If status === 'paused' -> Output "PAUSED" and stop
- If status === 'failed' -> Output "STOPPED" and stop
- If status === 'running' -> Continue
2. **Select Next Action**
Based on skill_state:
- If not initialized -> Execute INIT
- If mode === 'interactive' -> Output MENU and wait for input
- If mode === 'auto' -> Auto-select based on state
3. **Execute Action**
- Follow action instructions from ~/.codex/skills/ccw-loop/actions/
- Update progress files in ${progressDir}/
- Update state in .workflow/.loop/${loopId}.json
4. **Output Format**
\`\`\`
ACTION_RESULT:
- action: {action_name}
- status: success | failed | needs_input
- message: {user message}
- state_updates: {JSON of skill_state updates}
NEXT_ACTION_NEEDED: {action_name} | WAITING_INPUT | COMPLETED | PAUSED
\`\`\`
## FIRST ACTION
${!state.skill_state ? 'Execute: INIT' : mode === 'auto' ? 'Auto-select next action' : 'Show MENU'}
`
})
```
### Step 2.2: Main Orchestration Loop
```javascript
let iteration = state.current_iteration || 0
const maxIterations = state.max_iterations || 10
let continueLoop = true
while (continueLoop && iteration < maxIterations) {
iteration++
// Wait for agent output
const result = wait({ ids: [agent], timeout_ms: 600000 })
// Handle timeout
if (result.timed_out) {
console.log('Agent timeout, requesting convergence...')
send_input({
id: agent,
message: `
## TIMEOUT NOTIFICATION
Execution timeout reached. Please:
1. Output current progress
2. Save any pending state updates
3. Return ACTION_RESULT with current status
`
})
continue
}
const output = result.status[agent].completed
// Parse action result
const actionResult = parseActionResult(output)
console.log(`\n[Iteration ${iteration}] Action: ${actionResult.action}, Status: ${actionResult.status}`)
// Update iteration in state
state = readLoopState(loopId)
state.current_iteration = iteration
state.updated_at = getUtc8ISOString()
Write(`.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2))
// Handle different outcomes
switch (actionResult.next_action) {
case 'COMPLETED':
console.log('Loop completed successfully')
continueLoop = false
break
case 'PAUSED':
console.log('Loop paused by API, exiting gracefully')
continueLoop = false
break
case 'STOPPED':
console.log('Loop stopped by API')
continueLoop = false
break
case 'WAITING_INPUT':
// Interactive mode: display menu, get user choice
if (mode === 'interactive') {
const userChoice = await displayMenuAndGetChoice(actionResult)
send_input({
id: agent,
message: `
## USER INPUT RECEIVED
Action selected: ${userChoice.action}
${userChoice.data ? `Additional data: ${JSON.stringify(userChoice.data)}` : ''}
## EXECUTE SELECTED ACTION
Read action instructions and execute: ${userChoice.action}
Update state and progress files accordingly.
Output ACTION_RESULT when complete.
`
})
}
break
default:
// Continue with next action
if (actionResult.next_action && actionResult.next_action !== 'NONE') {
send_input({
id: agent,
message: `
## CONTINUE EXECUTION
Previous action completed: ${actionResult.action}
Result: ${actionResult.status}
${actionResult.message ? `Message: ${actionResult.message}` : ''}
## EXECUTE NEXT ACTION
Continue with: ${actionResult.next_action}
Read action instructions and execute.
Output ACTION_RESULT when complete.
`
})
} else {
if (actionResult.status === 'failed') {
console.log(`Action failed: ${actionResult.message}`)
}
continueLoop = false
}
}
}
```
### Step 2.3: Iteration Limit Check
```javascript
if (iteration >= maxIterations) {
console.log(`\nReached maximum iterations (${maxIterations})`)
console.log('Consider breaking down the task or taking a break.')
}
```
### Step 2.4: Cleanup
```javascript
close_agent({ id: agent })
console.log('\n=== CCW Loop Orchestrator Finished ===')
const finalState = readLoopState(loopId)
return {
status: finalState.status,
loop_id: loopId,
iterations: iteration,
final_state: finalState
}
```
## Helper Functions
### parseActionResult
```javascript
function parseActionResult(output) {
const result = {
action: 'unknown',
status: 'unknown',
message: '',
state_updates: {},
next_action: 'NONE'
}
// Parse ACTION_RESULT block
const actionMatch = output.match(/ACTION_RESULT:\s*([\s\S]*?)(?:FILES_UPDATED:|NEXT_ACTION_NEEDED:|$)/)
if (actionMatch) {
const lines = actionMatch[1].split('\n')
for (const line of lines) {
const match = line.match(/^-\s*(\w+):\s*(.+)$/)
if (match) {
const [, key, value] = match
if (key === 'state_updates') {
try {
result.state_updates = JSON.parse(value)
} catch (e) {
// Try parsing multi-line JSON
}
} else {
result[key] = value.trim()
}
}
}
}
// Parse NEXT_ACTION_NEEDED
const nextMatch = output.match(/NEXT_ACTION_NEEDED:\s*(\S+)/)
if (nextMatch) {
result.next_action = nextMatch[1]
}
return result
}
```
### displayMenuAndGetChoice
```javascript
async function displayMenuAndGetChoice(actionResult) {
const menuMatch = actionResult.message.match(/MENU_OPTIONS:\s*([\s\S]*?)(?:WAITING_INPUT:|$)/)
if (menuMatch) {
console.log('\n' + menuMatch[1])
}
const response = await AskUserQuestion({
questions: [{
question: "Select next action:",
header: "Action",
multiSelect: false,
options: [
{ label: "develop", description: "Continue development" },
{ label: "debug", description: "Start debugging" },
{ label: "validate", description: "Run validation" },
{ label: "complete", description: "Complete loop" },
{ label: "exit", description: "Exit and save" }
]
}]
})
return { action: response["Action"] }
}
```
## Termination Conditions
1. **API Paused**: `state.status === 'paused'` (Skill exits, wait for resume)
2. **API Stopped**: `state.status === 'failed'` (Skill terminates)
3. **Task Complete**: `NEXT_ACTION_NEEDED === 'COMPLETED'`
4. **Iteration Limit**: `current_iteration >= max_iterations`
5. **User Exit**: User selects 'exit' in interactive mode
## Output
- **Variable**: `finalState` - Final loop state after all iterations
- **Return**: `{ status, loop_id, iterations, final_state }`
- **TodoWrite**: Mark Phase 2 completed
## Next Phase
None. Phase 2 is the terminal phase of the orchestrator.

View File

@@ -1,416 +0,0 @@
# Orchestrator (Codex Pattern)
Orchestrate CCW Loop using Codex subagent pattern: `spawn_agent -> wait -> send_input -> close_agent`.
## Role
Check control signals -> Read file state -> Select action -> Execute via agent -> Update files -> Loop until complete or paused/stopped.
## Codex Pattern Overview
```
+-- spawn_agent (ccw-loop-executor role) --+
| |
| Phase 1: INIT or first action |
| | |
| v |
| wait() -> get result |
| | |
| v |
| [If needs input] Collect user input |
| | |
| v |
| send_input(user choice + next action) |
| | |
| v |
| wait() -> get result |
| | |
| v |
| [Loop until COMPLETED/PAUSED/STOPPED] |
| | |
+----------v-------------------------------+
|
close_agent()
```
## State Management (Unified Location)
### Read State
```javascript
const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()
/**
* Read loop state (unified location)
* @param loopId - Loop ID (e.g., "loop-v2-20260122-abc123")
*/
function readLoopState(loopId) {
const stateFile = `.workflow/.loop/${loopId}.json`
if (!fs.existsSync(stateFile)) {
return null
}
const state = JSON.parse(Read(stateFile))
return state
}
```
### Create New Loop State (Direct Call)
```javascript
/**
* Create new loop state (only for direct calls, API triggers have existing state)
*/
function createLoopState(loopId, taskDescription) {
const stateFile = `.workflow/.loop/${loopId}.json`
const now = getUtc8ISOString()
const state = {
// API compatible fields
loop_id: loopId,
title: taskDescription.substring(0, 100),
description: taskDescription,
max_iterations: 10,
status: 'running', // Direct call sets to running
current_iteration: 0,
created_at: now,
updated_at: now,
// Skill extension fields
skill_state: null // Initialized by INIT action
}
// Ensure directories exist
mkdir -p ".loop"
mkdir -p ".workflow/.loop/${loopId}.progress"
Write(stateFile, JSON.stringify(state, null, 2))
return state
}
```
## Main Execution Flow (Codex Subagent)
```javascript
/**
* Run CCW Loop orchestrator using Codex subagent pattern
* @param options.loopId - Existing Loop ID (API trigger)
* @param options.task - Task description (direct call)
* @param options.mode - 'interactive' | 'auto'
*/
async function runOrchestrator(options = {}) {
const { loopId: existingLoopId, task, mode = 'interactive' } = options
console.log('=== CCW Loop Orchestrator (Codex) Started ===')
// 1. Determine loopId and initial state
let loopId
let state
if (existingLoopId) {
// API trigger: use existing loopId
loopId = existingLoopId
state = readLoopState(loopId)
if (!state) {
console.error(`Loop not found: ${loopId}`)
return { status: 'error', message: 'Loop not found' }
}
console.log(`Resuming loop: ${loopId}`)
console.log(`Status: ${state.status}`)
} else if (task) {
// Direct call: create new loopId
const timestamp = getUtc8ISOString().replace(/[-:]/g, '').split('.')[0]
const random = Math.random().toString(36).substring(2, 10)
loopId = `loop-v2-${timestamp}-${random}`
console.log(`Creating new loop: ${loopId}`)
console.log(`Task: ${task}`)
state = createLoopState(loopId, task)
} else {
console.error('Either --loop-id or task description is required')
return { status: 'error', message: 'Missing loopId or task' }
}
const progressDir = `.workflow/.loop/${loopId}.progress`
// 2. Create executor agent (single agent for entire loop)
const agent = spawn_agent({
message: `
## TASK ASSIGNMENT
### MANDATORY FIRST STEPS (Agent Execute)
1. **Read role definition**: ~/.codex/agents/ccw-loop-executor.md (MUST read first)
2. Read: .workflow/project-tech.json (if exists)
3. Read: .workflow/project-guidelines.json (if exists)
---
## LOOP CONTEXT
- **Loop ID**: ${loopId}
- **State File**: .workflow/.loop/${loopId}.json
- **Progress Dir**: ${progressDir}
- **Mode**: ${mode}
## CURRENT STATE
${JSON.stringify(state, null, 2)}
## TASK DESCRIPTION
${state.description || task}
## FIRST ACTION
${!state.skill_state ? 'Execute: INIT' : mode === 'auto' ? 'Auto-select next action' : 'Show MENU'}
Read the role definition first, then execute the appropriate action.
`
})
// 3. Main orchestration loop
let iteration = state.current_iteration || 0
const maxIterations = state.max_iterations || 10
let continueLoop = true
while (continueLoop && iteration < maxIterations) {
iteration++
// Wait for agent output
const result = wait({ ids: [agent], timeout_ms: 600000 })
// Check for timeout
if (result.timed_out) {
console.log('Agent timeout, requesting convergence...')
send_input({
id: agent,
message: `
## TIMEOUT NOTIFICATION
Execution timeout reached. Please:
1. Output current progress
2. Save any pending state updates
3. Return ACTION_RESULT with current status
`
})
continue
}
const output = result.status[agent].completed
// Parse action result
const actionResult = parseActionResult(output)
console.log(`\n[Iteration ${iteration}] Action: ${actionResult.action}, Status: ${actionResult.status}`)
// Update iteration in state
state = readLoopState(loopId)
state.current_iteration = iteration
state.updated_at = getUtc8ISOString()
Write(`.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2))
// Handle different outcomes
switch (actionResult.next_action) {
case 'COMPLETED':
console.log('Loop completed successfully')
continueLoop = false
break
case 'PAUSED':
console.log('Loop paused by API, exiting gracefully')
continueLoop = false
break
case 'STOPPED':
console.log('Loop stopped by API')
continueLoop = false
break
case 'WAITING_INPUT':
// Interactive mode: display menu, get user choice
if (mode === 'interactive') {
const userChoice = await displayMenuAndGetChoice(actionResult)
// Send user choice back to agent
send_input({
id: agent,
message: `
## USER INPUT RECEIVED
Action selected: ${userChoice.action}
${userChoice.data ? `Additional data: ${JSON.stringify(userChoice.data)}` : ''}
## EXECUTE SELECTED ACTION
Read action instructions and execute: ${userChoice.action}
Update state and progress files accordingly.
Output ACTION_RESULT when complete.
`
})
}
break
default:
// Continue with next action
if (actionResult.next_action && actionResult.next_action !== 'NONE') {
send_input({
id: agent,
message: `
## CONTINUE EXECUTION
Previous action completed: ${actionResult.action}
Result: ${actionResult.status}
${actionResult.message ? `Message: ${actionResult.message}` : ''}
## EXECUTE NEXT ACTION
Continue with: ${actionResult.next_action}
Read action instructions and execute.
Output ACTION_RESULT when complete.
`
})
} else {
// No next action specified, check if should continue
if (actionResult.status === 'failed') {
console.log(`Action failed: ${actionResult.message}`)
}
continueLoop = false
}
}
}
// 4. Check iteration limit
if (iteration >= maxIterations) {
console.log(`\nReached maximum iterations (${maxIterations})`)
console.log('Consider breaking down the task or taking a break.')
}
// 5. Cleanup
close_agent({ id: agent })
console.log('\n=== CCW Loop Orchestrator (Codex) Finished ===')
// Return final state
const finalState = readLoopState(loopId)
return {
status: finalState.status,
loop_id: loopId,
iterations: iteration,
final_state: finalState
}
}
/**
* Parse action result from agent output
*/
function parseActionResult(output) {
const result = {
action: 'unknown',
status: 'unknown',
message: '',
state_updates: {},
next_action: 'NONE'
}
// Parse ACTION_RESULT block
const actionMatch = output.match(/ACTION_RESULT:\s*([\s\S]*?)(?:FILES_UPDATED:|NEXT_ACTION_NEEDED:|$)/)
if (actionMatch) {
const lines = actionMatch[1].split('\n')
for (const line of lines) {
const match = line.match(/^-\s*(\w+):\s*(.+)$/)
if (match) {
const [, key, value] = match
if (key === 'state_updates') {
try {
result.state_updates = JSON.parse(value)
} catch (e) {
// Try parsing multi-line JSON
}
} else {
result[key] = value.trim()
}
}
}
}
// Parse NEXT_ACTION_NEEDED
const nextMatch = output.match(/NEXT_ACTION_NEEDED:\s*(\S+)/)
if (nextMatch) {
result.next_action = nextMatch[1]
}
return result
}
/**
* Display menu and get user choice (interactive mode)
*/
async function displayMenuAndGetChoice(actionResult) {
// Parse MENU_OPTIONS from output
const menuMatch = actionResult.message.match(/MENU_OPTIONS:\s*([\s\S]*?)(?:WAITING_INPUT:|$)/)
if (menuMatch) {
console.log('\n' + menuMatch[1])
}
// Use AskUserQuestion to get choice
const response = await AskUserQuestion({
questions: [{
question: "Select next action:",
header: "Action",
multiSelect: false,
options: [
{ label: "develop", description: "Continue development" },
{ label: "debug", description: "Start debugging" },
{ label: "validate", description: "Run validation" },
{ label: "complete", description: "Complete loop" },
{ label: "exit", description: "Exit and save" }
]
}]
})
return { action: response["Action"] }
}
```
## Action Catalog
| Action | Purpose | Preconditions | Effects |
|--------|---------|---------------|---------|
| INIT | Initialize session | status=running, skill_state=null | skill_state initialized |
| MENU | Display menu | skill_state != null, mode=interactive | Wait for user input |
| DEVELOP | Execute dev task | pending tasks > 0 | Update progress.md |
| DEBUG | Hypothesis debug | needs debugging | Update understanding.md |
| VALIDATE | Run tests | needs validation | Update validation.md |
| COMPLETE | Finish loop | all done | status=completed |
## Termination Conditions
1. **API Paused**: `state.status === 'paused'` (Skill exits, wait for resume)
2. **API Stopped**: `state.status === 'failed'` (Skill terminates)
3. **Task Complete**: `NEXT_ACTION_NEEDED === 'COMPLETED'`
4. **Iteration Limit**: `current_iteration >= max_iterations`
5. **User Exit**: User selects 'exit' in interactive mode
## Error Recovery
| Error Type | Recovery Strategy |
|------------|-------------------|
| Agent timeout | send_input requesting convergence |
| Action failed | Log error, continue or prompt user |
| State corrupted | Rebuild from progress files |
| Agent closed unexpectedly | Re-spawn with previous output in message |
## Codex Best Practices Applied
1. **Single Agent Pattern**: One agent handles entire loop lifecycle
2. **Deep Interaction via send_input**: Multi-phase without context loss
3. **Delayed close_agent**: Only after confirming no more interaction
4. **Explicit wait()**: Always get results before proceeding
5. **Role Path Passing**: Agent reads role file, no content embedding

View File

@@ -1,388 +0,0 @@
# State Schema (Codex Version)
CCW Loop state structure definition for Codex subagent pattern.
## State File
**Location**: `.workflow/.loop/{loopId}.json` (unified location, API + Skill shared)
## Structure Definition
### Unified Loop State Interface
```typescript
/**
* Unified Loop State - API and Skill shared state structure
* API (loop-v2-routes.ts) owns state control
* Skill (ccw-loop) reads and updates this state via subagent
*/
interface LoopState {
// =====================================================
// API FIELDS (from loop-v2-routes.ts)
// These fields are managed by API, Skill read-only
// =====================================================
loop_id: string // Loop ID, e.g., "loop-v2-20260122-abc123"
title: string // Loop title
description: string // Loop description
max_iterations: number // Maximum iteration count
status: 'created' | 'running' | 'paused' | 'completed' | 'failed' | 'user_exit'
current_iteration: number // Current iteration count
created_at: string // Creation time (ISO8601)
updated_at: string // Last update time (ISO8601)
completed_at?: string // Completion time (ISO8601)
failure_reason?: string // Failure reason
// =====================================================
// SKILL EXTENSION FIELDS
// These fields are managed by Skill executor agent
// =====================================================
skill_state?: {
// Current execution action
current_action: 'init' | 'develop' | 'debug' | 'validate' | 'complete' | null
last_action: string | null
completed_actions: string[]
mode: 'interactive' | 'auto'
// === Development Phase ===
develop: {
total: number
completed: number
current_task?: string
tasks: DevelopTask[]
last_progress_at: string | null
}
// === Debug Phase ===
debug: {
active_bug?: string
hypotheses_count: number
hypotheses: Hypothesis[]
confirmed_hypothesis: string | null
iteration: number
last_analysis_at: string | null
}
// === Validation Phase ===
validate: {
pass_rate: number // Test pass rate (0-100)
coverage: number // Coverage (0-100)
test_results: TestResult[]
passed: boolean
failed_tests: string[]
last_run_at: string | null
}
// === Error Tracking ===
errors: Array<{
action: string
message: string
timestamp: string
}>
// === Summary (after completion) ===
summary?: {
duration: number
iterations: number
develop: object
debug: object
validate: object
}
}
}
interface DevelopTask {
id: string
description: string
tool: 'gemini' | 'qwen' | 'codex' | 'bash'
mode: 'analysis' | 'write'
status: 'pending' | 'in_progress' | 'completed' | 'failed'
files_changed: string[]
created_at: string
completed_at: string | null
}
interface Hypothesis {
id: string // H1, H2, ...
description: string
testable_condition: string
logging_point: string
evidence_criteria: {
confirm: string
reject: string
}
likelihood: number // 1 = most likely
status: 'pending' | 'confirmed' | 'rejected' | 'inconclusive'
evidence: Record<string, any> | null
verdict_reason: string | null
}
interface TestResult {
test_name: string
suite: string
status: 'passed' | 'failed' | 'skipped'
duration_ms: number
error_message: string | null
stack_trace: string | null
}
```
## Initial State
### Created by API (Dashboard Trigger)
```json
{
"loop_id": "loop-v2-20260122-abc123",
"title": "Implement user authentication",
"description": "Add login/logout functionality",
"max_iterations": 10,
"status": "created",
"current_iteration": 0,
"created_at": "2026-01-22T10:00:00+08:00",
"updated_at": "2026-01-22T10:00:00+08:00"
}
```
### After Skill Initialization (INIT action)
```json
{
"loop_id": "loop-v2-20260122-abc123",
"title": "Implement user authentication",
"description": "Add login/logout functionality",
"max_iterations": 10,
"status": "running",
"current_iteration": 0,
"created_at": "2026-01-22T10:00:00+08:00",
"updated_at": "2026-01-22T10:00:05+08:00",
"skill_state": {
"current_action": "init",
"last_action": null,
"completed_actions": [],
"mode": "auto",
"develop": {
"total": 3,
"completed": 0,
"current_task": null,
"tasks": [
{ "id": "task-001", "description": "Create auth component", "status": "pending" }
],
"last_progress_at": null
},
"debug": {
"active_bug": null,
"hypotheses_count": 0,
"hypotheses": [],
"confirmed_hypothesis": null,
"iteration": 0,
"last_analysis_at": null
},
"validate": {
"pass_rate": 0,
"coverage": 0,
"test_results": [],
"passed": false,
"failed_tests": [],
"last_run_at": null
},
"errors": []
}
}
```
## Control Signal Checking (Codex Pattern)
Agent checks control signals at start of every action:
```javascript
/**
* Check API control signals
* MUST be called at start of every action
* @returns { continue: boolean, action: 'pause_exit' | 'stop_exit' | 'continue' }
*/
function checkControlSignals(loopId) {
const state = JSON.parse(Read(`.workflow/.loop/${loopId}.json`))
switch (state.status) {
case 'paused':
// API paused the loop, Skill should exit and wait for resume
return { continue: false, action: 'pause_exit' }
case 'failed':
// API stopped the loop (user manual stop)
return { continue: false, action: 'stop_exit' }
case 'running':
// Normal continue
return { continue: true, action: 'continue' }
default:
// Abnormal status
return { continue: false, action: 'stop_exit' }
}
}
```
## State Transitions
### 1. Initialization (INIT action)
```javascript
{
status: 'created' -> 'running', // Or keep 'running' if API already set
updated_at: timestamp,
skill_state: {
current_action: 'init',
mode: 'auto',
develop: {
tasks: [...parsed_tasks],
total: N,
completed: 0
}
}
}
```
### 2. Development (DEVELOP action)
```javascript
{
updated_at: timestamp,
current_iteration: state.current_iteration + 1,
skill_state: {
current_action: 'develop',
last_action: 'DEVELOP',
completed_actions: [..., 'DEVELOP'],
develop: {
current_task: 'task-xxx',
completed: N+1,
last_progress_at: timestamp
}
}
}
```
### 3. Debugging (DEBUG action)
```javascript
{
updated_at: timestamp,
current_iteration: state.current_iteration + 1,
skill_state: {
current_action: 'debug',
last_action: 'DEBUG',
debug: {
active_bug: '...',
hypotheses_count: N,
hypotheses: [...new_hypotheses],
iteration: N+1,
last_analysis_at: timestamp
}
}
}
```
### 4. Validation (VALIDATE action)
```javascript
{
updated_at: timestamp,
current_iteration: state.current_iteration + 1,
skill_state: {
current_action: 'validate',
last_action: 'VALIDATE',
validate: {
test_results: [...results],
pass_rate: 95.5,
coverage: 85.0,
passed: true | false,
failed_tests: ['test1', 'test2'],
last_run_at: timestamp
}
}
}
```
### 5. Completion (COMPLETE action)
```javascript
{
status: 'running' -> 'completed',
completed_at: timestamp,
updated_at: timestamp,
skill_state: {
current_action: 'complete',
last_action: 'COMPLETE',
summary: { ... }
}
}
```
## File Sync
### Unified Location
State-to-file mapping:
| State Field | Sync File | Sync Timing |
|-------------|-----------|-------------|
| Entire LoopState | `.workflow/.loop/{loopId}.json` | Every state change (master) |
| `skill_state.develop` | `.workflow/.loop/{loopId}.progress/develop.md` | After each dev operation |
| `skill_state.debug` | `.workflow/.loop/{loopId}.progress/debug.md` | After each debug operation |
| `skill_state.validate` | `.workflow/.loop/{loopId}.progress/validate.md` | After each validation |
| Code changes log | `.workflow/.loop/{loopId}.progress/changes.log` | Each file modification (NDJSON) |
| Debug log | `.workflow/.loop/{loopId}.progress/debug.log` | Each debug log (NDJSON) |
### File Structure
```
.workflow/.loop/
+-- loop-v2-20260122-abc123.json # Master state file (API + Skill)
+-- loop-v2-20260122-abc123.tasks.jsonl # Task list (API managed)
+-- loop-v2-20260122-abc123.progress/ # Skill progress files
+-- develop.md # Development progress
+-- debug.md # Debug understanding
+-- validate.md # Validation report
+-- changes.log # Code changes (NDJSON)
+-- debug.log # Debug log (NDJSON)
+-- summary.md # Completion summary
```
## State Recovery
If master state file corrupted, rebuild skill_state from progress files:
```javascript
function rebuildSkillStateFromProgress(loopId) {
const progressDir = `.workflow/.loop/${loopId}.progress`
// Parse progress files to rebuild state
const skill_state = {
develop: parseProgressFile(`${progressDir}/develop.md`),
debug: parseProgressFile(`${progressDir}/debug.md`),
validate: parseProgressFile(`${progressDir}/validate.md`)
}
return skill_state
}
```
## Codex Pattern Notes
1. **Agent reads state**: Agent reads `.workflow/.loop/{loopId}.json` at action start
2. **Agent writes state**: Agent updates state after action completion
3. **Orchestrator tracks iterations**: Main loop tracks `current_iteration`
4. **Single agent context**: All state updates in same agent conversation via send_input
5. **No context serialization loss**: State transitions happen in-memory within agent

View File

@@ -1,182 +0,0 @@
# Action Catalog (Codex Version)
CCW Loop available actions and their specifications.
## Available Actions
| Action | Purpose | Preconditions | Effects | Output |
|--------|---------|---------------|---------|--------|
| INIT | Initialize session | status=running, skill_state=null | skill_state initialized | progress/*.md created |
| MENU | Display action menu | skill_state!=null, mode=interactive | Wait for user input | WAITING_INPUT |
| DEVELOP | Execute dev task | pending tasks > 0 | Update progress.md | develop.md updated |
| DEBUG | Hypothesis debug | needs debugging | Update understanding.md | debug.md updated |
| VALIDATE | Run tests | needs validation | Update validation.md | validate.md updated |
| COMPLETE | Finish loop | all done | status=completed | summary.md created |
## Action Flow (Codex Pattern)
```
spawn_agent (ccw-loop-executor)
|
v
+-------+
| INIT | (if skill_state is null)
+-------+
|
v
+-------+ send_input
| MENU | <------------- (user selection in interactive mode)
+-------+
|
+---+---+---+---+
| | | | |
v v v v v
DEV DBG VAL CMP EXIT
|
v
wait() -> get result
|
v
[Loop continues via send_input]
|
v
close_agent()
```
## Action Execution Pattern
### Single Agent Deep Interaction
All actions executed within same agent via `send_input`:
```javascript
// Initial spawn
const agent = spawn_agent({ message: role + initial_task })
// Execute INIT
const initResult = wait({ ids: [agent] })
// Continue with DEVELOP via send_input
send_input({ id: agent, message: 'Execute DEVELOP' })
const devResult = wait({ ids: [agent] })
// Continue with VALIDATE via send_input
send_input({ id: agent, message: 'Execute VALIDATE' })
const valResult = wait({ ids: [agent] })
// Only close when done
close_agent({ id: agent })
```
### Action Output Format (Standardized)
Every action MUST output:
```
ACTION_RESULT:
- action: {ACTION_NAME}
- status: success | failed | needs_input
- message: {user-facing message}
- state_updates: { ... }
FILES_UPDATED:
- {file_path}: {description}
NEXT_ACTION_NEEDED: {NEXT_ACTION} | WAITING_INPUT | COMPLETED | PAUSED
```
## Action Selection Logic
### Auto Mode
```javascript
function selectNextAction(state) {
const skillState = state.skill_state
// 1. Terminal conditions
if (state.status === 'completed') return null
if (state.status === 'failed') return null
if (state.current_iteration >= state.max_iterations) return 'COMPLETE'
// 2. Initialization check
if (!skillState) return 'INIT'
// 3. Auto selection based on state
const hasPendingDevelop = skillState.develop.tasks.some(t => t.status === 'pending')
if (hasPendingDevelop) {
return 'DEVELOP'
}
if (skillState.last_action === 'DEVELOP') {
const needsDebug = skillState.develop.completed < skillState.develop.total
if (needsDebug) return 'DEBUG'
}
if (skillState.last_action === 'DEBUG' || skillState.debug.confirmed_hypothesis) {
return 'VALIDATE'
}
if (skillState.last_action === 'VALIDATE') {
if (!skillState.validate.passed) return 'DEVELOP'
}
if (skillState.validate.passed && !hasPendingDevelop) {
return 'COMPLETE'
}
return 'DEVELOP'
}
```
### Interactive Mode
Returns `MENU` action, which displays options and waits for user input.
## Action Dependencies
| Action | Depends On | Leads To |
|--------|------------|----------|
| INIT | - | MENU or DEVELOP |
| MENU | INIT | User selection |
| DEVELOP | INIT | DEVELOP, DEBUG, VALIDATE |
| DEBUG | INIT | DEVELOP, VALIDATE |
| VALIDATE | DEVELOP or DEBUG | COMPLETE, DEBUG, DEVELOP |
| COMPLETE | - | Terminal |
## Action Sequences
### Happy Path (Auto Mode)
```
INIT -> DEVELOP -> DEVELOP -> DEVELOP -> VALIDATE (pass) -> COMPLETE
```
### Debug Iteration Path
```
INIT -> DEVELOP -> VALIDATE (fail) -> DEBUG -> DEBUG -> VALIDATE (pass) -> COMPLETE
```
### Interactive Path
```
INIT -> MENU -> (user: develop) -> DEVELOP -> MENU -> (user: validate) -> VALIDATE -> MENU -> (user: complete) -> COMPLETE
```
## Error Recovery
| Error | Recovery |
|-------|----------|
| Action timeout | send_input requesting convergence |
| Action failed | Log error, continue or retry |
| Agent closed unexpectedly | Re-spawn with previous output |
| State corrupted | Rebuild from progress files |
## Codex Best Practices
1. **Single agent for all actions**: No need to spawn new agent for each action
2. **Deep interaction via send_input**: Continue conversation in same context
3. **Delayed close_agent**: Only close after all actions complete
4. **Structured output**: Always use ACTION_RESULT format for parsing
5. **Control signal checking**: Check state.status before every action