feat: Add validation action and orchestrator for CCW Loop

- Implemented the VALIDATE action to run tests, check coverage, and generate reports.
- Created orchestrator for managing CCW Loop execution using Codex subagent pattern.
- Defined state schema for unified loop state management.
- Updated action catalog with new actions and their specifications.
- Enhanced CLI and issue routes to support new features and data structures.
- Improved documentation for Codex subagent design principles and action flow.
This commit is contained in:
catlog22
2026-01-22 22:32:37 +08:00
parent c0c1a2eb92
commit 2819f3597f
15 changed files with 3149 additions and 6 deletions

View File

@@ -0,0 +1,260 @@
---
name: ccw-loop-executor
description: |
Stateless iterative development loop executor. Handles develop, debug, and validate phases with file-based state tracking. Uses single-agent deep interaction pattern for context retention.
Examples:
- Context: New loop initialization
user: "Initialize loop for user authentication feature"
assistant: "I'll analyze the task and create development tasks"
commentary: Execute INIT action, create tasks, update state
- Context: Continue development
user: "Continue with next development task"
assistant: "I'll execute the next pending task and update progress"
commentary: Execute DEVELOP action, update progress.md
- Context: Debug mode
user: "Start debugging the login timeout issue"
assistant: "I'll generate hypotheses and add instrumentation"
commentary: Execute DEBUG action, update understanding.md
color: cyan
---
You are a CCW Loop Executor - a stateless iterative development specialist that handles development, debugging, and validation phases with documented progress.
## Core Execution Philosophy
- **Stateless with File-Based State** - Read state from files, never rely on memory
- **Control Signal Compliance** - Always check status before actions (paused/stopped)
- **File-Driven Progress** - All progress documented in Markdown files
- **Incremental Updates** - Small, verifiable steps with state updates
- **Deep Interaction** - Continue in same conversation via send_input
## Execution Process
### 1. State Reading (Every Action)
**MANDATORY**: Before ANY action, read and validate state:
```javascript
// Read current state
const state = JSON.parse(Read('.loop/{loopId}.json'))
// Check control signals
if (state.status === 'paused') {
return { action: 'PAUSED', message: 'Loop paused by API' }
}
if (state.status === 'failed') {
return { action: 'STOPPED', message: 'Loop stopped by API' }
}
if (state.status !== 'running') {
return { action: 'ERROR', message: `Unknown status: ${state.status}` }
}
// Continue with action
```
### 2. Action Execution
**Available Actions**:
| Action | When | Output Files |
|--------|------|--------------|
| INIT | skill_state is null | progress/*.md initialized |
| DEVELOP | Has pending tasks | develop.md, tasks.json |
| DEBUG | Needs debugging | understanding.md, hypotheses.json |
| VALIDATE | Needs validation | validation.md, test-results.json |
| COMPLETE | All tasks done | summary.md |
| MENU | Interactive mode | Display options |
**Action Selection (Auto Mode)**:
```
IF skill_state is null:
-> INIT
ELIF pending_develop_tasks > 0:
-> DEVELOP
ELIF last_action === 'develop' AND !debug_completed:
-> DEBUG
ELIF last_action === 'debug' AND !validation_completed:
-> VALIDATE
ELIF validation_failed:
-> DEVELOP (fix)
ELIF validation_passed AND no_pending_tasks:
-> COMPLETE
```
### 3. Output Format (Structured)
**Every action MUST output in this format**:
```
ACTION_RESULT:
- action: {action_name}
- status: success | failed | needs_input
- message: {user-facing message}
- state_updates: {
"skill_state_field": "new_value",
...
}
FILES_UPDATED:
- {file_path}: {description}
NEXT_ACTION_NEEDED: {action_name} | WAITING_INPUT | COMPLETED | PAUSED
```
### 4. State Updates
**Only update skill_state fields** (API fields are read-only):
```javascript
function updateState(loopId, skillStateUpdates) {
const state = JSON.parse(Read(`.loop/${loopId}.json`))
state.updated_at = getUtc8ISOString()
state.skill_state = {
...state.skill_state,
...skillStateUpdates,
last_action: currentAction,
completed_actions: [...state.skill_state.completed_actions, currentAction]
}
Write(`.loop/${loopId}.json`, JSON.stringify(state, null, 2))
}
```
## Action Instructions
### INIT Action
**Purpose**: Initialize loop session, create directory structure, generate tasks
**Steps**:
1. Create progress directory structure
2. Analyze task description
3. Generate development tasks (3-7 tasks)
4. Initialize progress.md
5. Update state with skill_state
**Output**:
- `.loop/{loopId}.progress/develop.md` (initialized)
- State: skill_state populated with tasks
### DEVELOP Action
**Purpose**: Execute next development task
**Steps**:
1. Find first pending task
2. Analyze task requirements
3. Implement code changes
4. Record changes to changes.log (NDJSON)
5. Update progress.md
6. Mark task as completed
**Output**:
- Updated develop.md with progress entry
- Updated changes.log with NDJSON entry
- State: task status -> completed
### DEBUG Action
**Purpose**: Hypothesis-driven debugging
**Modes**:
- **Explore**: First run - generate hypotheses, add instrumentation
- **Analyze**: Has debug.log - analyze evidence, confirm/reject hypotheses
**Steps (Explore)**:
1. Get bug description
2. Search codebase for related code
3. Generate 3-5 hypotheses with testable conditions
4. Add NDJSON logging points
5. Create understanding.md
6. Save hypotheses.json
**Steps (Analyze)**:
1. Parse debug.log entries
2. Evaluate evidence against hypotheses
3. Determine verdicts (confirmed/rejected/inconclusive)
4. Update understanding.md with corrections
5. If root cause found, generate fix
**Output**:
- understanding.md with exploration/analysis
- hypotheses.json with status
- State: debug iteration updated
### VALIDATE Action
**Purpose**: Run tests and verify implementation
**Steps**:
1. Detect test framework from package.json
2. Run tests with coverage
3. Parse test results
4. Generate validation.md report
5. Determine pass/fail
**Output**:
- validation.md with results
- test-results.json
- coverage.json (if available)
- State: validate.passed updated
### COMPLETE Action
**Purpose**: Finish loop, generate summary
**Steps**:
1. Aggregate statistics from all phases
2. Generate summary.md report
3. Offer expansion to issues
4. Mark status as completed
**Output**:
- summary.md
- State: status -> completed
### MENU Action
**Purpose**: Display interactive menu (interactive mode only)
**Output**:
```
MENU_OPTIONS:
1. [develop] Continue Development - {pending_count} tasks remaining
2. [debug] Start Debugging - {debug_status}
3. [validate] Run Validation - {validation_status}
4. [status] View Details
5. [complete] Complete Loop
6. [exit] Exit (save and quit)
WAITING_INPUT: Please select an option
```
## Quality Gates
Before completing any action, verify:
- [ ] State file read and validated
- [ ] Control signals checked (paused/stopped)
- [ ] Progress files updated
- [ ] State updates written
- [ ] Output format correct
- [ ] Next action determined
## Key Reminders
**NEVER:**
- Skip reading state file
- Ignore control signals (paused/stopped)
- Update API fields (only skill_state)
- Forget to output NEXT_ACTION_NEEDED
- Close agent prematurely (use send_input for multi-phase)
**ALWAYS:**
- Read state at start of every action
- Check control signals before execution
- Write progress to Markdown files
- Update state.json with skill_state changes
- Use structured output format
- Determine next action clearly

View File

@@ -0,0 +1,171 @@
# 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 .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
```
.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 `.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

@@ -0,0 +1,349 @@
---
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]
---
# CCW Loop - Codex Stateless Iterative Development Workflow
Stateless iterative development loop using Codex subagent pattern. Supports develop, debug, and validate phases with file-based state tracking.
## 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)
```
+-------------------------------------------------------------+
| Dashboard (UI) |
| [Create] [Start] [Pause] [Resume] [Stop] [View Progress] |
+-------------------------------------------------------------+
|
v
+-------------------------------------------------------------+
| loop-v2-routes.ts (Control Plane) |
| |
| State: .loop/{loopId}.json (MASTER) |
| Tasks: .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) |
+-------------------------------------------------------------+
|
v
+-------------------------------------------------------------+
| ccw-loop Skill (Execution Plane) |
| |
| Codex Pattern: spawn_agent -> wait -> send_input -> close |
| |
| Reads/Writes: .loop/{loopId}.json (unified state) |
| Writes: .loop/{loopId}.progress/* (progress files) |
| |
| BEFORE each action: |
| -> Check status: paused/stopped -> exit gracefully |
| -> running -> continue with action |
| |
| Actions: init -> develop -> debug -> validate -> complete |
+-------------------------------------------------------------+
```
## Key Design Principles (Codex Adaptation)
1. **Unified State**: API and Skill share `.loop/{loopId}.json` state file
2. **Control Signals**: Skill checks status field before each action (paused/stopped)
3. **File-Driven**: All progress documented in `.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
## Subagent 机制
### 核心 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: [...] })` 批量等待
- **混合模式**: 按需组合
## Execution Modes
### Mode 1: Interactive
User manually selects each action, suitable for complex tasks.
```
User -> Select action -> Execute -> View results -> Select next action
```
### Mode 2: Auto-Loop
Automatic execution in preset order, suitable for standard development flow.
```
Develop -> Debug -> Validate -> (if issues) -> Develop -> ...
```
## Session Structure (Unified Location)
```
.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)
```
## Implementation (Codex Subagent Pattern)
### Session Setup
```javascript
// Helper: Get UTC+8 (China Standard Time) ISO string
const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()
// loopId source:
// 1. API trigger: from --loop-id parameter
// 2. Direct call: generate new loop-v2-{timestamp}-{random}
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}`
})()
const loopFile = `.loop/${loopId}.json`
const progressDir = `.loop/${loopId}.progress`
// Create progress directory
mkdir -p "${progressDir}"
```
### Main Execution Flow (Single Agent Deep Interaction)
```javascript
// ==================== CODEX CCW-LOOP: SINGLE AGENT ORCHESTRATOR ====================
// Step 1: Read or create initial state
let state = null
if (existingLoopId) {
state = JSON.parse(Read(`.loop/${loopId}.json`))
if (!state) {
console.error(`Loop not found: ${loopId}`)
return
}
} else {
state = createInitialState(loopId, taskDescription)
Write(`.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**: .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 .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 .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(`.loop/${loopId}.json`))
currentState.current_iteration = iteration
currentState.updated_at = getUtc8ISOString()
Write(`.loop/${loopId}.json`, JSON.stringify(currentState, null, 2))
}
// Step 4: Cleanup
close_agent({ id: agent })
```
## 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 |
## Usage
```bash
# 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"
# 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,269 @@
# Action: COMPLETE
Complete CCW Loop session and generate summary report.
## Purpose
- Generate completion report
- Aggregate all phase results
- Provide follow-up recommendations
- Offer expansion to issues
- Mark status as completed
## Preconditions
- [ ] state.status === 'running'
- [ ] state.skill_state !== null
## Execution Steps
### Step 1: Verify Control Signals
```javascript
const state = JSON.parse(Read(`.loop/${loopId}.json`))
if (state.status !== 'running') {
return {
action: 'COMPLETE',
status: 'failed',
message: `Cannot complete: status is ${state.status}`,
next_action: state.status === 'paused' ? 'PAUSED' : 'STOPPED'
}
}
```
### Step 2: Aggregate Statistics
```javascript
const stats = {
// Time statistics
duration: Date.now() - new Date(state.created_at).getTime(),
iterations: state.current_iteration,
// Development statistics
develop: {
total_tasks: state.skill_state.develop.total,
completed_tasks: state.skill_state.develop.completed,
completion_rate: state.skill_state.develop.total > 0
? ((state.skill_state.develop.completed / state.skill_state.develop.total) * 100).toFixed(1)
: 0
},
// Debug statistics
debug: {
iterations: state.skill_state.debug.iteration,
hypotheses_tested: state.skill_state.debug.hypotheses.length,
root_cause_found: state.skill_state.debug.confirmed_hypothesis !== null
},
// Validation statistics
validate: {
runs: state.skill_state.validate.test_results.length,
passed: state.skill_state.validate.passed,
coverage: state.skill_state.validate.coverage,
failed_tests: state.skill_state.validate.failed_tests.length
}
}
```
### Step 3: Generate Summary Report
```javascript
const timestamp = getUtc8ISOString()
const summaryReport = `# CCW Loop Session Summary
**Loop ID**: ${loopId}
**Task**: ${state.description}
**Started**: ${state.created_at}
**Completed**: ${timestamp}
**Duration**: ${formatDuration(stats.duration)}
---
## Executive Summary
${state.skill_state.validate.passed
? 'All tests passed, validation successful'
: state.skill_state.develop.completed === state.skill_state.develop.total
? 'Development complete, validation not passed - needs debugging'
: 'Task partially complete - pending items remain'}
---
## Development Phase
| Metric | Value |
|--------|-------|
| Total Tasks | ${stats.develop.total_tasks} |
| Completed | ${stats.develop.completed_tasks} |
| Completion Rate | ${stats.develop.completion_rate}% |
### Completed Tasks
${state.skill_state.develop.tasks.filter(t => t.status === 'completed').map(t => `
- ${t.description}
- Files: ${t.files_changed?.join(', ') || 'N/A'}
- Completed: ${t.completed_at}
`).join('\n')}
### Pending Tasks
${state.skill_state.develop.tasks.filter(t => t.status !== 'completed').map(t => `
- ${t.description}
`).join('\n') || '_None_'}
---
## Debug Phase
| Metric | Value |
|--------|-------|
| Iterations | ${stats.debug.iterations} |
| Hypotheses Tested | ${stats.debug.hypotheses_tested} |
| Root Cause Found | ${stats.debug.root_cause_found ? 'Yes' : 'No'} |
${stats.debug.root_cause_found ? `
### Confirmed Root Cause
${state.skill_state.debug.confirmed_hypothesis}: ${state.skill_state.debug.hypotheses.find(h => h.id === state.skill_state.debug.confirmed_hypothesis)?.description}
` : ''}
---
## Validation Phase
| Metric | Value |
|--------|-------|
| Test Runs | ${stats.validate.runs} |
| Status | ${stats.validate.passed ? 'PASSED' : 'FAILED'} |
| Coverage | ${stats.validate.coverage || 'N/A'}% |
| Failed Tests | ${stats.validate.failed_tests} |
---
## Recommendations
${generateRecommendations(stats, state)}
---
## Session Artifacts
| File | Description |
|------|-------------|
| \`develop.md\` | Development progress timeline |
| \`debug.md\` | Debug exploration and learnings |
| \`validate.md\` | Validation report |
| \`test-results.json\` | Test execution results |
---
*Generated by CCW Loop at ${timestamp}*
`
Write(`${progressDir}/summary.md`, summaryReport)
```
### Step 4: Update State to Completed
```javascript
state.status = 'completed'
state.completed_at = timestamp
state.updated_at = timestamp
state.skill_state.last_action = 'COMPLETE'
state.skill_state.summary = stats
Write(`.loop/${loopId}.json`, JSON.stringify(state, null, 2))
```
## Output Format
```
ACTION_RESULT:
- action: COMPLETE
- status: success
- message: Loop completed. Duration: {duration}, Iterations: {N}
- state_updates: {
"status": "completed",
"completed_at": "{timestamp}"
}
FILES_UPDATED:
- .loop/{loopId}.json: Status set to completed
- .loop/{loopId}.progress/summary.md: Summary report generated
NEXT_ACTION_NEEDED: COMPLETED
```
## Expansion Options
After completion, offer expansion to issues:
```
## Expansion Options
Would you like to create follow-up issues?
1. [test] Add more test cases
2. [enhance] Feature enhancements
3. [refactor] Code refactoring
4. [doc] Documentation updates
5. [none] No expansion needed
Select options (comma-separated) or 'none':
```
## 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`
} else if (minutes > 0) {
return `${minutes}m ${seconds % 60}s`
} else {
return `${seconds}s`
}
}
function generateRecommendations(stats, state) {
const recommendations = []
if (stats.develop.completion_rate < 100) {
recommendations.push('- Complete remaining development tasks')
}
if (!stats.validate.passed) {
recommendations.push('- Fix failing tests')
}
if (stats.validate.coverage && stats.validate.coverage < 80) {
recommendations.push(`- Improve test coverage (current: ${stats.validate.coverage}%)`)
}
if (recommendations.length === 0) {
recommendations.push('- Consider code review')
recommendations.push('- Update documentation')
recommendations.push('- Prepare for deployment')
}
return recommendations.join('\n')
}
```
## Error Handling
| Error Type | Recovery |
|------------|----------|
| Report generation failed | Show basic stats, skip file write |
| Issue creation failed | Log error, continue completion |
## Next Actions
- None (terminal state)
- To continue: Use `/ccw-loop --loop-id={loopId}` to reopen (will set status back to running)

View File

@@ -0,0 +1,286 @@
# Action: DEBUG
Hypothesis-driven debugging with understanding evolution documentation.
## Purpose
- Locate error source
- Generate testable hypotheses
- Add NDJSON instrumentation
- Analyze log evidence
- Correct understanding based on evidence
- Apply fixes
## Preconditions
- [ ] state.status === 'running'
- [ ] state.skill_state !== null
## Mode Detection
```javascript
const understandingPath = `${progressDir}/debug.md`
const debugLogPath = `${progressDir}/debug.log`
const understandingExists = fs.existsSync(understandingPath)
const logHasContent = fs.existsSync(debugLogPath) && fs.statSync(debugLogPath).size > 0
const debugMode = logHasContent ? 'analyze' : (understandingExists ? 'continue' : 'explore')
```
## Execution Steps
### Mode: Explore (First Debug)
#### Step E1: Get Bug Description
```javascript
// From test failures or user input
const bugDescription = state.skill_state.validate?.failed_tests?.[0]
|| await getUserInput('Describe the bug:')
```
#### Step E2: Search Codebase
```javascript
// Use ACE search_context to find related code
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',
logging_point: 'file.ts:functionName:42',
evidence_criteria: {
confirm: 'If we see X, hypothesis confirmed',
reject: 'If we see Y, hypothesis rejected'
},
likelihood: 1,
status: 'pending',
evidence: null,
verdict_reason: null
},
// H2, H3...
]
```
#### Step E4: Create Understanding Document
```javascript
const initialUnderstanding = `# Understanding Document
**Loop ID**: ${loopId}
**Bug Description**: ${bugDescription}
**Started**: ${getUtc8ISOString()}
---
## Exploration Timeline
### Iteration 1 - Initial Exploration (${getUtc8ISOString()})
#### Current Understanding
Based on bug description and code search:
- Error pattern: [identified pattern]
- Affected areas: [files/modules]
- Initial hypothesis: [first thoughts]
#### Evidence from Code Search
[Search results summary]
#### Hypotheses
${hypotheses.map(h => `
**${h.id}**: ${h.description}
- Testable condition: ${h.testable_condition}
- Logging point: ${h.logging_point}
- Likelihood: ${h.likelihood}
`).join('\n')}
---
## Current Consolidated Understanding
[Summary of what we know so far]
`
Write(understandingPath, initialUnderstanding)
Write(`${progressDir}/hypotheses.json`, JSON.stringify({ hypotheses, iteration: 1 }, null, 2))
```
#### Step E5: Add NDJSON Logging Points
```javascript
// For each hypothesis, add instrumentation
for (const hypothesis of hypotheses) {
const [file, func, line] = hypothesis.logging_point.split(':')
const logStatement = `console.log(JSON.stringify({
hid: "${hypothesis.id}",
ts: Date.now(),
func: "${func}",
data: { /* relevant context */ }
}))`
// Add to file using Edit tool
}
```
### Mode: Analyze (Has Logs)
#### Step A1: Parse Debug Log
```javascript
const logContent = Read(debugLogPath)
const entries = logContent.split('\n')
.filter(l => l.trim())
.map(l => JSON.parse(l))
// Group by hypothesis ID
const byHypothesis = entries.reduce((acc, e) => {
acc[e.hid] = acc[e.hid] || []
acc[e.hid].push(e)
return acc
}, {})
```
#### Step A2: Evaluate Evidence
```javascript
const hypothesesData = JSON.parse(Read(`${progressDir}/hypotheses.json`))
for (const hypothesis of hypothesesData.hypotheses) {
const evidence = byHypothesis[hypothesis.id] || []
// Evaluate against criteria
if (matchesConfirmCriteria(evidence, hypothesis.evidence_criteria.confirm)) {
hypothesis.status = 'confirmed'
hypothesis.evidence = evidence
hypothesis.verdict_reason = 'Evidence matches confirm criteria'
} else if (matchesRejectCriteria(evidence, hypothesis.evidence_criteria.reject)) {
hypothesis.status = 'rejected'
hypothesis.evidence = evidence
hypothesis.verdict_reason = 'Evidence matches reject criteria'
} else {
hypothesis.status = 'inconclusive'
hypothesis.evidence = evidence
hypothesis.verdict_reason = 'Insufficient evidence'
}
}
```
#### Step A3: Update Understanding
```javascript
const iteration = hypothesesData.iteration + 1
const timestamp = getUtc8ISOString()
const analysisEntry = `
### Iteration ${iteration} - Evidence Analysis (${timestamp})
#### Log Analysis Results
${hypothesesData.hypotheses.map(h => `
**${h.id}**: ${h.status.toUpperCase()}
- Evidence: ${JSON.stringify(h.evidence?.slice(0, 3))}
- Reasoning: ${h.verdict_reason}
`).join('\n')}
#### Corrected Understanding
[Any corrections to previous assumptions]
${confirmedHypothesis ? `
#### Root Cause Identified
**${confirmedHypothesis.id}**: ${confirmedHypothesis.description}
` : `
#### Next Steps
[What to investigate next]
`}
---
`
const existingUnderstanding = Read(understandingPath)
Write(understandingPath, existingUnderstanding + analysisEntry)
```
### Step: Update State
```javascript
state.skill_state.debug.active_bug = bugDescription
state.skill_state.debug.hypotheses = hypothesesData.hypotheses
state.skill_state.debug.hypotheses_count = hypothesesData.hypotheses.length
state.skill_state.debug.iteration = iteration
state.skill_state.debug.last_analysis_at = timestamp
if (confirmedHypothesis) {
state.skill_state.debug.confirmed_hypothesis = confirmedHypothesis.id
}
state.skill_state.last_action = 'DEBUG'
state.updated_at = timestamp
Write(`.loop/${loopId}.json`, JSON.stringify(state, null, 2))
```
## Output Format
```
ACTION_RESULT:
- action: DEBUG
- status: success
- message: {Mode description} - {result summary}
- state_updates: {
"debug.iteration": {N},
"debug.confirmed_hypothesis": "{id or null}"
}
FILES_UPDATED:
- .loop/{loopId}.progress/debug.md: Understanding updated
- .loop/{loopId}.progress/hypotheses.json: Hypotheses updated
- [Source files]: Instrumentation added
NEXT_ACTION_NEEDED: {DEBUG | VALIDATE | DEVELOP | MENU}
```
## Next Action Selection
```javascript
if (confirmedHypothesis) {
// Root cause found, apply fix and validate
return 'VALIDATE'
} else if (allRejected) {
// Generate new hypotheses
return 'DEBUG'
} else {
// Need more evidence - prompt user to reproduce bug
return 'WAITING_INPUT' // User needs to trigger bug
}
```
## Error Handling
| Error Type | Recovery |
|------------|----------|
| Empty debug.log | Prompt user to reproduce bug |
| All hypotheses rejected | Generate new hypotheses |
| >5 iterations | Suggest escalation |
## Next Actions
- Root cause found: `VALIDATE`
- Need more evidence: `DEBUG` (after reproduction)
- All rejected: `DEBUG` (new hypotheses)

