Files
Claude-Code-Workflow/.claude/skills/ccw-loop/phases/state-schema.md
catlog22 60eab98782 feat: Add comprehensive tests for CCW Loop System flow state
- Implemented loop control tasks in JSON format for testing.
- Created comprehensive test scripts for loop flow and standalone tests.
- Developed a shell script to automate the testing of the entire loop system flow, including mock endpoints and state transitions.
- Added error handling and execution history tests to ensure robustness.
- Established variable substitution and success condition evaluations in tests.
- Set up cleanup and workspace management for test environments.
2026-01-22 10:13:00 +08:00

12 KiB
Raw Blame History

State Schema

CCW Loop 的状态结构定义(统一版本)。

状态文件

位置: .loop/{loopId}.json (统一位置API + Skill 共享)

旧版本位置 (仅向后兼容): .workflow/.loop/{session-id}/state.json

结构定义

统一状态接口 (Unified Loop State)

/**
 * Unified Loop State - API 和 Skill 共享的状态结构
 * API (loop-v2-routes.ts) 拥有状态的主控权
 * Skill (ccw-loop) 读取和更新此状态
 */
interface LoopState {
  // =====================================================
  // API FIELDS (from loop-v2-routes.ts)
  // 这些字段由 API 管理Skill 只读
  // =====================================================

  loop_id: string                // Loop ID, e.g., "loop-v2-20260122-abc123"
  title: string                  // Loop 标题
  description: string            // Loop 描述
  max_iterations: number         // 最大迭代次数
  status: 'created' | 'running' | 'paused' | 'completed' | 'failed'
  current_iteration: number      // 当前迭代次数
  created_at: string             // 创建时间 (ISO8601)
  updated_at: string             // 最后更新时间 (ISO8601)
  completed_at?: string          // 完成时间 (ISO8601)
  failure_reason?: string        // 失败原因

  // =====================================================
  // SKILL EXTENSION FIELDS
  // 这些字段由 Skill 管理API 只读
  // =====================================================

  skill_state?: {
    // 当前执行动作
    current_action: 'init' | 'develop' | 'debug' | 'validate' | 'complete' | null
    last_action: string | null
    completed_actions: string[]
    mode: 'interactive' | 'auto'

    // === 开发阶段 ===
    develop: {
      total: number
      completed: number
      current_task?: string
      tasks: DevelopTask[]
      last_progress_at: string | null
    }

    // === 调试阶段 ===
    debug: {
      active_bug?: string
      hypotheses_count: number
      hypotheses: Hypothesis[]
      confirmed_hypothesis: string | null
      iteration: number
      last_analysis_at: string | null
    }

    // === 验证阶段 ===
    validate: {
      pass_rate: number           // 测试通过率 (0-100)
      coverage: number            // 覆盖率 (0-100)
      test_results: TestResult[]
      passed: boolean
      failed_tests: string[]
      last_run_at: string | null
    }

    // === 错误追踪 ===
    errors: Array<{
      action: string
      message: string
      timestamp: string
    }>
  }
}

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 = 最可能
  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
}

初始状态

由 API 创建时 (Dashboard 触发)

{
  "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"
}

由 Skill 初始化后 (action-init)

