mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-12 02:37:45 +08:00
feat: Add interactive pre-flight checklists for ccw-loop and workflow-plan, including validation and task transformation steps
- Implemented `prep-loop.md` for ccw-loop, detailing source discovery, validation, task transformation, and auto-loop configuration. - Created `prep-plan.md` for workflow planning, covering environment checks, task quality assessment, execution preferences, and final confirmation. - Defined schemas and integration points for `prep-package.json` in both ccw-loop and workflow-plan skills, ensuring proper validation and task handling. - Added error handling mechanisms for various scenarios during the preparation phases.
This commit is contained in:
@@ -1,163 +0,0 @@
|
||||
# Phase 1: Session Initialization
|
||||
|
||||
Create or resume a development loop, initialize state file and directory structure, detect execution mode.
|
||||
|
||||
## Objective
|
||||
|
||||
- Parse user arguments (TASK, --loop-id, --mode)
|
||||
- Create new loop with unique ID OR resume existing loop
|
||||
- Initialize directory structure (progress + workers)
|
||||
- Create master state file
|
||||
- Output: loopId, state, progressDir, mode
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 0: Determine Project Root
|
||||
|
||||
```javascript
|
||||
// Step 0: Determine Project Root
|
||||
const projectRoot = Bash('git rev-parse --show-toplevel 2>/dev/null || pwd').trim()
|
||||
```
|
||||
|
||||
### Step 1.1: Parse Arguments
|
||||
|
||||
```javascript
|
||||
const { loopId: existingLoopId, task, mode = 'interactive' } = options
|
||||
|
||||
// Validate mutual exclusivity
|
||||
if (!existingLoopId && !task) {
|
||||
console.error('Either --loop-id or task description is required')
|
||||
return { status: 'error', message: 'Missing loopId or task' }
|
||||
}
|
||||
|
||||
// Validate mode
|
||||
const validModes = ['interactive', 'auto', 'parallel']
|
||||
if (!validModes.includes(mode)) {
|
||||
console.error(`Invalid mode: ${mode}. Use: ${validModes.join(', ')}`)
|
||||
return { status: 'error', message: 'Invalid mode' }
|
||||
}
|
||||
```
|
||||
|
||||
### Step 1.2: Utility Functions
|
||||
|
||||
```javascript
|
||||
const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()
|
||||
|
||||
function readState(loopId) {
|
||||
const stateFile = `${projectRoot}/.workflow/.loop/${loopId}.json`
|
||||
if (!fs.existsSync(stateFile)) return null
|
||||
return JSON.parse(Read(stateFile))
|
||||
}
|
||||
|
||||
function saveState(loopId, state) {
|
||||
state.updated_at = getUtc8ISOString()
|
||||
Write(`${projectRoot}/.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2))
|
||||
}
|
||||
```
|
||||
|
||||
### Step 1.3: New Loop Creation
|
||||
|
||||
When `TASK` is provided (no `--loop-id`):
|
||||
|
||||
```javascript
|
||||
const timestamp = getUtc8ISOString().replace(/[-:]/g, '').split('.')[0]
|
||||
const random = Math.random().toString(36).substring(2, 10)
|
||||
const loopId = `loop-b-${timestamp}-${random}`
|
||||
|
||||
console.log(`Creating new loop: ${loopId}`)
|
||||
```
|
||||
|
||||
#### Create Directory Structure
|
||||
|
||||
```bash
|
||||
mkdir -p ${projectRoot}/.workflow/.loop/${loopId}.workers
|
||||
mkdir -p ${projectRoot}/.workflow/.loop/${loopId}.progress
|
||||
```
|
||||
|
||||
#### Initialize State File
|
||||
|
||||
```javascript
|
||||
function createState(loopId, taskDescription, mode) {
|
||||
const now = getUtc8ISOString()
|
||||
|
||||
const state = {
|
||||
loop_id: loopId,
|
||||
title: taskDescription.substring(0, 100),
|
||||
description: taskDescription,
|
||||
mode: mode,
|
||||
status: 'running',
|
||||
current_iteration: 0,
|
||||
max_iterations: 10,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
|
||||
skill_state: {
|
||||
phase: 'init',
|
||||
action_index: 0,
|
||||
workers_completed: [],
|
||||
parallel_results: null,
|
||||
pending_tasks: [],
|
||||
completed_tasks: [],
|
||||
findings: []
|
||||
}
|
||||
}
|
||||
|
||||
Write(`${projectRoot}/.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2))
|
||||
return state
|
||||
}
|
||||
```
|
||||
|
||||
### Step 1.4: Resume Existing Loop
|
||||
|
||||
When `--loop-id` is provided:
|
||||
|
||||
```javascript
|
||||
const loopId = existingLoopId
|
||||
const state = readState(loopId)
|
||||
|
||||
if (!state) {
|
||||
console.error(`Loop not found: ${loopId}`)
|
||||
return { status: 'error', message: 'Loop not found' }
|
||||
}
|
||||
|
||||
console.log(`Resuming loop: ${loopId}`)
|
||||
console.log(`Mode: ${state.mode}, Status: ${state.status}`)
|
||||
|
||||
// Override mode if provided
|
||||
if (options['--mode']) {
|
||||
state.mode = options['--mode']
|
||||
saveState(loopId, state)
|
||||
}
|
||||
```
|
||||
|
||||
### Step 1.5: Control Signal Check
|
||||
|
||||
```javascript
|
||||
function checkControlSignals(loopId) {
|
||||
const state = readState(loopId)
|
||||
|
||||
switch (state?.status) {
|
||||
case 'paused':
|
||||
return { continue: false, action: 'pause_exit' }
|
||||
case 'failed':
|
||||
return { continue: false, action: 'stop_exit' }
|
||||
case 'running':
|
||||
return { continue: true, action: 'continue' }
|
||||
default:
|
||||
return { continue: false, action: 'stop_exit' }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
- **Variable**: `loopId` - Unique loop identifier
|
||||
- **Variable**: `state` - Initialized or resumed loop state object
|
||||
- **Variable**: `progressDir` - `${projectRoot}/.workflow/.loop/${loopId}.progress`
|
||||
- **Variable**: `workersDir` - `${projectRoot}/.workflow/.loop/${loopId}.workers`
|
||||
- **Variable**: `mode` - `'interactive'` / `'auto'` / `'parallel'`
|
||||
- **TodoWrite**: Mark Phase 1 completed, Phase 2 in_progress
|
||||
|
||||
## Next Phase
|
||||
|
||||
Return to orchestrator, then auto-continue to [Phase 2: Orchestration Loop](02-orchestration-loop.md).
|
||||
@@ -1,450 +0,0 @@
|
||||
# Phase 2: Orchestration Loop
|
||||
|
||||
Run main orchestration loop with 3-mode dispatch: Interactive, Auto, Parallel.
|
||||
|
||||
## Objective
|
||||
|
||||
- Dispatch to appropriate mode handler based on `state.mode`
|
||||
- Spawn workers with structured prompts (Goal/Scope/Context/Deliverables)
|
||||
- Handle batch wait, timeout, two-phase clarification
|
||||
- Parse WORKER_RESULT, update state per iteration
|
||||
- close_agent after confirming no more interaction needed
|
||||
- Exit on completion, pause, stop, or max iterations
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 2.1: Mode Dispatch
|
||||
|
||||
```javascript
|
||||
const mode = state.mode || 'interactive'
|
||||
|
||||
console.log(`=== CCW Loop-B Orchestrator (${mode} mode) ===`)
|
||||
|
||||
switch (mode) {
|
||||
case 'interactive':
|
||||
return await runInteractiveMode(loopId, state)
|
||||
|
||||
case 'auto':
|
||||
return await runAutoMode(loopId, state)
|
||||
|
||||
case 'parallel':
|
||||
return await runParallelMode(loopId, state)
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2.2: Interactive Mode
|
||||
|
||||
```javascript
|
||||
async function runInteractiveMode(loopId, state) {
|
||||
while (state.status === 'running') {
|
||||
// 1. Check control signals
|
||||
const signal = checkControlSignals(loopId)
|
||||
if (!signal.continue) break
|
||||
|
||||
// 2. Show menu, get user choice
|
||||
const action = await showMenuAndGetChoice(state)
|
||||
if (action === 'exit') {
|
||||
state.status = 'user_exit'
|
||||
saveState(loopId, state)
|
||||
break
|
||||
}
|
||||
|
||||
// 3. Spawn worker
|
||||
const workerId = spawn_agent({
|
||||
message: buildWorkerPrompt(action, loopId, state)
|
||||
})
|
||||
|
||||
// 4. Wait for result (with two-phase clarification support)
|
||||
let output = await waitWithClarification(workerId, action)
|
||||
|
||||
// 5. Process and persist output
|
||||
const workerResult = parseWorkerResult(output)
|
||||
persistWorkerOutput(loopId, action, workerResult)
|
||||
state = processWorkerOutput(loopId, action, workerResult, state)
|
||||
|
||||
// 6. Cleanup worker
|
||||
close_agent({ id: workerId })
|
||||
|
||||
// 7. Display result
|
||||
displayResult(workerResult)
|
||||
|
||||
// 8. Update iteration
|
||||
state.current_iteration++
|
||||
saveState(loopId, state)
|
||||
}
|
||||
|
||||
return { status: state.status, loop_id: loopId, iterations: state.current_iteration }
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2.3: Auto Mode
|
||||
|
||||
```javascript
|
||||
async function runAutoMode(loopId, state) {
|
||||
const sequence = ['init', 'develop', 'debug', 'validate', 'complete']
|
||||
let idx = state.skill_state?.action_index || 0
|
||||
|
||||
while (idx < sequence.length && state.status === 'running') {
|
||||
// Check control signals
|
||||
const signal = checkControlSignals(loopId)
|
||||
if (!signal.continue) break
|
||||
|
||||
// Check iteration limit
|
||||
if (state.current_iteration >= state.max_iterations) {
|
||||
console.log(`Max iterations (${state.max_iterations}) reached`)
|
||||
break
|
||||
}
|
||||
|
||||
const action = sequence[idx]
|
||||
|
||||
// Spawn worker
|
||||
const workerId = spawn_agent({
|
||||
message: buildWorkerPrompt(action, loopId, state)
|
||||
})
|
||||
|
||||
// Wait with two-phase clarification
|
||||
let output = await waitWithClarification(workerId, action)
|
||||
|
||||
// Parse and persist
|
||||
const workerResult = parseWorkerResult(output)
|
||||
persistWorkerOutput(loopId, action, workerResult)
|
||||
state = processWorkerOutput(loopId, action, workerResult, state)
|
||||
|
||||
close_agent({ id: workerId })
|
||||
|
||||
// Determine next step
|
||||
if (workerResult.loop_back_to && workerResult.loop_back_to !== 'null') {
|
||||
idx = sequence.indexOf(workerResult.loop_back_to)
|
||||
if (idx === -1) idx = sequence.indexOf('develop') // fallback
|
||||
} else if (workerResult.status === 'failed') {
|
||||
console.log(`Worker ${action} failed: ${workerResult.summary}`)
|
||||
break
|
||||
} else {
|
||||
idx++
|
||||
}
|
||||
|
||||
// Update state
|
||||
state.skill_state.action_index = idx
|
||||
state.current_iteration++
|
||||
saveState(loopId, state)
|
||||
}
|
||||
|
||||
return { status: state.status, loop_id: loopId, iterations: state.current_iteration }
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2.4: Parallel Mode
|
||||
|
||||
```javascript
|
||||
async function runParallelMode(loopId, state) {
|
||||
// 1. Run init worker first (sequential)
|
||||
const initWorker = spawn_agent({
|
||||
message: buildWorkerPrompt('init', loopId, state)
|
||||
})
|
||||
const initResult = wait({ ids: [initWorker], timeout_ms: 300000 })
|
||||
const initOutput = parseWorkerResult(initResult.status[initWorker].completed)
|
||||
persistWorkerOutput(loopId, 'init', initOutput)
|
||||
state = processWorkerOutput(loopId, 'init', initOutput, state)
|
||||
close_agent({ id: initWorker })
|
||||
|
||||
// 2. Spawn analysis workers in parallel
|
||||
const workers = {
|
||||
develop: spawn_agent({ message: buildWorkerPrompt('develop', loopId, state) }),
|
||||
debug: spawn_agent({ message: buildWorkerPrompt('debug', loopId, state) }),
|
||||
validate: spawn_agent({ message: buildWorkerPrompt('validate', loopId, state) })
|
||||
}
|
||||
|
||||
// 3. Batch wait for all workers
|
||||
const results = wait({
|
||||
ids: Object.values(workers),
|
||||
timeout_ms: 900000 // 15 minutes for all
|
||||
})
|
||||
|
||||
// 4. Handle partial timeout
|
||||
if (results.timed_out) {
|
||||
console.log('Partial timeout - using completed results')
|
||||
// Send convergence request to timed-out workers
|
||||
for (const [role, workerId] of Object.entries(workers)) {
|
||||
if (!results.status[workerId]?.completed) {
|
||||
send_input({
|
||||
id: workerId,
|
||||
message: '## TIMEOUT\nPlease output WORKER_RESULT with current progress immediately.'
|
||||
})
|
||||
}
|
||||
}
|
||||
// Brief second wait for convergence
|
||||
const retryResults = wait({ ids: Object.values(workers), timeout_ms: 60000 })
|
||||
Object.assign(results.status, retryResults.status)
|
||||
}
|
||||
|
||||
// 5. Collect and merge outputs
|
||||
const outputs = {}
|
||||
for (const [role, workerId] of Object.entries(workers)) {
|
||||
const completed = results.status[workerId]?.completed
|
||||
if (completed) {
|
||||
outputs[role] = parseWorkerResult(completed)
|
||||
persistWorkerOutput(loopId, role, outputs[role])
|
||||
}
|
||||
close_agent({ id: workerId })
|
||||
}
|
||||
|
||||
// 6. Merge analysis
|
||||
const mergedResults = mergeWorkerOutputs(outputs)
|
||||
state.skill_state.parallel_results = mergedResults
|
||||
state.current_iteration++
|
||||
saveState(loopId, state)
|
||||
|
||||
// 7. Run complete worker
|
||||
const completeWorker = spawn_agent({
|
||||
message: buildWorkerPrompt('complete', loopId, state)
|
||||
})
|
||||
const completeResult = wait({ ids: [completeWorker], timeout_ms: 300000 })
|
||||
const completeOutput = parseWorkerResult(completeResult.status[completeWorker].completed)
|
||||
persistWorkerOutput(loopId, 'complete', completeOutput)
|
||||
state = processWorkerOutput(loopId, 'complete', completeOutput, state)
|
||||
close_agent({ id: completeWorker })
|
||||
|
||||
return { status: state.status, loop_id: loopId, iterations: state.current_iteration }
|
||||
}
|
||||
```
|
||||
|
||||
## Helper Functions
|
||||
|
||||
### buildWorkerPrompt
|
||||
|
||||
```javascript
|
||||
function buildWorkerPrompt(action, loopId, state) {
|
||||
const roleFiles = {
|
||||
init: '~/.codex/agents/ccw-loop-b-init.md',
|
||||
develop: '~/.codex/agents/ccw-loop-b-develop.md',
|
||||
debug: '~/.codex/agents/ccw-loop-b-debug.md',
|
||||
validate: '~/.codex/agents/ccw-loop-b-validate.md',
|
||||
complete: '~/.codex/agents/ccw-loop-b-complete.md'
|
||||
}
|
||||
|
||||
return `
|
||||
## TASK ASSIGNMENT
|
||||
|
||||
### MANDATORY FIRST STEPS (Agent Execute)
|
||||
1. **Read role definition**: ${roleFiles[action]} (MUST read first)
|
||||
2. Read: ${projectRoot}/.workflow/project-tech.json
|
||||
3. Read: ${projectRoot}/.workflow/project-guidelines.json
|
||||
|
||||
---
|
||||
|
||||
Goal: Execute ${action} action for loop ${loopId}
|
||||
|
||||
Scope:
|
||||
- 可做: ${action} 相关的所有操作
|
||||
- 不可做: 其他 action 的操作
|
||||
- 目录限制: 项目根目录
|
||||
|
||||
Context:
|
||||
- Loop ID: ${loopId}
|
||||
- Action: ${action}
|
||||
- State File: ${projectRoot}/.workflow/.loop/${loopId}.json
|
||||
- Output File: ${projectRoot}/.workflow/.loop/${loopId}.workers/${action}.output.json
|
||||
- Progress File: ${projectRoot}/.workflow/.loop/${loopId}.progress/${action}.md
|
||||
|
||||
Deliverables:
|
||||
- WORKER_RESULT 格式输出
|
||||
- 写入 output.json 和 progress.md
|
||||
|
||||
## CURRENT STATE
|
||||
|
||||
${JSON.stringify(state, null, 2)}
|
||||
|
||||
## TASK DESCRIPTION
|
||||
|
||||
${state.description}
|
||||
|
||||
## EXPECTED OUTPUT
|
||||
|
||||
\`\`\`
|
||||
WORKER_RESULT:
|
||||
- action: ${action}
|
||||
- status: success | failed | needs_input
|
||||
- summary: <brief summary>
|
||||
- files_changed: [list]
|
||||
- next_suggestion: <suggested next action>
|
||||
- loop_back_to: <action name if needs loop back, or null>
|
||||
|
||||
DETAILED_OUTPUT:
|
||||
<action-specific structured output>
|
||||
\`\`\`
|
||||
|
||||
Execute the ${action} action now.
|
||||
`
|
||||
}
|
||||
```
|
||||
|
||||
### waitWithClarification (Two-Phase Workflow)
|
||||
|
||||
```javascript
|
||||
async function waitWithClarification(workerId, action) {
|
||||
const result = wait({ ids: [workerId], timeout_ms: 600000 })
|
||||
|
||||
// Handle timeout
|
||||
if (result.timed_out) {
|
||||
send_input({
|
||||
id: workerId,
|
||||
message: '## TIMEOUT\nPlease converge and output WORKER_RESULT with current progress.'
|
||||
})
|
||||
const retry = wait({ ids: [workerId], timeout_ms: 300000 })
|
||||
if (retry.timed_out) {
|
||||
return `WORKER_RESULT:\n- action: ${action}\n- status: failed\n- summary: Worker timeout\n\nNEXT_ACTION_NEEDED: NONE`
|
||||
}
|
||||
return retry.status[workerId].completed
|
||||
}
|
||||
|
||||
const output = result.status[workerId].completed
|
||||
|
||||
// Check if worker needs clarification (two-phase)
|
||||
if (output.includes('CLARIFICATION_NEEDED')) {
|
||||
// Collect user answers
|
||||
const questions = parseClarificationQuestions(output)
|
||||
const userAnswers = await collectUserAnswers(questions)
|
||||
|
||||
// Send answers back to worker
|
||||
send_input({
|
||||
id: workerId,
|
||||
message: `
|
||||
## CLARIFICATION ANSWERS
|
||||
|
||||
${userAnswers.map(a => `Q: ${a.question}\nA: ${a.answer}`).join('\n\n')}
|
||||
|
||||
## CONTINUE EXECUTION
|
||||
|
||||
Based on clarification answers, continue with the ${action} action.
|
||||
Output WORKER_RESULT when complete.
|
||||
`
|
||||
})
|
||||
|
||||
// Wait for final result
|
||||
const finalResult = wait({ ids: [workerId], timeout_ms: 600000 })
|
||||
return finalResult.status[workerId]?.completed || output
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
```
|
||||
|
||||
### parseWorkerResult
|
||||
|
||||
```javascript
|
||||
function parseWorkerResult(output) {
|
||||
const result = {
|
||||
action: 'unknown',
|
||||
status: 'unknown',
|
||||
summary: '',
|
||||
files_changed: [],
|
||||
next_suggestion: null,
|
||||
loop_back_to: null,
|
||||
detailed_output: ''
|
||||
}
|
||||
|
||||
// Parse WORKER_RESULT block
|
||||
const match = output.match(/WORKER_RESULT:\s*([\s\S]*?)(?:DETAILED_OUTPUT:|$)/)
|
||||
if (match) {
|
||||
const lines = match[1].split('\n')
|
||||
for (const line of lines) {
|
||||
const m = line.match(/^-\s*(\w[\w_]*):\s*(.+)$/)
|
||||
if (m) {
|
||||
const [, key, value] = m
|
||||
if (key === 'files_changed') {
|
||||
try { result.files_changed = JSON.parse(value) } catch {}
|
||||
} else {
|
||||
result[key] = value.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse DETAILED_OUTPUT
|
||||
const detailMatch = output.match(/DETAILED_OUTPUT:\s*([\s\S]*)$/)
|
||||
if (detailMatch) {
|
||||
result.detailed_output = detailMatch[1].trim()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
```
|
||||
|
||||
### mergeWorkerOutputs (Parallel Mode)
|
||||
|
||||
```javascript
|
||||
function mergeWorkerOutputs(outputs) {
|
||||
const merged = {
|
||||
develop: outputs.develop || null,
|
||||
debug: outputs.debug || null,
|
||||
validate: outputs.validate || null,
|
||||
conflicts: [],
|
||||
merged_at: getUtc8ISOString()
|
||||
}
|
||||
|
||||
// Detect file conflicts: multiple workers suggest modifying same file
|
||||
const allFiles = {}
|
||||
for (const [role, output] of Object.entries(outputs)) {
|
||||
if (output?.files_changed) {
|
||||
for (const file of output.files_changed) {
|
||||
if (allFiles[file]) {
|
||||
merged.conflicts.push({
|
||||
file,
|
||||
workers: [allFiles[file], role],
|
||||
resolution: 'manual'
|
||||
})
|
||||
} else {
|
||||
allFiles[file] = role
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return merged
|
||||
}
|
||||
```
|
||||
|
||||
### showMenuAndGetChoice
|
||||
|
||||
```javascript
|
||||
async function showMenuAndGetChoice(state) {
|
||||
const ss = state.skill_state
|
||||
const pendingCount = ss?.pending_tasks?.length || 0
|
||||
const completedCount = ss?.completed_tasks?.length || 0
|
||||
|
||||
const response = await ASK_USER([{
|
||||
id: "Action", type: "select",
|
||||
prompt: `Select next action (completed: ${completedCount}, pending: ${pendingCount}):`,
|
||||
options: [
|
||||
{ label: "develop", description: `Continue development (${pendingCount} pending)` },
|
||||
{ label: "debug", description: "Start debugging / diagnosis" },
|
||||
{ label: "validate", description: "Run tests and validation" },
|
||||
{ label: "complete", description: "Complete loop and generate summary" },
|
||||
{ label: "exit", description: "Exit and save progress" }
|
||||
]
|
||||
}]) // BLOCKS (wait for user response)
|
||||
|
||||
return response["Action"]
|
||||
}
|
||||
```
|
||||
|
||||
### persistWorkerOutput
|
||||
|
||||
```javascript
|
||||
function persistWorkerOutput(loopId, action, workerResult) {
|
||||
const outputPath = `${projectRoot}/.workflow/.loop/${loopId}.workers/${action}.output.json`
|
||||
Write(outputPath, JSON.stringify({
|
||||
...workerResult,
|
||||
timestamp: getUtc8ISOString()
|
||||
}, null, 2))
|
||||
}
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
- **Return**: `{ status, loop_id, iterations }`
|
||||
- **TodoWrite**: Mark Phase 2 completed
|
||||
|
||||
## Next Phase
|
||||
|
||||
None. Phase 2 is the terminal phase of the orchestrator.
|
||||
@@ -1,257 +0,0 @@
|
||||
# Orchestrator (Hybrid Pattern)
|
||||
|
||||
协调器负责状态管理、worker 调度、结果汇聚。
|
||||
|
||||
## Role
|
||||
|
||||
```
|
||||
Read state -> Select mode -> Spawn workers -> Wait results -> Merge -> Update state -> Loop/Exit
|
||||
```
|
||||
|
||||
## State Management
|
||||
|
||||
### Read State
|
||||
|
||||
```javascript
|
||||
function readState(loopId) {
|
||||
const stateFile = `${projectRoot}/.workflow/.loop/${loopId}.json`
|
||||
return fs.existsSync(stateFile)
|
||||
? JSON.parse(Read(stateFile))
|
||||
: null
|
||||
}
|
||||
```
|
||||
|
||||
### Create State
|
||||
|
||||
```javascript
|
||||
function createState(loopId, taskDescription, mode) {
|
||||
const now = new Date().toISOString()
|
||||
|
||||
return {
|
||||
loop_id: loopId,
|
||||
title: taskDescription.substring(0, 100),
|
||||
description: taskDescription,
|
||||
mode: mode,
|
||||
status: 'running',
|
||||
current_iteration: 0,
|
||||
max_iterations: 10,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
skill_state: {
|
||||
phase: 'init',
|
||||
action_index: 0,
|
||||
workers_completed: [],
|
||||
parallel_results: null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Mode Handlers
|
||||
|
||||
### Interactive Mode
|
||||
|
||||
```javascript
|
||||
async function runInteractiveMode(loopId, state) {
|
||||
while (state.status === 'running') {
|
||||
// 1. Show menu
|
||||
const action = await showMenu(state)
|
||||
if (action === 'exit') break
|
||||
|
||||
// 2. Spawn worker
|
||||
const worker = spawn_agent({
|
||||
message: buildWorkerPrompt(action, loopId, state)
|
||||
})
|
||||
|
||||
// 3. Wait for result
|
||||
const result = wait({ ids: [worker], timeout_ms: 600000 })
|
||||
|
||||
// 4. Handle timeout
|
||||
if (result.timed_out) {
|
||||
send_input({ id: worker, message: 'Please converge and output WORKER_RESULT' })
|
||||
const retryResult = wait({ ids: [worker], timeout_ms: 300000 })
|
||||
if (retryResult.timed_out) {
|
||||
console.log('Worker timeout, skipping')
|
||||
close_agent({ id: worker })
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Process output
|
||||
const output = result.status[worker].completed
|
||||
state = processWorkerOutput(loopId, action, output, state)
|
||||
|
||||
// 6. Cleanup
|
||||
close_agent({ id: worker })
|
||||
|
||||
// 7. Display result
|
||||
displayResult(output)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Auto Mode
|
||||
|
||||
```javascript
|
||||
async function runAutoMode(loopId, state) {
|
||||
const sequence = ['init', 'develop', 'debug', 'validate', 'complete']
|
||||
let idx = state.skill_state?.action_index || 0
|
||||
|
||||
while (idx < sequence.length && state.status === 'running') {
|
||||
const action = sequence[idx]
|
||||
|
||||
// Spawn and wait
|
||||
const worker = spawn_agent({ message: buildWorkerPrompt(action, loopId, state) })
|
||||
const result = wait({ ids: [worker], timeout_ms: 600000 })
|
||||
const output = result.status[worker].completed
|
||||
close_agent({ id: worker })
|
||||
|
||||
// Parse result
|
||||
const workerResult = parseWorkerResult(output)
|
||||
state = processWorkerOutput(loopId, action, output, state)
|
||||
|
||||
// Determine next
|
||||
if (workerResult.loop_back_to) {
|
||||
idx = sequence.indexOf(workerResult.loop_back_to)
|
||||
} else if (workerResult.status === 'failed') {
|
||||
break
|
||||
} else {
|
||||
idx++
|
||||
}
|
||||
|
||||
// Update action index
|
||||
state.skill_state.action_index = idx
|
||||
saveState(loopId, state)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Parallel Mode
|
||||
|
||||
```javascript
|
||||
async function runParallelMode(loopId, state) {
|
||||
// Spawn all workers
|
||||
const workers = {
|
||||
develop: spawn_agent({ message: buildWorkerPrompt('develop', loopId, state) }),
|
||||
debug: spawn_agent({ message: buildWorkerPrompt('debug', loopId, state) }),
|
||||
validate: spawn_agent({ message: buildWorkerPrompt('validate', loopId, state) })
|
||||
}
|
||||
|
||||
// Batch wait
|
||||
const results = wait({
|
||||
ids: Object.values(workers),
|
||||
timeout_ms: 900000
|
||||
})
|
||||
|
||||
// Collect outputs
|
||||
const outputs = {}
|
||||
for (const [role, id] of Object.entries(workers)) {
|
||||
if (results.status[id].completed) {
|
||||
outputs[role] = results.status[id].completed
|
||||
}
|
||||
close_agent({ id })
|
||||
}
|
||||
|
||||
// Merge analysis
|
||||
state.skill_state.parallel_results = outputs
|
||||
saveState(loopId, state)
|
||||
|
||||
// Coordinator analyzes merged results
|
||||
return analyzeAndDecide(outputs)
|
||||
}
|
||||
```
|
||||
|
||||
## Worker Prompt Template
|
||||
|
||||
```javascript
|
||||
function buildWorkerPrompt(action, loopId, state) {
|
||||
const roleFiles = {
|
||||
init: '~/.codex/agents/ccw-loop-b-init.md',
|
||||
develop: '~/.codex/agents/ccw-loop-b-develop.md',
|
||||
debug: '~/.codex/agents/ccw-loop-b-debug.md',
|
||||
validate: '~/.codex/agents/ccw-loop-b-validate.md',
|
||||
complete: '~/.codex/agents/ccw-loop-b-complete.md'
|
||||
}
|
||||
|
||||
return `
|
||||
## TASK ASSIGNMENT
|
||||
|
||||
### MANDATORY FIRST STEPS
|
||||
1. **Read role definition**: ${roleFiles[action]}
|
||||
2. Read: ${projectRoot}/.workflow/project-tech.json
|
||||
3. Read: ${projectRoot}/.workflow/project-guidelines.json
|
||||
|
||||
---
|
||||
|
||||
## CONTEXT
|
||||
- Loop ID: ${loopId}
|
||||
- Action: ${action}
|
||||
- State: ${JSON.stringify(state, null, 2)}
|
||||
|
||||
## TASK
|
||||
${state.description}
|
||||
|
||||
## OUTPUT FORMAT
|
||||
\`\`\`
|
||||
WORKER_RESULT:
|
||||
- action: ${action}
|
||||
- status: success | failed | needs_input
|
||||
- summary: <brief>
|
||||
- files_changed: []
|
||||
- next_suggestion: <action>
|
||||
- loop_back_to: <action or null>
|
||||
|
||||
DETAILED_OUTPUT:
|
||||
<action-specific output>
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
```
|
||||
|
||||
## Result Processing
|
||||
|
||||
```javascript
|
||||
function parseWorkerResult(output) {
|
||||
const result = {
|
||||
action: 'unknown',
|
||||
status: 'unknown',
|
||||
summary: '',
|
||||
files_changed: [],
|
||||
next_suggestion: null,
|
||||
loop_back_to: null
|
||||
}
|
||||
|
||||
const match = output.match(/WORKER_RESULT:\s*([\s\S]*?)(?:DETAILED_OUTPUT:|$)/)
|
||||
if (match) {
|
||||
const lines = match[1].split('\n')
|
||||
for (const line of lines) {
|
||||
const m = line.match(/^-\s*(\w+):\s*(.+)$/)
|
||||
if (m) {
|
||||
const [, key, value] = m
|
||||
if (key === 'files_changed') {
|
||||
try { result.files_changed = JSON.parse(value) } catch {}
|
||||
} else {
|
||||
result[key] = value.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
```
|
||||
|
||||
## Termination Conditions
|
||||
|
||||
1. User exits (interactive)
|
||||
2. Sequence complete (auto)
|
||||
3. Worker failed with no recovery
|
||||
4. Max iterations reached
|
||||
5. API paused/stopped
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Worker 生命周期**: spawn → wait → close,不保留 worker
|
||||
2. **结果持久化**: Worker 输出写入 `{projectRoot}/.workflow/.loop/{loopId}.workers/`
|
||||
3. **状态同步**: 每次 worker 完成后更新 state
|
||||
4. **超时处理**: send_input 请求收敛,再超时则跳过
|
||||
@@ -1,181 +0,0 @@
|
||||
# State Schema (CCW Loop-B)
|
||||
|
||||
## Master State Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"loop_id": "loop-b-20260122-abc123",
|
||||
"title": "Implement user authentication",
|
||||
"description": "Full task description here",
|
||||
"mode": "interactive | auto | parallel",
|
||||
"status": "running | paused | completed | failed",
|
||||
"current_iteration": 3,
|
||||
"max_iterations": 10,
|
||||
"created_at": "2026-01-22T10:00:00.000Z",
|
||||
"updated_at": "2026-01-22T10:30:00.000Z",
|
||||
|
||||
"skill_state": {
|
||||
"phase": "develop | debug | validate | complete",
|
||||
"action_index": 2,
|
||||
"workers_completed": ["init", "develop"],
|
||||
"parallel_results": null,
|
||||
"pending_tasks": [],
|
||||
"completed_tasks": [],
|
||||
"findings": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Field Descriptions
|
||||
|
||||
### Core Fields (API Compatible)
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `loop_id` | string | Unique identifier |
|
||||
| `title` | string | Short title (max 100 chars) |
|
||||
| `description` | string | Full task description |
|
||||
| `mode` | enum | Execution mode |
|
||||
| `status` | enum | Current status |
|
||||
| `current_iteration` | number | Iteration counter |
|
||||
| `max_iterations` | number | Safety limit |
|
||||
| `created_at` | ISO string | Creation timestamp |
|
||||
| `updated_at` | ISO string | Last update timestamp |
|
||||
|
||||
### Skill State Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `phase` | enum | Current execution phase |
|
||||
| `action_index` | number | Position in action sequence (auto mode) |
|
||||
| `workers_completed` | array | List of completed worker actions |
|
||||
| `parallel_results` | object | Merged results from parallel mode |
|
||||
| `pending_tasks` | array | Tasks waiting to be executed |
|
||||
| `completed_tasks` | array | Tasks already done |
|
||||
| `findings` | array | Discoveries during execution |
|
||||
|
||||
## Worker Output Structure
|
||||
|
||||
Each worker writes to `{projectRoot}/.workflow/.loop/{loopId}.workers/{action}.output.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "develop",
|
||||
"status": "success",
|
||||
"summary": "Implemented 3 functions",
|
||||
"files_changed": ["src/auth.ts", "src/utils.ts"],
|
||||
"next_suggestion": "validate",
|
||||
"loop_back_to": null,
|
||||
"timestamp": "2026-01-22T10:15:00.000Z",
|
||||
"detailed_output": {
|
||||
"tasks_completed": [
|
||||
{ "id": "T1", "description": "Create auth module" }
|
||||
],
|
||||
"metrics": {
|
||||
"lines_added": 150,
|
||||
"lines_removed": 20
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Progress File Structure
|
||||
|
||||
Human-readable progress in `{projectRoot}/.workflow/.loop/{loopId}.progress/{action}.md`:
|
||||
|
||||
```markdown
|
||||
# Develop Progress
|
||||
|
||||
## Session: loop-b-20260122-abc123
|
||||
|
||||
### Iteration 1 (2026-01-22 10:15)
|
||||
|
||||
**Task**: Implement auth module
|
||||
|
||||
**Changes**:
|
||||
- Created `src/auth.ts` with login/logout functions
|
||||
- Added JWT token handling in `src/utils.ts`
|
||||
|
||||
**Status**: Success
|
||||
|
||||
---
|
||||
|
||||
### Iteration 2 (2026-01-22 10:30)
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
## Status Transitions
|
||||
|
||||
```
|
||||
+--------+
|
||||
| init |
|
||||
+--------+
|
||||
|
|
||||
v
|
||||
+------> +---------+
|
||||
| | develop |
|
||||
| +---------+
|
||||
| |
|
||||
| +--------+--------+
|
||||
| | |
|
||||
| v v
|
||||
| +-------+ +---------+
|
||||
| | debug |<------| validate|
|
||||
| +-------+ +---------+
|
||||
| | |
|
||||
| +--------+--------+
|
||||
| |
|
||||
| v
|
||||
| [needs fix?]
|
||||
| yes | | no
|
||||
| v v
|
||||
+------------+ +----------+
|
||||
| complete |
|
||||
+----------+
|
||||
```
|
||||
|
||||
## Parallel Results Schema
|
||||
|
||||
When `mode === 'parallel'`:
|
||||
|
||||
```json
|
||||
{
|
||||
"parallel_results": {
|
||||
"develop": {
|
||||
"status": "success",
|
||||
"summary": "...",
|
||||
"suggestions": []
|
||||
},
|
||||
"debug": {
|
||||
"status": "success",
|
||||
"issues_found": [],
|
||||
"suggestions": []
|
||||
},
|
||||
"validate": {
|
||||
"status": "success",
|
||||
"test_results": {},
|
||||
"coverage": {}
|
||||
},
|
||||
"merged_at": "2026-01-22T10:45:00.000Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
{projectRoot}/.workflow/.loop/
|
||||
+-- loop-b-20260122-abc123.json # Master state
|
||||
+-- loop-b-20260122-abc123.workers/
|
||||
| +-- init.output.json
|
||||
| +-- develop.output.json
|
||||
| +-- debug.output.json
|
||||
| +-- validate.output.json
|
||||
| +-- complete.output.json
|
||||
+-- loop-b-20260122-abc123.progress/
|
||||
+-- develop.md
|
||||
+-- debug.md
|
||||
+-- validate.md
|
||||
+-- summary.md
|
||||
```
|
||||
Reference in New Issue
Block a user