View File

@@ -0,0 +1,183 @@
# Action: DEVELOP
Execute development task and record progress to develop.md.
## Purpose
- Execute next pending development task
- Implement code changes
- Record progress to Markdown file
- Update task status in state
## Preconditions
- [ ] state.status === 'running'
- [ ] state.skill_state !== null
- [ ] state.skill_state.develop.tasks.some(t => t.status === 'pending')
## Execution Steps
### Step 1: Verify Control Signals
```javascript
const state = JSON.parse(Read(`.loop/${loopId}.json`))
if (state.status !== 'running') {
return {
action: 'DEVELOP',
status: 'failed',
message: `Cannot develop: status is ${state.status}`,
next_action: state.status === 'paused' ? 'PAUSED' : 'STOPPED'
}
}
```
### Step 2: Find Next Pending Task
```javascript
const tasks = state.skill_state.develop.tasks
const currentTask = tasks.find(t => t.status === 'pending')
if (!currentTask) {
return {
action: 'DEVELOP',
status: 'success',
message: 'All development tasks completed',
next_action: mode === 'auto' ? 'VALIDATE' : 'MENU'
}
}
// Mark as in_progress
currentTask.status = 'in_progress'
```
### Step 3: Execute Development Task
```javascript
console.log(`Executing task: ${currentTask.description}`)
// Use appropriate tools based on task type
// - ACE search_context for finding patterns
// - Read for loading files
// - Edit/Write for making changes
// Record files changed
const filesChanged = []
// Implementation logic...
```
### Step 4: Record Changes to Log (NDJSON)
```javascript
const changesLogPath = `${progressDir}/changes.log`
const timestamp = getUtc8ISOString()
const changeEntry = {
timestamp: timestamp,
task_id: currentTask.id,
description: currentTask.description,
files_changed: filesChanged,
result: 'success'
}
// Append to NDJSON log
const existingLog = Read(changesLogPath) || ''
Write(changesLogPath, existingLog + JSON.stringify(changeEntry) + '\n')
```
### Step 5: Update Progress Document
```javascript
const progressPath = `${progressDir}/develop.md`
const iteration = state.skill_state.develop.completed + 1
const progressEntry = `
### Iteration ${iteration} - ${currentTask.description} (${timestamp})
#### Task Details
- **ID**: ${currentTask.id}
- **Tool**: ${currentTask.tool}
- **Mode**: ${currentTask.mode}
#### Implementation Summary
[Implementation description]
#### Files Changed
${filesChanged.map(f => `- \`${f}\``).join('\n') || '- No files changed'}
#### Status: COMPLETED
---
`
const existingProgress = Read(progressPath)
Write(progressPath, existingProgress + progressEntry)
```
### Step 6: Update State
```javascript
currentTask.status = 'completed'
currentTask.completed_at = timestamp
currentTask.files_changed = filesChanged
state.skill_state.develop.completed += 1
state.skill_state.develop.current_task = null
state.skill_state.develop.last_progress_at = timestamp
state.skill_state.last_action = 'DEVELOP'
state.skill_state.completed_actions.push('DEVELOP')
state.updated_at = timestamp
Write(`.loop/${loopId}.json`, JSON.stringify(state, null, 2))
```
## Output Format
```
ACTION_RESULT:
- action: DEVELOP
- status: success
- message: Task completed: {task_description}
- state_updates: {
"develop.completed": {N},
"develop.last_progress_at": "{timestamp}"
}
FILES_UPDATED:
- .loop/{loopId}.json: Task status updated
- .loop/{loopId}.progress/develop.md: Progress entry added
- .loop/{loopId}.progress/changes.log: Change entry added
NEXT_ACTION_NEEDED: {DEVELOP | DEBUG | VALIDATE | MENU}
```
## Auto Mode Next Action Selection
```javascript
const pendingTasks = tasks.filter(t => t.status === 'pending')
if (pendingTasks.length > 0) {
return 'DEVELOP' // More tasks to do
} else {
return 'DEBUG' // All done, check for issues
}
```
## Error Handling
| Error Type | Recovery |
|------------|----------|
| Task execution failed | Mark task as failed, continue to next |
| File write failed | Retry once, then report error |
| All tasks done | Move to DEBUG or VALIDATE |
## Next Actions
- More pending tasks: `DEVELOP`
- All tasks complete: `DEBUG` (auto) or `MENU` (interactive)
- Task failed: `DEVELOP` (retry) or `DEBUG` (investigate)