{
  "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 Signals)

Skill 在每个 Action 开始前必须检查控制信号:

/**
 * 检查 API 控制信号
 * @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 暂停了循环Skill 应退出等待 resume
      return { continue: false, action: 'pause_exit' }

    case 'failed':
      // API 停止了循环 (用户手动停止)
      return { continue: false, action: 'stop_exit' }

    case 'running':
      // 正常继续
      return { continue: true, action: 'continue' }

    default:
      // 异常状态
      return { continue: false, action: 'stop_exit' }
  }
}

在 Action 中使用

## Execution

### Step 1: Check Control Signals

\`\`\`javascript
const control = checkControlSignals(loopId)
if (!control.continue) {
  // 输出退出原因
  console.log(`Loop ${control.action}: status = ${state.status}`)

  // 如果是 pause_exit保存当前进度
  if (control.action === 'pause_exit') {
    updateSkillState(loopId, { current_action: 'paused' })
  }

  return  // 退出 Action
}
\`\`\`

### Step 2: Execute Action Logic
...

状态转换规则

1. 初始化 (action-init)

// Skill 初始化后
{
  // API 字段更新
  status: 'created'  'running',  // 或保持 'running' 如果 API 已设置
  updated_at: timestamp,

  // Skill 字段初始化
  skill_state: {
    current_action: 'init',
    mode: 'auto',
    develop: {
      tasks: [...parsed_tasks],
      total: N,
      completed: 0
    }
  }
}

2. 开发进行中 (action-develop-with-file)

// 开发任务执行后
{
  updated_at: timestamp,
  current_iteration: state.current_iteration + 1,

  skill_state: {
    current_action: 'develop',
    last_action: 'action-develop-with-file',
    completed_actions: [...state.skill_state.completed_actions, 'action-develop-with-file'],
    develop: {
      current_task: 'task-xxx',
      completed: N+1,
      last_progress_at: timestamp
    }
  }
}

3. 调试进行中 (action-debug-with-file)

// 调试执行后
{
  updated_at: timestamp,
  current_iteration: state.current_iteration + 1,

  skill_state: {
    current_action: 'debug',
    last_action: 'action-debug-with-file',
    debug: {
      active_bug: '...',
      hypotheses_count: N,
      hypotheses: [...new_hypotheses],
      iteration: N+1,
      last_analysis_at: timestamp
    }
  }
}

4. 验证完成 (action-validate-with-file)

// 验证执行后
{
  updated_at: timestamp,
  current_iteration: state.current_iteration + 1,

  skill_state: {
    current_action: 'validate',
    last_action: 'action-validate-with-file',
    validate: {
      test_results: [...results],
      pass_rate: 95.5,
      coverage: 85.0,
      passed: true | false,
      failed_tests: ['test1', 'test2'],
      last_run_at: timestamp
    }
  }
}

5. 完成 (action-complete)

// 循环完成后
{
  status: 'running'  'completed',
  completed_at: timestamp,
  updated_at: timestamp,

  skill_state: {
    current_action: 'complete',
    last_action: 'action-complete'
  }
}

状态派生字段

以下字段可从状态计算得出,不需要存储:

// 开发完成度
const developProgress = state.develop.total_count > 0
  ? (state.develop.completed_count / state.develop.total_count) * 100
  : 0

// 是否有待开发任务
const hasPendingDevelop = state.develop.tasks.some(t => t.status === 'pending')

// 调试是否完成
const debugCompleted = state.debug.confirmed_hypothesis !== null

// 验证是否通过
const validationPassed = state.validate.passed && state.validate.test_results.length > 0

// 整体进度
const overallProgress = (
  (developProgress * 0.5) +
  (debugCompleted ? 25 : 0) +
  (validationPassed ? 25 : 0)
)

文件同步

统一位置 (Unified Location)

状态与文件的对应关系:

状态字段 同步文件 同步时机
整个 LoopState .loop/{loopId}.json 每次状态变更 (主文件)
skill_state.develop .loop/{loopId}.progress/develop.md 每次开发操作后
skill_state.debug .loop/{loopId}.progress/debug.md 每次调试操作后
skill_state.validate .loop/{loopId}.progress/validate.md 每次验证操作后
代码变更日志 .loop/{loopId}.progress/changes.log 每次文件修改 (NDJSON)
调试日志 .loop/{loopId}.progress/debug.log 每次调试日志 (NDJSON)

文件结构示例

.loop/
├── loop-v2-20260122-abc123.json         # 主状态文件 (API + Skill)
├── loop-v2-20260122-abc123.tasks.jsonl  # 任务列表 (API 管理)
└── loop-v2-20260122-abc123.progress/    # Skill 进度文件
    ├── develop.md                       # 开发进度
    ├── debug.md                         # 调试理解
    ├── validate.md                      # 验证报告
    ├── changes.log                      # 代码变更 (NDJSON)
    └── debug.log                        # 调试日志 (NDJSON)

状态恢复

如果主状态文件 .loop/{loopId}.json 损坏,可以从进度文件重建 skill_state:

function rebuildSkillStateFromProgress(loopId) {
  const progressDir = `.loop/${loopId}.progress`

  // 尝试从进度文件解析状态
  const skill_state = {
    develop: parseProgressFile(`${progressDir}/develop.md`),
    debug: parseProgressFile(`${progressDir}/debug.md`),
    validate: parseProgressFile(`${progressDir}/validate.md`)
  }

  return skill_state
}

// 解析进度 Markdown 文件
function parseProgressFile(filePath) {
  const content = Read(filePath)
  if (!content) return null

  // 从 Markdown 表格和结构中提取数据
  // ... implementation
}

恢复策略

  1. API 字段: 无法恢复 - 需要从 API 重新获取或用户手动输入
  2. skill_state 字段: 可以从 .progress/ 目录的 Markdown 文件解析
  3. 任务列表: 从 .loop/{loopId}.tasks.jsonl 恢复

状态验证

function validateState(state) {
  const errors = []

  // 必需字段
  if (!state.session_id) errors.push('Missing session_id')
  if (!state.task_description) errors.push('Missing task_description')

  // 状态一致性
  if (state.initialized && state.status === 'pending') {
    errors.push('Inconsistent: initialized but status is pending')
  }

  if (state.status === 'completed' && !state.validate.passed) {
    errors.push('Inconsistent: completed but validation not passed')
  }

  // 开发任务一致性
  const completedTasks = state.develop.tasks.filter(t => t.status === 'completed').length
  if (completedTasks !== state.develop.completed_count) {
    errors.push('Inconsistent: completed_count mismatch')
  }

  return { valid: errors.length === 0, errors }
}