View File

@@ -0,0 +1,164 @@
# Action: INIT
Initialize CCW Loop session, create directory structure and initial state.
## Purpose
- Create session directory structure
- Initialize state file with skill_state
- Analyze task description to generate development tasks
- Prepare execution environment
## Preconditions
- [ ] state.status === 'running'
- [ ] state.skill_state === null
## Execution Steps
### Step 1: Verify Control Signals
```javascript
const state = JSON.parse(Read(`.loop/${loopId}.json`))
if (state.status !== 'running') {
return {
action: 'INIT',
status: 'failed',
message: `Cannot init: status is ${state.status}`,
next_action: state.status === 'paused' ? 'PAUSED' : 'STOPPED'
}
}
```
### Step 2: Create Directory Structure
```javascript
const progressDir = `.loop/${loopId}.progress`
// Directories created by orchestrator, verify they exist
// mkdir -p ${progressDir}
```
### Step 3: Analyze Task and Generate Tasks
```javascript
// Analyze task description
const taskDescription = state.description
// Generate 3-7 development tasks based on analysis
// Use ACE search or smart_search to find relevant patterns
const tasks = [
{
id: 'task-001',
description: 'Task description based on analysis',
tool: 'gemini',
mode: 'write',
status: 'pending',
priority: 1,
files: [],
created_at: getUtc8ISOString(),
completed_at: null
}
// ... more tasks
]
```
### Step 4: Initialize Progress Document
```javascript
const progressPath = `${progressDir}/develop.md`
const progressInitial = `# Development Progress
**Loop ID**: ${loopId}
**Task**: ${taskDescription}
**Started**: ${getUtc8ISOString()}
---
## Task List
${tasks.map((t, i) => `${i + 1}. [ ] ${t.description}`).join('\n')}
---
## Progress Timeline
`
Write(progressPath, progressInitial)
```
### Step 5: Update State
```javascript
const skillState = {
current_action: 'init',
last_action: null,
completed_actions: [],
mode: mode,
develop: {
total: tasks.length,
completed: 0,
current_task: null,
tasks: tasks,
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: []
}
state.skill_state = skillState
state.updated_at = getUtc8ISOString()
Write(`.loop/${loopId}.json`, JSON.stringify(state, null, 2))
```
## Output Format
```
ACTION_RESULT:
- action: INIT
- status: success
- message: Session initialized with {N} development tasks
FILES_UPDATED:
- .loop/{loopId}.json: skill_state initialized
- .loop/{loopId}.progress/develop.md: Progress document created
NEXT_ACTION_NEEDED: {DEVELOP (auto) | MENU (interactive)}
```
## Error Handling
| Error Type | Recovery |
|------------|----------|
| Directory creation failed | Report error, stop |
| Task analysis failed | Create single generic task |
| State write failed | Retry once, then stop |
## Next Actions
- Success (auto mode): `DEVELOP`
- Success (interactive): `MENU`
- Failed: Report error

View File

@@ -0,0 +1,205 @@
# Action: MENU
Display interactive action menu for user selection.
## Purpose
- Show current state summary
- Display available actions
- Wait for user selection
- Return selected action
## Preconditions
- [ ] state.status === 'running'
- [ ] state.skill_state !== null
- [ ] mode === 'interactive'
## Execution Steps
### Step 1: Verify Control Signals
```javascript
const state = JSON.parse(Read(`.loop/${loopId}.json`))
if (state.status !== 'running') {
return {
action: 'MENU',
status: 'failed',
message: `Cannot show menu: status is ${state.status}`,
next_action: state.status === 'paused' ? 'PAUSED' : 'STOPPED'
}
}
```
### Step 2: Generate Status Summary
```javascript
// Development progress
const developProgress = state.skill_state.develop.total > 0
? `${state.skill_state.develop.completed}/${state.skill_state.develop.total} (${((state.skill_state.develop.completed / state.skill_state.develop.total) * 100).toFixed(0)}%)`
: 'Not started'
// Debug status
const debugStatus = state.skill_state.debug.confirmed_hypothesis
? 'Root cause found'
: state.skill_state.debug.iteration > 0
? `Iteration ${state.skill_state.debug.iteration}`
: 'Not started'
// Validation status
const validateStatus = state.skill_state.validate.passed
? 'PASSED'
: state.skill_state.validate.test_results.length > 0
? `FAILED (${state.skill_state.validate.failed_tests.length} failures)`
: 'Not run'
```
### Step 3: Display Menu
```javascript
const menuDisplay = `
================================================================
CCW Loop - ${loopId}
================================================================
Task: ${state.description}
Iteration: ${state.current_iteration}
+-----------------------------------------------------+
| Phase | Status |
+-----------------------------------------------------+
| Develop | ${developProgress.padEnd(35)}|
| Debug | ${debugStatus.padEnd(35)}|
| Validate | ${validateStatus.padEnd(35)}|
+-----------------------------------------------------+
================================================================
MENU_OPTIONS:
1. [develop] Continue Development - ${state.skill_state.develop.total - state.skill_state.develop.completed} tasks pending
2. [debug] Start Debugging - ${debugStatus}
3. [validate] Run Validation - ${validateStatus}
4. [status] View Detailed Status
5. [complete] Complete Loop
6. [exit] Exit (save and quit)
`
console.log(menuDisplay)
```
## Output Format
```
ACTION_RESULT:
- action: MENU
- status: success
- message: ${menuDisplay}
MENU_OPTIONS:
1. [develop] Continue Development - {N} tasks pending
2. [debug] Start Debugging - {status}
3. [validate] Run Validation - {status}
4. [status] View Detailed Status
5. [complete] Complete Loop
6. [exit] Exit (save and quit)
NEXT_ACTION_NEEDED: WAITING_INPUT
```
## User Input Handling
When user provides input, orchestrator sends it back via `send_input`:
```javascript
// User selects "develop"
send_input({
id: agent,
message: `
## USER INPUT RECEIVED
Action selected: develop
## EXECUTE SELECTED ACTION
Execute DEVELOP action.
`
})
```
## Status Detail View
If user selects "status":
```javascript
const detailView = `
## Detailed Status
### Development Progress
${Read(`${progressDir}/develop.md`)?.substring(0, 1000) || 'No progress recorded'}
### Debug Status
${state.skill_state.debug.hypotheses.length > 0
? state.skill_state.debug.hypotheses.map(h => ` ${h.id}: ${h.status} - ${h.description.substring(0, 50)}...`).join('\n')
: ' No debugging started'}
### Validation Results
${state.skill_state.validate.test_results.length > 0
? ` Last run: ${state.skill_state.validate.last_run_at}
Pass rate: ${state.skill_state.validate.pass_rate}%`
: ' No validation run yet'}
`
console.log(detailView)
// Return to menu
return {
action: 'MENU',
status: 'success',
message: detailView,
next_action: 'MENU' // Show menu again
}
```
## Exit Handling
If user selects "exit":
```javascript
// Save current state
state.status = 'user_exit'
state.updated_at = getUtc8ISOString()
Write(`.loop/${loopId}.json`, JSON.stringify(state, null, 2))
return {
action: 'MENU',
status: 'success',
message: 'Session saved. Use --loop-id to resume.',
next_action: 'COMPLETED'
}
```
## Action Mapping
| User Selection | Next Action |
|----------------|-------------|
| develop | DEVELOP |
| debug | DEBUG |
| validate | VALIDATE |
| status | MENU (after showing details) |
| complete | COMPLETE |
| exit | COMPLETED (save and exit) |
## Error Handling
| Error Type | Recovery |
|------------|----------|
| Invalid selection | Show menu again |
| User cancels | Return to menu |
## Next Actions
Based on user selection - forwarded via `send_input` by orchestrator.

View File

@@ -0,0 +1,250 @@
# Action: VALIDATE
Run tests and verify implementation, record results to validate.md.
## Purpose
- Run unit tests
- Run integration tests
- Check code coverage
- Generate validation report
- Determine pass/fail status
## Preconditions
- [ ] state.status === 'running'
- [ ] state.skill_state !== null
- [ ] (develop.completed > 0) OR (debug.confirmed_hypothesis !== null)
## Execution Steps
### Step 1: Verify Control Signals
```javascript
const state = JSON.parse(Read(`.loop/${loopId}.json`))
if (state.status !== 'running') {
return {
action: 'VALIDATE',
status: 'failed',
message: `Cannot validate: status is ${state.status}`,
next_action: state.status === 'paused' ? 'PAUSED' : 'STOPPED'
}
}
```
### Step 2: 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 3: Run Tests
```javascript
const testResult = await Bash({
command: testScript,
timeout: 300000 // 5 minutes
})
// Parse test output based on framework
const testResults = parseTestOutput(testResult.stdout, testResult.stderr)
```
### Step 4: Run Coverage (if available)
```javascript
let coverageData = null
if (coverageScript) {
const coverageResult = await Bash({
command: coverageScript,
timeout: 300000
})
coverageData = parseCoverageReport(coverageResult.stdout)
Write(`${progressDir}/coverage.json`, JSON.stringify(coverageData, null, 2))
}
```
### Step 5: Generate Validation Report
```javascript
const timestamp = getUtc8ISOString()
const iteration = (state.skill_state.validate.test_results?.length || 0) + 1
const validationReport = `# Validation Report
**Loop ID**: ${loopId}
**Task**: ${state.description}
**Validated**: ${timestamp}
---
## Iteration ${iteration} - Validation Run
### Test Execution Summary
| Metric | Value |
|--------|-------|
| Total Tests | ${testResults.total} |
| Passed | ${testResults.passed} |
| Failed | ${testResults.failed} |
| Skipped | ${testResults.skipped} |
| Duration | ${testResults.duration_ms}ms |
| **Pass Rate** | **${((testResults.passed / testResults.total) * 100).toFixed(1)}%** |
### Coverage Report
${coverageData ? `
| File | Statements | Branches | Functions | Lines |
|------|------------|----------|-----------|-------|
${coverageData.files.map(f => `| ${f.path} | ${f.statements}% | ${f.branches}% | ${f.functions}% | ${f.lines}% |`).join('\n')}
**Overall Coverage**: ${coverageData.overall.statements}%
` : '_No coverage data available_'}
### Failed Tests
${testResults.failed > 0 ? testResults.failures.map(f => `
#### ${f.test_name}
- **Suite**: ${f.suite}
- **Error**: ${f.error_message}
`).join('\n') : '_All tests passed_'}
---
## Validation Decision
**Result**: ${testResults.failed === 0 ? 'PASS' : 'FAIL'}
${testResults.failed > 0 ? `
### Next Actions
1. Review failed tests
2. Debug failures using DEBUG action
3. Fix issues and re-run validation
` : `
### Next Actions
1. Consider code review
2. Complete loop
`}
`
Write(`${progressDir}/validate.md`, validationReport)
```
### Step 6: Save Test Results
```javascript
const testResultsData = {
iteration,
timestamp,
summary: {
total: testResults.total,
passed: testResults.passed,
failed: testResults.failed,
skipped: testResults.skipped,
pass_rate: ((testResults.passed / testResults.total) * 100).toFixed(1),
duration_ms: testResults.duration_ms
},
tests: testResults.tests,
failures: testResults.failures,
coverage: coverageData?.overall || null
}
Write(`${progressDir}/test-results.json`, JSON.stringify(testResultsData, null, 2))
```
### Step 7: Update State
```javascript
const validationPassed = testResults.failed === 0 && testResults.passed > 0
state.skill_state.validate.test_results.push(testResultsData)
state.skill_state.validate.pass_rate = parseFloat(testResultsData.summary.pass_rate)
state.skill_state.validate.coverage = coverageData?.overall?.statements || 0
state.skill_state.validate.passed = validationPassed
state.skill_state.validate.failed_tests = testResults.failures.map(f => f.test_name)
state.skill_state.validate.last_run_at = timestamp
state.skill_state.last_action = 'VALIDATE'
state.updated_at = timestamp
Write(`.loop/${loopId}.json`, JSON.stringify(state, null, 2))
```
## Output Format
```
ACTION_RESULT:
- action: VALIDATE
- status: success
- message: Validation {PASSED | FAILED} - {pass_count}/{total_count} tests passed
- state_updates: {
"validate.passed": {true | false},
"validate.pass_rate": {N},
"validate.failed_tests": [{list}]
}
FILES_UPDATED:
- .loop/{loopId}.progress/validate.md: Validation report created
- .loop/{loopId}.progress/test-results.json: Test results saved
- .loop/{loopId}.progress/coverage.json: Coverage data saved (if available)
NEXT_ACTION_NEEDED: {COMPLETE | DEBUG | DEVELOP | MENU}
```
## Next Action Selection
```javascript
if (validationPassed) {
const pendingTasks = state.skill_state.develop.tasks.filter(t => t.status === 'pending')
if (pendingTasks.length === 0) {
return 'COMPLETE'
} else {
return 'DEVELOP'
}
} else {
// Tests failed - need debugging
return 'DEBUG'
}
```
## Test Output Parsers
### Jest/Vitest Parser
```javascript
function parseJestOutput(stdout) {
const summaryMatch = stdout.match(/Tests:\s+(\d+)\s+passed.*?(\d+)\s+failed.*?(\d+)\s+total/)
// ... implementation
}
```
### Pytest Parser
```javascript
function parsePytestOutput(stdout) {
const summaryMatch = stdout.match(/(\d+)\s+passed.*?(\d+)\s+failed/)
// ... implementation
}
```
## Error Handling
| Error Type | Recovery |
|------------|----------|
| Tests don't run | Check test script config, report error |
| All tests fail | Suggest DEBUG action |
| Coverage tool missing | Skip coverage, run tests only |
| Timeout | Increase timeout or split tests |
## Next Actions
- Validation passed, no pending: `COMPLETE`
- Validation passed, has pending: `DEVELOP`
- Validation failed: `DEBUG`

View File

@@ -0,0 +1,416 @@
# 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 = `.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 = `.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 ".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 = `.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**: .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(`.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

@@ -0,0 +1,388 @@
# State Schema (Codex Version)
CCW Loop state structure definition for Codex subagent pattern.
## State File
**Location**: `.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(`.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 | `.loop/{loopId}.json` | Every state change (master) |
| `skill_state.develop` | `.loop/{loopId}.progress/develop.md` | After each dev operation |
| `skill_state.debug` | `.loop/{loopId}.progress/debug.md` | After each debug operation |
| `skill_state.validate` | `.loop/{loopId}.progress/validate.md` | After each validation |
| Code changes log | `.loop/{loopId}.progress/changes.log` | Each file modification (NDJSON) |
| Debug log | `.loop/{loopId}.progress/debug.log` | Each debug log (NDJSON) |
### File Structure
```
.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 = `.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 `.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

@@ -0,0 +1,182 @@
# 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

View File

@@ -302,7 +302,7 @@ export async function handleCliRoutes(ctx: RouteContext): Promise<boolean> {
if (req.method === 'PUT') { if (req.method === 'PUT') {
handlePostRequest(req, res, async (body: unknown) => { handlePostRequest(req, res, async (body: unknown) => {
try { try {
const updates = body as { enabled?: boolean; primaryModel?: string; secondaryModel?: string; tags?: string[] }; const updates = body as { enabled?: boolean; primaryModel?: string; secondaryModel?: string; tags?: string[]; envFile?: string | null };
const updated = updateToolConfig(initialPath, tool, updates); const updated = updateToolConfig(initialPath, tool, updates);
// Broadcast config updated event // Broadcast config updated event

View File

@@ -212,20 +212,22 @@ function getIssueDetail(issuesDir: string, issueId: string) {
function enrichIssues(issues: any[], issuesDir: string) { function enrichIssues(issues: any[], issuesDir: string) {
return issues.map(issue => { return issues.map(issue => {
const solutions = readSolutionsJsonl(issuesDir, issue.id); const solutions = readSolutionsJsonl(issuesDir, issue.id);
let taskCount = 0; let tasks: any[] = [];
// Get task count from bound solution // Get tasks from bound solution
if (issue.bound_solution_id) { if (issue.bound_solution_id) {
const boundSol = solutions.find(s => s.id === issue.bound_solution_id); const boundSol = solutions.find(s => s.id === issue.bound_solution_id);
if (boundSol?.tasks) { if (boundSol?.tasks) {
taskCount = boundSol.tasks.length; tasks = boundSol.tasks;
} }
} }
return { return {
...issue, ...issue,
solutions, // Add full solutions array
tasks, // Add full tasks array
solution_count: solutions.length, solution_count: solutions.length,
task_count: taskCount task_count: tasks.length
}; };
}); });
} }

View File

@@ -1000,10 +1000,28 @@ while (result.timed_out) {
转换 Claude 多 Agent 命令到 Codex 时,确保: 转换 Claude 多 Agent 命令到 Codex 时,确保:
- [ ] **角色加载**:首条 message 包含 `~/.codex/agents/*.md` 内容 - [ ] **角色加载**:首条 message 包含 `~/.codex/agents/*.md` 路径
- [ ] **并行优化**:多个独立 agent 使用批量 `wait({ ids: [...] })` - [ ] **并行优化**:多个独立 agent 使用批量 `wait({ ids: [...] })`
- [ ] **超时处理**:设置合理 `timeout_ms`,处理 `timed_out` 情况 - [ ] **超时处理**:设置合理 `timeout_ms`,处理 `timed_out` 情况
- [ ] **结果汇聚**:主 agent 负责合并 `status[id].completed` - [ ] **结果汇聚**:主 agent 负责合并 `status[id].completed`
- [ ] **显式清理**:任务完成后调用 `close_agent` - [ ] **显式清理**:任务完成后调用 `close_agent`
- [ ] **追问模式**:需要续做时使用 `send_input`,不要提前 close - [ ] **追问模式**:需要续做时使用 `send_input`,不要提前 close
- [ ] **输出模板**:要求 subagent 使用统一结构化输出格式 - [ ] **输出模板**:要求 subagent 使用统一结构化输出格式
## 18. Skill/命令设计原则
### 18.1 内容独立性
**Codex skill/命令文档中不应包含与 Claude 的对比内容**
- 对比表格属于转换准则(本文档),不属于最终产物
- skill 文档应专注于 Codex 机制本身的使用
- 避免引入无关上下文,保持文档简洁
### 18.2 模式选择灵活性
**不过度约束设计思路**
- 文档提供的模式(单 agent / 多 agent / 混合)是可选方案
- 根据具体场景选择合适模式
- 主 agent 可作为协调器,灵活利用各种机制