Add end-to-end tests for Graph Explorer, History, Orchestrator, and Project features

- Implemented E2E tests for code relationship visualization in Graph Explorer.
- Added tests for archived session management in History, including search, filter, restore, and delete functionalities.
- Created tests for workflow orchestration in Orchestrator, covering node creation, connection, deletion, and workflow management.
- Developed tests for project statistics and timeline visualization in Project, including error handling and internationalization checks.
This commit is contained in:
catlog22
2026-02-06 23:45:33 +08:00
parent 62a1c9ab21
commit 5b48bcff64
72 changed files with 8645 additions and 3492 deletions

View File

@@ -0,0 +1,639 @@
---
name: workflow-execute
description: Coordinate agent execution for workflow tasks with automatic session discovery, parallel task processing, and status tracking. Triggers on "workflow execute".
allowed-tools: Task, AskUserQuestion, TodoWrite, Read, Write, Edit, Bash, Glob, Grep, Skill, mcp__ace-tool__search_context
---
# Workflow Execute
Orchestrates autonomous workflow execution through systematic task discovery, agent coordination, and progress tracking. **Executes entire workflow without user interruption** (except initial session selection if multiple active sessions exist), providing complete context to agents and ensuring proper flow control execution with comprehensive TodoWrite tracking.
**Resume Mode**: When called with `--resume-session` flag, skips discovery phase and directly enters TodoWrite generation and agent execution for the specified session.
## Architecture Overview
```
┌──────────────────────────────────────────────────────────────┐
│ Workflow Execute Orchestrator (SKILL.md) │
│ → Parse args → Session discovery → Strategy → Execute tasks │
└──────────┬───────────────────────────────────────────────────┘
┌──────┴──────┐
│ Normal Mode │──→ Phase 1 → Phase 2 → Phase 3 → Phase 4 → Phase 5
│ Resume Mode │──→ Phase 3 → Phase 4 → Phase 5
└─────────────┘
┌──────┴──────────────────────────────────────────────┐
│ Phase 1: Discovery (session selection) │
│ Phase 2: Validation (planning doc checks) │
│ Phase 3: TodoWrite Gen (progress tracking init) │
│ Phase 4: Strategy+Execute (lazy load + agent loop) │
│ Phase 5: Completion (status sync + user choice)│
└─────────────────────────────────────────────────────┘
```
## Key Design Principles
1. **Autonomous Execution**: Complete entire workflow without user interruption
2. **Lazy Loading**: Task JSONs read on-demand during execution, not upfront
3. **ONE AGENT = ONE TASK JSON**: Each agent instance executes exactly one task JSON file
4. **IMPL_PLAN-Driven Strategy**: Execution model derived from planning document
5. **Continuous Progress Tracking**: TodoWrite updates throughout entire workflow
## Auto Mode
When `--yes` or `-y`:
- **Session Selection**: Automatically selects the first (most recent) active session
- **Completion Choice**: Automatically completes session (runs `Skill(skill="workflow:session:complete", args="--yes")`)
When `--with-commit`:
- **Auto-Commit**: After each agent task completes, commit changes based on summary document
- **Commit Principle**: Minimal commits - only commit files modified by the completed task
- **Commit Message**: Generated from task summary with format: "feat/fix/refactor: {task-title} - {summary}"
## Usage
```
Skill(skill="workflow-execute", args="<optional flags>")
Skill(skill="workflow-execute", args="[FLAGS]")
# Flags
-y, --yes Skip all confirmations (auto mode)
--resume-session="<session-id>" Skip discovery, resume specified session
--with-commit Auto-commit after each task completion
# Examples
Skill(skill="workflow-execute") # Interactive mode
Skill(skill="workflow-execute", args="--yes") # Auto mode
Skill(skill="workflow-execute", args="--resume-session=\"WFS-auth\"") # Resume specific session
Skill(skill="workflow-execute", args="-y --resume-session=\"WFS-auth\"") # Auto + resume
Skill(skill="workflow-execute", args="--with-commit") # With auto-commit
Skill(skill="workflow-execute", args="-y --with-commit") # Auto + commit
Skill(skill="workflow-execute", args="-y --with-commit --resume-session=\"WFS-auth\"") # All flags
```
## Execution Flow
```
Normal Mode:
Phase 1: Discovery
├─ Count active sessions
└─ Decision:
├─ count=0 → ERROR: No active sessions
├─ count=1 → Auto-select session → Phase 2
└─ count>1 → AskUserQuestion (max 4 options) → Phase 2
Phase 2: Planning Document Validation
├─ Check IMPL_PLAN.md exists
├─ Check TODO_LIST.md exists
└─ Validate .task/ contains IMPL-*.json files
Phase 3: TodoWrite Generation
├─ Update session status to "active" (Step 0)
├─ Parse TODO_LIST.md for task statuses
├─ Generate TodoWrite for entire workflow
└─ Prepare session context paths
Phase 4: Execution Strategy & Task Execution
├─ Step 4A: Parse execution strategy from IMPL_PLAN.md
└─ Step 4B: Execute tasks with lazy loading
└─ Loop:
├─ Get next in_progress task from TodoWrite
├─ Lazy load task JSON
├─ Launch agent with task context
├─ Mark task completed (update IMPL-*.json status)
│ # Quick fix: Update task status for ccw dashboard
│ # TS=$(date -Iseconds) && jq --arg ts "$TS" '.status="completed" | .status_history=(.status_history // [])+[{"from":"in_progress","to":"completed","changed_at":$ts}]' IMPL-X.json > tmp.json && mv tmp.json IMPL-X.json
├─ [with-commit] Commit changes based on summary (minimal principle)
│ # Read summary from .summaries/IMPL-X-summary.md
│ # Extract changed files from summary's "Files Modified" section
│ # Generate commit message: "feat/fix/refactor: {task-title} - {summary}"
│ # git add <changed-files> && git commit -m "<commit-message>"
└─ Advance to next task
Phase 5: Completion
├─ Update task statuses in JSON files
├─ Generate summaries
└─ AskUserQuestion: Choose next step
├─ "Enter Review" → Skill(skill="workflow:review")
└─ "Complete Session" → Skill(skill="workflow:session:complete")
Resume Mode (--resume-session):
├─ Skip Phase 1 & Phase 2
└─ Entry Point: Phase 3 (TodoWrite Generation)
├─ Update session status to "active" (if not already)
└─ Continue: Phase 4 → Phase 5
```
## Core Rules
**Complete entire workflow autonomously without user interruption, using TodoWrite for comprehensive progress tracking.**
**Execute all discovered pending tasks until workflow completion or blocking dependency.**
**User-choice completion: When all tasks finished, ask user to choose review or complete.**
**ONE AGENT = ONE TASK JSON: Each agent instance executes exactly one task JSON file - never batch multiple tasks into single agent execution.**
## Core Responsibilities
- **Session Discovery**: Identify and select active workflow sessions
- **Execution Strategy Parsing**: Extract execution model from IMPL_PLAN.md
- **TodoWrite Progress Tracking**: Maintain real-time execution status throughout entire workflow
- **Agent Orchestration**: Coordinate specialized agents with complete context
- **Status Synchronization**: Update task JSON files and workflow state
- **Autonomous Completion**: Continue execution until all tasks complete or reach blocking state
- **Session User-Choice Completion**: Ask user to choose review or complete when all tasks finished
## Execution Philosophy
- **Progress tracking**: Continuous TodoWrite updates throughout entire workflow execution
- **Autonomous completion**: Execute all tasks without user interruption until workflow complete
## Performance Optimization Strategy
**Lazy Loading**: Task JSONs read **on-demand** during execution, not upfront. TODO_LIST.md + IMPL_PLAN.md provide metadata for planning.
**Loading Strategy**:
- **TODO_LIST.md**: Read in Phase 3 (task metadata, status, dependencies for TodoWrite generation)
- **IMPL_PLAN.md**: Check existence in Phase 2 (normal mode), parse execution strategy in Phase 4A
- **Task JSONs**: Lazy loading - read only when task is about to execute (Phase 4B)
## Execution Lifecycle
### Phase 1: Discovery
**Applies to**: Normal mode only (skipped in resume mode)
**Purpose**: Find and select active workflow session with user confirmation when multiple sessions exist
**Process**:
#### Step 1.1: Count Active Sessions
```bash
bash(find .workflow/active/ -name "WFS-*" -type d 2>/dev/null | wc -l)
```
#### Step 1.2: Handle Session Selection
**Case A: No Sessions** (count = 0)
```
ERROR: No active workflow sessions found
Run Skill(skill="workflow:plan", args="\"task description\"") to create a session
```
**Case B: Single Session** (count = 1)
```bash
bash(find .workflow/active/ -name "WFS-*" -type d 2>/dev/null | head -1 | xargs basename)
```
Auto-select and continue to Phase 2.
**Case C: Multiple Sessions** (count > 1)
List sessions with metadata and prompt user selection:
```bash
bash(for dir in .workflow/active/WFS-*/; do [ -d "$dir" ] || continue; session=$(basename "$dir"); project=$(jq -r '.project // "Unknown"' "${dir}workflow-session.json" 2>/dev/null || echo "Unknown"); total=$(grep -c '^\- \[' "${dir}TODO_LIST.md" 2>/dev/null || echo 0); completed=$(grep -c '^\- \[x\]' "${dir}TODO_LIST.md" 2>/dev/null || echo 0); if [ "$total" -gt 0 ]; then progress=$((completed * 100 / total)); else progress=0; fi; echo "$session | $project | $completed/$total tasks ($progress%)"; done)
```
**Parse --yes flag**:
```javascript
const autoYes = $ARGUMENTS.includes('--yes') || $ARGUMENTS.includes('-y')
```
**Conditional Selection**:
```javascript
if (autoYes) {
// Auto mode: Select first session (most recent)
const firstSession = sessions[0]
console.log(`[--yes] Auto-selecting session: ${firstSession.id}`)
selectedSessionId = firstSession.id
// Continue to Phase 2
} else {
// Interactive mode: Use AskUserQuestion to present formatted options (max 4 options shown)
// If more than 4 sessions, show most recent 4 with "Other" option for manual input
const sessions = getActiveSessions() // sorted by last modified
const displaySessions = sessions.slice(0, 4)
AskUserQuestion({
questions: [{
question: "Multiple active sessions detected. Select one:",
header: "Session",
multiSelect: false,
options: displaySessions.map(s => ({
label: s.id,
description: `${s.project} | ${s.progress}`
}))
// Note: User can select "Other" to manually enter session ID
}]
})
}
```
**Input Validation**:
- If user selects from options: Use selected session ID
- If user selects "Other" and provides input: Validate session exists
- If validation fails: Show error and re-prompt or suggest available sessions
Parse user input (supports: number "1", full ID "WFS-auth-system", or partial "auth"), validate selection, and continue to Phase 2.
#### Step 1.3: Load Session Metadata
```bash
bash(cat .workflow/active/${sessionId}/workflow-session.json)
```
**Output**: Store session metadata in memory
**DO NOT read task JSONs yet** - defer until execution phase (lazy loading)
**Resume Mode**: This entire phase is skipped when `--resume-session="session-id"` flag is provided.
### Phase 2: Planning Document Validation
**Applies to**: Normal mode only (skipped in resume mode)
**Purpose**: Validate planning artifacts exist before execution
**Process**:
1. **Check IMPL_PLAN.md**: Verify file exists (defer detailed parsing to Phase 4A)
2. **Check TODO_LIST.md**: Verify file exists (defer reading to Phase 3)
3. **Validate Task Directory**: Ensure `.task/` contains at least one IMPL-*.json file
**Key Optimization**: Only existence checks here. Actual file reading happens in later phases.
**Resume Mode**: This phase is skipped when `--resume-session` flag is provided. Resume mode entry point is Phase 3.
### Phase 3: TodoWrite Generation
**Applies to**: Both normal and resume modes (resume mode entry point)
**Step 0: Update Session Status to Active**
Before generating TodoWrite, update session status from "planning" to "active":
```bash
# Update session status (idempotent - safe to run if already active)
jq '.status = "active" | .execution_started_at = (.execution_started_at // now | todate)' \
.workflow/active/${sessionId}/workflow-session.json > tmp.json && \
mv tmp.json .workflow/active/${sessionId}/workflow-session.json
```
This ensures the dashboard shows the session as "ACTIVE" during execution.
**Process**:
1. **Create TodoWrite List**: Generate task list from TODO_LIST.md (not from task JSONs)
- Parse TODO_LIST.md to extract all tasks with current statuses
- Identify first pending task with met dependencies
- Generate comprehensive TodoWrite covering entire workflow
2. **Prepare Session Context**: Inject workflow paths for agent use (using provided session-id)
3. **Validate Prerequisites**: Ensure IMPL_PLAN.md and TODO_LIST.md exist and are valid
**Resume Mode Behavior**:
- Load existing TODO_LIST.md directly from `.workflow/active/{session-id}/`
- Extract current progress from TODO_LIST.md
- Generate TodoWrite from TODO_LIST.md state
- Proceed immediately to agent execution (Phase 4)
### Phase 4: Execution Strategy Selection & Task Execution
**Applies to**: Both normal and resume modes
**Step 4A: Parse Execution Strategy from IMPL_PLAN.md**
Read IMPL_PLAN.md Section 4 to extract:
- **Execution Model**: Sequential | Parallel | Phased | TDD Cycles
- **Parallelization Opportunities**: Which tasks can run in parallel
- **Serialization Requirements**: Which tasks must run sequentially
- **Critical Path**: Priority execution order
If IMPL_PLAN.md lacks execution strategy, use intelligent fallback (analyze task structure).
**Step 4B: Execute Tasks with Lazy Loading**
**Key Optimization**: Read task JSON **only when needed** for execution
**Execution Loop Pattern**:
```
while (TODO_LIST.md has pending tasks) {
next_task_id = getTodoWriteInProgressTask()
task_json = Read(.workflow/active/{session}/.task/{next_task_id}.json) // Lazy load
executeTaskWithAgent(task_json)
updateTodoListMarkCompleted(next_task_id)
advanceTodoWriteToNextTask()
}
```
**Execution Process per Task**:
1. **Identify Next Task**: From TodoWrite, get the next `in_progress` task ID
2. **Load Task JSON on Demand**: Read `.task/{task-id}.json` for current task ONLY
3. **Validate Task Structure**: Ensure all 5 required fields exist (id, title, status, meta, context, flow_control)
4. **Launch Agent**: Invoke specialized agent with complete context including flow control steps
5. **Monitor Progress**: Track agent execution and handle errors without user interruption
6. **Collect Results**: Gather implementation results and outputs
7. **[with-commit] Auto-Commit**: If `--with-commit` flag enabled, commit changes based on summary
- Read summary from `.summaries/{task-id}-summary.md`
- Extract changed files from summary's "Files Modified" section
- Determine commit type from `meta.type` (feature→feat, bugfix→fix, refactor→refactor)
- Generate commit message: "{type}: {task-title} - {summary-first-line}"
- Commit only modified files (minimal principle): `git add <files> && git commit -m "<message>"`
8. **Continue Workflow**: Identify next pending task from TODO_LIST.md and repeat
**Note**: TODO_LIST.md updates are handled by agents (e.g., code-developer.md), not by the orchestrator.
### Phase 5: Completion
**Applies to**: Both normal and resume modes
**Process**:
1. **Update Task Status**: Mark completed tasks in JSON files
2. **Generate Summary**: Create task summary in `.summaries/`
3. **Update TodoWrite**: Mark current task complete, advance to next
4. **Synchronize State**: Update session state and workflow status
5. **Check Workflow Complete**: Verify all tasks are completed
6. **User Choice**: When all tasks finished, ask user to choose next step:
```javascript
// Parse --yes flag
const autoYes = $ARGUMENTS.includes('--yes') || $ARGUMENTS.includes('-y')
if (autoYes) {
// Auto mode: Complete session automatically
console.log(`[--yes] Auto-selecting: Complete Session`)
Skill(skill="workflow:session:complete", args="--yes")
} else {
// Interactive mode: Ask user
AskUserQuestion({
questions: [{
question: "All tasks completed. What would you like to do next?",
header: "Next Step",
multiSelect: false,
options: [
{
label: "Enter Review",
description: "Run specialized review (security/architecture/quality/action-items)"
},
{
label: "Complete Session",
description: "Archive session and update manifest"
}
]
}]
})
}
```
**Based on user selection**:
- **"Enter Review"**: Execute `Skill(skill="workflow:review")`
- **"Complete Session"**: Execute `Skill(skill="workflow:session:complete")`
### Post-Completion Expansion
完成后询问用户是否扩展为issue(test/enhance/refactor/doc),选中项调用 `Skill(skill="issue:new", args="\"{summary} - {dimension}\"")`
## Execution Strategy (IMPL_PLAN-Driven)
### Strategy Priority
**IMPL_PLAN-Driven Execution (Recommended)**:
1. **Read IMPL_PLAN.md execution strategy** (Section 4: Implementation Strategy)
2. **Follow explicit guidance**:
- Execution Model (Sequential/Parallel/Phased/TDD)
- Parallelization Opportunities (which tasks can run in parallel)
- Serialization Requirements (which tasks must run sequentially)
- Critical Path (priority execution order)
3. **Use TODO_LIST.md for status tracking** only
4. **IMPL_PLAN decides "HOW"**, execute implements it
**Intelligent Fallback (When IMPL_PLAN lacks execution details)**:
1. **Analyze task structure**:
- Check `meta.execution_group` in task JSONs
- Analyze `depends_on` relationships
- Understand task complexity and risk
2. **Apply smart defaults**:
- No dependencies + same execution_group → Parallel
- Has dependencies → Sequential (wait for deps)
- Critical/high-risk tasks → Sequential
3. **Conservative approach**: When uncertain, prefer sequential execution
### Execution Models
#### 1. Sequential Execution
**When**: IMPL_PLAN specifies "Sequential" OR no clear parallelization guidance
**Pattern**: Execute tasks one by one in TODO_LIST order
**TodoWrite**: ONE task marked as `in_progress` at a time
#### 2. Parallel Execution
**When**: IMPL_PLAN specifies "Parallel" with clear parallelization opportunities
**Pattern**: Execute independent task groups concurrently by launching multiple agent instances
**TodoWrite**: MULTIPLE tasks (in same batch) marked as `in_progress` simultaneously
**Agent Instantiation**: Launch one agent instance per task (respects ONE AGENT = ONE TASK JSON rule)
#### 3. Phased Execution
**When**: IMPL_PLAN specifies "Phased" with phase breakdown
**Pattern**: Execute tasks in phases, respect phase boundaries
**TodoWrite**: Within each phase, follow Sequential or Parallel rules
#### 4. Intelligent Fallback
**When**: IMPL_PLAN lacks execution strategy details
**Pattern**: Analyze task structure and apply smart defaults
**TodoWrite**: Follow Sequential or Parallel rules based on analysis
### Task Status Logic
```
pending + dependencies_met → executable
completed → skip
blocked → skip until dependencies clear
```
## TodoWrite Coordination
### TodoWrite Rules (Unified)
**Rule 1: Initial Creation**
- **Normal Mode**: Generate TodoWrite from discovered pending tasks for entire workflow
- **Resume Mode**: Generate from existing session state and current progress
**Rule 2: In-Progress Task Count (Execution-Model-Dependent)**
- **Sequential execution**: Mark ONLY ONE task as `in_progress` at a time
- **Parallel batch execution**: Mark ALL tasks in current batch as `in_progress` simultaneously
- **Execution group indicator**: Show `[execution_group: group-id]` for parallel tasks
**Rule 3: Status Updates**
- **Immediate Updates**: Update status after each task/batch completion without user interruption
- **Status Synchronization**: Sync with JSON task files after updates
- **Continuous Tracking**: Maintain TodoWrite throughout entire workflow execution until completion
**Rule 4: Workflow Completion Check**
- When all tasks marked `completed`, prompt user to choose review or complete session
### TodoWrite Tool Usage
**Example 1: Sequential Execution**
```javascript
TodoWrite({
todos: [
{
content: "Execute IMPL-1.1: Design auth schema [code-developer] [FLOW_CONTROL]",
status: "in_progress", // ONE task in progress
activeForm: "Executing IMPL-1.1: Design auth schema"
},
{
content: "Execute IMPL-1.2: Implement auth logic [code-developer] [FLOW_CONTROL]",
status: "pending",
activeForm: "Executing IMPL-1.2: Implement auth logic"
}
]
});
```
**Example 2: Parallel Batch Execution**
```javascript
TodoWrite({
todos: [
{
content: "Execute IMPL-1.1: Build Auth API [code-developer] [execution_group: parallel-auth-api]",
status: "in_progress", // Batch task 1
activeForm: "Executing IMPL-1.1: Build Auth API"
},
{
content: "Execute IMPL-1.2: Build User UI [code-developer] [execution_group: parallel-ui-comp]",
status: "in_progress", // Batch task 2 (running concurrently)
activeForm: "Executing IMPL-1.2: Build User UI"
},
{
content: "Execute IMPL-1.3: Setup Database [code-developer] [execution_group: parallel-db-schema]",
status: "in_progress", // Batch task 3 (running concurrently)
activeForm: "Executing IMPL-1.3: Setup Database"
},
{
content: "Execute IMPL-2.1: Integration Tests [test-fix-agent] [depends_on: IMPL-1.1, IMPL-1.2, IMPL-1.3]",
status: "pending", // Next batch (waits for current batch completion)
activeForm: "Executing IMPL-2.1: Integration Tests"
}
]
});
```
## Agent Execution Pattern
### Flow Control Execution
**[FLOW_CONTROL]** marker indicates task JSON contains `flow_control.pre_analysis` steps for context preparation.
**Note**: Orchestrator does NOT execute flow control steps - Agent interprets and executes them autonomously.
### Agent Prompt Template
**Path-Based Invocation**: Pass paths and trigger markers, let agent parse task JSON autonomously.
```bash
Task(subagent_type="{meta.agent}",
run_in_background=false,
prompt="Implement task {task.id}: {task.title}
[FLOW_CONTROL]
**Input**:
- Task JSON: {session.task_json_path}
- Context Package: {session.context_package_path}
**Output Location**:
- Workflow: {session.workflow_dir}
- TODO List: {session.todo_list_path}
- Summaries: {session.summaries_dir}
**Execution**: Read task JSON → Execute pre_analysis → Check execution_config.method → (CLI: handoff to CLI tool | Agent: direct implementation) → Update TODO_LIST.md → Generate summary",
description="Implement: {task.id}")
```
**Key Markers**:
- `Implement` keyword: Triggers tech stack detection and guidelines loading
- `[FLOW_CONTROL]`: Triggers flow_control.pre_analysis execution
**Why Path-Based**: Agent (code-developer.md) autonomously:
- Reads and parses task JSON (requirements, acceptance, flow_control, execution_config)
- Executes pre_analysis steps (Phase 1: context gathering)
- Checks execution_config.method (Phase 2: determine mode)
- CLI mode: Builds handoff prompt and executes via ccw cli with resume strategy
- Agent mode: Directly implements using modification_points and logic_flow
- Generates structured summary with integration points
Embedding task content in prompt creates duplication and conflicts with agent's parsing logic.
### Agent Assignment Rules
```
meta.agent specified → Use specified agent
meta.agent missing → Infer from meta.type:
- "feature" → @code-developer
- "test-gen" → @code-developer
- "test-fix" → @test-fix-agent
- "review" → @universal-executor
- "docs" → @doc-generator
```
## Data Flow
```
Phase 1 (Discovery) → selectedSessionId, sessionMetadata
Phase 2 (Validation) → validated paths (IMPL_PLAN.md, TODO_LIST.md, .task/)
Phase 3 (TodoWrite Gen) → todoWriteList, sessionContextPaths
Phase 4 (Execute) → per-task: taskJson (lazy), agentResult, summaryDoc
Phase 5 (Completion) → updatedStatuses, userChoice (review|complete)
```
## Workflow File Structure Reference
```
.workflow/active/WFS-[topic-slug]/
├── workflow-session.json # Session state and metadata
├── IMPL_PLAN.md # Planning document and requirements
├── TODO_LIST.md # Progress tracking (updated by agents)
├── .task/ # Task definitions (JSON only)
│ ├── IMPL-1.json # Main task definitions
│ └── IMPL-1.1.json # Subtask definitions
├── .summaries/ # Task completion summaries
│ ├── IMPL-1-summary.md # Task completion details
│ └── IMPL-1.1-summary.md # Subtask completion details
└── .process/ # Planning artifacts
├── context-package.json # Smart context package
└── ANALYSIS_RESULTS.md # Planning analysis results
```
## Auto-Commit Mode (--with-commit)
**Behavior**: After each agent task completes, automatically commit changes based on summary document.
**Minimal Principle**: Only commit files modified by the completed task.
**Commit Message Format**: `{type}: {task-title} - {summary}`
**Type Mapping** (from `meta.type`):
- `feature``feat` | `bugfix``fix` | `refactor``refactor`
- `test-gen``test` | `docs``docs` | `review``chore`
**Implementation**:
```bash
# 1. Read summary from .summaries/{task-id}-summary.md
# 2. Extract files from "Files Modified" section
# 3. Commit: git add <files> && git commit -m "{type}: {title} - {summary}"
```
**Error Handling**: Skip commit on no changes/missing summary, log errors, continue workflow.
## Error Handling & Recovery
### Common Errors & Recovery
| Error Type | Cause | Recovery Strategy | Max Attempts |
|-----------|-------|------------------|--------------|
| **Discovery Errors** |
| No active session | No sessions in `.workflow/active/` | Create or resume session: `Skill(skill="workflow:plan", args="\"project\"")` | N/A |
| Multiple sessions | Multiple sessions in `.workflow/active/` | Prompt user selection | N/A |
| Corrupted session | Invalid JSON files | Recreate session structure or validate files | N/A |
| **Execution Errors** |
| Agent failure | Agent crash/timeout | Retry with simplified context | 2 |
| Flow control error | Command failure | Skip optional, fail critical | 1 per step |
| Context loading error | Missing dependencies | Reload from JSON, use defaults | 3 |
| JSON file corruption | File system issues | Restore from backup/recreate | 1 |
### Error Prevention
- **Pre-flight Checks**: Validate session integrity before execution
- **Backup Strategy**: Create task snapshots before major operations
- **Atomic Updates**: Update JSON files atomically to prevent corruption
- **Dependency Validation**: Check all depends_on references exist
- **Context Verification**: Ensure all required context is available
## Flag Parsing
```javascript
const autoYes = $ARGUMENTS.includes('--yes') || $ARGUMENTS.includes('-y')
const withCommit = $ARGUMENTS.includes('--with-commit')
```

View File

@@ -0,0 +1,301 @@
# CCW Loop-B: Hybrid Orchestrator Pattern
Iterative development workflow using coordinator + specialized workers architecture.
## Overview
CCW Loop-B implements a flexible orchestration pattern:
- **Coordinator**: Main agent managing state, user interaction, worker scheduling
- **Workers**: Specialized agents (init, develop, debug, validate, complete)
- **Modes**: Interactive / Auto / Parallel execution
## Architecture
```
Coordinator (Main Agent)
|
+-- Spawns Workers
| - ccw-loop-b-init.md
| - ccw-loop-b-develop.md
| - ccw-loop-b-debug.md
| - ccw-loop-b-validate.md
| - ccw-loop-b-complete.md
|
+-- Batch Wait (parallel mode)
+-- Sequential Wait (auto/interactive)
+-- State Management
+-- User Interaction
```
## Subagent API
Core APIs for worker orchestration:
| API | 作用 |
|-----|------|
| `spawn_agent({ message })` | 创建 worker返回 `agent_id` |
| `wait({ ids, timeout_ms })` | 等待结果(唯一取结果入口) |
| `send_input({ id, message })` | 继续交互 |
| `close_agent({ id })` | 关闭回收 |
**可用模式**: 单 agent 深度交互 / 多 agent 并行 / 混合模式
## Execution Modes
### Interactive Mode (default)
Coordinator displays menu, user selects action, spawns corresponding worker.
```bash
/ccw-loop-b TASK="Implement feature X"
```
**Flow**:
1. Init: Parse task, create breakdown
2. Menu: Show options to user
3. User selects action (develop/debug/validate)
4. Spawn worker for selected action
5. Wait for result
6. Display result, back to menu
7. Repeat until complete
### Auto Mode
Automated sequential execution following predefined workflow.
```bash
/ccw-loop-b --mode=auto TASK="Fix bug Y"
```
**Flow**:
1. Init → 2. Develop → 3. Validate → 4. Complete
If issues found: loop back to Debug → Develop → Validate
### Parallel Mode
Spawn multiple workers simultaneously, batch wait for results.
```bash
/ccw-loop-b --mode=parallel TASK="Analyze module Z"
```
**Flow**:
1. Init: Create analysis plan
2. Spawn workers in parallel: [develop, debug, validate]
3. Batch wait: `wait({ ids: [w1, w2, w3] })`
4. Merge results
5. Coordinator decides next action
6. Complete
## Session Structure
```
.workflow/.loop/
+-- {loopId}.json # Master state
+-- {loopId}.workers/ # Worker outputs
| +-- init.output.json
| +-- develop.output.json
| +-- debug.output.json
| +-- validate.output.json
| +-- complete.output.json
+-- {loopId}.progress/ # Human-readable logs
+-- develop.md
+-- debug.md
+-- validate.md
+-- summary.md
```
## Worker Responsibilities
| Worker | Role | Specialization |
|--------|------|----------------|
| **init** | Session initialization | Task parsing, breakdown, planning |
| **develop** | Code implementation | File operations, pattern matching, incremental development |
| **debug** | Problem diagnosis | Root cause analysis, hypothesis testing, fix recommendations |
| **validate** | Testing & verification | Test execution, coverage analysis, quality gates |
| **complete** | Session finalization | Summary generation, commit preparation, cleanup |
## Usage Examples
### Example 1: Simple Feature Implementation
```bash
/ccw-loop-b TASK="Add user logout function"
```
**Auto flow**:
- Init: Parse requirements
- Develop: Implement logout in `src/auth.ts`
- Validate: Run tests
- Complete: Generate commit message
### Example 2: Bug Investigation
```bash
/ccw-loop-b TASK="Fix memory leak in WebSocket handler"
```
**Interactive flow**:
1. Init: Parse issue
2. User selects "debug" → Spawn debug worker
3. Debug: Root cause analysis → recommends fix
4. User selects "develop" → Apply fix
5. User selects "validate" → Verify fix works
6. User selects "complete" → Generate summary
### Example 3: Comprehensive Analysis
```bash
/ccw-loop-b --mode=parallel TASK="Analyze payment module for improvements"
```
**Parallel flow**:
- Spawn [develop, debug, validate] workers simultaneously
- Develop: Analyze code quality and patterns
- Debug: Identify potential issues
- Validate: Check test coverage
- Wait for all three to complete
- Merge findings into comprehensive report
### Example 4: Resume Existing Loop
```bash
/ccw-loop-b --loop-id=loop-b-20260122-abc123
```
Continues from previous state, respects status (running/paused).
## Key Features
### 1. Worker Specialization
Each worker focuses on one domain:
- **No overlap**: Clear boundaries between workers
- **Reusable**: Same worker for different tasks
- **Composable**: Combine workers for complex workflows
### 2. Flexible Coordination
Coordinator adapts to mode:
- **Interactive**: Menu-driven, user controls flow
- **Auto**: Predetermined sequence
- **Parallel**: Concurrent execution with batch wait
### 3. State Management
Unified state at `.workflow/.loop/{loopId}.json`:
- **API compatible**: Works with CCW API
- **Extension fields**: Skill-specific data in `skill_state`
- **Worker outputs**: Structured JSON for each action
### 4. Progress Tracking
Human-readable logs:
- **Per-worker progress**: `{action}.md` files
- **Summary**: Consolidated achievements
- **Commit-ready**: Formatted commit messages
## Best Practices
1. **Start with Init**: Always initialize before execution
2. **Use appropriate mode**:
- Interactive: Complex tasks needing user decisions
- Auto: Well-defined workflows
- Parallel: Independent analysis tasks
3. **Clean up workers**: `close_agent()` after each worker completes
4. **Batch wait wisely**: Use in parallel mode for efficiency
5. **Track progress**: Document in progress files
6. **Validate often**: After each develop phase
## Implementation Patterns
### Pattern 1: Single Worker Deep Interaction
```javascript
const workerId = spawn_agent({ message: workerPrompt })
const result1 = wait({ ids: [workerId] })
// Continue with same worker
send_input({ id: workerId, message: "Continue with next task" })
const result2 = wait({ ids: [workerId] })
close_agent({ id: workerId })
```
### Pattern 2: Multi-Worker Parallel
```javascript
const workers = {
develop: spawn_agent({ message: developPrompt }),
debug: spawn_agent({ message: debugPrompt }),
validate: spawn_agent({ message: validatePrompt })
}
// Batch wait
const results = wait({ ids: Object.values(workers), timeout_ms: 900000 })
// Process all results
Object.values(workers).forEach(id => close_agent({ id }))
```
### Pattern 3: Sequential Worker Chain
```javascript
const actions = ['init', 'develop', 'validate', 'complete']
for (const action of actions) {
const workerId = spawn_agent({ message: buildPrompt(action) })
const result = wait({ ids: [workerId] })
updateState(action, result)
close_agent({ id: workerId })
}
```
## Error Handling
| Error | Recovery |
|-------|----------|
| Worker timeout | `send_input` request convergence |
| Worker fails | Log error, coordinator decides retry strategy |
| Partial results | Use completed workers, mark incomplete |
| State corruption | Rebuild from progress files |
## File Structure
```
.codex/skills/ccw-loop-b/
+-- SKILL.md # Entry point
+-- README.md # This file
+-- phases/
| +-- state-schema.md # State structure definition
+-- specs/
+-- action-catalog.md # Action reference
.codex/agents/
+-- ccw-loop-b-init.md # Worker: Init
+-- ccw-loop-b-develop.md # Worker: Develop
+-- ccw-loop-b-debug.md # Worker: Debug
+-- ccw-loop-b-validate.md # Worker: Validate
+-- ccw-loop-b-complete.md # Worker: Complete
```
## Comparison: ccw-loop vs ccw-loop-b
| Aspect | ccw-loop | ccw-loop-b |
|--------|----------|------------|
| Pattern | Single agent, multi-phase | Coordinator + workers |
| Worker model | Single agent handles all | Specialized workers per action |
| Parallelization | Sequential only | Supports parallel mode |
| Flexibility | Fixed sequence | Mode-based (interactive/auto/parallel) |
| Best for | Simple linear workflows | Complex tasks needing specialization |
## Contributing
To add new workers:
1. Create worker role file in `.codex/agents/`
2. Define clear responsibilities
3. Update `action-catalog.md`
4. Add worker to coordinator spawn logic
5. Test integration with existing workers

View File

@@ -0,0 +1,323 @@
---
name: CCW Loop-B
description: Hybrid orchestrator pattern for iterative development. Coordinator + specialized workers with batch wait support. Triggers on "ccw-loop-b".
argument-hint: TASK="<task description>" [--loop-id=<id>] [--mode=<interactive|auto|parallel>]
---
# CCW Loop-B - Hybrid Orchestrator Pattern
协调器 + 专用 worker 的迭代开发工作流。支持单 agent 深度交互、多 agent 并行、混合模式灵活切换。
## Arguments
| Arg | Required | Description |
|-----|----------|-------------|
| TASK | No | Task description (for new loop) |
| --loop-id | No | Existing loop ID to continue |
| --mode | No | `interactive` (default) / `auto` / `parallel` |
## Architecture
```
+------------------------------------------------------------+
| Main Coordinator |
| 职责: 状态管理 + worker 调度 + 结果汇聚 + 用户交互 |
+------------------------------------------------------------+
|
+--------------------+--------------------+
| | |
v v v
+----------------+ +----------------+ +----------------+
| Worker-Develop | | Worker-Debug | | Worker-Validate|
| 专注: 代码实现 | | 专注: 问题诊断 | | 专注: 测试验证 |
+----------------+ +----------------+ +----------------+
```
## Execution Modes
### Mode: Interactive (default)
协调器展示菜单,用户选择 actionspawn 对应 worker 执行。
```
Coordinator -> Show menu -> User selects -> spawn worker -> wait -> Display result -> Loop
```
### Mode: Auto
自动按预设顺序执行worker 完成后自动切换到下一阶段。
```
Init -> Develop -> [if issues] Debug -> Validate -> [if fail] Loop back -> Complete
```
### Mode: Parallel
并行 spawn 多个 worker 分析不同维度batch wait 汇聚结果。
```
Coordinator -> spawn [develop, debug, validate] in parallel -> wait({ ids: all }) -> Merge -> Decide
```
## Session Structure
```
.workflow/.loop/
+-- {loopId}.json # Master state
+-- {loopId}.workers/ # Worker outputs
| +-- develop.output.json
| +-- debug.output.json
| +-- validate.output.json
+-- {loopId}.progress/ # Human-readable progress
+-- develop.md
+-- debug.md
+-- validate.md
+-- summary.md
```
## Subagent API
| API | 作用 |
|-----|------|
| `spawn_agent({ message })` | 创建 agent返回 `agent_id` |
| `wait({ ids, timeout_ms })` | 等待结果(唯一取结果入口) |
| `send_input({ id, message })` | 继续交互 |
| `close_agent({ id })` | 关闭回收 |
## Implementation
### Coordinator Logic
```javascript
// ==================== HYBRID ORCHESTRATOR ====================
// 1. Initialize
const loopId = args['--loop-id'] || generateLoopId()
const mode = args['--mode'] || 'interactive'
let state = readOrCreateState(loopId, taskDescription)
// 2. Mode selection
switch (mode) {
case 'interactive':
await runInteractiveMode(loopId, state)
break
case 'auto':
await runAutoMode(loopId, state)
break
case 'parallel':
await runParallelMode(loopId, state)
break
}
```
### Interactive Mode (单 agent 交互或按需 spawn worker)
```javascript
async function runInteractiveMode(loopId, state) {
while (state.status === 'running') {
// Show menu, get user choice
const action = await showMenuAndGetChoice(state)
if (action === 'exit') break
// Spawn specialized worker for the action
const workerId = spawn_agent({
message: buildWorkerPrompt(action, loopId, state)
})
// Wait for worker completion
const result = wait({ ids: [workerId], timeout_ms: 600000 })
const output = result.status[workerId].completed
// Update state and display result
state = updateState(loopId, action, output)
displayResult(output)
// Cleanup worker
close_agent({ id: workerId })
}
}
```
### Auto Mode (顺序执行 worker 链)
```javascript
async function runAutoMode(loopId, state) {
const actionSequence = ['init', 'develop', 'debug', 'validate', 'complete']
let currentIndex = state.skill_state?.action_index || 0
while (currentIndex < actionSequence.length && state.status === 'running') {
const action = actionSequence[currentIndex]
// Spawn worker
const workerId = spawn_agent({
message: buildWorkerPrompt(action, loopId, state)
})
const result = wait({ ids: [workerId], timeout_ms: 600000 })
const output = result.status[workerId].completed
// Parse worker result to determine next step
const workerResult = parseWorkerResult(output)
// Update state
state = updateState(loopId, action, output)
close_agent({ id: workerId })
// Determine next action
if (workerResult.needs_loop_back) {
// Loop back to develop or debug
currentIndex = actionSequence.indexOf(workerResult.loop_back_to)
} else if (workerResult.status === 'failed') {
// Stop on failure
break
} else {
currentIndex++
}
}
}
```
### Parallel Mode (批量 spawn + wait)
```javascript
async function runParallelMode(loopId, state) {
// Spawn multiple workers in parallel
const workers = {
develop: spawn_agent({ message: buildWorkerPrompt('develop', loopId, state) }),
debug: spawn_agent({ message: buildWorkerPrompt('debug', loopId, state) }),
validate: spawn_agent({ message: buildWorkerPrompt('validate', loopId, state) })
}
// Batch wait for all workers
const results = wait({
ids: Object.values(workers),
timeout_ms: 900000 // 15 minutes for all
})
// Collect outputs
const outputs = {}
for (const [role, workerId] of Object.entries(workers)) {
outputs[role] = results.status[workerId].completed
close_agent({ id: workerId })
}
// Merge and analyze results
const mergedAnalysis = mergeWorkerOutputs(outputs)
// Update state with merged results
updateState(loopId, 'parallel-analysis', mergedAnalysis)
// Coordinator decides next action based on merged results
const decision = decideNextAction(mergedAnalysis)
return decision
}
```
### Worker Prompt Builder
```javascript
function buildWorkerPrompt(action, loopId, state) {
const workerRoles = {
develop: '~/.codex/agents/ccw-loop-b-develop.md',
debug: '~/.codex/agents/ccw-loop-b-debug.md',
validate: '~/.codex/agents/ccw-loop-b-validate.md',
init: '~/.codex/agents/ccw-loop-b-init.md',
complete: '~/.codex/agents/ccw-loop-b-complete.md'
}
return `
## TASK ASSIGNMENT
### MANDATORY FIRST STEPS (Agent Execute)
1. **Read role definition**: ${workerRoles[action]} (MUST read first)
2. Read: .workflow/project-tech.json
3. Read: .workflow/project-guidelines.json
---
## LOOP CONTEXT
- **Loop ID**: ${loopId}
- **Action**: ${action}
- **State File**: .workflow/.loop/${loopId}.json
- **Output File**: .workflow/.loop/${loopId}.workers/${action}.output.json
- **Progress File**: .workflow/.loop/${loopId}.progress/${action}.md
## 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>
DETAILED_OUTPUT:
<structured output specific to action type>
\`\`\`
Execute the ${action} action now.
`
}
```
## Worker Roles
| Worker | Role File | 专注领域 |
|--------|-----------|----------|
| init | ccw-loop-b-init.md | 会话初始化、任务解析 |
| develop | ccw-loop-b-develop.md | 代码实现、重构 |
| debug | ccw-loop-b-debug.md | 问题诊断、假设验证 |
| validate | ccw-loop-b-validate.md | 测试执行、覆盖率 |
| complete | ccw-loop-b-complete.md | 总结收尾 |
## State Schema
See [phases/state-schema.md](phases/state-schema.md)
## Usage
```bash
# Interactive mode (default)
/ccw-loop-b TASK="Implement user authentication"
# Auto mode
/ccw-loop-b --mode=auto TASK="Fix login bug"
# Parallel analysis mode
/ccw-loop-b --mode=parallel TASK="Analyze and improve payment module"
# Resume existing loop
/ccw-loop-b --loop-id=loop-b-20260122-abc123
```
## Error Handling
| Situation | Action |
|-----------|--------|
| Worker timeout | send_input 请求收敛 |
| Worker failed | Log error, 协调器决策是否重试 |
| Batch wait partial timeout | 使用已完成结果继续 |
| State corrupted | 从 progress 文件重建 |
## Best Practices
1. **协调器保持轻量**: 只做调度和状态管理,具体工作交给 worker
2. **Worker 职责单一**: 每个 worker 专注一个领域
3. **结果标准化**: Worker 输出遵循统一 WORKER_RESULT 格式
4. **灵活模式切换**: 根据任务复杂度选择合适模式
5. **及时清理**: Worker 完成后 close_agent 释放资源

View File

@@ -0,0 +1,257 @@
# 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 = `.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: .workflow/project-tech.json
3. Read: .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 输出写入 `.workflow/.loop/{loopId}.workers/`
3. **状态同步**: 每次 worker 完成后更新 state
4. **超时处理**: send_input 请求收敛,再超时则跳过

View File

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

View File

@@ -0,0 +1,383 @@
# Action Catalog (CCW Loop-B)
Complete reference of worker actions and their capabilities.
## Action Matrix
| Action | Worker Agent | Purpose | Input Requirements | Output |
|--------|--------------|---------|-------------------|--------|
| init | ccw-loop-b-init.md | Session initialization | Task description | Task breakdown + execution plan |
| develop | ccw-loop-b-develop.md | Code implementation | Task list | Code changes + progress update |
| debug | ccw-loop-b-debug.md | Problem diagnosis | Issue description | Root cause analysis + fix suggestions |
| validate | ccw-loop-b-validate.md | Testing and verification | Files to test | Test results + coverage report |
| complete | ccw-loop-b-complete.md | Session finalization | All worker outputs | Summary + commit message |
## Detailed Action Specifications
### INIT
**Purpose**: Parse requirements, create execution plan
**Preconditions**:
- `status === 'running'`
- `skill_state === null` (first time)
**Input**:
```
- Task description (text)
- Project context files
```
**Execution**:
1. Read `.workflow/project-tech.json`
2. Read `.workflow/project-guidelines.json`
3. Parse task into phases
4. Create task breakdown
5. Generate execution plan
**Output**:
```
WORKER_RESULT:
- action: init
- status: success
- summary: "Initialized with 5 tasks"
- next_suggestion: develop
TASK_BREAKDOWN:
- T1: Create auth module
- T2: Implement JWT utils
- T3: Write tests
- T4: Validate implementation
- T5: Documentation
EXECUTION_PLAN:
1. Develop (T1-T2)
2. Validate (T3-T4)
3. Complete (T5)
```
**Effects**:
- `skill_state.pending_tasks` populated
- Progress structure created
- Ready for develop phase
---
### DEVELOP
**Purpose**: Implement code, create/modify files
**Preconditions**:
- `skill_state.pending_tasks.length > 0`
- `status === 'running'`
**Input**:
```
- Task list from state
- Project conventions
- Existing code patterns
```
**Execution**:
1. Load pending tasks
2. Find existing patterns
3. Implement tasks one by one
4. Update progress file
5. Mark tasks completed
**Output**:
```
WORKER_RESULT:
- action: develop
- status: success
- summary: "Implemented 3 tasks"
- files_changed: ["src/auth.ts", "src/utils.ts"]
- next_suggestion: validate
DETAILED_OUTPUT:
tasks_completed: [T1, T2]
metrics:
lines_added: 180
lines_removed: 15
```
**Effects**:
- Files created/modified
- `skill_state.completed_tasks` updated
- Progress documented
**Failure Modes**:
- Pattern unclear → suggest debug
- Task blocked → mark blocked, continue
- Partial completion → set `loop_back_to: "develop"`
---
### DEBUG
**Purpose**: Diagnose issues, root cause analysis
**Preconditions**:
- Issue exists (test failure, bug report, etc.)
- `status === 'running'`
**Input**:
```
- Issue description
- Error messages
- Stack traces
- Reproduction steps
```
**Execution**:
1. Understand problem symptoms
2. Gather evidence from code
3. Form hypothesis
4. Test hypothesis
5. Document root cause
6. Suggest fixes
**Output**:
```
WORKER_RESULT:
- action: debug
- status: success
- summary: "Root cause: memory leak in event listeners"
- next_suggestion: develop (apply fixes)
ROOT_CAUSE_ANALYSIS:
hypothesis: "Listener accumulation"
confidence: high
evidence: [...]
mechanism: "Detailed explanation"
FIX_RECOMMENDATIONS:
1. Add removeAllListeners() on disconnect
2. Verification: Monitor memory usage
```
**Effects**:
- `skill_state.findings` updated
- Fix recommendations documented
- Ready for develop to apply fixes
**Failure Modes**:
- Insufficient info → request more data
- Multiple hypotheses → rank by likelihood
- Inconclusive → suggest investigation areas
---
### VALIDATE
**Purpose**: Run tests, check coverage, quality gates
**Preconditions**:
- Code exists to validate
- `status === 'running'`
**Input**:
```
- Files to test
- Test configuration
- Coverage requirements
```
**Execution**:
1. Identify test framework
2. Run unit tests
3. Run integration tests
4. Measure coverage
5. Check quality (lint, types, security)
6. Generate report
**Output**:
```
WORKER_RESULT:
- action: validate
- status: success
- summary: "113 tests pass, coverage 95%"
- next_suggestion: complete (all pass) | develop (fix failures)
TEST_RESULTS:
unit_tests: { passed: 98, failed: 0 }
integration_tests: { passed: 15, failed: 0 }
coverage: "95%"
QUALITY_CHECKS:
lint: ✓ Pass
types: ✓ Pass
security: ✓ Pass
```
**Effects**:
- Test results documented
- Coverage measured
- Quality gates verified
**Failure Modes**:
- Tests fail → document failures, suggest fixes
- Coverage low → identify gaps
- Quality issues → flag problems
---
### COMPLETE
**Purpose**: Finalize session, generate summary, commit
**Preconditions**:
- All tasks completed
- Tests passing
- `status === 'running'`
**Input**:
```
- All worker outputs
- Progress files
- Current state
```
**Execution**:
1. Read all worker outputs
2. Consolidate achievements
3. Verify completeness
4. Generate summary
5. Prepare commit message
6. Cleanup and archive
**Output**:
```
WORKER_RESULT:
- action: complete
- status: success
- summary: "Session completed successfully"
- next_suggestion: null
SESSION_SUMMARY:
achievements: [...]
files_changed: [...]
test_results: { ... }
quality_checks: { ... }
COMMIT_SUGGESTION:
message: "feat: ..."
files: [...]
ready_for_pr: true
```
**Effects**:
- `status` → 'completed'
- Summary file created
- Progress archived
- Commit message ready
**Failure Modes**:
- Pending tasks remain → mark partial
- Quality gates fail → list failures
---
## Action Flow Diagrams
### Interactive Mode Flow
```
+------+
| INIT |
+------+
|
v
+------+ user selects
| MENU |-------------+
+------+ |
^ v
| +--------------+
| | spawn worker |
| +--------------+
| |
| v
| +------+-------+
+---------| wait result |
+------+-------+
|
v
+------+-------+
| update state |
+--------------+
|
v
[completed?] --no--> [back to MENU]
|
yes
v
+----------+
| COMPLETE |
+----------+
```
### Auto Mode Flow
```
+------+ +---------+ +-------+ +----------+ +----------+
| INIT | ---> | DEVELOP | ---> | DEBUG | ---> | VALIDATE | ---> | COMPLETE |
+------+ +---------+ +-------+ +----------+ +----------+
^ | |
| +--- [issues] |
+--------------------------------+
[tests fail]
```
### Parallel Mode Flow
```
+------+
| INIT |
+------+
|
v
+---------------------+
| spawn all workers |
| [develop, debug, |
| validate] |
+---------------------+
|
v
+---------------------+
| wait({ ids: all }) |
+---------------------+
|
v
+---------------------+
| merge results |
+---------------------+
|
v
+---------------------+
| coordinator decides |
+---------------------+
|
v
+----------+
| COMPLETE |
+----------+
```
## Worker Coordination
| Scenario | Worker Sequence | Mode |
|----------|-----------------|------|
| Simple task | init → develop → validate → complete | Auto |
| Complex task | init → develop → debug → develop → validate → complete | Auto |
| Bug fix | init → debug → develop → validate → complete | Auto |
| Analysis | init → [develop \|\| debug \|\| validate] → complete | Parallel |
| Interactive | init → menu → user selects → worker → menu → ... | Interactive |
## Best Practices
1. **Init always first**: Parse requirements before execution
2. **Validate often**: After each develop phase
3. **Debug when needed**: Don't skip diagnosis
4. **Complete always last**: Ensure proper cleanup
5. **Use parallel wisely**: For independent analysis tasks
6. **Follow sequence**: In auto mode, respect dependencies

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 .workflow/.loop/{loopId}.json
3. spawn_agent with ccw-loop-executor role
4. Main loop:
a. wait() for agent output
b. Parse ACTION_RESULT
c. Handle outcome:
- COMPLETED/PAUSED/STOPPED: exit loop
- WAITING_INPUT: collect user input, send_input
- Next action: send_input to continue
d. Update state file
5. close_agent when done
```
## Session Files
```
.workflow/.loop/
+-- {loopId}.json # Master state (API + Skill)
+-- {loopId}.progress/
+-- develop.md # Development timeline
+-- debug.md # Understanding evolution
+-- validate.md # Validation report
+-- changes.log # Code changes (NDJSON)
+-- debug.log # Debug log (NDJSON)
+-- summary.md # Completion summary
```
## Codex Pattern Highlights
### Single Agent Deep Interaction
Instead of creating multiple agents, use `send_input` for multi-phase:
```javascript
const agent = spawn_agent({ message: role + task })
// Phase 1: INIT
const initResult = wait({ ids: [agent] })
// Phase 2: DEVELOP (via send_input, same agent)
send_input({ id: agent, message: 'Execute DEVELOP' })
const devResult = wait({ ids: [agent] })
// Phase 3: VALIDATE (via send_input, same agent)
send_input({ id: agent, message: 'Execute VALIDATE' })
const valResult = wait({ ids: [agent] })
// Only close when all done
close_agent({ id: agent })
```
### Role Path Passing
Agent reads role file itself (no content embedding):
```javascript
spawn_agent({
message: `
### MANDATORY FIRST STEPS
1. **Read role definition**: ~/.codex/agents/ccw-loop-executor.md
2. Read: .workflow/project-tech.json
...
`
})
```
### Explicit Lifecycle Management
- Always use `wait({ ids })` to get results
- Never assume `close_agent` returns results
- Only `close_agent` when confirming no more interaction needed
## Error Handling
| Situation | Action |
|-----------|--------|
| Agent timeout | `send_input` requesting convergence |
| Session not found | Create new session |
| State corrupted | Rebuild from progress files |
| Tests fail | Loop back to DEBUG |
| >10 iterations | Warn and suggest break |
## Integration
### Dashboard Integration
Works with CCW Dashboard Loop Monitor:
- Dashboard creates loop via API
- API triggers this skill with `--loop-id`
- Skill reads/writes `.workflow/.loop/{loopId}.json`
- Dashboard polls state for real-time updates
### Control Signals
- `paused`: Skill exits gracefully, waits for resume
- `failed`: Skill terminates
- `running`: Skill continues execution
## License
MIT

View File

@@ -0,0 +1,350 @@
---
name: CCW Loop
description: Stateless iterative development loop workflow with documented progress. Supports develop, debug, and validate phases with file-based state tracking. Triggers on "ccw-loop", "dev loop", "development loop", "开发循环", "迭代开发".
argument-hint: TASK="<task description>" [--loop-id=<id>] [--auto]
---
# 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: .workflow/.loop/{loopId}.json (MASTER) |
| Tasks: .workflow/.loop/{loopId}.tasks.jsonl |
| |
| /start -> Trigger ccw-loop skill with --loop-id |
| /pause -> Set status='paused' (skill checks before action) |
| /stop -> Set status='failed' (skill terminates) |
| /resume -> Set status='running' (skill continues) |
+-------------------------------------------------------------+
|
v
+-------------------------------------------------------------+
| ccw-loop Skill (Execution Plane) |
| |
| Codex Pattern: spawn_agent -> wait -> send_input -> close |
| |
| Reads/Writes: .workflow/.loop/{loopId}.json (unified state) |
| Writes: .workflow/.loop/{loopId}.progress/* (progress files) |
| |
| BEFORE each action: |
| -> Check status: paused/stopped -> exit gracefully |
| -> running -> continue with action |
| |
| Actions: init -> develop -> debug -> validate -> complete |
+-------------------------------------------------------------+
```
## Key Design Principles (Codex Adaptation)
1. **Unified State**: API and Skill share `.workflow/.loop/{loopId}.json` state file
2. **Control Signals**: Skill checks status field before each action (paused/stopped)
3. **File-Driven**: All progress documented in `.workflow/.loop/{loopId}.progress/`
4. **Resumable**: Continue any loop with `--loop-id`
5. **Dual Trigger**: Supports API trigger (`--loop-id`) and direct call (task description)
6. **Single Agent Deep Interaction**: Use send_input for multi-phase execution instead of multiple agents
## 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)
```
.workflow/.loop/
+-- {loopId}.json # Master state file (API + Skill shared)
+-- {loopId}.tasks.jsonl # Task list (API managed)
+-- {loopId}.progress/ # Skill progress files
+-- develop.md # Development progress timeline
+-- debug.md # Understanding evolution document
+-- validate.md # Validation report
+-- changes.log # Code changes log (NDJSON)
+-- debug.log # Debug log (NDJSON)
```
## 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 = `.workflow/.loop/${loopId}.json`
const progressDir = `.workflow/.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(`.workflow/.loop/${loopId}.json`))
if (!state) {
console.error(`Loop not found: ${loopId}`)
return
}
} else {
state = createInitialState(loopId, taskDescription)
Write(`.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2))
}
// Step 2: Create orchestrator agent (single agent handles all phases)
const agent = spawn_agent({
message: `
## TASK ASSIGNMENT
### MANDATORY FIRST STEPS (Agent Execute)
1. **Read role definition**: ~/.codex/agents/ccw-loop-executor.md (MUST read first)
2. Read: .workflow/project-tech.json
3. Read: .workflow/project-guidelines.json
---
## LOOP CONTEXT
- **Loop ID**: ${loopId}
- **State File**: .workflow/.loop/${loopId}.json
- **Progress Dir**: ${progressDir}
- **Mode**: ${mode} // 'interactive' or 'auto'
## CURRENT STATE
${JSON.stringify(state, null, 2)}
## TASK DESCRIPTION
${taskDescription}
## EXECUTION INSTRUCTIONS
You are executing CCW Loop orchestrator. Your job:
1. **Check Control Signals**
- Read .workflow/.loop/${loopId}.json
- If status === 'paused' -> Output "PAUSED" and stop
- If status === 'failed' -> Output "STOPPED" and stop
- If status === 'running' -> Continue
2. **Select Next Action**
Based on skill_state:
- If not initialized -> Execute INIT
- If mode === 'interactive' -> Output MENU and wait for input
- If mode === 'auto' -> Auto-select based on state
3. **Execute Action**
- Follow action instructions from ~/.codex/skills/ccw-loop/phases/actions/
- Update progress files in ${progressDir}/
- Update state in .workflow/.loop/${loopId}.json
4. **Output Format**
\`\`\`
ACTION_RESULT:
- action: {action_name}
- status: success | failed | needs_input
- message: {user message}
- state_updates: {JSON of skill_state updates}
NEXT_ACTION_NEEDED: {action_name} | WAITING_INPUT | COMPLETED | PAUSED
\`\`\`
## FIRST ACTION
${!state.skill_state ? 'Execute: INIT' : mode === 'auto' ? 'Auto-select next action' : 'Show MENU'}
`
})
// Step 3: Main orchestration loop
let iteration = 0
const maxIterations = state.max_iterations || 10
while (iteration < maxIterations) {
iteration++
// Wait for agent output
const result = wait({ ids: [agent], timeout_ms: 600000 })
const output = result.status[agent].completed
// Parse action result
const actionResult = parseActionResult(output)
// Handle different outcomes
switch (actionResult.next_action) {
case 'COMPLETED':
case 'PAUSED':
case 'STOPPED':
close_agent({ id: agent })
return actionResult
case 'WAITING_INPUT':
// Interactive mode: display menu, get user choice
const userChoice = await displayMenuAndGetChoice(actionResult)
// Send user choice back to agent
send_input({
id: agent,
message: `
## USER INPUT RECEIVED
Action selected: ${userChoice.action}
${userChoice.data ? `Additional data: ${JSON.stringify(userChoice.data)}` : ''}
## EXECUTE SELECTED ACTION
Follow instructions for: ${userChoice.action}
Update state and progress files accordingly.
`
})
break
default:
// Auto mode: agent continues to next action
// Check if we need to prompt for continuation
if (actionResult.next_action && actionResult.next_action !== 'NONE') {
send_input({
id: agent,
message: `
## CONTINUE EXECUTION
Previous action completed: ${actionResult.action}
Result: ${actionResult.status}
## EXECUTE NEXT ACTION
Continue with: ${actionResult.next_action}
`
})
}
}
// Update iteration count in state
const currentState = JSON.parse(Read(`.workflow/.loop/${loopId}.json`))
currentState.current_iteration = iteration
currentState.updated_at = getUtc8ISOString()
Write(`.workflow/.loop/${loopId}.json`, JSON.stringify(currentState, null, 2))
}
// Step 4: Cleanup
close_agent({ id: agent })
```
## 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(`.workflow/.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(`.workflow/.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:
- .workflow/.loop/{loopId}.json: Status set to completed
- .workflow/.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(`.workflow/.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:
- .workflow/.loop/{loopId}.progress/debug.md: Understanding updated
- .workflow/.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(`.workflow/.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(`.workflow/.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:
- .workflow/.loop/{loopId}.json: Task status updated
- .workflow/.loop/{loopId}.progress/develop.md: Progress entry added
- .workflow/.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(`.workflow/.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 = `.workflow/.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(`.workflow/.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:
- .workflow/.loop/{loopId}.json: skill_state initialized
- .workflow/.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(`.workflow/.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(`.workflow/.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(`.workflow/.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(`.workflow/.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:
- .workflow/.loop/{loopId}.progress/validate.md: Validation report created
- .workflow/.loop/{loopId}.progress/test-results.json: Test results saved
- .workflow/.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 = `.workflow/.loop/${loopId}.json`
if (!fs.existsSync(stateFile)) {
return null
}
const state = JSON.parse(Read(stateFile))
return state
}
```
### Create New Loop State (Direct Call)
```javascript
/**
* Create new loop state (only for direct calls, API triggers have existing state)
*/
function createLoopState(loopId, taskDescription) {
const stateFile = `.workflow/.loop/${loopId}.json`
const now = getUtc8ISOString()
const state = {
// API compatible fields
loop_id: loopId,
title: taskDescription.substring(0, 100),
description: taskDescription,
max_iterations: 10,
status: 'running', // Direct call sets to running
current_iteration: 0,
created_at: now,
updated_at: now,
// Skill extension fields
skill_state: null // Initialized by INIT action
}
// Ensure directories exist
mkdir -p ".loop"
mkdir -p ".workflow/.loop/${loopId}.progress"
Write(stateFile, JSON.stringify(state, null, 2))
return state
}
```
## Main Execution Flow (Codex Subagent)
```javascript
/**
* Run CCW Loop orchestrator using Codex subagent pattern
* @param options.loopId - Existing Loop ID (API trigger)
* @param options.task - Task description (direct call)
* @param options.mode - 'interactive' | 'auto'
*/
async function runOrchestrator(options = {}) {
const { loopId: existingLoopId, task, mode = 'interactive' } = options
console.log('=== CCW Loop Orchestrator (Codex) Started ===')
// 1. Determine loopId and initial state
let loopId
let state
if (existingLoopId) {
// API trigger: use existing loopId
loopId = existingLoopId
state = readLoopState(loopId)
if (!state) {
console.error(`Loop not found: ${loopId}`)
return { status: 'error', message: 'Loop not found' }
}
console.log(`Resuming loop: ${loopId}`)
console.log(`Status: ${state.status}`)
} else if (task) {
// Direct call: create new loopId
const timestamp = getUtc8ISOString().replace(/[-:]/g, '').split('.')[0]
const random = Math.random().toString(36).substring(2, 10)
loopId = `loop-v2-${timestamp}-${random}`
console.log(`Creating new loop: ${loopId}`)
console.log(`Task: ${task}`)
state = createLoopState(loopId, task)
} else {
console.error('Either --loop-id or task description is required')
return { status: 'error', message: 'Missing loopId or task' }
}
const progressDir = `.workflow/.loop/${loopId}.progress`
// 2. Create executor agent (single agent for entire loop)
const agent = spawn_agent({
message: `
## TASK ASSIGNMENT
### MANDATORY FIRST STEPS (Agent Execute)
1. **Read role definition**: ~/.codex/agents/ccw-loop-executor.md (MUST read first)
2. Read: .workflow/project-tech.json (if exists)
3. Read: .workflow/project-guidelines.json (if exists)
---
## LOOP CONTEXT
- **Loop ID**: ${loopId}
- **State File**: .workflow/.loop/${loopId}.json
- **Progress Dir**: ${progressDir}
- **Mode**: ${mode}
## CURRENT STATE
${JSON.stringify(state, null, 2)}
## TASK DESCRIPTION
${state.description || task}
## FIRST ACTION
${!state.skill_state ? 'Execute: INIT' : mode === 'auto' ? 'Auto-select next action' : 'Show MENU'}
Read the role definition first, then execute the appropriate action.
`
})
// 3. Main orchestration loop
let iteration = state.current_iteration || 0
const maxIterations = state.max_iterations || 10
let continueLoop = true
while (continueLoop && iteration < maxIterations) {
iteration++
// Wait for agent output
const result = wait({ ids: [agent], timeout_ms: 600000 })
// Check for timeout
if (result.timed_out) {
console.log('Agent timeout, requesting convergence...')
send_input({
id: agent,
message: `
## TIMEOUT NOTIFICATION
Execution timeout reached. Please:
1. Output current progress
2. Save any pending state updates
3. Return ACTION_RESULT with current status
`
})
continue
}
const output = result.status[agent].completed
// Parse action result
const actionResult = parseActionResult(output)
console.log(`\n[Iteration ${iteration}] Action: ${actionResult.action}, Status: ${actionResult.status}`)
// Update iteration in state
state = readLoopState(loopId)
state.current_iteration = iteration
state.updated_at = getUtc8ISOString()
Write(`.workflow/.loop/${loopId}.json`, JSON.stringify(state, null, 2))
// Handle different outcomes
switch (actionResult.next_action) {
case 'COMPLETED':
console.log('Loop completed successfully')
continueLoop = false
break
case 'PAUSED':
console.log('Loop paused by API, exiting gracefully')
continueLoop = false
break
case 'STOPPED':
console.log('Loop stopped by API')
continueLoop = false
break
case 'WAITING_INPUT':
// Interactive mode: display menu, get user choice
if (mode === 'interactive') {
const userChoice = await displayMenuAndGetChoice(actionResult)
// Send user choice back to agent
send_input({
id: agent,
message: `
## USER INPUT RECEIVED
Action selected: ${userChoice.action}
${userChoice.data ? `Additional data: ${JSON.stringify(userChoice.data)}` : ''}
## EXECUTE SELECTED ACTION
Read action instructions and execute: ${userChoice.action}
Update state and progress files accordingly.
Output ACTION_RESULT when complete.
`
})
}
break
default:
// Continue with next action
if (actionResult.next_action && actionResult.next_action !== 'NONE') {
send_input({
id: agent,
message: `
## CONTINUE EXECUTION
Previous action completed: ${actionResult.action}
Result: ${actionResult.status}
${actionResult.message ? `Message: ${actionResult.message}` : ''}
## EXECUTE NEXT ACTION
Continue with: ${actionResult.next_action}
Read action instructions and execute.
Output ACTION_RESULT when complete.
`
})
} else {
// No next action specified, check if should continue
if (actionResult.status === 'failed') {
console.log(`Action failed: ${actionResult.message}`)
}
continueLoop = false
}
}
}
// 4. Check iteration limit
if (iteration >= maxIterations) {
console.log(`\nReached maximum iterations (${maxIterations})`)
console.log('Consider breaking down the task or taking a break.')
}
// 5. Cleanup
close_agent({ id: agent })
console.log('\n=== CCW Loop Orchestrator (Codex) Finished ===')
// Return final state
const finalState = readLoopState(loopId)
return {
status: finalState.status,
loop_id: loopId,
iterations: iteration,
final_state: finalState
}
}
/**
* Parse action result from agent output
*/
function parseActionResult(output) {
const result = {
action: 'unknown',
status: 'unknown',
message: '',
state_updates: {},
next_action: 'NONE'
}
// Parse ACTION_RESULT block
const actionMatch = output.match(/ACTION_RESULT:\s*([\s\S]*?)(?:FILES_UPDATED:|NEXT_ACTION_NEEDED:|$)/)
if (actionMatch) {
const lines = actionMatch[1].split('\n')
for (const line of lines) {
const match = line.match(/^-\s*(\w+):\s*(.+)$/)
if (match) {
const [, key, value] = match
if (key === 'state_updates') {
try {
result.state_updates = JSON.parse(value)
} catch (e) {
// Try parsing multi-line JSON
}
} else {
result[key] = value.trim()
}
}
}
}
// Parse NEXT_ACTION_NEEDED
const nextMatch = output.match(/NEXT_ACTION_NEEDED:\s*(\S+)/)
if (nextMatch) {
result.next_action = nextMatch[1]
}
return result
}
/**
* Display menu and get user choice (interactive mode)
*/
async function displayMenuAndGetChoice(actionResult) {
// Parse MENU_OPTIONS from output
const menuMatch = actionResult.message.match(/MENU_OPTIONS:\s*([\s\S]*?)(?:WAITING_INPUT:|$)/)
if (menuMatch) {
console.log('\n' + menuMatch[1])
}
// Use AskUserQuestion to get choice
const response = await AskUserQuestion({
questions: [{
question: "Select next action:",
header: "Action",
multiSelect: false,
options: [
{ label: "develop", description: "Continue development" },
{ label: "debug", description: "Start debugging" },
{ label: "validate", description: "Run validation" },
{ label: "complete", description: "Complete loop" },
{ label: "exit", description: "Exit and save" }
]
}]
})
return { action: response["Action"] }
}
```
## Action Catalog
| Action | Purpose | Preconditions | Effects |
|--------|---------|---------------|---------|
| INIT | Initialize session | status=running, skill_state=null | skill_state initialized |
| MENU | Display menu | skill_state != null, mode=interactive | Wait for user input |
| DEVELOP | Execute dev task | pending tasks > 0 | Update progress.md |
| DEBUG | Hypothesis debug | needs debugging | Update understanding.md |
| VALIDATE | Run tests | needs validation | Update validation.md |
| COMPLETE | Finish loop | all done | status=completed |
## Termination Conditions
1. **API Paused**: `state.status === 'paused'` (Skill exits, wait for resume)
2. **API Stopped**: `state.status === 'failed'` (Skill terminates)
3. **Task Complete**: `NEXT_ACTION_NEEDED === 'COMPLETED'`
4. **Iteration Limit**: `current_iteration >= max_iterations`
5. **User Exit**: User selects 'exit' in interactive mode
## Error Recovery
| Error Type | Recovery Strategy |
|------------|-------------------|
| Agent timeout | send_input requesting convergence |
| Action failed | Log error, continue or prompt user |
| State corrupted | Rebuild from progress files |
| Agent closed unexpectedly | Re-spawn with previous output in message |
## Codex Best Practices Applied
1. **Single Agent Pattern**: One agent handles entire loop lifecycle
2. **Deep Interaction via send_input**: Multi-phase without context loss
3. **Delayed close_agent**: Only after confirming no more interaction
4. **Explicit wait()**: Always get results before proceeding
5. **Role Path Passing**: Agent reads role file, no content embedding

View File

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

View File

@@ -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

@@ -1,385 +0,0 @@
# Parallel Dev Cycle Skill
Multi-agent parallel development cycle using Codex subagent pattern with continuous iteration support.
## Overview
This skill implements a **single-file-per-agent** development workflow:
- **RA**: `requirements.md` (all requirements + edge cases + history)
- **EP**: `exploration.md`, `architecture.md`, `plan.json` (codebase exploration + architecture + structured tasks)
- **CD**: `implementation.md` (progress + files + decisions + testing)
- **VAS**: `summary.md` (validation + test results + recommendations)
Each file is **completely rewritten** on each iteration, with old versions auto-archived to `history/`.
## Installation
Files are in `.codex/skills/parallel-dev-cycle/`:
```
.codex/skills/parallel-dev-cycle/
├── SKILL.md # Main skill definition
├── README.md # This file
├── phases/
│ ├── orchestrator.md # Multi-agent coordination
│ ├── state-schema.md # Unified state structure
│ └── agents/
│ ├── requirements-analyst.md # RA role
│ ├── exploration-planner.md # EP role
│ ├── code-developer.md # CD role
│ └── validation-archivist.md # VAS role
└── specs/
├── coordination-protocol.md # Agent communication
└── versioning-strategy.md # Version management
```
## Quick Start
### Launch New Cycle
```bash
/parallel-dev-cycle TASK="Implement OAuth authentication"
```
Creates:
```
.workflow/.cycle/cycle-v1-20260122-abc123.progress/
├── ra/
│ ├── requirements.md (v1.0.0)
│ └── changes.log (NDJSON)
├── ep/
│ ├── exploration.md (v1.0.0)
│ ├── architecture.md (v1.0.0)
│ ├── plan.json (v1.0.0)
│ └── changes.log (NDJSON)
├── cd/
│ ├── implementation.md (v1.0.0)
│ └── changes.log (NDJSON)
└── vas/
├── summary.md (v1.0.0)
└── changes.log (NDJSON)
```
### Continue With Extension (XX-1 Pattern)
User adds requirement: "Also support Google OAuth"
```bash
/parallel-dev-cycle --cycle-id=cycle-v1-20260122-abc123 --extend="Add Google OAuth"
```
Automatically:
1. Archives old `requirements.md (v1.0.0)``history/requirements-v1.0.0.md`
2. Rewrites `requirements.md (v1.1.0)` - complete file replacement
3. Appends change to `changes.log` (NDJSON audit trail)
### Next Iteration (XX-2)
```bash
/parallel-dev-cycle --cycle-id=cycle-v1-20260122-abc123 --extend="Add GitHub provider"
```
All files update to v1.2.0, previous versions archived.
## Execution Flow
### Phase 1: Parallel Agent Execution
```
Time RA EP CD VAS
──── ── ── ── ──
0ms [spawned] [spawned] [spawned] [spawned]
↓ ↓ ↓ ↓
Analyzing Exploring Reading plan Waiting
task codebase from EP...
5min Outputs req. Outputs plan Requirements
v1.0.0 ✓ v1.0.0 ✓ unclear - BLOCKED
10min Clarifies req Updates plan ✓ Ready
v1.0.1 ✓ v1.0.1 ✓ Implementing...
15min ✓ Complete ✓ Complete ✓ Code done [waiting for CD]
20min [starts tests]
25min Outputs summary
v1.0.0 ✓
```
### Phase 2: Version Transition
When iteration completes, next extends to v1.1.0:
```
Current State (v1.0.0)
├── requirements.md (v1.0.0)
├── plan.json (v1.0.0)
├── implementation.md (v1.0.0)
└── summary.md (v1.0.0)
User: "Add GitHub provider"
Archive Old Write New
├── history/requirements-v1.0.0.md → requirements.md (v1.1.0) - REWRITTEN
├── history/plan-v1.0.0.json → plan.json (v1.1.0) - REWRITTEN
├── history/impl-v1.0.0.md → implementation.md (v1.1.0) - REWRITTEN
└── history/summary-v1.0.0.md → summary.md (v1.1.0) - REWRITTEN
Append to changes.log (NDJSON)
```
## Session Files
```
.workflow/.cycle/{cycleId}.progress/
ra/ - Requirements Analyst
├── requirements.md # v1.2.0 (current, complete rewrite)
├── changes.log # NDJSON audit trail
└── history/
├── requirements-v1.0.0.md
└── requirements-v1.1.0.md
ep/ - Exploration & Planning
├── exploration.md # v1.2.0 (codebase exploration)
├── architecture.md # v1.2.0 (architecture design)
├── plan.json # v1.2.0 (structured task list, current)
├── changes.log # NDJSON audit trail
└── history/
├── plan-v1.0.0.json
└── plan-v1.1.0.json
cd/ - Code Developer
├── implementation.md # v1.2.0 (current)
├── changes.log # NDJSON audit trail
└── history/
├── implementation-v1.0.0.md
└── implementation-v1.1.0.md
vas/ - Validation & Archival
├── summary.md # v1.2.0 (current)
├── changes.log # NDJSON audit trail
└── history/
├── summary-v1.0.0.md
└── summary-v1.1.0.md
```
## Versioning Strategy
### Semantic Versioning
- **1.0.0**: Initial cycle
- **1.1.0**: User extends with new requirement
- **1.2.0**: Another iteration with more requirements
### What Gets Versioned
**Main Document File**
- Completely rewritten each iteration
- Auto-archived to `history/`
- No inline version history (stays clean)
**Changes.log (NDJSON)**
- Append-only (never deleted)
- Complete audit trail of all changes
- Used to trace requirement origins
**Historical Snapshots**
- Auto-created in `history/` directory
- Keep last N versions (default: 5)
- For reference when needed
### Key Principle
> **主文档简洁清晰** ← Agent 只关注当前版本
>
> **完整历史记录** ← Changes.log 保留每个变更
>
> **版本快照归档** ← History/ 备份旧版本
## File Maintenance
### Each Agent
| Agent | File | Contains | Size |
|-------|------|----------|------|
| **RA** | requirements.md | All FR, NFR, edge cases, history summary | ~2-5KB |
| **EP** | exploration.md + architecture.md + plan.json | Codebase exploration, architecture design, structured task list | ~5-10KB total |
| **CD** | implementation.md | Completed tasks, files changed, decisions, tests | ~4-10KB |
| **VAS** | summary.md | Test results, coverage, issues, recommendations | ~5-12KB |
### Changes.log (Shared)
NDJSON format - one line per change:
```jsonl
{"timestamp":"2026-01-22T10:00:00+08:00","version":"1.0.0","agent":"ra","action":"create","change":"Initial requirements","iteration":1}
{"timestamp":"2026-01-22T11:00:00+08:00","version":"1.1.0","agent":"ra","action":"update","change":"Added Google OAuth","iteration":2}
{"timestamp":"2026-01-22T12:00:00+08:00","version":"1.2.0","agent":"ra","action":"update","change":"Added GitHub, MFA","iteration":3}
```
## Accessing History
### Current Version
```bash
# View latest requirements
cat .workflow/.cycle/cycle-xxx.progress/ra/requirements.md
# Quick check - version is in header
head -5 requirements.md # "# Requirements Specification - v1.2.0"
```
### Version History
```bash
# View previous version
cat .workflow/.cycle/cycle-xxx.progress/ra/history/requirements-v1.1.0.md
# Audit trail - all changes
cat .workflow/.cycle/cycle-xxx.progress/ra/changes.log | jq .
# Changes in specific iteration
cat changes.log | jq 'select(.iteration==2)'
# Trace requirement history
cat changes.log | jq 'select(.change | contains("OAuth"))'
```
## Codex Pattern Implementation
### Multi-Agent Parallel
```javascript
// Create 4 agents in parallel
const agents = {
ra: spawn_agent({ message: raRoleAndTask }),
ep: spawn_agent({ message: epRoleAndTask }),
cd: spawn_agent({ message: cdRoleAndTask }),
vas: spawn_agent({ message: vasRoleAndTask })
}
// Wait for all 4 in parallel
const results = wait({ ids: [agents.ra, agents.ep, agents.cd, agents.vas] })
```
### Role Path Passing
Each agent reads its own role definition:
```javascript
spawn_agent({
message: `
## MANDATORY FIRST STEPS
1. Read role: ~/.codex/agents/requirements-analyst.md
2. Read: .workflow/project-tech.json
3. Read: .workflow/project-guidelines.json
## TASK
${taskDescription}
`
})
```
### Deep Interaction
Use `send_input` for iteration refinement:
```javascript
// First output
const initial = wait({ ids: [agent] })
// User feedback
send_input({
id: agent,
message: `
## Feedback
${feedback}
## Next Steps
Update ${filename} based on feedback. Increment version.
Output PHASE_RESULT when complete.
`
})
// Updated output
const revised = wait({ ids: [agent] })
// Only close when done
close_agent({ id: agent })
```
## Error Handling
| Situation | Recovery |
|-----------|----------|
| Agent timeout | send_input requesting convergence or retry |
| State corrupted | Rebuild from changes.log NDJSON |
| Version mismatch | Agent checks version in state before reading |
| Blocked dependency | Orchestrator sends updated file path |
## Best Practices
1. **Let agents rewrite** - Don't maintain incremental history in main doc
2. **Trust changes.log** - NDJSON is the source of truth for history
3. **Archive on version bump** - Automatic, no manual versioning needed
4. **Keep files focused** - Each file should be readable in 5 minutes
5. **Version header always present** - Makes version obvious at a glance
## Integration
This skill works standalone or integrated with:
- Dashboard Loop Monitor (API triggers)
- CCW workflow system
- Custom orchestration
### API Trigger
```bash
POST /api/cycles/start
{
"task": "Implement OAuth",
"mode": "auto"
}
→ Returns cycle_id
GET /api/cycles/{cycle_id}/status
→ Returns agents status and progress
```
## Architecture Diagram
```
User Task
Orchestrator (main coordinator)
├─→ spawn_agent(RA)
├─→ spawn_agent(EP)
├─→ spawn_agent(CD)
└─→ spawn_agent(VAS)
wait({ ids: [all 4] })
All write to:
- requirements.md (v1.x.0)
- exploration.md, architecture.md, plan.json (v1.x.0)
- implementation.md (v1.x.0)
- summary.md (v1.x.0)
- changes.log (NDJSON append)
[Automatic archival]
- history/requirements-v1.{x-1}.0.md
- history/plan-v1.{x-1}.0.json
- etc...
Orchestrator: Next iteration?
- Yes: send_input with feedback
- No: close_agent, report summary
```
## License
MIT

View File

@@ -1,28 +1,18 @@
---
name: Parallel Dev Cycle
description: Multi-agent parallel development cycle with requirement analysis, exploration planning, code development, and validation. Supports continuous iteration with markdown progress documentation.
argument-hint: TASK="<task description>" | --cycle-id=<id> [--extend="<extension>"] [--auto] [--parallel=<count>]
name: parallel-dev-cycle
description: Multi-agent parallel development cycle with requirement analysis, exploration planning, code development, and validation. Supports continuous iteration with markdown progress documentation. Triggers on "parallel-dev-cycle".
allowed-tools: Task, AskUserQuestion, TodoWrite, Read, Write, Edit, Bash, Glob, Grep
---
# Parallel Dev Cycle - Multi-Agent Development Workflow
# Parallel Dev Cycle
Multi-agent parallel development cycle using Codex subagent pattern with four specialized workers:
1. **Requirements Analysis & Extension** (RA) - Requirement analysis and self-enhancement
2. **Exploration & Planning** (EP) - Exploration and planning
2. **Exploration & Planning** (EP) - Codebase exploration and implementation planning
3. **Code Development** (CD) - Code development with debug strategy support
4. **Validation & Archival Summary** (VAS) - Validation and archival summary
Each agent **maintains one main document** (e.g., requirements.md, plan.json, implementation.md) that is completely rewritten per iteration, plus auxiliary logs (changes.log, debug-log.ndjson) that are append-only. Supports versioning, automatic archival, and complete history tracking.
## Arguments
| Arg | Required | Description |
|-----|----------|-------------|
| TASK | One of TASK or --cycle-id | Task description (for new cycle, mutually exclusive with --cycle-id) |
| --cycle-id | One of TASK or --cycle-id | Existing cycle ID to continue (from API or previous session) |
| --extend | No | Extension description (only valid with --cycle-id) |
| --auto | No | Auto-cycle mode (run all phases sequentially) |
| --parallel | No | Number of parallel agents (default: 4, max: 4) |
Each agent **maintains one main document** (e.g., requirements.md, plan.json, implementation.md) that is completely rewritten per iteration, plus auxiliary logs (changes.log, debug-log.ndjson) that are append-only.
## Architecture Overview
@@ -70,93 +60,273 @@ Each agent **maintains one main document** (e.g., requirements.md, plan.json, im
6. **File References**: Use short file paths instead of content passing
7. **Self-Enhancement**: RA agent proactively extends requirements based on context
## Arguments
| Arg | Required | Description |
|-----|----------|-------------|
| TASK | One of TASK or --cycle-id | Task description (for new cycle, mutually exclusive with --cycle-id) |
| --cycle-id | One of TASK or --cycle-id | Existing cycle ID to continue (from API or previous session) |
| --extend | No | Extension description (only valid with --cycle-id) |
| --auto | No | Auto-cycle mode (run all phases sequentially without user confirmation) |
| --parallel | No | Number of parallel agents (default: 4, max: 4) |
## Auto Mode
When `--auto`: Run all phases sequentially without user confirmation between iterations. Use recommended defaults for all decisions. Automatically continue iteration loop until tests pass or max iterations reached.
## Execution Flow
```
Input Parsing:
└─ Parse arguments (TASK | --cycle-id + --extend)
└─ Convert to structured context (cycleId, state, progressDir)
Phase 1: Session Initialization
└─ Ref: phases/01-session-init.md
├─ Create new cycle OR resume existing cycle
├─ Initialize state file and directory structure
└─ Output: cycleId, state, progressDir
Phase 2: Agent Execution (Parallel)
└─ Ref: phases/02-agent-execution.md
├─ Tasks attached: Spawn RA → Spawn EP → Spawn CD → Spawn VAS → Wait all
├─ Spawn RA, EP, CD, VAS agents in parallel
├─ Wait for all agents with timeout handling
└─ Output: agentOutputs (4 agent results)
Phase 3: Result Aggregation & Iteration
└─ Ref: phases/03-result-aggregation.md
├─ Parse PHASE_RESULT from each agent
├─ Detect issues (test failures, blockers)
├─ Decision: Issues found AND iteration < max?
│ ├─ Yes → Send feedback via send_input, loop back to Phase 2
│ └─ No → Proceed to Phase 4
└─ Output: parsedResults, iteration status
Phase 4: Completion & Summary
└─ Ref: phases/04-completion-summary.md
├─ Generate unified summary report
├─ Update final state
├─ Close all agents
└─ Output: final cycle report with continuation instructions
```
**Phase Reference Documents** (read on-demand when phase executes):
| Phase | Document | Purpose |
|-------|----------|---------|
| 1 | [phases/01-session-init.md](phases/01-session-init.md) | Session creation/resume and state initialization |
| 2 | [phases/02-agent-execution.md](phases/02-agent-execution.md) | Parallel agent spawning and execution |
| 3 | [phases/03-result-aggregation.md](phases/03-result-aggregation.md) | Result parsing, feedback generation, iteration handling |
| 4 | [phases/04-completion-summary.md](phases/04-completion-summary.md) | Final summary generation and cleanup |
## Data Flow
```
User Input (TASK | --cycle-id + --extend)
[Parse Arguments]
↓ cycleId, state, progressDir
Phase 1: Session Initialization
↓ cycleId, state, progressDir (initialized/resumed)
Phase 2: Agent Execution
↓ agentOutputs {ra, ep, cd, vas}
Phase 3: Result Aggregation
↓ parsedResults, hasIssues, iteration count
↓ [Loop back to Phase 2 if issues and iteration < max]
Phase 4: Completion & Summary
↓ finalState, summaryReport
Return: cycle_id, iterations, final_state
```
## Session Structure
```
.workflow/.cycle/
+-- {cycleId}.json # Master state file
+-- {cycleId}.progress/
+-- ra/
| +-- requirements.md # Current version (complete rewrite)
| +-- changes.log # NDJSON complete history (append-only)
|-- history/
| +-- requirements-v1.0.0.md # Archived snapshot
| +-- requirements-v1.1.0.md # Archived snapshot
+-- ep/
| +-- exploration.md # Codebase exploration report
| +-- architecture.md # Architecture design
| +-- plan.json # Structured task list (current version)
| +-- changes.log # NDJSON complete history
| └-- history/
| +-- plan-v1.0.0.json
| +-- plan-v1.1.0.json
+-- cd/
| +-- implementation.md # Current version
| +-- debug-log.ndjson # Debug hypothesis tracking
| +-- changes.log # NDJSON complete history
|-- history/
| +-- implementation-v1.0.0.md
| +-- implementation-v1.1.0.md
+-- vas/
| +-- summary.md # Current version
| +-- changes.log # NDJSON complete history
| └-- history/
| +-- summary-v1.0.0.md
| +-- summary-v1.1.0.md
└-- coordination/
+-- timeline.md # Execution timeline
+-- decisions.log # Decision log
├── {cycleId}.json # Master state file
├── {cycleId}.progress/
├── ra/
├── requirements.md # Current version (complete rewrite)
├── changes.log # NDJSON complete history (append-only)
── history/ # Archived snapshots
├── ep/
│ ├── exploration.md # Codebase exploration report
│ ├── architecture.md # Architecture design
├── plan.json # Structured task list (current version)
├── changes.log # NDJSON complete history
│ └── history/
├── cd/
├── implementation.md # Current version
│ ├── debug-log.ndjson # Debug hypothesis tracking
├── changes.log # NDJSON complete history
│ └── history/
├── vas/
├── summary.md # Current version
├── changes.log # NDJSON complete history
── history/
└── coordination/
├── timeline.md # Execution timeline
└── decisions.log # Decision log
```
## State Management
State schema is defined in [phases/state-schema.md](phases/state-schema.md). The master state file (`{cycleId}.json`) tracks:
Master state file: `.workflow/.cycle/{cycleId}.json`
- Cycle metadata (id, title, status, iterations)
- Agent states (status, output files, version)
- Shared context (requirements, plan, changes, test results)
- Coordination data (feedback log, decisions, blockers)
## Versioning Workflow
### Initial Version (v1.0.0)
```bash
/parallel-dev-cycle TASK="Implement OAuth login"
```json
{
"cycle_id": "cycle-v1-20260122T100000-abc123",
"title": "Task title",
"description": "Full task description",
"status": "created | running | paused | completed | failed",
"created_at": "ISO8601", "updated_at": "ISO8601",
"max_iterations": 5, "current_iteration": 0,
"agents": {
"ra": { "status": "idle | running | completed | failed", "output_files": [] },
"ep": { "status": "idle", "output_files": [] },
"cd": { "status": "idle", "output_files": [] },
"vas": { "status": "idle", "output_files": [] }
},
"current_phase": "init | ra | ep | cd | vas | aggregation | complete",
"completed_phases": [],
"requirements": null, "plan": null, "changes": [], "test_results": null,
"coordination": { "feedback_log": [], "blockers": [] }
}
```
Generates:
```
requirements.md (v1.0.0)
exploration.md (v1.0.0)
architecture.md (v1.0.0)
plan.json (v1.0.0)
implementation.md (v1.0.0) - if applicable
summary.md (v1.0.0) - if applicable
**Recovery**: If state corrupted, rebuild from `.progress/` markdown files and changes.log.
## TodoWrite Pattern
### Phase-Level Tracking (Tasks Attached)
```json
[
{"content": "Phase 1: Session Initialization", "status": "completed"},
{"content": "Phase 2: Agent Execution", "status": "in_progress"},
{"content": " → Spawn RA Agent", "status": "completed"},
{"content": " → Spawn EP Agent", "status": "completed"},
{"content": " → Spawn CD Agent", "status": "in_progress"},
{"content": " → Spawn VAS Agent", "status": "pending"},
{"content": "Phase 3: Result Aggregation", "status": "pending"},
{"content": "Phase 4: Completion & Summary", "status": "pending"}
]
```
### Iteration Versions (v1.1.0, v1.2.0)
### Phase-Level Tracking (Collapsed)
```bash
/parallel-dev-cycle --cycle-id=cycle-v1-xxx --extend="Add GitHub support"
```json
[
{"content": "Phase 1: Session Initialization", "status": "completed"},
{"content": "Phase 2: Agent Execution (4 agents completed)", "status": "completed"},
{"content": "Phase 3: Result Aggregation", "status": "in_progress"},
{"content": "Phase 4: Completion & Summary", "status": "pending"}
]
```
**Automatic handling**:
1. Read current `requirements.md (v1.0.0)`
2. Auto-archive to `history/requirements-v1.0.0.md`
3. Recreate `requirements.md (v1.1.0)` - complete overwrite
4. Append changes to `changes.log` (NDJSON)
### Iteration Loop Tracking
## Changes.log Format (NDJSON)
Permanent audit log (append-only, never deleted):
```jsonl
{"timestamp":"2026-01-22T10:00:00+08:00","version":"1.0.0","agent":"ra","action":"create","change":"Initial requirements","iteration":1}
{"timestamp":"2026-01-22T11:00:00+08:00","version":"1.1.0","agent":"ra","action":"update","change":"Added Google OAuth requirement","iteration":2}
{"timestamp":"2026-01-22T11:30:00+08:00","version":"1.0.0","agent":"ep","action":"create","change":"Initial implementation plan","iteration":1}
```json
[
{"content": "Phase 1: Session Initialization", "status": "completed"},
{"content": "Iteration 1: Agent Execution + Aggregation", "status": "completed"},
{"content": "Iteration 2: Feedback → Re-execution → Aggregation", "status": "in_progress"},
{"content": "Phase 4: Completion & Summary", "status": "pending"}
]
```
## Versioning
- **1.0.0**: Initial cycle → **1.x.0**: Each iteration (minor bump)
- Each iteration: archive old → complete rewrite → append changes.log
```
Archive: copy requirements.md → history/requirements-v1.0.0.md
Rewrite: overwrite requirements.md with v1.1.0 (complete new content)
Append: changes.log ← {"timestamp","version":"1.1.0","action":"update","description":"..."}
```
| Agent Output | Rewrite (per iteration) | Append-only |
|-------------|------------------------|-------------|
| RA | requirements.md | changes.log |
| EP | exploration.md, architecture.md, plan.json | changes.log |
| CD | implementation.md, issues.md | changes.log, debug-log.ndjson |
| VAS | summary.md, test-results.json | changes.log |
## Coordination Protocol
**Execution Order**: RA → EP → CD → VAS (dependency chain, all spawned in parallel but block on dependencies)
**Agent → Orchestrator**: Each agent outputs `PHASE_RESULT:` block:
```
PHASE_RESULT:
- phase: ra | ep | cd | vas
- status: success | failed | partial
- files_written: [list]
- summary: one-line summary
- issues: []
```
**Orchestrator → Agent**: Feedback via `send_input` (file refs + issue summary, never full content):
```
## FEEDBACK FROM [Source]
[Issue summary with file:line references]
## Reference
- File: .progress/vas/test-results.json (v1.0.0)
## Actions Required
1. [Specific fix]
```
**Rules**: Only orchestrator writes state file. Agents read state, write to own `.progress/{agent}/` directory only.
## Core Rules
1. **Start Immediately**: First action is TodoWrite initialization, then Phase 1 execution
2. **Progressive Phase Loading**: Read phase docs ONLY when that phase is about to execute
3. **Parse Every Output**: Extract PHASE_RESULT data from each agent for next phase
4. **Auto-Continue**: After each phase, execute next pending phase automatically
5. **Track Progress**: Update TodoWrite dynamically with attachment/collapse pattern
6. **Single Writer**: Only orchestrator writes to master state file; agents report via PHASE_RESULT
7. **File References**: Pass file paths between agents, not content
8. **DO NOT STOP**: Continuous execution until all phases complete or max iterations reached
## Error Handling
| Error Type | Recovery |
|------------|----------|
| Agent timeout | send_input requesting convergence, then retry |
| State corrupted | Rebuild from progress markdown files and changes.log |
| Agent failed | Re-spawn agent with previous context |
| Conflicting results | Orchestrator sends reconciliation request |
| Missing files | RA/EP agents identify and request clarification |
| Max iterations reached | Generate summary with remaining issues documented |
## Coordinator Checklist
### Before Each Phase
- [ ] Read phase reference document
- [ ] Check current state for dependencies
- [ ] Update TodoWrite with phase tasks
### After Each Phase
- [ ] Parse agent outputs (PHASE_RESULT)
- [ ] Update master state file
- [ ] Collapse TodoWrite sub-tasks
- [ ] Determine next action (continue / iterate / complete)
## Reference Documents
| Document | Purpose |
|----------|---------|
| [roles/](roles/) | Agent role definitions (RA, EP, CD, VAS) |
## Usage
```bash
@@ -172,23 +342,3 @@ Permanent audit log (append-only, never deleted):
# Auto mode
/parallel-dev-cycle --auto TASK="Add OAuth authentication"
```
## Key Benefits
- **Simple**: Each agent maintains only 1 file + changes.log
- **Efficient**: Version rewrite without complex version marking
- **Traceable**: Complete history in `history/` and `changes.log`
- **Fast**: Agent reads current version quickly (no history parsing needed)
- **Auditable**: NDJSON changes.log fully traces every change
- **Self-Enhancing**: RA agent proactively extends requirements
- **Debug-Ready**: CD agent supports hypothesis-driven debugging
## Reference Documents
| Document | Purpose |
|----------|---------|
| [phases/orchestrator.md](phases/orchestrator.md) | Orchestrator logic |
| [phases/state-schema.md](phases/state-schema.md) | State structure definition |
| [phases/agents/](phases/agents/) | Four agent role definitions |
| [specs/coordination-protocol.md](specs/coordination-protocol.md) | Communication protocol |
| [specs/versioning-strategy.md](specs/versioning-strategy.md) | Version management |

View File

@@ -0,0 +1,159 @@
# Phase 1: Session Initialization
Create or resume a development cycle, initialize state file and directory structure.
## Objective
- Parse user arguments (TASK, --cycle-id, --extend, --auto, --parallel)
- Create new cycle with unique ID OR resume existing cycle
- Initialize directory structure for all agents
- Create master state file
- Output: cycleId, state, progressDir
## Execution
### Step 1.1: Parse Arguments
```javascript
const { cycleId: existingCycleId, task, mode = 'interactive', extension } = options
// Validate mutual exclusivity
if (!existingCycleId && !task) {
console.error('Either --cycle-id or task description is required')
return { status: 'error', message: 'Missing cycleId or task' }
}
```
### Step 1.2: Utility Functions
```javascript
const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()
function readCycleState(cycleId) {
const stateFile = `.workflow/.cycle/${cycleId}.json`
if (!fs.existsSync(stateFile)) {
return null
}
return JSON.parse(Read(stateFile))
}
```
### Step 1.3: New Cycle Creation
When `TASK` is provided (no `--cycle-id`):
```javascript
// Generate unique cycle ID
const timestamp = getUtc8ISOString().replace(/[-:]/g, '').split('.')[0]
const random = Math.random().toString(36).substring(2, 10)
const cycleId = `cycle-v1-${timestamp}-${random}`
console.log(`Creating new cycle: ${cycleId}`)
```
#### Create Directory Structure
```bash
mkdir -p .workflow/.cycle/${cycleId}.progress/{ra,ep,cd,vas,coordination}
mkdir -p .workflow/.cycle/${cycleId}.progress/ra/history
mkdir -p .workflow/.cycle/${cycleId}.progress/ep/history
mkdir -p .workflow/.cycle/${cycleId}.progress/cd/history
mkdir -p .workflow/.cycle/${cycleId}.progress/vas/history
```
#### Initialize State File
```javascript
function createCycleState(cycleId, taskDescription) {
const stateFile = `.workflow/.cycle/${cycleId}.json`
const now = getUtc8ISOString()
const state = {
// Metadata
cycle_id: cycleId,
title: taskDescription.substring(0, 100),
description: taskDescription,
max_iterations: 5,
status: 'running',
created_at: now,
updated_at: now,
// Agent tracking
agents: {
ra: { status: 'idle', output_files: [] },
ep: { status: 'idle', output_files: [] },
cd: { status: 'idle', output_files: [] },
vas: { status: 'idle', output_files: [] }
},
// Phase tracking
current_phase: 'init',
completed_phases: [],
current_iteration: 0,
// Shared context (populated by agents)
requirements: null,
exploration: null,
plan: null,
changes: [],
test_results: null
}
Write(stateFile, JSON.stringify(state, null, 2))
return state
}
```
### Step 1.4: Resume Existing Cycle
When `--cycle-id` is provided:
```javascript
const cycleId = existingCycleId
const state = readCycleState(cycleId)
if (!state) {
console.error(`Cycle not found: ${cycleId}`)
return { status: 'error', message: 'Cycle not found' }
}
console.log(`Resuming cycle: ${cycleId}`)
// Apply extension if provided
if (extension) {
console.log(`Extension: ${extension}`)
state.description += `\n\n--- ITERATION ${state.current_iteration + 1} ---\n${extension}`
}
```
### Step 1.5: Control Signal Check
Before proceeding, verify cycle status allows continuation:
```javascript
function checkControlSignals(cycleId) {
const state = readCycleState(cycleId)
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**: `cycleId` - Unique cycle identifier
- **Variable**: `state` - Initialized or resumed cycle state object
- **Variable**: `progressDir` - `.workflow/.cycle/${cycleId}.progress`
- **TodoWrite**: Mark Phase 1 completed, Phase 2 in_progress
## Next Phase
Return to orchestrator, then auto-continue to [Phase 2: Agent Execution](02-agent-execution.md).

View File

@@ -0,0 +1,309 @@
# Phase 2: Agent Execution (Parallel)
Spawn four specialized agents in parallel and wait for all to complete with timeout handling.
## Objective
- Spawn RA, EP, CD, VAS agents simultaneously using Codex subagent pattern
- Pass cycle context and role references to each agent
- Wait for all agents with configurable timeout
- Handle timeout with convergence request
- Output: agentOutputs from all 4 agents
## Agent Role References
Each agent reads its detailed role definition at execution time:
| Agent | Role File | Main Output |
|-------|-----------|-------------|
| RA | [roles/requirements-analyst.md](../roles/requirements-analyst.md) | requirements.md |
| EP | [roles/exploration-planner.md](../roles/exploration-planner.md) | exploration.md, architecture.md, plan.json |
| CD | [roles/code-developer.md](../roles/code-developer.md) | implementation.md |
| VAS | [roles/validation-archivist.md](../roles/validation-archivist.md) | summary.md |
## Execution
### Step 2.1: Spawn RA Agent (Requirements Analyst)
```javascript
function spawnRAAgent(cycleId, state, progressDir) {
return spawn_agent({
message: `
## TASK ASSIGNMENT
### MANDATORY FIRST STEPS (Agent Execute)
1. **Read role definition**: ~/.codex/agents/requirements-analyst.md
2. Read: .workflow/project-tech.json (if exists)
3. Read: .workflow/project-guidelines.json (if exists)
4. Read: .workflow/.cycle/${cycleId}.progress/coordination/feedback.md (if exists)
---
## CYCLE CONTEXT
- **Cycle ID**: ${cycleId}
- **Progress Dir**: ${progressDir}/ra/
- **Current Iteration**: ${state.current_iteration}
- **Task Description**: ${state.description}
## CURRENT REQUIREMENTS STATE
${state.requirements ? JSON.stringify(state.requirements, null, 2) : 'No previous requirements'}
## YOUR ROLE
Requirements Analyst - Analyze and refine requirements throughout the cycle.
## RESPONSIBILITIES
1. Analyze initial task description
2. Generate comprehensive requirements specification
3. Identify edge cases and implicit requirements
4. Track requirement changes across iterations
5. Maintain requirements.md and changes.log
## DELIVERABLES
Write files to ${progressDir}/ra/:
- requirements.md: Full requirements specification
- edge-cases.md: Edge case analysis
- changes.log: NDJSON format change tracking
## OUTPUT FORMAT
\`\`\`
PHASE_RESULT:
- phase: ra
- status: success | failed
- files_written: [list]
- summary: one-line summary
- issues: []
\`\`\`
`
})
}
```
### Step 2.2: Spawn EP Agent (Exploration & Planning)
```javascript
function spawnEPAgent(cycleId, state, progressDir) {
return spawn_agent({
message: `
## TASK ASSIGNMENT
### MANDATORY FIRST STEPS (Agent Execute)
1. **Read role definition**: ~/.codex/agents/exploration-planner.md
2. Read: .workflow/project-tech.json
3. Read: .workflow/project-guidelines.json
4. Read: ${progressDir}/ra/requirements.md
---
## CYCLE CONTEXT
- **Cycle ID**: ${cycleId}
- **Progress Dir**: ${progressDir}/ep/
- **Requirements**: See requirements.md
- **Current Plan**: ${state.plan ? 'Existing' : 'None - first iteration'}
## YOUR ROLE
Exploration & Planning Agent - Explore architecture and generate implementation plan.
## RESPONSIBILITIES
1. Explore codebase architecture
2. Map integration points
3. Design implementation approach
4. Generate plan.json with task breakdown
5. Update or iterate on existing plan
## DELIVERABLES
Write files to ${progressDir}/ep/:
- exploration.md: Codebase exploration findings
- architecture.md: Architecture design
- plan.json: Implementation plan (structured)
## OUTPUT FORMAT
\`\`\`
PHASE_RESULT:
- phase: ep
- status: success | failed
- files_written: [list]
- summary: one-line summary
- plan_version: X.Y.Z
\`\`\`
`
})
}
```
### Step 2.3: Spawn CD Agent (Code Developer)
```javascript
function spawnCDAgent(cycleId, state, progressDir) {
return spawn_agent({
message: `
## TASK ASSIGNMENT
### MANDATORY FIRST STEPS (Agent Execute)
1. **Read role definition**: ~/.codex/agents/code-developer.md
2. Read: ${progressDir}/ep/plan.json
3. Read: ${progressDir}/ra/requirements.md
---
## CYCLE CONTEXT
- **Cycle ID**: ${cycleId}
- **Progress Dir**: ${progressDir}/cd/
- **Plan Version**: ${state.plan?.version || 'N/A'}
- **Previous Changes**: ${state.changes?.length || 0} files
## YOUR ROLE
Code Developer - Implement features based on plan and requirements.
## RESPONSIBILITIES
1. Implement features from plan
2. Track code changes
3. Handle integration issues
4. Maintain code quality
5. Report implementation progress and issues
## DELIVERABLES
Write files to ${progressDir}/cd/:
- implementation.md: Implementation progress and decisions
- changes.log: NDJSON format, each line: {file, action, timestamp}
- issues.md: Development issues and blockers
## OUTPUT FORMAT
\`\`\`
PHASE_RESULT:
- phase: cd
- status: success | failed | partial
- files_changed: [count]
- summary: one-line summary
- blockers: []
\`\`\`
`
})
}
```
### Step 2.4: Spawn VAS Agent (Validation & Archival)
```javascript
function spawnVASAgent(cycleId, state, progressDir) {
return spawn_agent({
message: `
## TASK ASSIGNMENT
### MANDATORY FIRST STEPS (Agent Execute)
1. **Read role definition**: ~/.codex/agents/validation-archivist.md
2. Read: ${progressDir}/cd/changes.log
---
## CYCLE CONTEXT
- **Cycle ID**: ${cycleId}
- **Progress Dir**: ${progressDir}/vas/
- **Changes Count**: ${state.changes?.length || 0}
- **Iteration**: ${state.current_iteration}
## YOUR ROLE
Validation & Archival Specialist - Validate quality and create documentation.
## RESPONSIBILITIES
1. Run tests on implemented features
2. Generate coverage reports
3. Create archival documentation
4. Summarize cycle results
5. Generate version history
## DELIVERABLES
Write files to ${progressDir}/vas/:
- validation.md: Test validation results
- test-results.json: Detailed test results
- coverage.md: Coverage report
- summary.md: Cycle summary and recommendations
## OUTPUT FORMAT
\`\`\`
PHASE_RESULT:
- phase: vas
- status: success | failed
- test_pass_rate: X%
- coverage: X%
- issues: []
\`\`\`
`
})
}
```
### Step 2.5: Launch All Agents & Wait
```javascript
// Spawn all 4 agents in parallel
console.log('Spawning agents...')
const agents = {
ra: spawnRAAgent(cycleId, state, progressDir),
ep: spawnEPAgent(cycleId, state, progressDir),
cd: spawnCDAgent(cycleId, state, progressDir),
vas: spawnVASAgent(cycleId, state, progressDir)
}
// Wait for all agents to complete
console.log('Waiting for all agents...')
const results = wait({
ids: [agents.ra, agents.ep, agents.cd, agents.vas],
timeout_ms: 1800000 // 30 minutes
})
```
### Step 2.6: Timeout Handling
```javascript
if (results.timed_out) {
console.log('Some agents timed out, sending convergence request...')
Object.entries(agents).forEach(([name, id]) => {
if (!results.status[id].completed) {
send_input({
id: id,
message: `
## TIMEOUT NOTIFICATION
Execution timeout reached. Please:
1. Output current progress to markdown file
2. Save all state updates
3. Return completion status
`
})
}
})
}
```
## Output
- **Variable**: `agents` - Map of agent names to agent IDs
- **Variable**: `results` - Wait results with completion status for each agent
- **Variable**: `agentOutputs` - Collected outputs from all 4 agents
- **TodoWrite**: Mark Phase 2 completed, Phase 3 in_progress
## Next Phase
Return to orchestrator, then auto-continue to [Phase 3: Result Aggregation & Iteration](03-result-aggregation.md).

View File

@@ -0,0 +1,230 @@
# Phase 3: Result Aggregation & Iteration
Parse agent outputs, detect issues, generate feedback, and manage the iteration loop.
## Objective
- Parse PHASE_RESULT from each agent's output
- Aggregate results into unified state
- Detect issues (test failures, blockers)
- Generate targeted feedback for affected agents
- Manage iteration loop (continue or proceed to completion)
- Output: parsedResults, iteration decision
## Execution
### Step 3.1: Collect Agent Outputs
```javascript
// Collect outputs from all 4 agents
const agentOutputs = {
ra: results.status[agents.ra].completed,
ep: results.status[agents.ep].completed,
cd: results.status[agents.cd].completed,
vas: results.status[agents.vas].completed
}
```
### Step 3.2: Parse PHASE_RESULT
Each agent outputs a structured PHASE_RESULT block. Parse it to extract status and data:
```javascript
function parseAgentOutputs(agentOutputs) {
const results = {
ra: parseOutput(agentOutputs.ra, 'ra'),
ep: parseOutput(agentOutputs.ep, 'ep'),
cd: parseOutput(agentOutputs.cd, 'cd'),
vas: parseOutput(agentOutputs.vas, 'vas')
}
return results
}
function parseOutput(output, agent) {
const result = {
agent: agent,
status: 'unknown',
data: {}
}
// Parse PHASE_RESULT block
const match = output.match(/PHASE_RESULT:\s*([\s\S]*?)(?:\n\n|$)/)
if (match) {
const lines = match[1].split('\n')
for (const line of lines) {
const m = line.match(/^-\s*(\w+):\s*(.+)$/)
if (m) {
result[m[1]] = m[2].trim()
}
}
}
return result
}
```
### Step 3.3: Update State with Results
```javascript
// Update agent states
state.agents.ra.status = 'completed'
state.agents.ep.status = 'completed'
state.agents.cd.status = 'completed'
state.agents.vas.status = 'completed'
// Update shared context from parsed results
state.requirements = parsedResults.ra.requirements
state.exploration = parsedResults.ep.exploration
state.plan = parsedResults.ep.plan
state.changes = parsedResults.cd.changes
state.test_results = parsedResults.vas.test_results
state.completed_phases.push(...['ra', 'ep', 'cd', 'vas'])
state.updated_at = getUtc8ISOString()
// Persist state
Write(`.workflow/.cycle/${cycleId}.json`, JSON.stringify(state, null, 2))
```
### Step 3.4: Issue Detection
```javascript
const hasIssues = parsedResults.vas.test_results?.passed === false ||
parsedResults.cd.issues?.length > 0
if (hasIssues && iteration < maxIterations) {
console.log('Issues detected, preparing for next iteration...')
// → Proceed to Step 3.5 (Feedback Generation)
} else if (!hasIssues) {
console.log('All phases completed successfully')
// → Proceed to Phase 4
} else if (iteration >= maxIterations) {
console.log(`Reached maximum iterations (${maxIterations})`)
// → Proceed to Phase 4 with issues documented
}
```
### Step 3.5: Feedback Generation
Generate targeted feedback based on issue type:
```javascript
function generateFeedback(parsedResults) {
const feedback = {}
// Check VAS results → feedback to CD
if (parsedResults.vas.test_pass_rate < 100) {
feedback.cd = `
## FEEDBACK FROM VALIDATION
Test pass rate: ${parsedResults.vas.test_pass_rate}%
## ISSUES TO FIX
${parsedResults.vas.data.issues || 'See test-results.json for details'}
## NEXT STEP
Fix failing tests and update implementation.md with resolution.
`
}
// Check CD blockers → feedback to RA
if (parsedResults.cd.blockers?.length > 0) {
feedback.ra = `
## FEEDBACK FROM DEVELOPMENT
Blockers encountered:
${parsedResults.cd.blockers.map(b => `- ${b}`).join('\n')}
## NEXT STEP
Clarify requirements or identify alternative approaches.
Update requirements.md if needed.
`
}
return feedback
}
```
### Step 3.6: Send Feedback via send_input
```javascript
const feedback = generateFeedback(parsedResults)
// Send feedback to relevant agents
if (feedback.ra) {
send_input({
id: agents.ra,
message: feedback.ra
})
}
if (feedback.cd) {
send_input({
id: agents.cd,
message: feedback.cd
})
}
// Wait for agents to process feedback and update
const updatedResults = wait({
ids: [agents.ra, agents.cd].filter(Boolean),
timeout_ms: 900000 // 15 minutes for fixes
})
console.log('Agents updated, continuing...')
```
### Step 3.7: Iteration Loop Decision
```javascript
// After feedback processing, decide next action:
//
// Option A: Issues remain AND iteration < max
// → Loop back to Phase 2 (re-spawn or continue agents)
//
// Option B: No issues remaining
// → Proceed to Phase 4 (Completion)
//
// Option C: Max iterations reached
// → Proceed to Phase 4 with issues documented
if (hasIssues && iteration < maxIterations) {
// Continue iteration loop
iteration++
state.current_iteration = iteration
// → Back to Phase 2
} else {
// Exit loop → Phase 4
continueLoop = false
}
```
## Iteration Flow Diagram
```
Phase 2: Agent Execution
Phase 3: Result Aggregation
┌─ Issues detected?
│ ├─ No → Phase 4 (Complete)
│ └─ Yes
│ ├─ iteration < max?
│ │ ├─ Yes → Generate feedback → send_input → Wait → Back to Phase 2
│ │ └─ No → Phase 4 (Complete with issues)
```
## Output
- **Variable**: `parsedResults` - Parsed results from all 4 agents
- **Variable**: `hasIssues` - Boolean indicating if issues were found
- **Variable**: `continueLoop` - Boolean indicating if iteration should continue
- **TodoWrite**: Mark Phase 3 completed, Phase 4 in_progress (or loop)
## Next Phase
If iteration continues: Return to Phase 2.
If iteration completes: Return to orchestrator, then auto-continue to [Phase 4: Completion & Summary](04-completion-summary.md).

View File

@@ -0,0 +1,109 @@
# Phase 4: Completion & Summary
Generate unified summary report, update final state, close all agents, and provide continuation instructions.
## Objective
- Generate comprehensive cycle summary report
- Update master state file with final status
- Close all agent sessions
- Provide continuation instructions for future iterations
- Output: final cycle report
## Execution
### Step 4.1: Generate Final Summary
```javascript
function generateFinalSummary(cycleId, state) {
const summaryFile = `.workflow/.cycle/${cycleId}.progress/coordination/summary.md`
const summary = `# Cycle Summary - ${cycleId}
## Metadata
- Cycle ID: ${cycleId}
- Started: ${state.created_at}
- Completed: ${state.completed_at}
- Iterations: ${state.current_iteration}
- Status: ${state.status}
## Phase Results
- Requirements Analysis: ✓ Completed
- Exploration & Planning: ✓ Completed
- Code Development: ✓ Completed
- Validation & Archival: ✓ Completed
## Key Deliverables
- Requirements: ${state.requirements ? '✓' : '✗'}
- Architecture Plan: ${state.plan ? '✓' : '✗'}
- Code Changes: ${state.changes?.length || 0} files
- Test Results: ${state.test_results?.pass_rate || '0'}% passing
## Generated Files
- .workflow/.cycle/${cycleId}.progress/ra/requirements.md
- .workflow/.cycle/${cycleId}.progress/ep/plan.json
- .workflow/.cycle/${cycleId}.progress/cd/changes.log
- .workflow/.cycle/${cycleId}.progress/vas/summary.md
## Continuation Instructions
To extend this cycle:
\`\`\`bash
/parallel-dev-cycle --cycle-id=${cycleId} --extend="New requirement or feedback"
\`\`\`
This will spawn agents for iteration ${state.current_iteration + 1}.
`
Write(summaryFile, summary)
}
```
### Step 4.2: Update Final State
```javascript
state.status = 'completed'
state.completed_at = getUtc8ISOString()
Write(`.workflow/.cycle/${cycleId}.json`, JSON.stringify(state, null, 2))
```
### Step 4.3: Close All Agents
```javascript
Object.values(agents).forEach(id => {
try {
close_agent({ id })
} catch (e) {
console.warn(`Failed to close agent ${id}`)
}
})
```
### Step 4.4: Return Result
```javascript
console.log('\n=== Parallel Dev Cycle Orchestrator Finished ===')
return {
status: 'completed',
cycle_id: cycleId,
iterations: iteration,
final_state: state
}
```
## Output
- **File**: `.workflow/.cycle/{cycleId}.progress/coordination/summary.md`
- **File**: `.workflow/.cycle/{cycleId}.json` (final state)
- **TodoWrite**: Mark Phase 4 completed (all tasks done)
## Completion
Parallel Dev Cycle has completed. The cycle report is at `.workflow/.cycle/{cycleId}.progress/coordination/summary.md`.
To continue iterating:
```bash
/parallel-dev-cycle --cycle-id={cycleId} --extend="Additional requirements or feedback"
```

View File

@@ -1,696 +0,0 @@
# Orchestrator - Multi-Agent Coordination (Codex Pattern)
Orchestrate parallel dev cycle using Codex subagent pattern with continuous iteration support.
## Role
Coordinate four specialized agents → Manage state → Support continuous iteration → Generate unified documentation.
## Codex Pattern Overview
```
Main Orchestrator Flow:
┌─── spawn_agent (orchestrator role) ────────────────────────────┐
│ │
│ Phase 1: INIT (Check control signals) │
│ ↓ │
│ wait() → Parse cycle state │
│ ↓ │
│ Phase 2: AGENT ORCHESTRATION │
│ ↓ │
│ spawn_agent(RA) | spawn_agent(EP) │
│ spawn_agent(CD) | spawn_agent(VAS) │
│ ↓ │
│ wait({ ids: [RA, EP, CD, VAS] }) → Collect all results │
│ ↓ │
│ Phase 3: ITERATION HANDLING │
│ ↓ │
│ [If extension needed] │
│ send_input to affected agents │
│ wait() for updated results │
│ ↓ │
│ Phase 4: AGGREGATION │
│ ↓ │
│ Merge all outputs → Generate unified documentation │
│ ↓ │
│ Update cycle state │
│ ↓ │
│ [Loop if more iterations] │
│ ↓ │
│ close_agent() when complete │
│ │
└─────────────────────────────────────────────────────────────────┘
```
## State Management
### Read Cycle State
```javascript
const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()
function readCycleState(cycleId) {
const stateFile = `.workflow/.cycle/${cycleId}.json`
if (!fs.existsSync(stateFile)) {
return null
}
return JSON.parse(Read(stateFile))
}
```
### Create New Cycle State
```javascript
function createCycleState(cycleId, taskDescription) {
const stateFile = `.workflow/.cycle/${cycleId}.json`
const now = getUtc8ISOString()
const state = {
// Metadata
cycle_id: cycleId,
title: taskDescription.substring(0, 100),
description: taskDescription,
max_iterations: 5,
status: 'running',
created_at: now,
updated_at: now,
// Agent tracking
agents: {
ra: { status: 'idle', output_files: [] },
ep: { status: 'idle', output_files: [] },
cd: { status: 'idle', output_files: [] },
vas: { status: 'idle', output_files: [] }
},
// Phase tracking
current_phase: 'init',
completed_phases: [],
current_iteration: 0,
// Shared context (populated by agents)
requirements: null,
exploration: null,
plan: null,
changes: [],
test_results: null
}
// Create directories
mkdir -p `.workflow/.cycle/${cycleId}.progress/{ra,ep,cd,vas,coordination}`
Write(stateFile, JSON.stringify(state, null, 2))
return state
}
```
## Main Execution Flow (Codex Subagent)
```javascript
async function runOrchestrator(options = {}) {
const { cycleId: existingCycleId, task, mode = 'interactive', extension } = options
console.log('=== Parallel Dev Cycle Orchestrator Started ===')
// 1. Determine cycleId and initial state
let cycleId
let state
if (existingCycleId) {
// Continue existing cycle
cycleId = existingCycleId
state = readCycleState(cycleId)
if (!state) {
console.error(`Cycle not found: ${cycleId}`)
return { status: 'error', message: 'Cycle not found' }
}
console.log(`Resuming cycle: ${cycleId}`)
if (extension) {
console.log(`Extension: ${extension}`)
state.description += `\n\n--- ITERATION ${state.current_iteration + 1} ---\n${extension}`
}
} else if (task) {
// Create new cycle
const timestamp = getUtc8ISOString().replace(/[-:]/g, '').split('.')[0]
const random = Math.random().toString(36).substring(2, 10)
cycleId = `cycle-v1-${timestamp}-${random}`
console.log(`Creating new cycle: ${cycleId}`)
state = createCycleState(cycleId, task)
} else {
console.error('Either --cycle-id or task description is required')
return { status: 'error', message: 'Missing cycleId or task' }
}
const progressDir = `.workflow/.cycle/${cycleId}.progress`
// 2. Main orchestration loop
let iteration = state.current_iteration || 0
const maxIterations = state.max_iterations || 5
let continueLoop = true
while (continueLoop && iteration < maxIterations) {
iteration++
state.current_iteration = iteration
console.log(`\n========== ITERATION ${iteration} ==========`)
// 3. Spawn four agents in parallel
console.log('Spawning agents...')
const agents = {
ra: spawnRAAgent(cycleId, state, progressDir),
ep: spawnEPAgent(cycleId, state, progressDir),
cd: spawnCDAgent(cycleId, state, progressDir),
vas: spawnVASAgent(cycleId, state, progressDir)
}
// 4. Wait for all agents to complete
console.log('Waiting for all agents...')
const results = wait({
ids: [agents.ra, agents.ep, agents.cd, agents.vas],
timeout_ms: 1800000 // 30 minutes
})
if (results.timed_out) {
console.log('Some agents timed out, sending convergence request...')
Object.entries(agents).forEach(([name, id]) => {
if (!results.status[id].completed) {
send_input({
id: id,
message: `
## TIMEOUT NOTIFICATION
Execution timeout reached. Please:
1. Output current progress to markdown file
2. Save all state updates
3. Return completion status
`
})
}
})
continue
}
// 5. Collect all agent outputs
const agentOutputs = {
ra: results.status[agents.ra].completed,
ep: results.status[agents.ep].completed,
cd: results.status[agents.cd].completed,
vas: results.status[agents.vas].completed
}
// 6. Parse and aggregate results
const parsedResults = parseAgentOutputs(agentOutputs)
// Update state with agent results
state.agents.ra.status = 'completed'
state.agents.ep.status = 'completed'
state.agents.cd.status = 'completed'
state.agents.vas.status = 'completed'
state.requirements = parsedResults.ra.requirements
state.exploration = parsedResults.ep.exploration
state.plan = parsedResults.ep.plan
state.changes = parsedResults.cd.changes
state.test_results = parsedResults.vas.test_results
state.completed_phases.push(...['ra', 'ep', 'cd', 'vas'])
state.updated_at = getUtc8ISOString()
// Save state
Write(`.workflow/.cycle/${cycleId}.json`, JSON.stringify(state, null, 2))
// 7. Check for issues and determine next iteration
const hasIssues = parsedResults.vas.test_results?.passed === false ||
parsedResults.cd.issues?.length > 0
if (hasIssues && iteration < maxIterations) {
console.log('Issues detected, preparing for next iteration...')
// Generate feedback for agents
const feedback = generateFeedback(parsedResults)
// Send feedback to relevant agents
if (feedback.ra) {
send_input({
id: agents.ra,
message: feedback.ra
})
}
if (feedback.cd) {
send_input({
id: agents.cd,
message: feedback.cd
})
}
// Wait for updates
const updatedResults = wait({
ids: [agents.ra, agents.cd].filter(Boolean),
timeout_ms: 900000
})
console.log('Agents updated, continuing...')
} else if (!hasIssues) {
console.log('All phases completed successfully')
continueLoop = false
} else if (iteration >= maxIterations) {
console.log(`Reached maximum iterations (${maxIterations})`)
continueLoop = false
}
}
// 8. Generate unified summary
console.log('Generating final summary...')
generateFinalSummary(cycleId, state)
// 9. Update final state
state.status = 'completed'
state.completed_at = getUtc8ISOString()
Write(`.workflow/.cycle/${cycleId}.json`, JSON.stringify(state, null, 2))
// 10. Cleanup
Object.values(agents).forEach(id => {
try {
close_agent({ id })
} catch (e) {
console.warn(`Failed to close agent ${id}`)
}
})
console.log('\n=== Parallel Dev Cycle Orchestrator Finished ===')
return {
status: 'completed',
cycle_id: cycleId,
iterations: iteration,
final_state: state
}
}
```
## Agent Spawning Functions
### Spawn RA Agent
```javascript
function spawnRAAgent(cycleId, state, progressDir) {
return spawn_agent({
message: `
## TASK ASSIGNMENT
### MANDATORY FIRST STEPS (Agent Execute)
1. **Read role definition**: ~/.codex/agents/requirements-analyst.md
2. Read: .workflow/project-tech.json (if exists)
3. Read: .workflow/project-guidelines.json (if exists)
4. Read: .workflow/.cycle/${cycleId}.progress/coordination/feedback.md (if exists)
---
## CYCLE CONTEXT
- **Cycle ID**: ${cycleId}
- **Progress Dir**: ${progressDir}/ra/
- **Current Iteration**: ${state.current_iteration}
- **Task Description**: ${state.description}
## CURRENT REQUIREMENTS STATE
${state.requirements ? JSON.stringify(state.requirements, null, 2) : 'No previous requirements'}
## YOUR ROLE
Requirements Analyst - Analyze and refine requirements throughout the cycle.
## RESPONSIBILITIES
1. Analyze initial task description
2. Generate comprehensive requirements specification
3. Identify edge cases and implicit requirements
4. Track requirement changes across iterations
5. Maintain requirements.md and changes.log
## DELIVERABLES
Write files to ${progressDir}/ra/:
- requirements.md: Full requirements specification
- edge-cases.md: Edge case analysis
- changes.log: NDJSON format change tracking
## OUTPUT FORMAT
\`\`\`
PHASE_RESULT:
- phase: ra
- status: success | failed
- files_written: [list]
- summary: one-line summary
- issues: []
\`\`\`
`
})
}
```
### Spawn EP Agent
```javascript
function spawnEPAgent(cycleId, state, progressDir) {
return spawn_agent({
message: `
## TASK ASSIGNMENT
### MANDATORY FIRST STEPS (Agent Execute)
1. **Read role definition**: ~/.codex/agents/exploration-planner.md
2. Read: .workflow/project-tech.json
3. Read: .workflow/project-guidelines.json
4. Read: ${progressDir}/ra/requirements.md
---
## CYCLE CONTEXT
- **Cycle ID**: ${cycleId}
- **Progress Dir**: ${progressDir}/ep/
- **Requirements**: See requirements.md
- **Current Plan**: ${state.plan ? 'Existing' : 'None - first iteration'}
## YOUR ROLE
Exploration & Planning Agent - Explore architecture and generate implementation plan.
## RESPONSIBILITIES
1. Explore codebase architecture
2. Map integration points
3. Design implementation approach
4. Generate plan.json with task breakdown
5. Update or iterate on existing plan
## DELIVERABLES
Write files to ${progressDir}/ep/:
- exploration.md: Codebase exploration findings
- architecture.md: Architecture design
- plan.json: Implementation plan (structured)
## OUTPUT FORMAT
\`\`\`
PHASE_RESULT:
- phase: ep
- status: success | failed
- files_written: [list]
- summary: one-line summary
- plan_version: X.Y.Z
\`\`\`
`
})
}
```
### Spawn CD Agent
```javascript
function spawnCDAgent(cycleId, state, progressDir) {
return spawn_agent({
message: `
## TASK ASSIGNMENT
### MANDATORY FIRST STEPS (Agent Execute)
1. **Read role definition**: ~/.codex/agents/code-developer.md
2. Read: ${progressDir}/ep/plan.json
3. Read: ${progressDir}/ra/requirements.md
---
## CYCLE CONTEXT
- **Cycle ID**: ${cycleId}
- **Progress Dir**: ${progressDir}/cd/
- **Plan Version**: ${state.plan?.version || 'N/A'}
- **Previous Changes**: ${state.changes?.length || 0} files
## YOUR ROLE
Code Developer - Implement features based on plan and requirements.
## RESPONSIBILITIES
1. Implement features from plan
2. Track code changes
3. Handle integration issues
4. Maintain code quality
5. Report implementation progress and issues
## DELIVERABLES
Write files to ${progressDir}/cd/:
- implementation.md: Implementation progress and decisions
- changes.log: NDJSON format, each line: {file, action, timestamp}
- issues.md: Development issues and blockers
## OUTPUT FORMAT
\`\`\`
PHASE_RESULT:
- phase: cd
- status: success | failed | partial
- files_changed: [count]
- summary: one-line summary
- blockers: []
\`\`\`
`
})
}
```
### Spawn VAS Agent
```javascript
function spawnVASAgent(cycleId, state, progressDir) {
return spawn_agent({
message: `
## TASK ASSIGNMENT
### MANDATORY FIRST STEPS (Agent Execute)
1. **Read role definition**: ~/.codex/agents/validation-archivist.md
2. Read: ${progressDir}/cd/changes.log
---
## CYCLE CONTEXT
- **Cycle ID**: ${cycleId}
- **Progress Dir**: ${progressDir}/vas/
- **Changes Count**: ${state.changes?.length || 0}
- **Iteration**: ${state.current_iteration}
## YOUR ROLE
Validation & Archival Specialist - Validate quality and create documentation.
## RESPONSIBILITIES
1. Run tests on implemented features
2. Generate coverage reports
3. Create archival documentation
4. Summarize cycle results
5. Generate version history
## DELIVERABLES
Write files to ${progressDir}/vas/:
- validation.md: Test validation results
- test-results.json: Detailed test results
- coverage.md: Coverage report
- summary.md: Cycle summary and recommendations
## OUTPUT FORMAT
\`\`\`
PHASE_RESULT:
- phase: vas
- status: success | failed
- test_pass_rate: X%
- coverage: X%
- issues: []
\`\`\`
`
})
}
```
## Result Parsing
```javascript
function parseAgentOutputs(agentOutputs) {
const results = {
ra: parseOutput(agentOutputs.ra, 'ra'),
ep: parseOutput(agentOutputs.ep, 'ep'),
cd: parseOutput(agentOutputs.cd, 'cd'),
vas: parseOutput(agentOutputs.vas, 'vas')
}
return results
}
function parseOutput(output, agent) {
const result = {
agent: agent,
status: 'unknown',
data: {}
}
// Parse PHASE_RESULT block
const match = output.match(/PHASE_RESULT:\s*([\s\S]*?)(?:\n\n|$)/)
if (match) {
const lines = match[1].split('\n')
for (const line of lines) {
const m = line.match(/^-\s*(\w+):\s*(.+)$/)
if (m) {
result[m[1]] = m[2].trim()
}
}
}
return result
}
```
## Feedback Generation
```javascript
function generateFeedback(parsedResults) {
const feedback = {}
// Check VAS results
if (parsedResults.vas.test_pass_rate < 100) {
feedback.cd = `
## FEEDBACK FROM VALIDATION
Test pass rate: ${parsedResults.vas.test_pass_rate}%
## ISSUES TO FIX
${parsedResults.vas.data.issues || 'See test-results.json for details'}
## NEXT STEP
Fix failing tests and update implementation.md with resolution.
`
}
// Check CD blockers
if (parsedResults.cd.blockers?.length > 0) {
feedback.ra = `
## FEEDBACK FROM DEVELOPMENT
Blockers encountered:
${parsedResults.cd.blockers.map(b => `- ${b}`).join('\n')}
## NEXT STEP
Clarify requirements or identify alternative approaches.
Update requirements.md if needed.
`
}
return feedback
}
```
## Summary Generation
```javascript
function generateFinalSummary(cycleId, state) {
const summaryFile = `.workflow/.cycle/${cycleId}.progress/coordination/summary.md`
const summary = `# Cycle Summary - ${cycleId}
## Metadata
- Cycle ID: ${cycleId}
- Started: ${state.created_at}
- Completed: ${state.completed_at}
- Iterations: ${state.current_iteration}
- Status: ${state.status}
## Phase Results
- Requirements Analysis: ✓ Completed
- Exploration & Planning: ✓ Completed
- Code Development: ✓ Completed
- Validation & Archival: ✓ Completed
## Key Deliverables
- Requirements: ${state.requirements ? '✓' : '✗'}
- Architecture Plan: ${state.plan ? '✓' : '✗'}
- Code Changes: ${state.changes?.length || 0} files
- Test Results: ${state.test_results?.pass_rate || '0'}% passing
## Generated Files
- .workflow/.cycle/${cycleId}.progress/ra/requirements.md
- .workflow/.cycle/${cycleId}.progress/ep/plan.json
- .workflow/.cycle/${cycleId}.progress/cd/changes.log
- .workflow/.cycle/${cycleId}.progress/vas/summary.md
## Continuation Instructions
To extend this cycle:
\`\`\`bash
/parallel-dev-cycle --cycle-id=${cycleId} --extend="New requirement or feedback"
\`\`\`
This will spawn agents for iteration ${state.current_iteration + 1}.
`
Write(summaryFile, summary)
}
```
## Control Signal Checking
```javascript
function checkControlSignals(cycleId) {
const state = readCycleState(cycleId)
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' }
}
}
```
## Error Recovery Strategies
| Error Type | Recovery |
|------------|----------|
| Agent timeout | send_input requesting convergence |
| State corrupted | Rebuild from progress markdown files |
| Agent failed | Re-spawn agent with previous context |
| Conflicting results | Orchestrator sends reconciliation request |
| Missing files | RA/EP agents identify and request clarification |
## Codex Best Practices Applied
1. **Single Orchestrator**: One main agent manages all phases
2. **Parallel Workers**: Four specialized agents execute simultaneously
3. **Batch wait()**: Wait for all agents with `wait({ ids: [...] })`
4. **Deep Interaction**: Use send_input for iteration and refinement
5. **Delayed close_agent**: Only after all phases and iterations complete
6. **Role Path Passing**: Each agent reads its own role definition
7. **Persistent Context**: Cycle state shared across all agents

View File

@@ -1,436 +0,0 @@
# State Schema - Parallel Dev Cycle
Unified cycle state structure for multi-agent coordination and iteration support.
## State File Location
**Location**: `.workflow/.cycle/{cycleId}.json` (unified state, all agents access)
**Format**: JSON
## Cycle State Interface
```typescript
interface CycleState {
// =====================================================
// CORE METADATA
// =====================================================
cycle_id: string // Unique cycle identifier
title: string // Task title (first 100 chars)
description: string // Full task description
task_history: string[] // All task descriptions across iterations
// =====================================================
// STATUS & TIMING
// =====================================================
status: 'created' | 'running' | 'paused' | 'completed' | 'failed'
created_at: string // ISO8601 format
updated_at: string // ISO8601 format
completed_at?: string // ISO8601 format
max_iterations: number // Maximum iteration limit
current_iteration: number // Current iteration count
failure_reason?: string // If failed, why
// =====================================================
// MULTI-AGENT TRACKING
// =====================================================
agents: {
ra: AgentState // Requirements Analyst
ep: AgentState // Exploration Planner
cd: AgentState // Code Developer
vas: AgentState // Validation Archivist
}
// =====================================================
// PHASE TRACKING
// =====================================================
current_phase: 'init' | 'ra' | 'ep' | 'cd' | 'vas' | 'aggregation' | 'complete'
completed_phases: string[]
phase_errors: Array<{
phase: string
error: string
timestamp: string
}>
// =====================================================
// SHARED CONTEXT (Populated by agents)
// =====================================================
requirements?: {
version: string // e.g., "1.0.0", "1.1.0"
specification: string // Full spec from requirements.md
edge_cases: string[]
last_updated: string
}
exploration?: {
version: string
architecture_summary: string
integration_points: string[]
identified_risks: string[]
last_updated: string
}
plan?: {
version: string
tasks: PlanTask[]
total_estimated_effort: string
critical_path: string[]
last_updated: string
}
changes?: {
total_files: number
changes: ChangeLog[]
iteration_markers: Record<number, string> // Iteration timestamps
}
test_results?: {
version: string
pass_rate: number // 0-100
coverage: number // 0-100
failed_tests: string[]
total_tests: number
last_run: string
}
// =====================================================
// ITERATION TRACKING
// =====================================================
iterations: IterationRecord[]
// =====================================================
// COORDINATION DATA
// =====================================================
coordination: {
feedback_log: FeedbackEntry[]
pending_decisions: Decision[]
blockers: Blocker[]
}
}
// =====================================================
// SUPPORTING TYPES
// =====================================================
interface AgentState {
status: 'idle' | 'running' | 'waiting' | 'completed' | 'failed'
started_at?: string
completed_at?: string
output_files: string[]
last_message?: string
error?: string
iterations_completed: number
}
interface PlanTask {
id: string // e.g., "TASK-001"
description: string
effort: 'small' | 'medium' | 'large'
depends_on: string[]
status: 'pending' | 'in_progress' | 'completed' | 'blocked'
assigned_to?: string // Agent name
files: string[]
}
interface ChangeLog {
timestamp: string
file: string
action: 'create' | 'modify' | 'delete'
iteration: number
agent: string // which agent made change
description: string
}
interface IterationRecord {
number: number
extension?: string // User feedback/extension for this iteration
started_at: string
completed_at: string
agent_results: Record<string, {
status: string
files_modified: number
}>
issues_found: string[]
resolved: boolean
}
interface FeedbackEntry {
timestamp: string
source: string // Agent or 'user'
target: string // Recipient agent
content: string
type: 'requirement_update' | 'bug_report' | 'issue_fix' | 'clarification'
}
interface Decision {
id: string
description: string
options: string[]
made_by?: string
chosen_option?: string
status: 'pending' | 'made' | 'implemented'
}
interface Blocker {
id: string
description: string
reported_by: string
status: 'open' | 'resolved' | 'workaround'
resolution?: string
}
```
## Initial State (New Cycle)
When creating a new cycle:
```json
{
"cycle_id": "cycle-v1-20260122T100000-abc123",
"title": "Implement OAuth authentication",
"description": "Add OAuth2 login support with Google and GitHub providers",
"task_history": [
"Implement OAuth authentication"
],
"status": "created",
"created_at": "2026-01-22T10:00:00+08:00",
"updated_at": "2026-01-22T10:00:00+08:00",
"max_iterations": 5,
"current_iteration": 0,
"agents": {
"ra": { "status": "idle", "output_files": [], "iterations_completed": 0 },
"ep": { "status": "idle", "output_files": [], "iterations_completed": 0 },
"cd": { "status": "idle", "output_files": [], "iterations_completed": 0 },
"vas": { "status": "idle", "output_files": [], "iterations_completed": 0 }
},
"current_phase": "init",
"completed_phases": [],
"phase_errors": [],
"iterations": [],
"coordination": {
"feedback_log": [],
"pending_decisions": [],
"blockers": []
}
}
```
## State Transitions
### Iteration 1: Initial Execution
```json
{
"status": "running",
"current_iteration": 1,
"current_phase": "ra",
"agents": {
"ra": { "status": "running", "started_at": "2026-01-22T10:05:00+08:00" },
"ep": { "status": "idle" },
"cd": { "status": "idle" },
"vas": { "status": "idle" }
},
"requirements": {
"version": "1.0.0",
"specification": "...",
"edge_cases": ["OAuth timeout handling", "PKCE validation"],
"last_updated": "2026-01-22T10:15:00+08:00"
},
"iterations": [{
"number": 1,
"started_at": "2026-01-22T10:00:00+08:00",
"agent_results": {
"ra": { "status": "completed", "files_modified": 3 },
"ep": { "status": "completed", "files_modified": 2 },
"cd": { "status": "partial", "files_modified": 5 },
"vas": { "status": "pending", "files_modified": 0 }
}
}]
}
```
### After Phase Completion
```json
{
"current_phase": "aggregation",
"completed_phases": ["ra", "ep", "cd", "vas"],
"plan": {
"version": "1.0.0",
"tasks": [
{
"id": "TASK-001",
"description": "Setup OAuth application credentials",
"effort": "small",
"status": "completed",
"files": ["src/config/oauth.ts"]
}
]
},
"changes": {
"total_files": 12,
"iteration_markers": {
"1": "2026-01-22T10:30:00+08:00"
}
},
"test_results": {
"version": "1.0.0",
"pass_rate": 85,
"coverage": 78,
"failed_tests": ["test: OAuth timeout retry"],
"total_tests": 20
}
}
```
### Iteration 2: User Extension
User provides feedback: "Also add multi-factor authentication"
```json
{
"status": "running",
"current_iteration": 2,
"task_history": [
"Implement OAuth authentication",
"Also add multi-factor authentication"
],
"description": "Add OAuth2 login support with Google and GitHub providers\n\n--- ITERATION 2 ---\nAlso add multi-factor authentication",
"agents": {
"ra": { "status": "running", "iterations_completed": 1 },
"ep": { "status": "idle", "iterations_completed": 1 },
"cd": { "status": "idle", "iterations_completed": 1 },
"vas": { "status": "idle", "iterations_completed": 1 }
},
"requirements": {
"version": "1.1.0",
"specification": "...",
"last_updated": "2026-01-22T11:00:00+08:00"
},
"iterations": [
{ "number": 1, "completed_at": "..." },
{
"number": 2,
"extension": "Also add multi-factor authentication",
"started_at": "2026-01-22T10:45:00+08:00",
"agent_results": {}
}
],
"coordination": {
"feedback_log": [{
"timestamp": "2026-01-22T10:45:00+08:00",
"source": "user",
"target": "ra",
"content": "Add multi-factor authentication to requirements",
"type": "requirement_update"
}]
}
}
```
## Version Tracking
Each component tracks its version:
- **Requirements**: `1.0.0``1.1.0``1.2.0` (each iteration)
- **Plan**: `1.0.0``1.1.0` (updated based on requirements)
- **Code**: Changes appended with iteration markers
- **Tests**: Results tracked per iteration
## File Sync Protocol
State changes trigger file writes:
| State Change | File Sync |
|--------------|-----------|
| `requirements` updated | `.progress/ra/requirements.md` + version bump |
| `plan` updated | `.progress/ep/plan.json` + version bump |
| `changes` appended | `.progress/cd/changes.log` + iteration marker |
| `test_results` updated | `.progress/vas/test-results.json` + version bump |
| Full iteration done | `.progress/coordination/timeline.md` appended |
## Control Signal Checking
Agents check status before each action:
```javascript
function checkControlSignals(cycleId) {
const state = JSON.parse(Read(`.workflow/.cycle/${cycleId}.json`))
if (state.status === 'paused') {
return { continue: false, action: 'pause' }
}
if (state.status === 'failed') {
return { continue: false, action: 'stop' }
}
if (state.status === 'running') {
return { continue: true, action: 'continue' }
}
return { continue: false, action: 'unknown' }
}
```
## State Persistence
### Write Operations
After each agent completes or phase transitions:
```javascript
Write(
`.workflow/.cycle/${cycleId}.json`,
JSON.stringify(state, null, 2)
)
```
### Read Operations
Agents always read fresh state before executing:
```javascript
const currentState = JSON.parse(
Read(`.workflow/.cycle/${cycleId}.json`)
)
```
## State Rebuild (Recovery)
If master state corrupted, rebuild from markdown files:
```javascript
function rebuildState(cycleId) {
const progressDir = `.workflow/.cycle/${cycleId}.progress`
// Read markdown files
const raMarkdown = Read(`${progressDir}/ra/requirements.md`)
const epMarkdown = Read(`${progressDir}/ep/plan.json`)
const cdChanges = Read(`${progressDir}/cd/changes.log`)
const vasResults = Read(`${progressDir}/vas/test-results.json`)
// Reconstruct state from files
return {
requirements: parseMarkdown(raMarkdown),
plan: JSON.parse(epMarkdown),
changes: parseNDJSON(cdChanges),
test_results: JSON.parse(vasResults)
}
}
```
## Best Practices
1. **Immutable Reads**: Never modify state during read
2. **Version Bumps**: Increment version on each iteration
3. **Timestamp Accuracy**: Use UTC+8 consistently
4. **Append-Only Logs**: Never delete history
5. **Atomic Writes**: Write complete state, not partial updates
6. **Coordination Tracking**: Log all inter-agent communication

View File

@@ -1,423 +0,0 @@
# Agent Communication Optimization
优化 agent 通信机制:使用简短的产出文件引用而不是内容传递。
## 背景
在多 agent 系统中,传递完整的文件内容会导致:
- 消息体积过大
- 上下文使用量增加
- 通信效率低下
- 容易引入上下文断层
**优化方案**: 使用文件路径引用,让 agent 自动读取需要的文件。
## 优化原则
### 原则 1: 文件引用而非内容传递
**错误做法**(传递内容):
```javascript
send_input({
id: agents.cd,
message: `
Requirements:
${requirements_content} // 完整内容 - 浪费空间
Plan:
${plan_json} // 完整 JSON - 重复信息
`
})
```
**正确做法**(引用文件):
```javascript
send_input({
id: agents.cd,
message: `
## Feedback from Validation
Test failures found. Review these outputs:
## Reference
- Requirements: .workflow/.cycle/${cycleId}.progress/ra/requirements.md (v1.0.0)
- Plan: .workflow/.cycle/${cycleId}.progress/ep/plan.json (v1.0.0)
- Test Results: .workflow/.cycle/${cycleId}.progress/vas/test-results.json
## Issues Found
${summary_of_issues} // 只传递摘要
## Actions Required
1. Fix OAuth token refresh (test line 45)
2. Update implementation.md with fixes
`
})
```
### 原则 2: 摘要而非全文
**错误**
```javascript
// 传递所有文件内容
RA输出: "requirements.md (2000 lines) + edge-cases.md (1000 lines) + changes.log (500 lines)"
EP读取: 全文解析所有内容浪费token
```
**正确**
```javascript
// 只传递关键摘要
RA输出:
- 10个功能需求
- 5个非功能需求
- 8个边界场景
- 文件路径用于完整查看
EP读取: 读取摘要 + 需要时查看完整文件高效
```
### 原则 3: 文件版本跟踪
每个引用必须包含版本:
```javascript
send_input({
id: agents.cd,
message: `
Requirements: .workflow/.cycle/${cycleId}.progress/ra/requirements.md (v1.1.0)
^^^^^^^ 版本号
Plan: .workflow/.cycle/${cycleId}.progress/ep/plan.json (v1.0.0)
^^^^^^^ 版本号
`
})
```
**好处**:
- 避免使用过期信息
- 自动检测版本不匹配
- 支持多版本迭代
## 实现模式
### Pattern 1: 通知 + 引用
Agent 向其他 agent 通知输出,而非传递内容:
```javascript
// RA 输出摘要
const raSummary = {
requirements_count: 10,
edge_cases_count: 8,
version: "1.0.0",
output_file: ".workflow/.cycle/${cycleId}.progress/ra/requirements.md",
key_requirements: [
"FR-001: OAuth authentication",
"FR-002: Multi-provider support",
"..." // 只列出标题,不传递完整内容
]
}
// 更新状态,让其他 agent 读取
state.requirements = {
version: raSummary.version,
output_file: raSummary.output_file,
summary: raSummary.key_requirements
}
// EP agent 从状态读取
const requiredDetails = state.requirements
const outputFile = requiredDetails.output_file
const requirements = JSON.parse(Read(outputFile)) // EP 自己读取完整文件
```
### Pattern 2: 反馈通知
Orchestrator 发送反馈时只传递摘要和行号:
```javascript
// ❌ 错误:传递完整测试结果
send_input({
id: agents.cd,
message: `
Test Results:
${entire_test_results_json} // 完整 JSON - 太大
`
})
// ✅ 正确:引用文件 + 问题摘要
send_input({
id: agents.cd,
message: `
## Test Failures
Full results: .workflow/.cycle/${cycleId}.progress/vas/test-results.json (v1.0.0)
## Quick Summary
- Failed: oauth-refresh (line 45, expected token refresh)
- Failed: concurrent-login (line 78, race condition)
## Fix Instructions
1. Review test cases at referenced lines
2. Fix implementation
3. Re-run tests
4. Update implementation.md
Reference previous file paths if you need full details.
`
})
```
### Pattern 3: 依赖链路
Agent 通过文件引用获取依赖:
```javascript
// EP agent: 从状态读取 RA 输出路径
const raOutputPath = state.requirements?.output_file
if (raOutputPath && exists(raOutputPath)) {
const requirements = Read(raOutputPath)
// 使用 requirements 生成计划
}
// CD agent: 从状态读取 EP 输出路径
const epPlanPath = state.plan?.output_file
if (epPlanPath && exists(epPlanPath)) {
const plan = JSON.parse(Read(epPlanPath))
// 根据 plan 实现功能
}
// VAS agent: 从状态读取 CD 输出路径
const cdChangesPath = state.changes?.output_file
if (cdChangesPath && exists(cdChangesPath)) {
const changes = readNDJSON(cdChangesPath)
// 根据 changes 生成测试
}
```
## 状态文件引用结构
优化后的状态文件应该包含文件路径而不是内容:
```json
{
"cycle_id": "cycle-v1-20260122-abc123",
"requirements": {
"version": "1.0.0",
"output_files": {
"specification": ".workflow/.cycle/cycle-v1-20260122-abc123.progress/ra/requirements.md",
"edge_cases": ".workflow/.cycle/cycle-v1-20260122-abc123.progress/ra/edge-cases.md",
"changes_log": ".workflow/.cycle/cycle-v1-20260122-abc123.progress/ra/changes.log"
},
"summary": {
"functional_requirements": 10,
"edge_cases": 8,
"constraints": 5
}
},
"exploration": {
"version": "1.0.0",
"output_files": {
"exploration": ".workflow/.cycle/cycle-v1-20260122-abc123.progress/ep/exploration.md",
"architecture": ".workflow/.cycle/cycle-v1-20260122-abc123.progress/ep/architecture.md"
},
"summary": {
"key_components": ["Auth Module", "User Service"],
"integration_points": 5,
"identified_risks": 3
}
},
"plan": {
"version": "1.0.0",
"output_file": ".workflow/.cycle/cycle-v1-20260122-abc123.progress/ep/plan.json",
"summary": {
"total_tasks": 8,
"critical_path": ["TASK-001", "TASK-003", "TASK-004"],
"estimated_hours": 16
}
},
"implementation": {
"version": "1.0.0",
"output_files": {
"progress": ".workflow/.cycle/cycle-v1-20260122-abc123.progress/cd/implementation.md",
"changes": ".workflow/.cycle/cycle-v1-20260122-abc123.progress/cd/changes.log",
"issues": ".workflow/.cycle/cycle-v1-20260122-abc123.progress/cd/issues.md"
},
"summary": {
"tasks_completed": 3,
"files_modified": 5,
"blockers": 0
}
},
"validation": {
"version": "1.0.0",
"output_files": {
"validation": ".workflow/.cycle/cycle-v1-20260122-abc123.progress/vas/validation.md",
"test_results": ".workflow/.cycle/cycle-v1-20260122-abc123.progress/vas/test-results.json",
"coverage": ".workflow/.cycle/cycle-v1-20260122-abc123.progress/vas/coverage.md"
},
"summary": {
"pass_rate": 92,
"coverage": 87,
"failures": 4
}
}
}
```
## Agent 通信模板优化
### 优化前: 完整内容传递
```javascript
send_input({
id: agents.cd,
message: `
## Requirements (Complete Content)
${fs.readFileSync(requirementsFile, 'utf8')} // 2000+ lines
## Plan (Complete JSON)
${fs.readFileSync(planFile, 'utf8')} // 1000+ lines
## Test Results (Complete)
${fs.readFileSync(testResultsFile, 'utf8')} // 500+ lines
## Your Task
Fix the implementation...
` // 总消息体: 4000+ 行
})
```
### 优化后: 文件引用 + 摘要
```javascript
send_input({
id: agents.cd,
message: `
## Test Failures - Action Required
Full Test Report: .workflow/.cycle/${cycleId}.progress/vas/test-results.json (v1.0.0)
## Summary of Failures
- oauth-refresh: Expected token refresh, got error (test line 45)
- concurrent-login: Race condition in session writes (test line 78)
## Implementation Reference
- Current Code: .workflow/.cycle/${cycleId}.progress/cd/implementation.md (v1.0.0)
- Code Changes: .workflow/.cycle/${cycleId}.progress/cd/changes.log (v1.0.0)
## Action Required
1. Review failing tests in referenced test results file
2. Fix root causes (race condition, token handling)
3. Update implementation.md with fixes
4. Re-run tests
## Context
- Requirement: .workflow/.cycle/${cycleId}.progress/ra/requirements.md (v1.0.0)
- Plan: .workflow/.cycle/${cycleId}.progress/ep/plan.json (v1.0.0)
Output PHASE_RESULT when complete.
` // 总消息体: <500 行,高效传递
})
```
## 版本控制最佳实践
### 版本不匹配检测
```javascript
function validateVersionConsistency(state) {
const versions = {
ra: state.requirements?.version,
ep: state.plan?.version,
cd: state.implementation?.version,
vas: state.validation?.version
}
// 检查版本一致性
const allVersions = Object.values(versions).filter(v => v)
const unique = new Set(allVersions)
if (unique.size > 1) {
console.warn('Version mismatch detected:')
console.warn(versions)
// 返回版本差异,让 orchestrator 决定是否继续
}
return unique.size === 1
}
```
### 文件存在性检查
```javascript
function validateReferences(state, cycleId) {
const checks = []
// 检查所有引用的文件是否存在
for (const [agent, data] of Object.entries(state)) {
if (data?.output_files) {
for (const [name, path] of Object.entries(data.output_files)) {
if (!fs.existsSync(path)) {
checks.push({
agent: agent,
file: name,
path: path,
status: 'missing'
})
}
}
}
}
return checks
}
```
## 好处总结
| 方面 | 改进 |
|------|------|
| 消息体积 | 减少 80-90% |
| Token 使用 | 减少 60-70% |
| 读取速度 | 无需解析冗余内容 |
| 版本控制 | 清晰的版本跟踪 |
| 上下文清晰 | 不会混淆版本 |
| 可维护性 | 文件变更不需要修改消息 |
## 迁移建议
### 第一步: 更新状态结构
```json
// 从这样:
"requirements": "完整内容"
// 改为这样:
"requirements": {
"version": "1.0.0",
"output_file": "path/to/file",
"summary": {...}
}
```
### 第二步: 更新通信模板
所有 `send_input` 消息改为引用路径。
### 第三步: Agent 自动读取
Agent 从引用路径自动读取所需文件。
### 第四步: 测试版本检测
确保版本不匹配时有警告。

View File

@@ -1,406 +0,0 @@
# Coordination Protocol - Multi-Agent Communication
Inter-agent communication protocols and patterns for parallel-dev-cycle skill.
## Overview
The coordination protocol enables four parallel agents (RA, EP, CD, VAS) to communicate efficiently while maintaining clear responsibilities and avoiding conflicts.
## Communication Channels
### 1. Shared State File (Primary)
**Location**: `.workflow/.cycle/{cycleId}.json`
**Access Pattern**:
- **Agents**: READ ONLY - check dependencies and status
- **Orchestrator**: READ-WRITE - updates state after each phase
```javascript
// Every agent: Read state to check dependencies
const state = JSON.parse(Read(`.workflow/.cycle/${cycleId}.json`))
const canProceed = checkDependencies(state)
// Agent outputs PHASE_RESULT (reports to orchestrator, NOT writes directly)
console.log("PHASE_RESULT: ...")
// Only Orchestrator writes to state file after receiving PHASE_RESULT
// Write(`.workflow/.cycle/${cycleId}.json`, JSON.stringify(updatedState, null, 2))
```
**Protocol**:
- Only orchestrator writes to state file (no concurrent writes, no lock needed)
- Agents read state to understand dependencies
- Timestamp all orchestrator updates with ISO8601 format
- Never delete existing data, only append
### 2. Progress Markdown Files (Async Log)
**Location**: `.workflow/.cycle/{cycleId}.progress/{agent}/`
Each agent writes progress to dedicated markdown files:
| Agent | Main Documents (Rewrite) | Logs (Append-Only) |
|-------|--------------------------|-------------------|
| RA | requirements.md | changes.log |
| EP | exploration.md, architecture.md, plan.json | changes.log |
| CD | implementation.md, issues.md | changes.log, debug-log.ndjson |
| VAS | validation.md, summary.md, test-results.json | changes.log |
**Protocol**:
- **Main documents**: Complete rewrite per iteration, archived to `history/`
- **Log files**: Append-only (changes.log, debug-log.ndjson) - never delete
- **Version synchronization**: All main documents share same version (e.g., all v1.1.0 in iteration 2)
- Include timestamp on each update
### 3. Orchestrator send_input (Synchronous)
**When**: Orchestrator needs to send feedback or corrections
```javascript
// Example: CD agent receives test failure feedback
send_input({
id: agents.cd,
message: `
## FEEDBACK FROM VALIDATION
Test failures detected: ${failures}
## REQUIRED ACTION
Fix the following:
${actionItems}
## NEXT STEP
Update implementation.md with fixes, then re-run tests.
Output PHASE_RESULT when complete.
`
})
```
**Protocol**:
- Only orchestrator initiates send_input
- Clear action items and expected output
- Single message per iteration (no rapid-fire sends)
### 4. Coordination Log
**Location**: `.workflow/.cycle/{cycleId}.progress/coordination/`
Centralized log for inter-agent decisions and communication:
**feedback.md**:
```markdown
# Feedback & Coordination Log - Version X.Y.Z
## Timeline
- [10:00:00] Orchestrator: Created cycle
- [10:05:00] RA: Requirements analysis started
- [10:10:00] RA: Requirements completed, v1.0.0
- [10:10:01] EP: Starting exploration (depends on RA output)
- [10:15:00] EP: Architecture designed, plan.json v1.0.0
- [10:15:01] CD: Starting implementation (depends on EP plan)
- [10:30:00] CD: Implementation progressing, found blocker
- [10:31:00] RA: Clarified requirement after CD blocker
- [10:31:01] CD: Continuing with clarification
- [10:40:00] CD: Implementation complete
- [10:40:01] VAS: Starting validation
- [10:45:00] VAS: Testing complete, found failures
- [10:45:01] Orchestrator: Sending feedback to CD
- [10:46:00] CD: Fixed issues
- [10:50:00] VAS: Re-validation, all passing
- [10:50:01] Orchestrator: Cycle complete
## Decision Records
- [10:31:00] RA Clarification: OAuth optional vs required?
- Decision: Optional (can use password)
- Rationale: More flexible for users
- Impact: Affects FR-003 implementation
## Blockers & Resolutions
- [10:30:00] Blocker: Database migration for existing users
- Reported by: CD
- Resolution: Set oauth_id = null for existing users
- Status: Resolved
## Cross-Agent Dependencies
- EP depends on: RA requirements (v1.0.0)
- CD depends on: EP plan (v1.0.0)
- VAS depends on: CD code changes
```
## Message Formats
### Agent Status Update
Each agent updates state with its status:
```json
{
"agents": {
"ra": {
"status": "completed",
"started_at": "2026-01-22T10:05:00+08:00",
"completed_at": "2026-01-22T10:15:00+08:00",
"output_files": [
".workflow/.cycle/cycle-xxx.progress/ra/requirements.md",
".workflow/.cycle/cycle-xxx.progress/ra/edge-cases.md",
".workflow/.cycle/cycle-xxx.progress/ra/changes.log"
],
"iterations_completed": 1
}
}
}
```
### Feedback Message Format
When orchestrator sends feedback via send_input:
```text
## FEEDBACK FROM [Agent Name]
[Summary of findings or issues]
## REFERENCED OUTPUT
File: [path to agent output]
Version: [X.Y.Z]
## REQUIRED ACTION
1. [Action 1 with specific details]
2. [Action 2 with specific details]
## SUCCESS CRITERIA
- [ ] Item 1
- [ ] Item 2
## NEXT STEP
[What agent should do next]
Output PHASE_RESULT when complete.
## CONTEXT
Previous iteration: [N]
Current iteration: [N+1]
```
### Phase Result Format
Every agent outputs PHASE_RESULT:
```text
PHASE_RESULT:
- phase: [ra|ep|cd|vas]
- status: success | failed | partial
- files_written: [list of files]
- summary: [one-line summary]
- [agent-specific fields]
- issues: [list of issues if any]
PHASE_DETAILS:
[Additional details or metrics]
```
## Dependency Resolution
**Execution Model**: All four agents are spawned in parallel, but execution blocks based on dependencies. Orchestrator manages dependency resolution via shared state.
### Build Order (Default)
```
RA (Requirements) → EP (Planning) → CD (Development) → VAS (Validation)
↓ ↓ ↓ ↓
Block EP Block CD Block VAS Block completion
```
**Explanation**:
- All agents spawned simultaneously
- Each agent checks dependencies in shared state before proceeding
- Blocked agents wait for dependency completion
- Orchestrator uses `send_input` to notify dependent agents when ready
### Parallel Opportunities
Some phases can run in parallel:
```
RA + FrontendCode (independent)
EP + RA (not blocking)
CD.Task1 + CD.Task2 (if no dependencies)
```
### Dependency Tracking
State file tracks dependencies:
```json
{
"agents": {
"ep": {
"depends_on": ["ra"],
"ready": true, // RA completed
"can_start": true
},
"cd": {
"depends_on": ["ep"],
"ready": true, // EP completed
"can_start": true
},
"vas": {
"depends_on": ["cd"],
"ready": false, // CD not yet complete
"can_start": false
}
}
}
```
## Iteration Flow with Communication
### Iteration 1: Initial Execution
```
Time Agent Action State Update
──────────────────────────────────────────────────────
10:00 Init Create cycle status: running
10:05 RA Start analysis agents.ra.status: running
10:10 RA Complete (v1.0.0) agents.ra.status: completed
10:10 EP Start planning agents.ep.status: running
(depends on RA completion)
10:15 EP Complete (v1.0.0) agents.ep.status: completed
10:15 CD Start development agents.cd.status: running
(depends on EP completion)
10:30 CD Found blocker coordination.blockers.add()
10:31 RA Clarify blocker requirements.v1.1.0 created
10:35 CD Continue (with fix) agents.cd.status: running
10:40 CD Complete agents.cd.status: completed
10:40 VAS Start validation agents.vas.status: running
(depends on CD completion)
10:45 VAS Tests failing coordination.feedback_log.add()
10:45 Orch Send feedback agents.cd.message: "Fix these tests"
10:46 CD Resume (send_input) agents.cd.status: running
10:48 CD Fix complete agents.cd.status: completed
10:50 VAS Re-validate agents.vas.status: running
10:55 VAS All pass agents.vas.status: completed
11:00 Orch Complete cycle status: completed
```
## Conflict Resolution
### Conflict Type 1: Unclear Requirement
**Scenario**: CD needs clarification on FR-X
**Resolution Flow**:
1. CD reports blocker in issues.md
2. Orchestrator extracts blocker
3. Orchestrator sends message to RA
4. RA updates requirements with clarification
5. RA outputs new requirements.md (v1.1.0)
6. Orchestrator sends message to CD with clarification
7. CD resumes and continues
### Conflict Type 2: Test Failure
**Scenario**: VAS finds test failures
**Resolution Flow**:
1. VAS reports failures in validation.md
2. VAS outputs test-results.json with details
3. Orchestrator extracts failure details
4. Orchestrator categorizes failures
5. If blocker: Orchestrator sends to CD/RA for fixes
6. CD/RA fix and report completion
7. Orchestrator sends CD/VAS to retry
8. VAS re-validates
### Conflict Type 3: Plan Mismatch
**Scenario**: CD realizes plan tasks are incomplete
**Resolution Flow**:
1. CD reports in issues.md
2. Orchestrator extracts issue
3. Orchestrator sends to EP to revise plan
4. EP updates plan.json (v1.1.0)
5. EP adds new tasks or dependencies
6. Orchestrator sends to CD with updated plan
7. CD implements remaining tasks
## Escalation Path
For issues that block resolution:
```
Agent Issue
Agent reports blocker
Orchestrator analyzes
Can fix automatically?
├─ Yes: send_input to agent with fix
└─ No: Escalate to user
User provides guidance
Orchestrator applies guidance
Resume agents
```
## Communication Best Practices
1. **Clear Timestamps**: All events timestamped ISO8601 format
2. **Structured Messages**: Use consistent format for feedback
3. **Version Tracking**: Always include version numbers
4. **Audit Trail**: Maintain complete log of decisions
5. **No Direct Agent Communication**: All communication via orchestrator
6. **Document Decisions**: Record why decisions were made
7. **Append-Only Logs**: Never delete history
## State Consistency Rules
1. **Single Writer Per Field**: Only one agent updates each field
- RA writes: requirements, edge_cases
- EP writes: exploration, plan
- CD writes: changes, implementation
- VAS writes: test_results, summary
2. **Read-Write Serialization**: Orchestrator ensures no conflicts
3. **Version Synchronization**: All versions increment together
- v1.0.0 → v1.1.0 (all docs updated)
4. **Timestamp Consistency**: All timestamps in state file UTC+8
## Monitoring & Debugging
### State Inspection
```javascript
// Check agent status
const state = JSON.parse(Read(`.workflow/.cycle/${cycleId}.json`))
console.log(state.agents) // See status of all agents
// Check for blockers
console.log(state.coordination.blockers)
// Check feedback history
console.log(state.coordination.feedback_log)
```
### Log Analysis
```bash
# Check RA progress
tail .workflow/.cycle/cycle-xxx.progress/ra/changes.log
# Check CD changes
grep "TASK-001" .workflow/.cycle/cycle-xxx.progress/cd/changes.log
# Check coordination timeline
tail -50 .workflow/.cycle/cycle-xxx.progress/coordination/feedback.md
```

View File

@@ -1,331 +0,0 @@
# Document Versioning Strategy
Document version management strategy: Complete Rewrite + Archive History
## Recommended Approach: Complete Rewrite + Archive History
For each iteration, **completely rewrite** the main document, and automatically archive the old version to the `history/` directory.
### File Structure
```
.workflow/.cycle/cycle-v1-20260122-abc123.progress/
├── ra/
│ ├── requirements.md # v1.2.0 (current version, complete rewrite)
│ ├── edge-cases.md # v1.2.0 (current version, complete rewrite)
│ ├── changes.log # NDJSON complete change history (append-only)
│ └── history/
│ ├── requirements-v1.0.0.md (archived)
│ ├── requirements-v1.1.0.md (archived)
│ ├── edge-cases-v1.0.0.md (archived)
│ └── edge-cases-v1.1.0.md (archived)
├── ep/
│ ├── exploration.md # v1.2.0 (current)
│ ├── architecture.md # v1.2.0 (current)
│ ├── plan.json # v1.2.0 (current)
│ └── history/
│ ├── plan-v1.0.0.json
│ └── plan-v1.1.0.json
├── cd/
│ ├── implementation.md # v1.2.0 (current)
│ ├── changes.log # NDJSON complete history
│ ├── debug-log.ndjson # Debug hypothesis tracking
│ ├── issues.md # Current unresolved issues
│ └── history/
│ ├── implementation-v1.0.0.md
│ └── implementation-v1.1.0.md
└── vas/
├── validation.md # v1.2.0 (current)
├── test-results.json # v1.2.0 (current)
├── summary.md # v1.2.0 (current)
└── history/
├── validation-v1.0.0.md
└── test-results-v1.0.0.json
```
## Optimized Document Template
### Requirements.md (Complete Rewrite Version)
```markdown
# Requirements Specification - v1.2.0
## Document Metadata
| Field | Value |
|-------|-------|
| Version | 1.2.0 |
| Previous | 1.1.0 (Added Google OAuth) |
| Changes | Added MFA, GitHub provider |
| Date | 2026-01-23T10:00:00+08:00 |
| Cycle | cycle-v1-20260122-abc123 |
| Iteration | 3 |
---
## Functional Requirements
### FR-001: OAuth Authentication
**Description**: Users can log in using OAuth providers.
**Supported Providers**: Google, GitHub
**Priority**: High
**Status**: ✓ Implemented (v1.0.0), Enhanced (v1.1.0, v1.2.0)
**Success Criteria**:
- User can click provider button
- Redirect to provider
- Return with valid token
- Session created
---
### FR-002: Multi-Provider Support
**Description**: System supports multiple OAuth providers simultaneously.
**Providers**:
- Google (v1.1.0)
- GitHub (v1.2.0)
**Priority**: High
**Status**: ✓ Implemented
---
### FR-003: Multi-Factor Authentication
**Description**: Optional MFA for enhanced security.
**Method**: TOTP (Time-based One-Time Password)
**Priority**: Medium
**Status**: 🆕 New in v1.2.0
**Success Criteria**:
- User can enable MFA in settings
- TOTP QR code generated
- Verification on login
---
## Non-Functional Requirements
### NFR-001: Performance
Response time < 500ms for all OAuth flows.
**Status**: ✓ Met (v1.0.0)
---
## Edge Cases
### EC-001: OAuth Provider Timeout
**Scenario**: Provider doesn't respond in 5 seconds
**Expected**: Display error, offer retry
**Status**: ✓ Handled
---
### EC-002: Invalid MFA Code (NEW v1.2.0)
**Scenario**: User enters incorrect TOTP code
**Expected**: Display error, max 3 attempts, lock after
**Status**: 🔄 To be implemented
---
## Constraints
- Must use existing JWT session management
- No new database servers
- Compatible with existing user table
---
## Assumptions
- Users have access to authenticator app for MFA
- OAuth providers are always available
---
## Version History Summary
| Version | Date | Summary |
|---------|------|---------|
| 1.0.0 | 2026-01-22 | Initial OAuth login (Google only implicit) |
| 1.1.0 | 2026-01-22 | + Explicit Google OAuth support |
| 1.2.0 | 2026-01-23 | + GitHub provider, + MFA (current) |
**Detailed History**: See `history/` directory and `changes.log`
```
### Changes.log (NDJSON - Complete History)
```jsonl
{"timestamp":"2026-01-22T10:00:00+08:00","iteration":1,"version":"1.0.0","action":"create","type":"requirement","id":"FR-001","description":"Initial OAuth requirement"}
{"timestamp":"2026-01-22T10:05:00+08:00","iteration":1,"version":"1.0.0","action":"create","type":"requirement","id":"NFR-001","description":"Performance requirement"}
{"timestamp":"2026-01-22T11:00:00+08:00","iteration":2,"version":"1.1.0","action":"update","type":"requirement","id":"FR-001","description":"Clarified Google OAuth support"}
{"timestamp":"2026-01-22T11:05:00+08:00","iteration":2,"version":"1.1.0","action":"create","type":"requirement","id":"FR-002","description":"Multi-provider support"}
{"timestamp":"2026-01-23T10:00:00+08:00","iteration":3,"version":"1.2.0","action":"create","type":"requirement","id":"FR-003","description":"MFA requirement"}
{"timestamp":"2026-01-23T10:05:00+08:00","iteration":3,"version":"1.2.0","action":"update","type":"requirement","id":"FR-002","description":"Added GitHub provider"}
```
## Implementation Flow
### Agent Workflow (RA Example)
```javascript
// ==================== RA Agent Iteration Flow ====================
// Read current state
const state = JSON.parse(Read(`.workflow/.cycle/${cycleId}.json`))
const currentVersion = state.requirements?.version || "0.0.0"
const iteration = state.current_iteration
// If iteration (old version exists)
if (currentVersion !== "0.0.0") {
// 1. Archive old version
const oldFile = `.workflow/.cycle/${cycleId}.progress/ra/requirements.md`
const archiveFile = `.workflow/.cycle/${cycleId}.progress/ra/history/requirements-v${currentVersion}.md`
Copy(oldFile, archiveFile) // Archive
// 2. Read old version (optional, for context understanding)
const oldRequirements = Read(oldFile)
// 3. Read change history
const changesLog = readNDJSON(`.workflow/.cycle/${cycleId}.progress/ra/changes.log`)
}
// 4. Generate new version number
const newVersion = bumpVersion(currentVersion, 'minor') // 1.1.0 -> 1.2.0
// 5. Generate new document (complete rewrite)
const newRequirements = generateRequirements({
version: newVersion,
previousVersion: currentVersion,
previousSummary: "Added Google OAuth support",
currentChanges: "Added MFA and GitHub provider",
iteration: iteration,
taskDescription: state.description,
changesLog: changesLog // For understanding history
})
// 6. Write new document (overwrite old)
Write(`.workflow/.cycle/${cycleId}.progress/ra/requirements.md`, newRequirements)
// 7. Append change to changes.log
appendNDJSON(`.workflow/.cycle/${cycleId}.progress/ra/changes.log`, {
timestamp: getUtc8ISOString(),
iteration: iteration,
version: newVersion,
action: "create",
type: "requirement",
id: "FR-003",
description: "Added MFA requirement"
})
// 8. Update state
state.requirements = {
version: newVersion,
output_file: `.workflow/.cycle/${cycleId}.progress/ra/requirements.md`,
summary: {
functional_requirements: 3,
edge_cases: 2,
constraints: 3
}
}
Write(`.workflow/.cycle/${cycleId}.json`, JSON.stringify(state, null, 2))
```
## Advantages Comparison
| Aspect | Incremental Update | Complete Rewrite + Archive |
|--------|-------------------|---------------------------|
| **Document Conciseness** | ❌ Gets longer | ✅ Always concise |
| **Agent Parsing** | ❌ Must parse history | ✅ Only read current version |
| **Maintenance Complexity** | ❌ High (version marking) | ✅ Low (direct rewrite) |
| **File Size** | ❌ Bloats | ✅ Fixed |
| **History Tracking** | ✅ In main document | ✅ In history/ + changes.log |
| **Human Readability** | ❌ Must skip history | ✅ Direct current view |
| **Token Usage** | ❌ More (read complete history) | ✅ Less (only read current) |
## Archive Strategy
### Auto-Archive Trigger
```javascript
function shouldArchive(currentVersion, state) {
// Archive on each version update
return currentVersion !== state.requirements?.version
}
function archiveOldVersion(cycleId, agent, filename, currentVersion) {
const currentFile = `.workflow/.cycle/${cycleId}.progress/${agent}/${filename}`
const archiveDir = `.workflow/.cycle/${cycleId}.progress/${agent}/history`
const archiveFile = `${archiveDir}/${filename.replace('.', `-v${currentVersion}.`)}`
// Ensure archive directory exists
mkdir -p ${archiveDir}
// Copy (not move, keep current file until new version written)
Copy(currentFile, archiveFile)
console.log(`Archived ${filename} v${currentVersion} to history/`)
}
```
### Cleanup Strategy (Optional)
Keep most recent N versions, delete older archives:
```javascript
function cleanupArchives(cycleId, agent, keepVersions = 3) {
const historyDir = `.workflow/.cycle/${cycleId}.progress/${agent}/history`
const archives = listFiles(historyDir)
// Sort by version number
archives.sort((a, b) => compareVersions(extractVersion(a), extractVersion(b)))
// Delete oldest versions (keep most recent N)
if (archives.length > keepVersions) {
const toDelete = archives.slice(0, archives.length - keepVersions)
toDelete.forEach(file => Delete(`${historyDir}/${file}`))
}
}
```
## Importance of Changes.log
Although main document is completely rewritten, **changes.log (NDJSON) permanently preserves complete history**:
```bash
# View all changes
cat .workflow/.cycle/cycle-xxx.progress/ra/changes.log | jq .
# View history of specific requirement
cat .workflow/.cycle/cycle-xxx.progress/ra/changes.log | jq 'select(.id=="FR-001")'
# View changes by iteration
cat .workflow/.cycle/cycle-xxx.progress/ra/changes.log | jq 'select(.iteration==2)'
```
This way:
- **Main Document**: Clear and concise (current state)
- **Changes.log**: Complete traceability (all history)
- **History/**: Snapshot backups (view on demand)
## Recommended Implementation
1. ✅ Adopt "Complete Rewrite" strategy
2. ✅ Main document only keeps "previous version summary"
3. ✅ Auto-archive to `history/` directory
4. ✅ Changes.log (NDJSON) preserves complete history
5. ✅ Optional: Keep most recent 3-5 historical versions
This approach keeps documents concise (agent-friendly) while preserving complete history (audit-friendly).

View File

@@ -1,84 +1 @@
{
"theme.AnnouncementBar.closeButtonAriaLabel": "关闭",
"theme.BackToTopButton.buttonAriaLabel": "回到顶部",
"theme.CodeBlock.copied": "复制成功",
"theme.CodeBlock.copy": "复制",
"theme.CodeBlock.copyButtonAriaLabel": "复制代码到剪贴板",
"theme.CodeBlock.wordWrapToggle": "切换自动换行",
"theme.DocSidebarItem.collapseCategoryAriaLabel": "折叠侧边栏分类 '{label}'",
"theme.DocSidebarItem.expandCategoryAriaLabel": "展开侧边栏分类 '{label}'",
"theme.ErrorPageContent.title": "页面已崩溃。",
"theme.ErrorPageContent.tryAgain": "重试",
"theme.IconExternalLink.ariaLabel": "(opens in new tab)",
"theme.NavBar.navAriaLabel": "主导航",
"theme.NotFound.p1": "我们找不到您要找的页面。",
"theme.NotFound.p2": "请联系原始链接来源网站的所有者,并告知他们链接已损坏。",
"theme.NotFound.title": "找不到页面",
"theme.TOCCollapsible.toggleButtonLabel": "本页总览",
"theme.admonition.caution": "警告",
"theme.admonition.danger": "危险",
"theme.admonition.info": "信息",
"theme.admonition.note": "备注",
"theme.admonition.tip": "提示",
"theme.admonition.warning": "注意",
"theme.blog.archive.description": "历史博文",
"theme.blog.archive.title": "历史博文",
"theme.blog.author.noPosts": "该作者尚未撰写任何文章。",
"theme.blog.author.pageTitle": "{authorName} - {nPosts}",
"theme.blog.authorsList.pageTitle": "作者",
"theme.blog.authorsList.viewAll": "查看所有作者",
"theme.blog.paginator.navAriaLabel": "博文列表分页导航",
"theme.blog.paginator.newerEntries": "较新的博文",
"theme.blog.paginator.olderEntries": "较旧的博文",
"theme.blog.post.paginator.navAriaLabel": "博文分页导航",
"theme.blog.post.paginator.newerPost": "较新一篇",
"theme.blog.post.paginator.olderPost": "较旧一篇",
"theme.blog.post.plurals": "{count} 篇博文",
"theme.blog.post.readMore": "阅读更多",
"theme.blog.post.readMoreLabel": "阅读 {title} 的全文",
"theme.blog.post.readingTime.plurals": "阅读需 {readingTime} 分钟",
"theme.blog.sidebar.navAriaLabel": "最近博文导航",
"theme.blog.tagTitle": "{nPosts} 含有标签「{tagName}」",
"theme.colorToggle.ariaLabel": "切换浅色/暗黑模式(当前为{mode}",
"theme.colorToggle.ariaLabel.mode.dark": "暗黑模式",
"theme.colorToggle.ariaLabel.mode.light": "浅色模式",
"theme.colorToggle.ariaLabel.mode.system": "system mode",
"theme.common.editThisPage": "编辑此页",
"theme.common.headingLinkTitle": "{heading}的直接链接",
"theme.common.skipToMainContent": "跳到主要内容",
"theme.contentVisibility.draftBanner.message": "此页面是草稿,仅在开发环境中可见,不会包含在正式版本中。",
"theme.contentVisibility.draftBanner.title": "草稿页",
"theme.contentVisibility.unlistedBanner.message": "此页面未列出。搜索引擎不会对其索引,只有拥有直接链接的用户才能访问。",
"theme.contentVisibility.unlistedBanner.title": "未列出页",
"theme.docs.DocCard.categoryDescription.plurals": "{count} 个项目",
"theme.docs.breadcrumbs.home": "主页面",
"theme.docs.breadcrumbs.navAriaLabel": "页面路径",
"theme.docs.paginator.navAriaLabel": "文件选项卡",
"theme.docs.paginator.next": "下一页",
"theme.docs.paginator.previous": "上一页",
"theme.docs.sidebar.closeSidebarButtonAriaLabel": "关闭导航栏",
"theme.docs.sidebar.collapseButtonAriaLabel": "收起侧边栏",
"theme.docs.sidebar.collapseButtonTitle": "收起侧边栏",
"theme.docs.sidebar.expandButtonAriaLabel": "展开侧边栏",
"theme.docs.sidebar.expandButtonTitle": "展开侧边栏",
"theme.docs.sidebar.navAriaLabel": "文档侧边栏",
"theme.docs.sidebar.toggleSidebarButtonAriaLabel": "切换导航栏",
"theme.docs.tagDocListPageTitle": "{nDocsTagged}「{tagName}」",
"theme.docs.tagDocListPageTitle.nDocsTagged": "{count} 篇文档带有标签",
"theme.docs.versionBadge.label": "版本:{versionLabel}",
"theme.docs.versions.latestVersionLinkLabel": "最新版本",
"theme.docs.versions.latestVersionSuggestionLabel": "最新的文档请参阅 {latestVersionLink} ({versionLabel})。",
"theme.docs.versions.unmaintainedVersionLabel": "此为 {siteTitle} {versionLabel} 版的文档,现已不再积极维护。",
"theme.docs.versions.unreleasedVersionLabel": "此为 {siteTitle} {versionLabel} 版尚未发行的文档。",
"theme.lastUpdated.atDate": "于 {date} ",
"theme.lastUpdated.byUser": "由 {user} ",
"theme.lastUpdated.lastUpdatedAtBy": "最后{byUser}{atDate}更新",
"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel": "Collapse the dropdown",
"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel": "Expand the dropdown",
"theme.navbar.mobileLanguageDropdown.label": "选择语言",
"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": "← 回到主菜单",
"theme.navbar.mobileVersionsDropdown.label": "选择版本",
"theme.tags.tagsListLabel": "标签:",
"theme.tags.tagsPageLink": "查看所有标签",
"theme.tags.tagsPageTitle": "标签"
}
{}

View File

@@ -1 +1 @@
{"options":{"path":"docs","routeBasePath":"/","sidebarPath":"D:\\Claude_dms3\\ccw\\docs-site\\sidebars.ts","editUrl":"https://github.com/ccw/docs/tree/main/","editCurrentVersion":false,"editLocalizedFiles":false,"tagsBasePath":"tags","include":["**/*.{md,mdx}"],"exclude":["**/_*.{js,jsx,ts,tsx,md,mdx}","**/_*/**","**/*.test.{js,jsx,ts,tsx}","**/__tests__/**"],"sidebarCollapsible":true,"sidebarCollapsed":true,"docsRootComponent":"@theme/DocsRoot","docVersionRootComponent":"@theme/DocVersionRoot","docRootComponent":"@theme/DocRoot","docItemComponent":"@theme/DocItem","docTagsListComponent":"@theme/DocTagsListPage","docTagDocListComponent":"@theme/DocTagDocListPage","docCategoryGeneratedIndexComponent":"@theme/DocCategoryGeneratedIndexPage","remarkPlugins":[],"rehypePlugins":[],"recmaPlugins":[],"beforeDefaultRemarkPlugins":[],"beforeDefaultRehypePlugins":[],"admonitions":true,"showLastUpdateTime":false,"showLastUpdateAuthor":false,"includeCurrentVersion":true,"disableVersioning":false,"versions":{},"breadcrumbs":true,"onInlineTags":"warn","id":"default"},"versionsMetadata":[{"versionName":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","path":"/docs/zh/","tagsPath":"/docs/zh/tags","editUrl":"https://github.com/ccw/docs/tree/main/docs","editUrlLocalized":"https://github.com/ccw/docs/tree/main/i18n/zh/docusaurus-plugin-content-docs/current","isLast":true,"routePriority":-1,"sidebarFilePath":"D:\\Claude_dms3\\ccw\\docs-site\\sidebars.ts","contentPath":"D:\\Claude_dms3\\ccw\\docs-site\\docs","contentPathLocalized":"D:\\Claude_dms3\\ccw\\docs-site\\i18n\\zh\\docusaurus-plugin-content-docs\\current"}]}
{"options":{"path":"docs","routeBasePath":"/","sidebarPath":"D:\\Claude_dms3\\ccw\\docs-site\\sidebars.ts","editUrl":"https://github.com/ccw/docs/tree/main/","editCurrentVersion":false,"editLocalizedFiles":false,"tagsBasePath":"tags","include":["**/*.{md,mdx}"],"exclude":["**/_*.{js,jsx,ts,tsx,md,mdx}","**/_*/**","**/*.test.{js,jsx,ts,tsx}","**/__tests__/**"],"sidebarCollapsible":true,"sidebarCollapsed":true,"docsRootComponent":"@theme/DocsRoot","docVersionRootComponent":"@theme/DocVersionRoot","docRootComponent":"@theme/DocRoot","docItemComponent":"@theme/DocItem","docTagsListComponent":"@theme/DocTagsListPage","docTagDocListComponent":"@theme/DocTagDocListPage","docCategoryGeneratedIndexComponent":"@theme/DocCategoryGeneratedIndexPage","remarkPlugins":[],"rehypePlugins":[],"recmaPlugins":[],"beforeDefaultRemarkPlugins":[],"beforeDefaultRehypePlugins":[],"admonitions":true,"showLastUpdateTime":false,"showLastUpdateAuthor":false,"includeCurrentVersion":true,"disableVersioning":false,"versions":{},"breadcrumbs":true,"onInlineTags":"warn","id":"default"},"versionsMetadata":[{"versionName":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","path":"/docs/","tagsPath":"/docs/tags","editUrl":"https://github.com/ccw/docs/tree/main/docs","isLast":true,"routePriority":-1,"sidebarFilePath":"D:\\Claude_dms3\\ccw\\docs-site\\sidebars.ts","contentPath":"D:\\Claude_dms3\\ccw\\docs-site\\docs"}]}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/cli/cli-init.mdx",
"sourceDirName": "commands/cli",
"slug": "/commands/cli/cli-init",
"permalink": "/docs/zh/commands/cli/cli-init",
"permalink": "/docs/commands/cli/cli-init",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/cli/cli-init.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "issue:convert-to-plan",
"permalink": "/docs/zh/commands/issue/issue-convert-to-plan"
"permalink": "/docs/commands/issue/issue-convert-to-plan"
},
"next": {
"title": "/cli:codex-review",
"permalink": "/docs/zh/commands/cli/codex-review"
"permalink": "/docs/commands/cli/codex-review"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/cli/codex-review.mdx",
"sourceDirName": "commands/cli",
"slug": "/commands/cli/codex-review",
"permalink": "/docs/zh/commands/cli/codex-review",
"permalink": "/docs/commands/cli/codex-review",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/cli/codex-review.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/cli:cli-init",
"permalink": "/docs/zh/commands/cli/cli-init"
"permalink": "/docs/commands/cli/cli-init"
},
"next": {
"title": "/memory:update-full",
"permalink": "/docs/zh/commands/memory/memory-update-full"
"permalink": "/docs/commands/memory/memory-update-full"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/general/ccw-coordinator.mdx",
"sourceDirName": "commands/general",
"slug": "/commands/general/ccw-coordinator",
"permalink": "/docs/zh/commands/general/ccw-coordinator",
"permalink": "/docs/commands/general/ccw-coordinator",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/general/ccw-coordinator.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/ccw-test",
"permalink": "/docs/zh/commands/general/ccw-test"
"permalink": "/docs/commands/general/ccw-test"
},
"next": {
"title": "/ccw-debug",
"permalink": "/docs/zh/commands/general/ccw-debug"
"permalink": "/docs/commands/general/ccw-debug"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/general/ccw-debug.mdx",
"sourceDirName": "commands/general",
"slug": "/commands/general/ccw-debug",
"permalink": "/docs/zh/commands/general/ccw-debug",
"permalink": "/docs/commands/general/ccw-debug",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/general/ccw-debug.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/ccw-coordinator",
"permalink": "/docs/zh/commands/general/ccw-coordinator"
"permalink": "/docs/commands/general/ccw-coordinator"
},
"next": {
"title": "/flow-create",
"permalink": "/docs/zh/commands/general/flow-create"
"permalink": "/docs/commands/general/flow-create"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/general/ccw.mdx",
"sourceDirName": "commands/general",
"slug": "/commands/general/ccw",
"permalink": "/docs/zh/commands/general/ccw",
"permalink": "/docs/commands/general/ccw",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/general/ccw.mdx",
@@ -20,11 +20,11 @@
},
"sidebar": "docs",
"previous": {
"title": "概览",
"permalink": "/docs/zh/overview"
"title": "Overview",
"permalink": "/docs/overview"
},
"next": {
"title": "/ccw-plan",
"permalink": "/docs/zh/commands/general/ccw-plan"
"permalink": "/docs/commands/general/ccw-plan"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/general/ccw-plan.mdx",
"sourceDirName": "commands/general",
"slug": "/commands/general/ccw-plan",
"permalink": "/docs/zh/commands/general/ccw-plan",
"permalink": "/docs/commands/general/ccw-plan",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/general/ccw-plan.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/ccw",
"permalink": "/docs/zh/commands/general/ccw"
"permalink": "/docs/commands/general/ccw"
},
"next": {
"title": "/ccw-test",
"permalink": "/docs/zh/commands/general/ccw-test"
"permalink": "/docs/commands/general/ccw-test"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/general/ccw-test.mdx",
"sourceDirName": "commands/general",
"slug": "/commands/general/ccw-test",
"permalink": "/docs/zh/commands/general/ccw-test",
"permalink": "/docs/commands/general/ccw-test",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/general/ccw-test.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/ccw-plan",
"permalink": "/docs/zh/commands/general/ccw-plan"
"permalink": "/docs/commands/general/ccw-plan"
},
"next": {
"title": "/ccw-coordinator",
"permalink": "/docs/zh/commands/general/ccw-coordinator"
"permalink": "/docs/commands/general/ccw-coordinator"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/general/codex-coordinator.mdx",
"sourceDirName": "commands/general",
"slug": "/commands/general/codex-coordinator",
"permalink": "/docs/zh/commands/general/codex-coordinator",
"permalink": "/docs/commands/general/codex-coordinator",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/general/codex-coordinator.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/flow-create",
"permalink": "/docs/zh/commands/general/flow-create"
"permalink": "/docs/commands/general/flow-create"
},
"next": {
"title": "issue:new",
"permalink": "/docs/zh/commands/issue/issue-new"
"permalink": "/docs/commands/issue/issue-new"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/general/flow-create.mdx",
"sourceDirName": "commands/general",
"slug": "/commands/general/flow-create",
"permalink": "/docs/zh/commands/general/flow-create",
"permalink": "/docs/commands/general/flow-create",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/general/flow-create.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/ccw-debug",
"permalink": "/docs/zh/commands/general/ccw-debug"
"permalink": "/docs/commands/general/ccw-debug"
},
"next": {
"title": "/codex-coordinator",
"permalink": "/docs/zh/commands/general/codex-coordinator"
"permalink": "/docs/commands/general/codex-coordinator"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/issue/issue-convert-to-plan.md",
"sourceDirName": "commands/issue",
"slug": "/commands/issue/issue-convert-to-plan",
"permalink": "/docs/zh/commands/issue/issue-convert-to-plan",
"permalink": "/docs/commands/issue/issue-convert-to-plan",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/issue/issue-convert-to-plan.md",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "issue:from-brainstorm",
"permalink": "/docs/zh/commands/issue/issue-from-brainstorm"
"permalink": "/docs/commands/issue/issue-from-brainstorm"
},
"next": {
"title": "/cli:cli-init",
"permalink": "/docs/zh/commands/cli/cli-init"
"permalink": "/docs/commands/cli/cli-init"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/issue/issue-discover.md",
"sourceDirName": "commands/issue",
"slug": "/commands/issue/issue-discover",
"permalink": "/docs/zh/commands/issue/issue-discover",
"permalink": "/docs/commands/issue/issue-discover",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/issue/issue-discover.md",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "issue:new",
"permalink": "/docs/zh/commands/issue/issue-new"
"permalink": "/docs/commands/issue/issue-new"
},
"next": {
"title": "issue:plan",
"permalink": "/docs/zh/commands/issue/issue-plan"
"permalink": "/docs/commands/issue/issue-plan"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/issue/issue-execute.md",
"sourceDirName": "commands/issue",
"slug": "/commands/issue/issue-execute",
"permalink": "/docs/zh/commands/issue/issue-execute",
"permalink": "/docs/commands/issue/issue-execute",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/issue/issue-execute.md",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "issue:queue",
"permalink": "/docs/zh/commands/issue/issue-queue"
"permalink": "/docs/commands/issue/issue-queue"
},
"next": {
"title": "issue:from-brainstorm",
"permalink": "/docs/zh/commands/issue/issue-from-brainstorm"
"permalink": "/docs/commands/issue/issue-from-brainstorm"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/issue/issue-from-brainstorm.md",
"sourceDirName": "commands/issue",
"slug": "/commands/issue/issue-from-brainstorm",
"permalink": "/docs/zh/commands/issue/issue-from-brainstorm",
"permalink": "/docs/commands/issue/issue-from-brainstorm",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/issue/issue-from-brainstorm.md",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "issue:execute",
"permalink": "/docs/zh/commands/issue/issue-execute"
"permalink": "/docs/commands/issue/issue-execute"
},
"next": {
"title": "issue:convert-to-plan",
"permalink": "/docs/zh/commands/issue/issue-convert-to-plan"
"permalink": "/docs/commands/issue/issue-convert-to-plan"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/issue/issue-new.md",
"sourceDirName": "commands/issue",
"slug": "/commands/issue/issue-new",
"permalink": "/docs/zh/commands/issue/issue-new",
"permalink": "/docs/commands/issue/issue-new",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/issue/issue-new.md",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/codex-coordinator",
"permalink": "/docs/zh/commands/general/codex-coordinator"
"permalink": "/docs/commands/general/codex-coordinator"
},
"next": {
"title": "issue:discover",
"permalink": "/docs/zh/commands/issue/issue-discover"
"permalink": "/docs/commands/issue/issue-discover"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/issue/issue-plan.md",
"sourceDirName": "commands/issue",
"slug": "/commands/issue/issue-plan",
"permalink": "/docs/zh/commands/issue/issue-plan",
"permalink": "/docs/commands/issue/issue-plan",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/issue/issue-plan.md",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "issue:discover",
"permalink": "/docs/zh/commands/issue/issue-discover"
"permalink": "/docs/commands/issue/issue-discover"
},
"next": {
"title": "issue:queue",
"permalink": "/docs/zh/commands/issue/issue-queue"
"permalink": "/docs/commands/issue/issue-queue"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/issue/issue-queue.md",
"sourceDirName": "commands/issue",
"slug": "/commands/issue/issue-queue",
"permalink": "/docs/zh/commands/issue/issue-queue",
"permalink": "/docs/commands/issue/issue-queue",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/issue/issue-queue.md",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "issue:plan",
"permalink": "/docs/zh/commands/issue/issue-plan"
"permalink": "/docs/commands/issue/issue-plan"
},
"next": {
"title": "issue:execute",
"permalink": "/docs/zh/commands/issue/issue-execute"
"permalink": "/docs/commands/issue/issue-execute"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/memory/memory-compact.mdx",
"sourceDirName": "commands/memory",
"slug": "/commands/memory/memory-compact",
"permalink": "/docs/zh/commands/memory/memory-compact",
"permalink": "/docs/commands/memory/memory-compact",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/memory/memory-compact.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/memory:docs-related-cli",
"permalink": "/docs/zh/commands/memory/memory-docs-related-cli"
"permalink": "/docs/commands/memory/memory-docs-related-cli"
},
"next": {
"title": "Introduction",
"permalink": "/docs/zh/workflows/introduction"
"permalink": "/docs/workflows/introduction"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/memory/memory-docs-full-cli.mdx",
"sourceDirName": "commands/memory",
"slug": "/commands/memory/memory-docs-full-cli",
"permalink": "/docs/zh/commands/memory/memory-docs-full-cli",
"permalink": "/docs/commands/memory/memory-docs-full-cli",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/memory/memory-docs-full-cli.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/memory:load",
"permalink": "/docs/zh/commands/memory/memory-load"
"permalink": "/docs/commands/memory/memory-load"
},
"next": {
"title": "/memory:docs-related-cli",
"permalink": "/docs/zh/commands/memory/memory-docs-related-cli"
"permalink": "/docs/commands/memory/memory-docs-related-cli"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/memory/memory-docs-related-cli.mdx",
"sourceDirName": "commands/memory",
"slug": "/commands/memory/memory-docs-related-cli",
"permalink": "/docs/zh/commands/memory/memory-docs-related-cli",
"permalink": "/docs/commands/memory/memory-docs-related-cli",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/memory/memory-docs-related-cli.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/memory:docs-full-cli",
"permalink": "/docs/zh/commands/memory/memory-docs-full-cli"
"permalink": "/docs/commands/memory/memory-docs-full-cli"
},
"next": {
"title": "/memory:compact",
"permalink": "/docs/zh/commands/memory/memory-compact"
"permalink": "/docs/commands/memory/memory-compact"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/memory/memory-load.mdx",
"sourceDirName": "commands/memory",
"slug": "/commands/memory/memory-load",
"permalink": "/docs/zh/commands/memory/memory-load",
"permalink": "/docs/commands/memory/memory-load",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/memory/memory-load.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/memory:update-related",
"permalink": "/docs/zh/commands/memory/memory-update-related"
"permalink": "/docs/commands/memory/memory-update-related"
},
"next": {
"title": "/memory:docs-full-cli",
"permalink": "/docs/zh/commands/memory/memory-docs-full-cli"
"permalink": "/docs/commands/memory/memory-docs-full-cli"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/memory/memory-update-full.mdx",
"sourceDirName": "commands/memory",
"slug": "/commands/memory/memory-update-full",
"permalink": "/docs/zh/commands/memory/memory-update-full",
"permalink": "/docs/commands/memory/memory-update-full",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/memory/memory-update-full.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/cli:codex-review",
"permalink": "/docs/zh/commands/cli/codex-review"
"permalink": "/docs/commands/cli/codex-review"
},
"next": {
"title": "/memory:update-related",
"permalink": "/docs/zh/commands/memory/memory-update-related"
"permalink": "/docs/commands/memory/memory-update-related"
}
}

View File

@@ -5,7 +5,7 @@
"source": "@site/docs/commands/memory/memory-update-related.mdx",
"sourceDirName": "commands/memory",
"slug": "/commands/memory/memory-update-related",
"permalink": "/docs/zh/commands/memory/memory-update-related",
"permalink": "/docs/commands/memory/memory-update-related",
"draft": false,
"unlisted": false,
"editUrl": "https://github.com/ccw/docs/tree/main/docs/commands/memory/memory-update-related.mdx",
@@ -21,10 +21,10 @@
"sidebar": "docs",
"previous": {
"title": "/memory:update-full",
"permalink": "/docs/zh/commands/memory/memory-update-full"
"permalink": "/docs/commands/memory/memory-update-full"
},
"next": {
"title": "/memory:load",
"permalink": "/docs/zh/commands/memory/memory-load"
"permalink": "/docs/commands/memory/memory-load"
}
}

View File

@@ -8,7 +8,7 @@ export default {
"tagline": "Professional Workflow Automation Platform",
"favicon": "img/favicon.svg",
"url": "http://localhost:3001",
"baseUrl": "/docs/zh/",
"baseUrl": "/docs/",
"organizationName": "ccw",
"projectName": "docs",
"trailingSlash": false,
@@ -48,9 +48,9 @@ export default {
],
"themeConfig": {
"navbar": {
"title": "CCW 帮助",
"title": "CCW Help",
"logo": {
"alt": "CCW 标志",
"alt": "CCW Logo",
"src": "img/logo.svg"
},
"items": [
@@ -65,7 +65,7 @@ export default {
},
"footer": {
"style": "dark",
"copyright": "版权 © 2026 CCW。使用 Docusaurus 构建。",
"copyright": "Copyright © 2026 CCW. Built with Docusaurus.",
"links": []
},
"prism": {

View File

@@ -1,172 +1,172 @@
{
"docusaurus-plugin-content-docs": {
"default": {
"path": "/docs/zh/",
"path": "/docs/",
"versions": [
{
"name": "current",
"label": "当前",
"label": "Next",
"isLast": true,
"path": "/docs/zh/",
"path": "/docs/",
"mainDocId": "index",
"docs": [
{
"id": "commands/cli/cli-init",
"path": "/docs/zh/commands/cli/cli-init",
"path": "/docs/commands/cli/cli-init",
"sidebar": "docs"
},
{
"id": "commands/cli/codex-review",
"path": "/docs/zh/commands/cli/codex-review",
"path": "/docs/commands/cli/codex-review",
"sidebar": "docs"
},
{
"id": "commands/general/ccw",
"path": "/docs/zh/commands/general/ccw",
"path": "/docs/commands/general/ccw",
"sidebar": "docs"
},
{
"id": "commands/general/ccw-coordinator",
"path": "/docs/zh/commands/general/ccw-coordinator",
"path": "/docs/commands/general/ccw-coordinator",
"sidebar": "docs"
},
{
"id": "commands/general/ccw-debug",
"path": "/docs/zh/commands/general/ccw-debug",
"path": "/docs/commands/general/ccw-debug",
"sidebar": "docs"
},
{
"id": "commands/general/ccw-plan",
"path": "/docs/zh/commands/general/ccw-plan",
"path": "/docs/commands/general/ccw-plan",
"sidebar": "docs"
},
{
"id": "commands/general/ccw-test",
"path": "/docs/zh/commands/general/ccw-test",
"path": "/docs/commands/general/ccw-test",
"sidebar": "docs"
},
{
"id": "commands/general/codex-coordinator",
"path": "/docs/zh/commands/general/codex-coordinator",
"path": "/docs/commands/general/codex-coordinator",
"sidebar": "docs"
},
{
"id": "commands/general/flow-create",
"path": "/docs/zh/commands/general/flow-create",
"path": "/docs/commands/general/flow-create",
"sidebar": "docs"
},
{
"id": "commands/issue/issue-convert-to-plan",
"path": "/docs/zh/commands/issue/issue-convert-to-plan",
"path": "/docs/commands/issue/issue-convert-to-plan",
"sidebar": "docs"
},
{
"id": "commands/issue/issue-discover",
"path": "/docs/zh/commands/issue/issue-discover",
"path": "/docs/commands/issue/issue-discover",
"sidebar": "docs"
},
{
"id": "commands/issue/issue-execute",
"path": "/docs/zh/commands/issue/issue-execute",
"path": "/docs/commands/issue/issue-execute",
"sidebar": "docs"
},
{
"id": "commands/issue/issue-from-brainstorm",
"path": "/docs/zh/commands/issue/issue-from-brainstorm",
"path": "/docs/commands/issue/issue-from-brainstorm",
"sidebar": "docs"
},
{
"id": "commands/issue/issue-new",
"path": "/docs/zh/commands/issue/issue-new",
"path": "/docs/commands/issue/issue-new",
"sidebar": "docs"
},
{
"id": "commands/issue/issue-plan",
"path": "/docs/zh/commands/issue/issue-plan",
"path": "/docs/commands/issue/issue-plan",
"sidebar": "docs"
},
{
"id": "commands/issue/issue-queue",
"path": "/docs/zh/commands/issue/issue-queue",
"path": "/docs/commands/issue/issue-queue",
"sidebar": "docs"
},
{
"id": "commands/memory/memory-compact",
"path": "/docs/zh/commands/memory/memory-compact",
"path": "/docs/commands/memory/memory-compact",
"sidebar": "docs"
},
{
"id": "commands/memory/memory-docs-full-cli",
"path": "/docs/zh/commands/memory/memory-docs-full-cli",
"path": "/docs/commands/memory/memory-docs-full-cli",
"sidebar": "docs"
},
{
"id": "commands/memory/memory-docs-related-cli",
"path": "/docs/zh/commands/memory/memory-docs-related-cli",
"path": "/docs/commands/memory/memory-docs-related-cli",
"sidebar": "docs"
},
{
"id": "commands/memory/memory-load",
"path": "/docs/zh/commands/memory/memory-load",
"path": "/docs/commands/memory/memory-load",
"sidebar": "docs"
},
{
"id": "commands/memory/memory-update-full",
"path": "/docs/zh/commands/memory/memory-update-full",
"path": "/docs/commands/memory/memory-update-full",
"sidebar": "docs"
},
{
"id": "commands/memory/memory-update-related",
"path": "/docs/zh/commands/memory/memory-update-related",
"path": "/docs/commands/memory/memory-update-related",
"sidebar": "docs"
},
{
"id": "faq",
"path": "/docs/zh/faq",
"path": "/docs/faq",
"sidebar": "docs"
},
{
"id": "index",
"path": "/docs/zh/",
"path": "/docs/",
"sidebar": "docs"
},
{
"id": "overview",
"path": "/docs/zh/overview",
"path": "/docs/overview",
"sidebar": "docs"
},
{
"id": "workflows/faq",
"path": "/docs/zh/workflows/faq"
"path": "/docs/workflows/faq"
},
{
"id": "workflows/introduction",
"path": "/docs/zh/workflows/introduction",
"path": "/docs/workflows/introduction",
"sidebar": "docs"
},
{
"id": "workflows/level-1-ultra-lightweight",
"path": "/docs/zh/workflows/level-1-ultra-lightweight",
"path": "/docs/workflows/level-1-ultra-lightweight",
"sidebar": "docs"
},
{
"id": "workflows/level-2-rapid",
"path": "/docs/zh/workflows/level-2-rapid",
"path": "/docs/workflows/level-2-rapid",
"sidebar": "docs"
},
{
"id": "workflows/level-3-standard",
"path": "/docs/zh/workflows/level-3-standard",
"path": "/docs/workflows/level-3-standard",
"sidebar": "docs"
},
{
"id": "workflows/level-4-brainstorm",
"path": "/docs/zh/workflows/level-4-brainstorm",
"path": "/docs/workflows/level-4-brainstorm",
"sidebar": "docs"
},
{
"id": "workflows/level-5-intelligent",
"path": "/docs/zh/workflows/level-5-intelligent",
"path": "/docs/workflows/level-5-intelligent",
"sidebar": "docs"
}
],
@@ -174,7 +174,7 @@
"sidebars": {
"docs": {
"link": {
"path": "/docs/zh/",
"path": "/docs/",
"label": "Home"
}
}

View File

@@ -5,7 +5,7 @@
"zh"
],
"path": "i18n",
"currentLocale": "zh",
"currentLocale": "en",
"localeConfigs": {
"en": {
"label": "English",

View File

@@ -1,39 +1,47 @@
export default {
"04db0a2e": [() => import(/* webpackChunkName: "04db0a2e" */ "@site/docs/commands/general/ccw-plan.mdx"), "@site/docs/commands/general/ccw-plan.mdx", require.resolveWeak("@site/docs/commands/general/ccw-plan.mdx")],
"05467734": [() => import(/* webpackChunkName: "05467734" */ "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-2-rapid.mdx"), "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-2-rapid.mdx", require.resolveWeak("@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-2-rapid.mdx")],
"0566a0a8": [() => import(/* webpackChunkName: "0566a0a8" */ "@site/docs/commands/cli/cli-init.mdx"), "@site/docs/commands/cli/cli-init.mdx", require.resolveWeak("@site/docs/commands/cli/cli-init.mdx")],
"157db180": [() => import(/* webpackChunkName: "157db180" */ "@site/docs/commands/memory/memory-load.mdx"), "@site/docs/commands/memory/memory-load.mdx", require.resolveWeak("@site/docs/commands/memory/memory-load.mdx")],
"17896441": [() => import(/* webpackChunkName: "17896441" */ "@theme/DocItem"), "@theme/DocItem", require.resolveWeak("@theme/DocItem")],
"1bac9067": [() => import(/* webpackChunkName: "1bac9067" */ "@site/docs/commands/issue/issue-queue.md"), "@site/docs/commands/issue/issue-queue.md", require.resolveWeak("@site/docs/commands/issue/issue-queue.md")],
"1e3006f3": [() => import(/* webpackChunkName: "1e3006f3" */ "@site/docs/commands/issue/issue-discover.md"), "@site/docs/commands/issue/issue-discover.md", require.resolveWeak("@site/docs/commands/issue/issue-discover.md")],
"2a5e3eff": [() => import(/* webpackChunkName: "2a5e3eff" */ "@site/i18n/zh/docusaurus-plugin-content-docs/current/faq.mdx"), "@site/i18n/zh/docusaurus-plugin-content-docs/current/faq.mdx", require.resolveWeak("@site/i18n/zh/docusaurus-plugin-content-docs/current/faq.mdx")],
"2ecf8b4a": [() => import(/* webpackChunkName: "2ecf8b4a" */ "@site/docs/commands/issue/issue-from-brainstorm.md"), "@site/docs/commands/issue/issue-from-brainstorm.md", require.resolveWeak("@site/docs/commands/issue/issue-from-brainstorm.md")],
"3f1fe4a1": [() => import(/* webpackChunkName: "3f1fe4a1" */ "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-3-standard.mdx"), "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-3-standard.mdx", require.resolveWeak("@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-3-standard.mdx")],
"46f40178": [() => import(/* webpackChunkName: "46f40178" */ "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/faq.mdx"), "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/faq.mdx", require.resolveWeak("@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/faq.mdx")],
"4ad7db0f": [() => import(/* webpackChunkName: "4ad7db0f" */ "@site/docs/commands/issue/issue-new.md"), "@site/docs/commands/issue/issue-new.md", require.resolveWeak("@site/docs/commands/issue/issue-new.md")],
"4cc74730": [() => import(/* webpackChunkName: "4cc74730" */ "@site/docs/commands/memory/memory-docs-full-cli.mdx"), "@site/docs/commands/memory/memory-docs-full-cli.mdx", require.resolveWeak("@site/docs/commands/memory/memory-docs-full-cli.mdx")],
"562bb8cb": [() => import(/* webpackChunkName: "562bb8cb" */ "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-5-intelligent.mdx"), "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-5-intelligent.mdx", require.resolveWeak("@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-5-intelligent.mdx")],
"5c7b2278": [() => import(/* webpackChunkName: "5c7b2278" */ "@site/docs/commands/issue/issue-convert-to-plan.md"), "@site/docs/commands/issue/issue-convert-to-plan.md", require.resolveWeak("@site/docs/commands/issue/issue-convert-to-plan.md")],
"5e95c892": [() => import(/* webpackChunkName: "5e95c892" */ "@theme/DocsRoot"), "@theme/DocsRoot", require.resolveWeak("@theme/DocsRoot")],
"60eef997": [() => import(/* webpackChunkName: "60eef997" */ "@site/docs/commands/memory/memory-docs-related-cli.mdx"), "@site/docs/commands/memory/memory-docs-related-cli.mdx", require.resolveWeak("@site/docs/commands/memory/memory-docs-related-cli.mdx")],
"611877e1": [() => import(/* webpackChunkName: "611877e1" */ "@site/docs/commands/memory/memory-update-related.mdx"), "@site/docs/commands/memory/memory-update-related.mdx", require.resolveWeak("@site/docs/commands/memory/memory-update-related.mdx")],
"666bb1bf": [() => import(/* webpackChunkName: "666bb1bf" */ "@site/docs/commands/memory/memory-update-full.mdx"), "@site/docs/commands/memory/memory-update-full.mdx", require.resolveWeak("@site/docs/commands/memory/memory-update-full.mdx")],
"6ab014e9": [() => import(/* webpackChunkName: "6ab014e9" */ "@site/i18n/zh/docusaurus-plugin-content-docs/current/index.mdx"), "@site/i18n/zh/docusaurus-plugin-content-docs/current/index.mdx", require.resolveWeak("@site/i18n/zh/docusaurus-plugin-content-docs/current/index.mdx")],
"775938bf": [() => import(/* webpackChunkName: "775938bf" */ "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-4-brainstorm.mdx"), "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-4-brainstorm.mdx", require.resolveWeak("@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-4-brainstorm.mdx")],
"7a1ee27c": [() => import(/* webpackChunkName: "7a1ee27c" */ "@site/docs/commands/memory/memory-compact.mdx"), "@site/docs/commands/memory/memory-compact.mdx", require.resolveWeak("@site/docs/commands/memory/memory-compact.mdx")],
"8a7e39ed": [() => import(/* webpackChunkName: "8a7e39ed" */ "@site/i18n/zh/docusaurus-plugin-content-docs/current/overview.mdx"), "@site/i18n/zh/docusaurus-plugin-content-docs/current/overview.mdx", require.resolveWeak("@site/i18n/zh/docusaurus-plugin-content-docs/current/overview.mdx")],
"97c6e66a": [() => import(/* webpackChunkName: "97c6e66a" */ "@site/docs/commands/general/ccw-debug.mdx"), "@site/docs/commands/general/ccw-debug.mdx", require.resolveWeak("@site/docs/commands/general/ccw-debug.mdx")],
"9cf7cb6b": [() => import(/* webpackChunkName: "9cf7cb6b" */ "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-1-ultra-lightweight.mdx"), "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-1-ultra-lightweight.mdx", require.resolveWeak("@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/level-1-ultra-lightweight.mdx")],
"a6c3df16": [() => import(/* webpackChunkName: "a6c3df16" */ "@site/docs/commands/issue/issue-plan.md"), "@site/docs/commands/issue/issue-plan.md", require.resolveWeak("@site/docs/commands/issue/issue-plan.md")],
"a7bd4aaa": [() => import(/* webpackChunkName: "a7bd4aaa" */ "@theme/DocVersionRoot"), "@theme/DocVersionRoot", require.resolveWeak("@theme/DocVersionRoot")],
"a94703ab": [() => import(/* webpackChunkName: "a94703ab" */ "@theme/DocRoot"), "@theme/DocRoot", require.resolveWeak("@theme/DocRoot")],
"aba21aa0": [() => import(/* webpackChunkName: "aba21aa0" */ "@generated/docusaurus-plugin-content-docs/default/__plugin.json"), "@generated/docusaurus-plugin-content-docs/default/__plugin.json", require.resolveWeak("@generated/docusaurus-plugin-content-docs/default/__plugin.json")],
"b17e4002": [() => import(/* webpackChunkName: "b17e4002" */ "@generated/docusaurus-plugin-content-docs/default/p/docs-zh-d2a.json"), "@generated/docusaurus-plugin-content-docs/default/p/docs-zh-d2a.json", require.resolveWeak("@generated/docusaurus-plugin-content-docs/default/p/docs-zh-d2a.json")],
"ccef5d0f": [() => import(/* webpackChunkName: "ccef5d0f" */ "@site/docs/commands/general/ccw-test.mdx"), "@site/docs/commands/general/ccw-test.mdx", require.resolveWeak("@site/docs/commands/general/ccw-test.mdx")],
"d550a629": [() => import(/* webpackChunkName: "d550a629" */ "@site/docs/commands/general/ccw-coordinator.mdx"), "@site/docs/commands/general/ccw-coordinator.mdx", require.resolveWeak("@site/docs/commands/general/ccw-coordinator.mdx")],
"e5f6eee3": [() => import(/* webpackChunkName: "e5f6eee3" */ "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/introduction.mdx"), "@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/introduction.mdx", require.resolveWeak("@site/i18n/zh/docusaurus-plugin-content-docs/current/workflows/introduction.mdx")],
"f1bf82ec": [() => import(/* webpackChunkName: "f1bf82ec" */ "@site/docs/commands/cli/codex-review.mdx"), "@site/docs/commands/cli/codex-review.mdx", require.resolveWeak("@site/docs/commands/cli/codex-review.mdx")],
"f4817052": [() => import(/* webpackChunkName: "f4817052" */ "@site/docs/commands/general/ccw.mdx"), "@site/docs/commands/general/ccw.mdx", require.resolveWeak("@site/docs/commands/general/ccw.mdx")],
"f9222419": [() => import(/* webpackChunkName: "f9222419" */ "@site/docs/commands/general/codex-coordinator.mdx"), "@site/docs/commands/general/codex-coordinator.mdx", require.resolveWeak("@site/docs/commands/general/codex-coordinator.mdx")],
"fabaf1c8": [() => import(/* webpackChunkName: "fabaf1c8" */ "@site/docs/commands/general/flow-create.mdx"), "@site/docs/commands/general/flow-create.mdx", require.resolveWeak("@site/docs/commands/general/flow-create.mdx")],
"fe8e3dcf": [() => import(/* webpackChunkName: "fe8e3dcf" */ "@site/docs/commands/issue/issue-execute.md"), "@site/docs/commands/issue/issue-execute.md", require.resolveWeak("@site/docs/commands/issue/issue-execute.md")],};
"__comp---theme-debug-config-23-a-2ff": [() => import(/* webpackChunkName: "__comp---theme-debug-config-23-a-2ff" */ "@theme/DebugConfig"), "@theme/DebugConfig", require.resolveWeak("@theme/DebugConfig")],
"__comp---theme-debug-contentba-8-ce7": [() => import(/* webpackChunkName: "__comp---theme-debug-contentba-8-ce7" */ "@theme/DebugContent"), "@theme/DebugContent", require.resolveWeak("@theme/DebugContent")],
"__comp---theme-debug-global-dataede-0fa": [() => import(/* webpackChunkName: "__comp---theme-debug-global-dataede-0fa" */ "@theme/DebugGlobalData"), "@theme/DebugGlobalData", require.resolveWeak("@theme/DebugGlobalData")],
"__comp---theme-debug-registry-679-501": [() => import(/* webpackChunkName: "__comp---theme-debug-registry-679-501" */ "@theme/DebugRegistry"), "@theme/DebugRegistry", require.resolveWeak("@theme/DebugRegistry")],
"__comp---theme-debug-routes-946-699": [() => import(/* webpackChunkName: "__comp---theme-debug-routes-946-699" */ "@theme/DebugRoutes"), "@theme/DebugRoutes", require.resolveWeak("@theme/DebugRoutes")],
"__comp---theme-debug-site-metadata-68-e-3d4": [() => import(/* webpackChunkName: "__comp---theme-debug-site-metadata-68-e-3d4" */ "@theme/DebugSiteMetadata"), "@theme/DebugSiteMetadata", require.resolveWeak("@theme/DebugSiteMetadata")],
"__comp---theme-doc-item-178-a40": [() => import(/* webpackChunkName: "__comp---theme-doc-item-178-a40" */ "@theme/DocItem"), "@theme/DocItem", require.resolveWeak("@theme/DocItem")],
"__comp---theme-doc-roota-94-67a": [() => import(/* webpackChunkName: "__comp---theme-doc-roota-94-67a" */ "@theme/DocRoot"), "@theme/DocRoot", require.resolveWeak("@theme/DocRoot")],
"__comp---theme-doc-version-roota-7-b-5de": [() => import(/* webpackChunkName: "__comp---theme-doc-version-roota-7-b-5de" */ "@theme/DocVersionRoot"), "@theme/DocVersionRoot", require.resolveWeak("@theme/DocVersionRoot")],
"__comp---theme-docs-root-5-e-9-0b6": [() => import(/* webpackChunkName: "__comp---theme-docs-root-5-e-9-0b6" */ "@theme/DocsRoot"), "@theme/DocsRoot", require.resolveWeak("@theme/DocsRoot")],
"__props---docs-11-b-f70": [() => import(/* webpackChunkName: "__props---docs-11-b-f70" */ "@generated/docusaurus-plugin-content-docs/default/p/docs-7fc.json"), "@generated/docusaurus-plugin-content-docs/default/p/docs-7fc.json", require.resolveWeak("@generated/docusaurus-plugin-content-docs/default/p/docs-7fc.json")],
"__props---docs-docusaurus-debug-content-344-8d5": [() => import(/* webpackChunkName: "__props---docs-docusaurus-debug-content-344-8d5" */ "@generated/docusaurus-plugin-debug/default/p/docs-docusaurus-debug-content-a52.json"), "@generated/docusaurus-plugin-debug/default/p/docs-docusaurus-debug-content-a52.json", require.resolveWeak("@generated/docusaurus-plugin-debug/default/p/docs-docusaurus-debug-content-a52.json")],
"content---docs-4-ed-831": [() => import(/* webpackChunkName: "content---docs-4-ed-831" */ "@site/docs/index.mdx"), "@site/docs/index.mdx", require.resolveWeak("@site/docs/index.mdx")],
"content---docs-commands-cli-cli-init-056-ce1": [() => import(/* webpackChunkName: "content---docs-commands-cli-cli-init-056-ce1" */ "@site/docs/commands/cli/cli-init.mdx"), "@site/docs/commands/cli/cli-init.mdx", require.resolveWeak("@site/docs/commands/cli/cli-init.mdx")],
"content---docs-commands-cli-codex-reviewf-1-b-55f": [() => import(/* webpackChunkName: "content---docs-commands-cli-codex-reviewf-1-b-55f" */ "@site/docs/commands/cli/codex-review.mdx"), "@site/docs/commands/cli/codex-review.mdx", require.resolveWeak("@site/docs/commands/cli/codex-review.mdx")],
"content---docs-commands-general-ccw-coordinatord-55-c6b": [() => import(/* webpackChunkName: "content---docs-commands-general-ccw-coordinatord-55-c6b" */ "@site/docs/commands/general/ccw-coordinator.mdx"), "@site/docs/commands/general/ccw-coordinator.mdx", require.resolveWeak("@site/docs/commands/general/ccw-coordinator.mdx")],
"content---docs-commands-general-ccw-debug-97-c-a72": [() => import(/* webpackChunkName: "content---docs-commands-general-ccw-debug-97-c-a72" */ "@site/docs/commands/general/ccw-debug.mdx"), "@site/docs/commands/general/ccw-debug.mdx", require.resolveWeak("@site/docs/commands/general/ccw-debug.mdx")],
"content---docs-commands-general-ccw-plan-04-d-fe0": [() => import(/* webpackChunkName: "content---docs-commands-general-ccw-plan-04-d-fe0" */ "@site/docs/commands/general/ccw-plan.mdx"), "@site/docs/commands/general/ccw-plan.mdx", require.resolveWeak("@site/docs/commands/general/ccw-plan.mdx")],
"content---docs-commands-general-ccw-testcce-912": [() => import(/* webpackChunkName: "content---docs-commands-general-ccw-testcce-912" */ "@site/docs/commands/general/ccw-test.mdx"), "@site/docs/commands/general/ccw-test.mdx", require.resolveWeak("@site/docs/commands/general/ccw-test.mdx")],
"content---docs-commands-general-ccwf-48-8c4": [() => import(/* webpackChunkName: "content---docs-commands-general-ccwf-48-8c4" */ "@site/docs/commands/general/ccw.mdx"), "@site/docs/commands/general/ccw.mdx", require.resolveWeak("@site/docs/commands/general/ccw.mdx")],
"content---docs-commands-general-codex-coordinatorf-92-1dc": [() => import(/* webpackChunkName: "content---docs-commands-general-codex-coordinatorf-92-1dc" */ "@site/docs/commands/general/codex-coordinator.mdx"), "@site/docs/commands/general/codex-coordinator.mdx", require.resolveWeak("@site/docs/commands/general/codex-coordinator.mdx")],
"content---docs-commands-general-flow-createfab-98a": [() => import(/* webpackChunkName: "content---docs-commands-general-flow-createfab-98a" */ "@site/docs/commands/general/flow-create.mdx"), "@site/docs/commands/general/flow-create.mdx", require.resolveWeak("@site/docs/commands/general/flow-create.mdx")],
"content---docs-commands-issue-issue-convert-to-plan-5-c-7-184": [() => import(/* webpackChunkName: "content---docs-commands-issue-issue-convert-to-plan-5-c-7-184" */ "@site/docs/commands/issue/issue-convert-to-plan.md"), "@site/docs/commands/issue/issue-convert-to-plan.md", require.resolveWeak("@site/docs/commands/issue/issue-convert-to-plan.md")],
"content---docs-commands-issue-issue-discover-1-e-3-569": [() => import(/* webpackChunkName: "content---docs-commands-issue-issue-discover-1-e-3-569" */ "@site/docs/commands/issue/issue-discover.md"), "@site/docs/commands/issue/issue-discover.md", require.resolveWeak("@site/docs/commands/issue/issue-discover.md")],
"content---docs-commands-issue-issue-executefe-8-c03": [() => import(/* webpackChunkName: "content---docs-commands-issue-issue-executefe-8-c03" */ "@site/docs/commands/issue/issue-execute.md"), "@site/docs/commands/issue/issue-execute.md", require.resolveWeak("@site/docs/commands/issue/issue-execute.md")],
"content---docs-commands-issue-issue-from-brainstorm-2-ec-eeb": [() => import(/* webpackChunkName: "content---docs-commands-issue-issue-from-brainstorm-2-ec-eeb" */ "@site/docs/commands/issue/issue-from-brainstorm.md"), "@site/docs/commands/issue/issue-from-brainstorm.md", require.resolveWeak("@site/docs/commands/issue/issue-from-brainstorm.md")],
"content---docs-commands-issue-issue-new-4-ad-3f0": [() => import(/* webpackChunkName: "content---docs-commands-issue-issue-new-4-ad-3f0" */ "@site/docs/commands/issue/issue-new.md"), "@site/docs/commands/issue/issue-new.md", require.resolveWeak("@site/docs/commands/issue/issue-new.md")],
"content---docs-commands-issue-issue-plana-6-c-fbd": [() => import(/* webpackChunkName: "content---docs-commands-issue-issue-plana-6-c-fbd" */ "@site/docs/commands/issue/issue-plan.md"), "@site/docs/commands/issue/issue-plan.md", require.resolveWeak("@site/docs/commands/issue/issue-plan.md")],
"content---docs-commands-issue-issue-queue-1-ba-55f": [() => import(/* webpackChunkName: "content---docs-commands-issue-issue-queue-1-ba-55f" */ "@site/docs/commands/issue/issue-queue.md"), "@site/docs/commands/issue/issue-queue.md", require.resolveWeak("@site/docs/commands/issue/issue-queue.md")],
"content---docs-commands-memory-memory-compact-7-a-1-41c": [() => import(/* webpackChunkName: "content---docs-commands-memory-memory-compact-7-a-1-41c" */ "@site/docs/commands/memory/memory-compact.mdx"), "@site/docs/commands/memory/memory-compact.mdx", require.resolveWeak("@site/docs/commands/memory/memory-compact.mdx")],
"content---docs-commands-memory-memory-docs-full-cli-4-cc-96f": [() => import(/* webpackChunkName: "content---docs-commands-memory-memory-docs-full-cli-4-cc-96f" */ "@site/docs/commands/memory/memory-docs-full-cli.mdx"), "@site/docs/commands/memory/memory-docs-full-cli.mdx", require.resolveWeak("@site/docs/commands/memory/memory-docs-full-cli.mdx")],
"content---docs-commands-memory-memory-docs-related-cli-60-e-dd0": [() => import(/* webpackChunkName: "content---docs-commands-memory-memory-docs-related-cli-60-e-dd0" */ "@site/docs/commands/memory/memory-docs-related-cli.mdx"), "@site/docs/commands/memory/memory-docs-related-cli.mdx", require.resolveWeak("@site/docs/commands/memory/memory-docs-related-cli.mdx")],
"content---docs-commands-memory-memory-load-157-952": [() => import(/* webpackChunkName: "content---docs-commands-memory-memory-load-157-952" */ "@site/docs/commands/memory/memory-load.mdx"), "@site/docs/commands/memory/memory-load.mdx", require.resolveWeak("@site/docs/commands/memory/memory-load.mdx")],
"content---docs-commands-memory-memory-update-full-666-002": [() => import(/* webpackChunkName: "content---docs-commands-memory-memory-update-full-666-002" */ "@site/docs/commands/memory/memory-update-full.mdx"), "@site/docs/commands/memory/memory-update-full.mdx", require.resolveWeak("@site/docs/commands/memory/memory-update-full.mdx")],
"content---docs-commands-memory-memory-update-related-611-8d3": [() => import(/* webpackChunkName: "content---docs-commands-memory-memory-update-related-611-8d3" */ "@site/docs/commands/memory/memory-update-related.mdx"), "@site/docs/commands/memory/memory-update-related.mdx", require.resolveWeak("@site/docs/commands/memory/memory-update-related.mdx")],
"content---docs-faqea-3-888": [() => import(/* webpackChunkName: "content---docs-faqea-3-888" */ "@site/docs/faq.mdx"), "@site/docs/faq.mdx", require.resolveWeak("@site/docs/faq.mdx")],
"content---docs-overview-188-429": [() => import(/* webpackChunkName: "content---docs-overview-188-429" */ "@site/docs/overview.mdx"), "@site/docs/overview.mdx", require.resolveWeak("@site/docs/overview.mdx")],
"content---docs-workflows-faqbcf-045": [() => import(/* webpackChunkName: "content---docs-workflows-faqbcf-045" */ "@site/docs/workflows/faq.mdx"), "@site/docs/workflows/faq.mdx", require.resolveWeak("@site/docs/workflows/faq.mdx")],
"content---docs-workflows-introduction-9-f-4-275": [() => import(/* webpackChunkName: "content---docs-workflows-introduction-9-f-4-275" */ "@site/docs/workflows/introduction.mdx"), "@site/docs/workflows/introduction.mdx", require.resolveWeak("@site/docs/workflows/introduction.mdx")],
"content---docs-workflows-level-1-ultra-lightweightc-5-a-5db": [() => import(/* webpackChunkName: "content---docs-workflows-level-1-ultra-lightweightc-5-a-5db" */ "@site/docs/workflows/level-1-ultra-lightweight.mdx"), "@site/docs/workflows/level-1-ultra-lightweight.mdx", require.resolveWeak("@site/docs/workflows/level-1-ultra-lightweight.mdx")],
"content---docs-workflows-level-2-rapid-19-b-095": [() => import(/* webpackChunkName: "content---docs-workflows-level-2-rapid-19-b-095" */ "@site/docs/workflows/level-2-rapid.mdx"), "@site/docs/workflows/level-2-rapid.mdx", require.resolveWeak("@site/docs/workflows/level-2-rapid.mdx")],
"content---docs-workflows-level-3-standardbdb-61a": [() => import(/* webpackChunkName: "content---docs-workflows-level-3-standardbdb-61a" */ "@site/docs/workflows/level-3-standard.mdx"), "@site/docs/workflows/level-3-standard.mdx", require.resolveWeak("@site/docs/workflows/level-3-standard.mdx")],
"content---docs-workflows-level-4-brainstormd-04-14f": [() => import(/* webpackChunkName: "content---docs-workflows-level-4-brainstormd-04-14f" */ "@site/docs/workflows/level-4-brainstorm.mdx"), "@site/docs/workflows/level-4-brainstorm.mdx", require.resolveWeak("@site/docs/workflows/level-4-brainstorm.mdx")],
"content---docs-workflows-level-5-intelligent-186-b05": [() => import(/* webpackChunkName: "content---docs-workflows-level-5-intelligent-186-b05" */ "@site/docs/workflows/level-5-intelligent.mdx"), "@site/docs/workflows/level-5-intelligent.mdx", require.resolveWeak("@site/docs/workflows/level-5-intelligent.mdx")],
"plugin---docs-aba-4f5": [() => import(/* webpackChunkName: "plugin---docs-aba-4f5" */ "@generated/docusaurus-plugin-content-docs/default/__plugin.json"), "@generated/docusaurus-plugin-content-docs/default/__plugin.json", require.resolveWeak("@generated/docusaurus-plugin-content-docs/default/__plugin.json")],
"plugin---docs-docusaurus-debugb-38-c84": [() => import(/* webpackChunkName: "plugin---docs-docusaurus-debugb-38-c84" */ "@generated/docusaurus-plugin-debug/default/__plugin.json"), "@generated/docusaurus-plugin-debug/default/__plugin.json", require.resolveWeak("@generated/docusaurus-plugin-debug/default/__plugin.json")],};

View File

@@ -3,205 +3,240 @@ import ComponentCreator from '@docusaurus/ComponentCreator';
export default [
{
path: '/docs/zh/',
component: ComponentCreator('/docs/zh/', 'b34'),
path: '/docs/__docusaurus/debug',
component: ComponentCreator('/docs/__docusaurus/debug', 'e58'),
exact: true
},
{
path: '/docs/__docusaurus/debug/config',
component: ComponentCreator('/docs/__docusaurus/debug/config', '2ce'),
exact: true
},
{
path: '/docs/__docusaurus/debug/content',
component: ComponentCreator('/docs/__docusaurus/debug/content', '11b'),
exact: true
},
{
path: '/docs/__docusaurus/debug/globalData',
component: ComponentCreator('/docs/__docusaurus/debug/globalData', 'f13'),
exact: true
},
{
path: '/docs/__docusaurus/debug/metadata',
component: ComponentCreator('/docs/__docusaurus/debug/metadata', 'bff'),
exact: true
},
{
path: '/docs/__docusaurus/debug/registry',
component: ComponentCreator('/docs/__docusaurus/debug/registry', '830'),
exact: true
},
{
path: '/docs/__docusaurus/debug/routes',
component: ComponentCreator('/docs/__docusaurus/debug/routes', '13e'),
exact: true
},
{
path: '/docs/',
component: ComponentCreator('/docs/', 'a3f'),
routes: [
{
path: '/docs/zh/',
component: ComponentCreator('/docs/zh/', 'a8e'),
path: '/docs/',
component: ComponentCreator('/docs/', 'fa7'),
routes: [
{
path: '/docs/zh/',
component: ComponentCreator('/docs/zh/', '632'),
path: '/docs/',
component: ComponentCreator('/docs/', '294'),
routes: [
{
path: '/docs/zh/commands/cli/cli-init',
component: ComponentCreator('/docs/zh/commands/cli/cli-init', 'fe3'),
path: '/docs/commands/cli/cli-init',
component: ComponentCreator('/docs/commands/cli/cli-init', '159'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/cli/codex-review',
component: ComponentCreator('/docs/zh/commands/cli/codex-review', 'e65'),
path: '/docs/commands/cli/codex-review',
component: ComponentCreator('/docs/commands/cli/codex-review', 'c66'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/general/ccw',
component: ComponentCreator('/docs/zh/commands/general/ccw', '83a'),
path: '/docs/commands/general/ccw',
component: ComponentCreator('/docs/commands/general/ccw', '3c1'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/general/ccw-coordinator',
component: ComponentCreator('/docs/zh/commands/general/ccw-coordinator', 'f35'),
path: '/docs/commands/general/ccw-coordinator',
component: ComponentCreator('/docs/commands/general/ccw-coordinator', '3b4'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/general/ccw-debug',
component: ComponentCreator('/docs/zh/commands/general/ccw-debug', 'b0a'),
path: '/docs/commands/general/ccw-debug',
component: ComponentCreator('/docs/commands/general/ccw-debug', 'e0c'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/general/ccw-plan',
component: ComponentCreator('/docs/zh/commands/general/ccw-plan', '39d'),
path: '/docs/commands/general/ccw-plan',
component: ComponentCreator('/docs/commands/general/ccw-plan', '9ae'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/general/ccw-test',
component: ComponentCreator('/docs/zh/commands/general/ccw-test', '765'),
path: '/docs/commands/general/ccw-test',
component: ComponentCreator('/docs/commands/general/ccw-test', 'e6f'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/general/codex-coordinator',
component: ComponentCreator('/docs/zh/commands/general/codex-coordinator', '486'),
path: '/docs/commands/general/codex-coordinator',
component: ComponentCreator('/docs/commands/general/codex-coordinator', 'e7d'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/general/flow-create',
component: ComponentCreator('/docs/zh/commands/general/flow-create', 'd53'),
path: '/docs/commands/general/flow-create',
component: ComponentCreator('/docs/commands/general/flow-create', '507'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/issue/issue-convert-to-plan',
component: ComponentCreator('/docs/zh/commands/issue/issue-convert-to-plan', '0df'),
path: '/docs/commands/issue/issue-convert-to-plan',
component: ComponentCreator('/docs/commands/issue/issue-convert-to-plan', 'a36'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/issue/issue-discover',
component: ComponentCreator('/docs/zh/commands/issue/issue-discover', '9b4'),
path: '/docs/commands/issue/issue-discover',
component: ComponentCreator('/docs/commands/issue/issue-discover', '5ae'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/issue/issue-execute',
component: ComponentCreator('/docs/zh/commands/issue/issue-execute', 'cfd'),
path: '/docs/commands/issue/issue-execute',
component: ComponentCreator('/docs/commands/issue/issue-execute', '20b'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/issue/issue-from-brainstorm',
component: ComponentCreator('/docs/zh/commands/issue/issue-from-brainstorm', 'd2f'),
path: '/docs/commands/issue/issue-from-brainstorm',
component: ComponentCreator('/docs/commands/issue/issue-from-brainstorm', '10c'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/issue/issue-new',
component: ComponentCreator('/docs/zh/commands/issue/issue-new', '7f9'),
path: '/docs/commands/issue/issue-new',
component: ComponentCreator('/docs/commands/issue/issue-new', 'abb'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/issue/issue-plan',
component: ComponentCreator('/docs/zh/commands/issue/issue-plan', 'ed4'),
path: '/docs/commands/issue/issue-plan',
component: ComponentCreator('/docs/commands/issue/issue-plan', '57f'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/issue/issue-queue',
component: ComponentCreator('/docs/zh/commands/issue/issue-queue', 'a4b'),
path: '/docs/commands/issue/issue-queue',
component: ComponentCreator('/docs/commands/issue/issue-queue', '316'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/memory/memory-compact',
component: ComponentCreator('/docs/zh/commands/memory/memory-compact', '8dc'),
path: '/docs/commands/memory/memory-compact',
component: ComponentCreator('/docs/commands/memory/memory-compact', 'fbd'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/memory/memory-docs-full-cli',
component: ComponentCreator('/docs/zh/commands/memory/memory-docs-full-cli', '1a7'),
path: '/docs/commands/memory/memory-docs-full-cli',
component: ComponentCreator('/docs/commands/memory/memory-docs-full-cli', '8b8'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/memory/memory-docs-related-cli',
component: ComponentCreator('/docs/zh/commands/memory/memory-docs-related-cli', 'f28'),
path: '/docs/commands/memory/memory-docs-related-cli',
component: ComponentCreator('/docs/commands/memory/memory-docs-related-cli', '707'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/memory/memory-load',
component: ComponentCreator('/docs/zh/commands/memory/memory-load', 'aee'),
path: '/docs/commands/memory/memory-load',
component: ComponentCreator('/docs/commands/memory/memory-load', '1db'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/memory/memory-update-full',
component: ComponentCreator('/docs/zh/commands/memory/memory-update-full', '2a1'),
path: '/docs/commands/memory/memory-update-full',
component: ComponentCreator('/docs/commands/memory/memory-update-full', '3fa'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/commands/memory/memory-update-related',
component: ComponentCreator('/docs/zh/commands/memory/memory-update-related', '991'),
path: '/docs/commands/memory/memory-update-related',
component: ComponentCreator('/docs/commands/memory/memory-update-related', 'c50'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/faq',
component: ComponentCreator('/docs/zh/faq', 'd6c'),
path: '/docs/faq',
component: ComponentCreator('/docs/faq', '296'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/overview',
component: ComponentCreator('/docs/zh/overview', '2d1'),
path: '/docs/overview',
component: ComponentCreator('/docs/overview', 'f90'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/workflows/faq',
component: ComponentCreator('/docs/zh/workflows/faq', '319'),
path: '/docs/workflows/faq',
component: ComponentCreator('/docs/workflows/faq', '58c'),
exact: true
},
{
path: '/docs/zh/workflows/introduction',
component: ComponentCreator('/docs/zh/workflows/introduction', 'dc8'),
path: '/docs/workflows/introduction',
component: ComponentCreator('/docs/workflows/introduction', '702'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/workflows/level-1-ultra-lightweight',
component: ComponentCreator('/docs/zh/workflows/level-1-ultra-lightweight', '4d3'),
path: '/docs/workflows/level-1-ultra-lightweight',
component: ComponentCreator('/docs/workflows/level-1-ultra-lightweight', 'b4b'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/workflows/level-2-rapid',
component: ComponentCreator('/docs/zh/workflows/level-2-rapid', 'e2a'),
path: '/docs/workflows/level-2-rapid',
component: ComponentCreator('/docs/workflows/level-2-rapid', 'fe1'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/workflows/level-3-standard',
component: ComponentCreator('/docs/zh/workflows/level-3-standard', '936'),
path: '/docs/workflows/level-3-standard',
component: ComponentCreator('/docs/workflows/level-3-standard', '65f'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/workflows/level-4-brainstorm',
component: ComponentCreator('/docs/zh/workflows/level-4-brainstorm', '87d'),
path: '/docs/workflows/level-4-brainstorm',
component: ComponentCreator('/docs/workflows/level-4-brainstorm', 'fae'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/workflows/level-5-intelligent',
component: ComponentCreator('/docs/zh/workflows/level-5-intelligent', 'b09'),
path: '/docs/workflows/level-5-intelligent',
component: ComponentCreator('/docs/workflows/level-5-intelligent', 'fa9'),
exact: true,
sidebar: "docs"
},
{
path: '/docs/zh/',
component: ComponentCreator('/docs/zh/', '0e3'),
path: '/docs/',
component: ComponentCreator('/docs/', '6df'),
exact: true,
sidebar: "docs"
}

View File

@@ -1,143 +1,186 @@
{
"/docs/zh/-b34": {
"__comp": "5e95c892",
"/docs/__docusaurus/debug-e58": {
"__comp": "__comp---theme-debug-config-23-a-2ff",
"__context": {
"plugin": "aba21aa0"
"plugin": "plugin---docs-docusaurus-debugb-38-c84"
}
},
"/docs/zh/-a8e": {
"__comp": "a7bd4aaa",
"__props": "b17e4002"
"/docs/__docusaurus/debug/config-2ce": {
"__comp": "__comp---theme-debug-config-23-a-2ff",
"__context": {
"plugin": "plugin---docs-docusaurus-debugb-38-c84"
}
},
"/docs/zh/-632": {
"__comp": "a94703ab"
"/docs/__docusaurus/debug/content-11b": {
"__comp": "__comp---theme-debug-contentba-8-ce7",
"__context": {
"plugin": "plugin---docs-docusaurus-debugb-38-c84"
},
"__props": "__props---docs-docusaurus-debug-content-344-8d5"
},
"/docs/zh/commands/cli/cli-init-fe3": {
"__comp": "17896441",
"content": "0566a0a8"
"/docs/__docusaurus/debug/globalData-f13": {
"__comp": "__comp---theme-debug-global-dataede-0fa",
"__context": {
"plugin": "plugin---docs-docusaurus-debugb-38-c84"
}
},
"/docs/zh/commands/cli/codex-review-e65": {
"__comp": "17896441",
"content": "f1bf82ec"
"/docs/__docusaurus/debug/metadata-bff": {
"__comp": "__comp---theme-debug-site-metadata-68-e-3d4",
"__context": {
"plugin": "plugin---docs-docusaurus-debugb-38-c84"
}
},
"/docs/zh/commands/general/ccw-83a": {
"__comp": "17896441",
"content": "f4817052"
"/docs/__docusaurus/debug/registry-830": {
"__comp": "__comp---theme-debug-registry-679-501",
"__context": {
"plugin": "plugin---docs-docusaurus-debugb-38-c84"
}
},
"/docs/zh/commands/general/ccw-coordinator-f35": {
"__comp": "17896441",
"content": "d550a629"
"/docs/__docusaurus/debug/routes-13e": {
"__comp": "__comp---theme-debug-routes-946-699",
"__context": {
"plugin": "plugin---docs-docusaurus-debugb-38-c84"
}
},
"/docs/zh/commands/general/ccw-debug-b0a": {
"__comp": "17896441",
"content": "97c6e66a"
"/docs/-a3f": {
"__comp": "__comp---theme-docs-root-5-e-9-0b6",
"__context": {
"plugin": "plugin---docs-aba-4f5"
}
},
"/docs/zh/commands/general/ccw-plan-39d": {
"__comp": "17896441",
"content": "04db0a2e"
"/docs/-fa7": {
"__comp": "__comp---theme-doc-version-roota-7-b-5de",
"__props": "__props---docs-11-b-f70"
},
"/docs/zh/commands/general/ccw-test-765": {
"__comp": "17896441",
"content": "ccef5d0f"
"/docs/-294": {
"__comp": "__comp---theme-doc-roota-94-67a"
},
"/docs/zh/commands/general/codex-coordinator-486": {
"__comp": "17896441",
"content": "f9222419"
"/docs/commands/cli/cli-init-159": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-cli-cli-init-056-ce1"
},
"/docs/zh/commands/general/flow-create-d53": {
"__comp": "17896441",
"content": "fabaf1c8"
"/docs/commands/cli/codex-review-c66": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-cli-codex-reviewf-1-b-55f"
},
"/docs/zh/commands/issue/issue-convert-to-plan-0df": {
"__comp": "17896441",
"content": "5c7b2278"
"/docs/commands/general/ccw-3c1": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-general-ccwf-48-8c4"
},
"/docs/zh/commands/issue/issue-discover-9b4": {
"__comp": "17896441",
"content": "1e3006f3"
"/docs/commands/general/ccw-coordinator-3b4": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-general-ccw-coordinatord-55-c6b"
},
"/docs/zh/commands/issue/issue-execute-cfd": {
"__comp": "17896441",
"content": "fe8e3dcf"
"/docs/commands/general/ccw-debug-e0c": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-general-ccw-debug-97-c-a72"
},
"/docs/zh/commands/issue/issue-from-brainstorm-d2f": {
"__comp": "17896441",
"content": "2ecf8b4a"
"/docs/commands/general/ccw-plan-9ae": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-general-ccw-plan-04-d-fe0"
},
"/docs/zh/commands/issue/issue-new-7f9": {
"__comp": "17896441",
"content": "4ad7db0f"
"/docs/commands/general/ccw-test-e6f": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-general-ccw-testcce-912"
},
"/docs/zh/commands/issue/issue-plan-ed4": {
"__comp": "17896441",
"content": "a6c3df16"
"/docs/commands/general/codex-coordinator-e7d": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-general-codex-coordinatorf-92-1dc"
},
"/docs/zh/commands/issue/issue-queue-a4b": {
"__comp": "17896441",
"content": "1bac9067"
"/docs/commands/general/flow-create-507": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-general-flow-createfab-98a"
},
"/docs/zh/commands/memory/memory-compact-8dc": {
"__comp": "17896441",
"content": "7a1ee27c"
"/docs/commands/issue/issue-convert-to-plan-a36": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-issue-issue-convert-to-plan-5-c-7-184"
},
"/docs/zh/commands/memory/memory-docs-full-cli-1a7": {
"__comp": "17896441",
"content": "4cc74730"
"/docs/commands/issue/issue-discover-5ae": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-issue-issue-discover-1-e-3-569"
},
"/docs/zh/commands/memory/memory-docs-related-cli-f28": {
"__comp": "17896441",
"content": "60eef997"
"/docs/commands/issue/issue-execute-20b": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-issue-issue-executefe-8-c03"
},
"/docs/zh/commands/memory/memory-load-aee": {
"__comp": "17896441",
"content": "157db180"
"/docs/commands/issue/issue-from-brainstorm-10c": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-issue-issue-from-brainstorm-2-ec-eeb"
},
"/docs/zh/commands/memory/memory-update-full-2a1": {
"__comp": "17896441",
"content": "666bb1bf"
"/docs/commands/issue/issue-new-abb": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-issue-issue-new-4-ad-3f0"
},
"/docs/zh/commands/memory/memory-update-related-991": {
"__comp": "17896441",
"content": "611877e1"
"/docs/commands/issue/issue-plan-57f": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-issue-issue-plana-6-c-fbd"
},
"/docs/zh/faq-d6c": {
"__comp": "17896441",
"content": "2a5e3eff"
"/docs/commands/issue/issue-queue-316": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-issue-issue-queue-1-ba-55f"
},
"/docs/zh/overview-2d1": {
"__comp": "17896441",
"content": "8a7e39ed"
"/docs/commands/memory/memory-compact-fbd": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-memory-memory-compact-7-a-1-41c"
},
"/docs/zh/workflows/faq-319": {
"__comp": "17896441",
"content": "46f40178"
"/docs/commands/memory/memory-docs-full-cli-8b8": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-memory-memory-docs-full-cli-4-cc-96f"
},
"/docs/zh/workflows/introduction-dc8": {
"__comp": "17896441",
"content": "e5f6eee3"
"/docs/commands/memory/memory-docs-related-cli-707": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-memory-memory-docs-related-cli-60-e-dd0"
},
"/docs/zh/workflows/level-1-ultra-lightweight-4d3": {
"__comp": "17896441",
"content": "9cf7cb6b"
"/docs/commands/memory/memory-load-1db": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-memory-memory-load-157-952"
},
"/docs/zh/workflows/level-2-rapid-e2a": {
"__comp": "17896441",
"content": "05467734"
"/docs/commands/memory/memory-update-full-3fa": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-memory-memory-update-full-666-002"
},
"/docs/zh/workflows/level-3-standard-936": {
"__comp": "17896441",
"content": "3f1fe4a1"
"/docs/commands/memory/memory-update-related-c50": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-commands-memory-memory-update-related-611-8d3"
},
"/docs/zh/workflows/level-4-brainstorm-87d": {
"__comp": "17896441",
"content": "775938bf"
"/docs/faq-296": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-faqea-3-888"
},
"/docs/zh/workflows/level-5-intelligent-b09": {
"__comp": "17896441",
"content": "562bb8cb"
"/docs/overview-f90": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-overview-188-429"
},
"/docs/zh/-0e3": {
"__comp": "17896441",
"content": "6ab014e9"
"/docs/workflows/faq-58c": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-workflows-faqbcf-045"
},
"/docs/workflows/introduction-702": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-workflows-introduction-9-f-4-275"
},
"/docs/workflows/level-1-ultra-lightweight-b4b": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-workflows-level-1-ultra-lightweightc-5-a-5db"
},
"/docs/workflows/level-2-rapid-fe1": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-workflows-level-2-rapid-19-b-095"
},
"/docs/workflows/level-3-standard-65f": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-workflows-level-3-standardbdb-61a"
},
"/docs/workflows/level-4-brainstorm-fae": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-workflows-level-4-brainstormd-04-14f"
},
"/docs/workflows/level-5-intelligent-fa9": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-workflows-level-5-intelligent-186-b05"
},
"/docs/-6df": {
"__comp": "__comp---theme-doc-item-178-a40",
"content": "content---docs-4-ed-831"
}
}

View File

@@ -12,9 +12,9 @@
"name": "@docusaurus/plugin-content-pages",
"version": "3.9.2"
},
"docusaurus-plugin-sitemap": {
"docusaurus-plugin-debug": {
"type": "package",
"name": "@docusaurus/plugin-sitemap",
"name": "@docusaurus/plugin-debug",
"version": "3.9.2"
},
"docusaurus-plugin-svgr": {

View File

@@ -0,0 +1,480 @@
// ========================================
// E2E Tests: API Settings - CLI Provider Configuration
// ========================================
// End-to-end tests for API provider management and testing
import { test, expect } from '@playwright/test';
import { setupEnhancedMonitoring, switchLanguageAndVerify } from './helpers/i18n-helpers';
test.describe('[API Settings] - CLI Provider Configuration Tests', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/api-settings', { waitUntil: 'networkidle' as const });
});
test('L3.21 - Page loads and displays current configuration', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API response for current settings
await page.route('**/api/settings/cli', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
providers: [
{
id: 'provider-1',
name: 'Gemini',
endpoint: 'https://api.example.com',
enabled: true
}
]
})
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for API settings form
const settingsForm = page.getByTestId('api-settings-form').or(
page.locator('form').filter({ hasText: /api|settings|provider/i })
);
const isFormVisible = await settingsForm.isVisible().catch(() => false);
if (isFormVisible) {
// Verify provider list is displayed
const providerList = page.getByTestId('provider-list').or(
settingsForm.locator('*').filter({ hasText: /provider|gemini|cli/i })
);
const hasProviders = await providerList.isVisible().catch(() => false);
expect(hasProviders).toBe(true);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.22 - Add new CLI provider', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for adding provider
await page.route('**/api/settings/cli', (route) => {
if (route.request().method() === 'POST') {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
success: true,
provider: {
id: 'new-provider',
name: 'Test Provider',
endpoint: 'https://test.api.com'
}
})
});
} else {
route.continue();
}
});
// Look for add provider button
const addButton = page.getByRole('button', { name: /add|create|new provider/i }).or(
page.getByTestId('provider-add-button')
);
const hasAddButton = await addButton.isVisible().catch(() => false);
if (hasAddButton) {
await addButton.click();
// Look for provider form
const form = page.getByTestId('provider-form').or(
page.getByRole('dialog').locator('form')
);
const hasForm = await form.isVisible().catch(() => false);
if (hasForm) {
// Fill in provider details
const nameInput = form.getByRole('textbox', { name: /name/i }).or(
form.getByLabel(/name/i)
);
const hasNameInput = await nameInput.isVisible().catch(() => false);
if (hasNameInput) {
await nameInput.fill('E2E Test Provider');
const endpointInput = form.getByRole('textbox', { name: /endpoint|url|api/i });
const hasEndpointInput = await endpointInput.isVisible().catch(() => false);
if (hasEndpointInput) {
await endpointInput.fill('https://e2e-test.api.com');
// Submit form
const saveButton = form.getByRole('button', { name: /save|create|submit/i });
await saveButton.click();
// Verify success
const successMessage = page.getByText(/added|created|success/i);
const hasSuccess = await successMessage.isVisible().catch(() => false);
expect(hasSuccess).toBe(true);
}
}
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.23 - Edit existing provider', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for editing provider
await page.route('**/api/settings/cli', (route) => {
if (route.request().method() === 'POST') {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true })
});
} else {
route.continue();
}
});
// Look for existing provider
const providerItems = page.getByTestId(/provider-item|provider-card/).or(
page.locator('.provider-item')
);
const providerCount = await providerItems.count();
if (providerCount > 0) {
const firstProvider = providerItems.first();
// Look for edit button
const editButton = firstProvider.getByRole('button', { name: /edit|modify/i }).or(
firstProvider.getByTestId('edit-button')
);
const hasEditButton = await editButton.isVisible().catch(() => false);
if (hasEditButton) {
await editButton.click();
// Edit provider details
const nameInput = page.getByRole('textbox', { name: /name/i });
const hasNameInput = await nameInput.isVisible().catch(() => false);
if (hasNameInput) {
await nameInput.fill('Updated Provider Name');
const saveButton = page.getByRole('button', { name: /save|update/i });
await saveButton.click();
// Verify success
const successMessage = page.getByText(/updated|saved|success/i);
const hasSuccess = await successMessage.isVisible().catch(() => false);
expect(hasSuccess).toBe(true);
}
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.24 - Delete provider', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for deleting provider
await page.route('**/api/settings/cli/*', (route) => {
if (route.request().method() === 'DELETE') {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true })
});
} else {
route.continue();
}
});
// Look for existing provider
const providerItems = page.getByTestId(/provider-item|provider-card/).or(
page.locator('.provider-item')
);
const providerCount = await providerItems.count();
if (providerCount > 0) {
const firstProvider = providerItems.first();
// Look for delete button
const deleteButton = firstProvider.getByRole('button', { name: /delete|remove/i }).or(
firstProvider.getByTestId('delete-button')
);
const hasDeleteButton = await deleteButton.isVisible().catch(() => false);
if (hasDeleteButton) {
await deleteButton.click();
// Confirm deletion if dialog appears
const confirmDialog = page.getByRole('dialog').filter({ hasText: /delete|confirm/i });
const hasDialog = await confirmDialog.isVisible().catch(() => false);
if (hasDialog) {
const confirmButton = confirmDialog.getByRole('button', { name: /delete|confirm|yes/i });
await confirmButton.click();
}
// Verify success
const successMessage = page.getByText(/deleted|removed|success/i);
const hasSuccess = await successMessage.isVisible().catch(() => false);
expect(hasSuccess).toBe(true);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.25 - Test API connection', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for connection test
await page.route('**/api/settings/test', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
success: true,
latency: 45,
status: 'connected'
})
});
});
// Look for test connection button
const testButton = page.getByRole('button', { name: /test|check connection/i }).or(
page.getByTestId('connection-test-button')
);
const hasTestButton = await testButton.isVisible().catch(() => false);
if (hasTestButton) {
await testButton.click();
// Wait for test to complete
await page.waitForTimeout(1000);
// Verify connection result
const successMessage = page.getByText(/connected|success|available/i);
const hasSuccess = await successMessage.isVisible().catch(() => false);
expect(hasSuccess).toBe(true);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.26 - Save configuration persists', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for saving
await page.route('**/api/settings/cli', (route) => {
if (route.request().method() === 'POST') {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true, persisted: true })
});
} else {
route.continue();
}
});
// Look for save button
const saveButton = page.getByRole('button', { name: /save|apply/i }).or(
page.getByTestId('provider-save-button')
);
const hasSaveButton = await saveButton.isVisible().catch(() => false);
if (hasSaveButton) {
await saveButton.click();
// Verify persistence via localStorage
const configStore = await page.evaluate(() => {
const storage = localStorage.getItem('ccw-config-store');
return storage ? JSON.parse(storage) : null;
});
const hasPersisted = configStore !== null;
expect(hasPersisted).toBe(true);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.27 - i18n - Form labels in EN/ZH', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Get language switcher
const languageSwitcher = page.getByRole('combobox', { name: /select language|language/i }).first();
const hasLanguageSwitcher = await languageSwitcher.isVisible().catch(() => false);
if (hasLanguageSwitcher) {
// Switch to Chinese
await switchLanguageAndVerify(page, 'zh', languageSwitcher);
// Verify form is visible in Chinese
const form = page.getByTestId('api-settings-form').or(
page.locator('form')
);
const isFormVisible = await form.isVisible().catch(() => false);
expect(isFormVisible).toBe(true);
// Check for Chinese text
const pageContent = await page.content();
const hasChineseText = /[\u4e00-\u9fa5]/.test(pageContent);
expect(hasChineseText).toBe(true);
// Switch back to English
await switchLanguageAndVerify(page, 'en', languageSwitcher);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.28 - Error - Invalid API endpoint', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API error for invalid endpoint
await page.route('**/api/settings/test', (route) => {
route.fulfill({
status: 400,
contentType: 'application/json',
body: JSON.stringify({
error: 'Invalid endpoint URL',
details: 'Endpoint must be a valid HTTPS URL'
})
});
});
// Look for endpoint input
const endpointInput = page.getByRole('textbox', { name: /endpoint|url|api/i }).or(
page.getByLabel(/endpoint|url/i)
);
const hasEndpointInput = await endpointInput.isVisible().catch(() => false);
if (hasEndpointInput) {
await endpointInput.fill('not-a-valid-url');
const testButton = page.getByRole('button', { name: /test|validate/i });
const hasTestButton = await testButton.isVisible().catch(() => false);
if (hasTestButton) {
await testButton.click();
// Verify error message
const errorMessage = page.getByText(/invalid|error|url/i);
const hasError = await errorMessage.isVisible().catch(() => false);
expect(hasError).toBe(true);
}
}
monitoring.assertClean({ ignoreAPIPatterns: ['/api/settings/test'], allowWarnings: true });
monitoring.stop();
});
test('L3.29 - Error - Connection timeout', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock timeout
await page.route('**/api/settings/test', (route) => {
// Simulate timeout by never responding
setTimeout(() => {
route.abort('timedout');
}, 35000);
});
// Look for test connection button
const testButton = page.getByRole('button', { name: /test|check/i });
const hasTestButton = await testButton.isVisible().catch(() => false);
if (hasTestButton) {
await testButton.click();
// Wait for timeout message
await page.waitForTimeout(3000);
// Verify timeout error
const timeoutMessage = page.getByText(/timeout|timed out|unavailable/i);
const hasTimeout = await timeoutMessage.isVisible().catch(() => false);
// Timeout message may or may not appear
}
monitoring.assertClean({ ignoreAPIPatterns: ['/api/settings/test'], allowWarnings: true });
monitoring.stop();
});
test('L3.30 - Edge - Maximum providers limit', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API to enforce limit
await page.route('**/api/settings/cli', (route) => {
if (route.request().method() === 'POST') {
route.fulfill({
status: 409,
contentType: 'application/json',
body: JSON.stringify({
error: 'Maximum provider limit reached',
limit: 10
})
});
} else {
route.continue();
}
});
// Look for add provider button
const addButton = page.getByRole('button', { name: /add|create/i });
const hasAddButton = await addButton.isVisible().catch(() => false);
if (hasAddButton) {
await addButton.click();
// Try to add provider
const nameInput = page.getByRole('textbox', { name: /name/i });
const hasNameInput = await nameInput.isVisible().catch(() => false);
if (hasNameInput) {
await nameInput.fill('Test Provider');
const saveButton = page.getByRole('button', { name: /save|create/i });
await saveButton.click();
// Look for limit error
const limitMessage = page.getByText(/limit|maximum|too many/i);
const hasLimitMessage = await limitMessage.isVisible().catch(() => false);
// Limit message may or may not appear
}
}
monitoring.assertClean({ ignoreAPIPatterns: ['/api/settings/cli'], allowWarnings: true });
monitoring.stop();
});
});

View File

@@ -18,7 +18,7 @@ test.describe('[Dashboard Charts] - Chart Rendering & Interaction Tests', () =>
await waitForDashboardLoad(page);
});
describe('Pie Chart Rendering', () => {
test.describe('Pie Chart Rendering', () => {
test('DC-1.1 - should render workflow status pie chart with data', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
@@ -84,7 +84,7 @@ test.describe('[Dashboard Charts] - Chart Rendering & Interaction Tests', () =>
});
});
describe('Line Chart Rendering', () => {
test.describe('Line Chart Rendering', () => {
test('DC-2.1 - should render activity timeline line chart', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
@@ -167,7 +167,7 @@ test.describe('[Dashboard Charts] - Chart Rendering & Interaction Tests', () =>
});
});
describe('Bar Chart Rendering', () => {
test.describe('Bar Chart Rendering', () => {
test('DC-3.1 - should render task type bar chart', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
@@ -232,7 +232,7 @@ test.describe('[Dashboard Charts] - Chart Rendering & Interaction Tests', () =>
});
});
describe('Chart Responsiveness', () => {
test.describe('Chart Responsiveness', () => {
test('DC-4.1 - should resize charts on mobile viewport', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
@@ -290,7 +290,7 @@ test.describe('[Dashboard Charts] - Chart Rendering & Interaction Tests', () =>
});
});
describe('Chart Empty States', () => {
test.describe('Chart Empty States', () => {
test('DC-5.1 - should display empty state when no data available', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
@@ -342,7 +342,7 @@ test.describe('[Dashboard Charts] - Chart Rendering & Interaction Tests', () =>
});
});
describe('Chart Legend Interaction', () => {
test.describe('Chart Legend Interaction', () => {
test('DC-6.1 - should toggle line visibility when clicking legend', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
@@ -373,7 +373,7 @@ test.describe('[Dashboard Charts] - Chart Rendering & Interaction Tests', () =>
});
});
describe('Chart Performance', () => {
test.describe('Chart Performance', () => {
test('DC-7.1 - should render all charts within performance budget', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);

View File

@@ -23,7 +23,7 @@ test.describe('[Dashboard Redesign] - Navigation & Layout Tests', () => {
await waitForDashboardLoad(page);
});
describe('Navigation Grouping', () => {
test.describe('Navigation Grouping', () => {
test('DR-1.1 - should display all 6 navigation groups', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
@@ -146,7 +146,7 @@ test.describe('[Dashboard Redesign] - Navigation & Layout Tests', () => {
});
});
describe('Dashboard Loading', () => {
test.describe('Dashboard Loading', () => {
test('DR-2.1 - should load all 5 widgets successfully', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
@@ -213,7 +213,7 @@ test.describe('[Dashboard Redesign] - Navigation & Layout Tests', () => {
});
});
describe('Drag-Drop Persistence', () => {
test.describe('Drag-Drop Persistence', () => {
test('DR-3.1 - should allow dragging widgets to new positions', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
@@ -293,7 +293,7 @@ test.describe('[Dashboard Redesign] - Navigation & Layout Tests', () => {
});
});
describe('Ticker Real-time Updates', () => {
test.describe('Ticker Real-time Updates', () => {
test('DR-4.1 - should display ticker marquee component', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
@@ -374,7 +374,7 @@ test.describe('[Dashboard Redesign] - Navigation & Layout Tests', () => {
});
});
describe('Responsive Layout', () => {
test.describe('Responsive Layout', () => {
test('DR-5.1 - should adapt layout for mobile viewport', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);

View File

@@ -0,0 +1,205 @@
// ========================================
// E2E Tests: Explorer - File System Navigation
// ========================================
// End-to-end tests for file explorer with tree navigation and file details
import { test, expect } from '@playwright/test';
import { setupEnhancedMonitoring, switchLanguageAndVerify } from './helpers/i18n-helpers';
test.describe('[Explorer] - File System Navigation Tests', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/explorer', { waitUntil: 'networkidle' as const });
});
test('L3.45 - Page loads and displays file tree', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for file tree
await page.route('**/api/explorer', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
files: [
{
id: 'file-1',
name: 'src',
type: 'directory',
children: [
{ id: 'file-2', name: 'index.ts', type: 'file' }
]
}
]
})
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for file tree
const fileTree = page.getByTestId('file-tree').or(
page.locator('.file-tree')
);
const isTreeVisible = await fileTree.isVisible().catch(() => false);
if (isTreeVisible) {
// Verify tree nodes are displayed
const treeNodes = page.getByTestId(/tree-node|file-node|directory-node/).or(
page.locator('.tree-node')
);
const nodeCount = await treeNodes.count();
expect(nodeCount).toBeGreaterThan(0);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.46 - Navigate directories', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Look for directory nodes
const directoryNodes = page.getByTestId(/directory-node|folder-node/).or(
page.locator('.directory-node')
);
const nodeCount = await directoryNodes.count();
if (nodeCount > 0) {
const firstDirectory = directoryNodes.first();
// Click to expand
await firstDirectory.click();
// Wait for children to load
await page.waitForTimeout(300);
// Verify directory state changed
const isExpanded = await firstDirectory.getAttribute('aria-expanded');
expect(isExpanded).toBe('true');
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.47 - View file details', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Look for file nodes
const fileNodes = page.getByTestId(/file-node/).filter({ hasText: /\.(ts|tsx|js|jsx)$/i }).or(
page.locator('.file-node').filter({ hasText: /\.(ts|tsx|js|jsx)$/i })
);
const fileCount = await fileNodes.count();
if (fileCount > 0) {
const firstFile = fileNodes.first();
// Click to view details
await firstFile.click();
// Look for file details panel
const detailsPanel = page.getByTestId('file-details-panel').or(
page.locator('.file-details')
);
const hasDetails = await detailsPanel.isVisible().catch(() => false);
if (hasDetails) {
expect(detailsPanel).toBeVisible();
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.48 - i18n - File tree labels in EN/ZH', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Get language switcher
const languageSwitcher = page.getByRole('combobox', { name: /select language|language/i }).first();
const hasLanguageSwitcher = await languageSwitcher.isVisible().catch(() => false);
if (hasLanguageSwitcher) {
// Switch to Chinese
await switchLanguageAndVerify(page, 'zh', languageSwitcher);
// Verify file tree content
const fileTree = page.getByTestId('file-tree').or(
page.locator('.file-tree')
);
const isTreeVisible = await fileTree.isVisible().catch(() => false);
expect(isTreeVisible).toBe(true);
// Switch back to English
await switchLanguageAndVerify(page, 'en', languageSwitcher);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.49 - Error - Directory not found', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API error
await page.route('**/api/explorer', (route) => {
route.fulfill({
status: 404,
contentType: 'application/json',
body: JSON.stringify({ error: 'Directory not found' })
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for error indicator
const errorIndicator = page.getByText(/not found|error|unable/i).or(
page.getByTestId('error-state')
);
const hasError = await errorIndicator.isVisible().catch(() => false);
if (hasError) {
expect(errorIndicator).toBeVisible();
}
monitoring.assertClean({ ignoreAPIPatterns: ['/api/explorer'], allowWarnings: true });
monitoring.stop();
});
test('L3.50 - Edge - Empty directory', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for empty directory
await page.route('**/api/explorer', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ files: [] })
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for empty state
const emptyState = page.getByText(/empty|no files|directory is empty/i);
const hasEmptyState = await emptyState.isVisible().catch(() => false);
if (hasEmptyState) {
expect(emptyState).toBeVisible();
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
});

View File

@@ -0,0 +1,198 @@
// ========================================
// E2E Tests: Graph Explorer - Code Relationship Visualization
// ========================================
// End-to-end tests for code graph visualization with node relationships
import { test, expect } from '@playwright/test';
import { setupEnhancedMonitoring, switchLanguageAndVerify } from './helpers/i18n-helpers';
test.describe('[Graph Explorer] - Code Relationship Visualization Tests', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/graph', { waitUntil: 'networkidle' as const });
});
test('L3.51 - Page loads and displays code graph', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for graph data
await page.route('**/api/graph', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
nodes: [
{ id: 'node-1', label: 'Component', type: 'component' },
{ id: 'node-2', label: 'Service', type: 'service' }
],
edges: [
{ id: 'edge-1', source: 'node-1', target: 'node-2', label: 'imports' }
]
})
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for code graph
const codeGraph = page.getByTestId('code-graph').or(
page.locator('.code-graph')
);
const isGraphVisible = await codeGraph.isVisible().catch(() => false);
if (isGraphVisible) {
// Verify graph has nodes
const graphNodes = page.getByTestId(/graph-node|node-/).or(
page.locator('.graph-node')
);
const nodeCount = await graphNodes.count();
expect(nodeCount).toBeGreaterThan(0);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.52 - Navigate graph nodes', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Look for graph nodes
const graphNodes = page.getByTestId(/graph-node|node-/).or(
page.locator('.graph-node')
);
const nodeCount = await graphNodes.count();
if (nodeCount > 0) {
const firstNode = graphNodes.first();
// Click to select node
await firstNode.click();
// Verify node is selected
const isSelected = await firstNode.getAttribute('aria-selected');
expect(isSelected).toBe('true');
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.53 - Expand/collapse node relationships', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Look for expandable nodes
const expandableNodes = page.getByTestId(/graph-node/).filter({ hasText: /\+/ });
const nodeCount = await expandableNodes.count();
if (nodeCount > 0) {
const firstNode = expandableNodes.first();
// Click to expand
await firstNode.click();
// Wait for expansion
await page.waitForTimeout(300);
// Look for expanded children
const childNodes = page.getByTestId(/graph-node/);
const nodeCountAfter = await childNodes.count();
expect(nodeCountAfter).toBeGreaterThanOrEqual(0);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.54 - i18n - Graph labels in EN/ZH', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Get language switcher
const languageSwitcher = page.getByRole('combobox', { name: /select language|language/i }).first();
const hasLanguageSwitcher = await languageSwitcher.isVisible().catch(() => false);
if (hasLanguageSwitcher) {
// Switch to Chinese
await switchLanguageAndVerify(page, 'zh', languageSwitcher);
// Verify graph is visible
const codeGraph = page.getByTestId('code-graph').or(
page.locator('.code-graph')
);
const isGraphVisible = await codeGraph.isVisible().catch(() => false);
expect(isGraphVisible).toBe(true);
// Switch back to English
await switchLanguageAndVerify(page, 'en', languageSwitcher);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.55 - Error - Graph generation fails', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API error
await page.route('**/api/graph', (route) => {
route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ error: 'Failed to generate graph' })
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for error indicator
const errorIndicator = page.getByText(/error|failed|unable/i).or(
page.getByTestId('error-state')
);
const hasError = await errorIndicator.isVisible().catch(() => false);
if (hasError) {
expect(errorIndicator).toBeVisible();
}
monitoring.assertClean({ ignoreAPIPatterns: ['/api/graph'], allowWarnings: true });
monitoring.stop();
});
test('L3.56 - Edge - No relationships found', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for empty graph
await page.route('**/api/graph', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
nodes: [],
edges: []
})
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for empty state
const emptyState = page.getByText(/no relationships|empty|no data/i);
const hasEmptyState = await emptyState.isVisible().catch(() => false);
if (hasEmptyState) {
expect(emptyState).toBeVisible();
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
});

View File

@@ -0,0 +1,356 @@
// ========================================
// E2E Tests: History - Archived Session Management
// ========================================
// End-to-end tests for history page with search, filter, restore operations
import { test, expect } from '@playwright/test';
import { setupEnhancedMonitoring, switchLanguageAndVerify } from './helpers/i18n-helpers';
test.describe('[History] - Archived Session Management Tests', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/history', { waitUntil: 'networkidle' as const });
});
test('L3.31 - Page loads and displays archived sessions', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for history
await page.route('**/api/history', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
sessions: [
{
id: 'session-1',
title: 'Archived Session 1',
archivedAt: Date.now(),
status: 'completed'
}
]
})
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for history list
const historyList = page.getByTestId('history-list').or(
page.locator('.history-list')
);
const isListVisible = await historyList.isVisible().catch(() => false);
if (isListVisible) {
// Verify session items are displayed
const sessionItems = page.getByTestId(/session-item|history-item/).or(
page.locator('.history-item')
);
const itemCount = await sessionItems.count();
expect(itemCount).toBeGreaterThanOrEqual(0);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.32 - Search archived sessions', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for search
await page.route('**/api/history?q=**', (route) => {
const requestUrl = route.request().url();
const url = new URL(requestUrl);
const query = url.searchParams.get('q');
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
sessions: [
{
id: 'session-search',
title: `Search Result for ${query}`,
archivedAt: Date.now()
}
]
})
});
});
// Look for search input
const searchInput = page.getByRole('textbox', { name: /search|find/i }).or(
page.getByTestId('search-input')
);
const hasSearchInput = await searchInput.isVisible().catch(() => false);
if (hasSearchInput) {
await searchInput.fill('test');
await page.waitForTimeout(500);
// Verify search was performed
const searchResults = page.getByTestId(/history-item|session-item/);
const resultCount = await searchResults.count();
expect(resultCount).toBeGreaterThanOrEqual(0);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.33 - Filter by date range', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for date filter
await page.route('**/api/history**', (route) => {
const requestUrl = route.request().url();
const url = new URL(requestUrl);
const fromDate = url.searchParams.get('from');
const toDate = url.searchParams.get('to');
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
sessions: [
{
id: 'session-filtered',
title: 'Filtered by Date',
archivedAt: Date.now()
}
]
})
});
});
// Look for date filter controls
const dateFilter = page.getByTestId('date-filter').or(
page.locator('*').filter({ hasText: /date|filter/i })
);
const hasDateFilter = await dateFilter.isVisible().catch(() => false);
if (hasDateFilter) {
const fromDateInput = page.getByRole('textbox', { name: /from|start/i });
const hasFromDate = await fromDateInput.isVisible().catch(() => false);
if (hasFromDate) {
await fromDateInput.fill('2024-01-01');
const toDateInput = page.getByRole('textbox', { name: /to|end/i });
const hasToDate = await toDateInput.isVisible().catch(() => false);
if (hasToDate) {
await toDateInput.fill('2024-12-31');
// Look for apply button
const applyButton = page.getByRole('button', { name: /apply|filter/i });
const hasApplyButton = await applyButton.isVisible().catch(() => false);
if (hasApplyButton) {
await applyButton.click();
await page.waitForTimeout(500);
}
}
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.34 - Restore archived session', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for restore
await page.route('**/api/history/*/restore', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true, sessionId: 'restored-session' })
});
});
// Look for archived session
const sessionItems = page.getByTestId(/session-item|history-item/).or(
page.locator('.history-item')
);
const itemCount = await sessionItems.count();
if (itemCount > 0) {
const firstSession = sessionItems.first();
// Look for restore button
const restoreButton = firstSession.getByRole('button', { name: /restore|reload/i }).or(
firstSession.getByTestId('restore-button')
);
const hasRestoreButton = await restoreButton.isVisible().catch(() => false);
if (hasRestoreButton) {
await restoreButton.click();
// Verify success message
const successMessage = page.getByText(/restored|success/i);
const hasSuccess = await successMessage.isVisible().catch(() => false);
expect(hasSuccess).toBe(true);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.35 - Delete archived session', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for delete
await page.route('**/api/history/*', (route) => {
if (route.request().method() === 'DELETE') {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true })
});
} else {
route.continue();
}
});
// Look for archived session
const sessionItems = page.getByTestId(/session-item|history-item/).or(
page.locator('.history-item')
);
const itemCount = await sessionItems.count();
if (itemCount > 0) {
const firstSession = sessionItems.first();
// Look for delete button
const deleteButton = firstSession.getByRole('button', { name: /delete|remove/i }).or(
firstSession.getByTestId('delete-button')
);
const hasDeleteButton = await deleteButton.isVisible().catch(() => false);
if (hasDeleteButton) {
await deleteButton.click();
// Confirm deletion if dialog appears
const confirmDialog = page.getByRole('dialog').filter({ hasText: /delete|confirm/i });
const hasDialog = await confirmDialog.isVisible().catch(() => false);
if (hasDialog) {
const confirmButton = confirmDialog.getByRole('button', { name: /delete|confirm|yes/i });
await confirmButton.click();
}
// Verify success message
const successMessage = page.getByText(/deleted|success/i);
const hasSuccess = await successMessage.isVisible().catch(() => false);
expect(hasSuccess).toBe(true);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.36 - i18n - Archive messages in EN/ZH', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Get language switcher
const languageSwitcher = page.getByRole('combobox', { name: /select language|language/i }).first();
const hasLanguageSwitcher = await languageSwitcher.isVisible().catch(() => false);
if (hasLanguageSwitcher) {
// Switch to Chinese
await switchLanguageAndVerify(page, 'zh', languageSwitcher);
// Verify history content is in Chinese
const pageContent = await page.content();
const hasChineseText = /[\u4e00-\u9fa5]/.test(pageContent);
expect(hasChineseText).toBe(true);
// Switch back to English
await switchLanguageAndVerify(page, 'en', languageSwitcher);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.37 - Error - Restore fails', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API error for restore
await page.route('**/api/history/*/restore', (route) => {
route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ error: 'Failed to restore session' })
});
});
// Look for archived session
const sessionItems = page.getByTestId(/session-item|history-item/).or(
page.locator('.history-item')
);
const itemCount = await sessionItems.count();
if (itemCount > 0) {
const firstSession = sessionItems.first();
// Look for restore button
const restoreButton = firstSession.getByRole('button', { name: /restore|reload/i });
const hasRestoreButton = await restoreButton.isVisible().catch(() => false);
if (hasRestoreButton) {
await restoreButton.click();
// Verify error message
const errorMessage = page.getByText(/error|failed|unable/i);
const hasError = await errorMessage.isVisible().catch(() => false);
expect(hasError).toBe(true);
}
}
monitoring.assertClean({ ignoreAPIPatterns: ['/api/history'], allowWarnings: true });
monitoring.stop();
});
test('L3.38 - Edge - Empty archive state', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for empty history
await page.route('**/api/history', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ sessions: [] })
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for empty state
const emptyState = page.getByTestId('empty-state').or(
page.getByText(/no history|empty|no sessions/i)
);
const hasEmptyState = await emptyState.isVisible().catch(() => false);
expect(hasEmptyState).toBe(true);
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
});

View File

@@ -1,273 +1,197 @@
// ========================================
// E2E Tests: Loops Management
// E2E Tests: Loops Monitor - Real-time Workflow Execution
// ========================================
// End-to-end tests for loop CRUD operations and controls
// End-to-end tests for loop monitoring with WebSocket mocking
import { test, expect } from '@playwright/test';
import { setupEnhancedMonitoring } from './helpers/i18n-helpers';
import { setupEnhancedMonitoring, switchLanguageAndVerify } from './helpers/i18n-helpers';
test.describe('[Loops] - Loop Management Tests', () => {
test.describe('[Loops Monitor] - Real-time Loop Tracking Tests', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/', { waitUntil: 'networkidle' as const });
// Mock WebSocket connection for real-time updates
await page.route('**/ws/loops**', (route) => {
route.fulfill({
status: 101,
headers: {
'Connection': 'Upgrade',
'Upgrade': 'websocket'
},
body: ''
});
});
});
test('L3.1 - should display loops list', async ({ page }) => {
test('L3.13 - Page loads and displays active loops', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Navigate to loops page
// Mock API for loops list
await page.route('**/api/loops', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
loops: [
{
id: 'loop-1',
name: 'Test Loop',
status: 'running',
progress: 50,
startedAt: Date.now()
}
]
})
});
});
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Look for loops list container
// Look for loops list
const loopsList = page.getByTestId('loops-list').or(
page.locator('.loops-list')
);
const isVisible = await loopsList.isVisible().catch(() => false);
const isListVisible = await loopsList.isVisible().catch(() => false);
if (isVisible) {
// Verify loop items exist or empty state is shown
if (isListVisible) {
// Verify loop items are displayed
const loopItems = page.getByTestId(/loop-item|loop-card/).or(
page.locator('.loop-item')
);
const itemCount = await loopItems.count();
if (itemCount === 0) {
const emptyState = page.getByTestId('empty-state').or(
page.getByText(/no loops/i)
);
const hasEmptyState = await emptyState.isVisible().catch(() => false);
expect(hasEmptyState).toBe(true);
}
expect(itemCount).toBeGreaterThanOrEqual(0);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.2 - should create new loop', async ({ page }) => {
test('L3.14 - Real-time loop status updates (mock WS)', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Navigate to loops page
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Look for create loop button
const createButton = page.getByRole('button', { name: /create|new|add loop/i }).or(
page.getByTestId('create-loop-button')
// Inject mock WebSocket message for status update
await page.evaluate(() => {
const event = new MessageEvent('message', {
data: JSON.stringify({
type: 'loop-update',
loopId: 'loop-1',
status: 'running',
progress: 75
})
});
window.dispatchEvent(event);
});
// Wait for update to be processed
await page.waitForTimeout(500);
// Look for updated status display
const statusBadge = page.getByTestId('loop-status-badge').or(
page.locator('*').filter({ hasText: /running|75%/i })
);
const hasCreateButton = await createButton.isVisible().catch(() => false);
const hasStatus = await statusBadge.isVisible().catch(() => false);
// Status update may or may not be visible
if (hasCreateButton) {
await createButton.click();
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
// Look for create loop dialog/form
const dialog = page.getByRole('dialog').filter({ hasText: /create loop|new loop/i });
const form = page.getByTestId('create-loop-form');
test('L3.15 - Filter loops by status', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
const hasDialog = await dialog.isVisible().catch(() => false);
const hasForm = await form.isVisible().catch(() => false);
// Mock API for filtered loops
await page.route('**/api/loops?status=**', (route) => {
const requestUrl = route.request().url();
const url = new URL(requestUrl);
const status = url.searchParams.get('status');
if (hasDialog || hasForm) {
// Fill in loop details
const promptInput = page.getByRole('textbox', { name: /prompt|description/i }).or(
page.getByLabel(/prompt|description/i)
);
const hasPromptInput = await promptInput.isVisible().catch(() => false);
if (hasPromptInput) {
await promptInput.fill('E2E Test Loop prompt');
// Select tool if available
const toolSelect = page.getByRole('combobox', { name: /tool/i });
const hasToolSelect = await toolSelect.isVisible().catch(() => false);
if (hasToolSelect) {
const toolOptions = await toolSelect.locator('option').count();
if (toolOptions > 0) {
await toolSelect.selectOption({ index: 0 });
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
loops: [
{
id: `loop-${status}`,
name: `Filtered ${status} Loop`,
status: status
}
}
]
})
});
});
const submitButton = page.getByRole('button', { name: /create|save|submit|start/i });
await submitButton.click();
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Verify loop was created
// Look for status filter
const statusFilter = page.getByRole('combobox', { name: /status|filter/i }).or(
page.getByTestId('status-filter')
);
const successMessage = page.getByText(/created|started|success/i).or(
page.getByTestId('success-message')
);
const hasFilter = await statusFilter.isVisible().catch(() => false);
const hasSuccess = await successMessage.isVisible().catch(() => false);
expect(hasSuccess).toBe(true);
}
}
if (hasFilter) {
await statusFilter.selectOption('running');
// Wait for filtered results
await page.waitForTimeout(500);
// Verify filter applied
const currentUrl = page.url();
const hasFilterParam = currentUrl.includes('status=');
// URL may or may not have filter parameter
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.3 - should pause running loop', async ({ page }) => {
test('L3.16 - Terminate running loop', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Navigate to loops page
// Mock API for termination
await page.route('**/api/loops/*/terminate', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true, status: 'terminated' })
});
});
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Look for running loop
const runningLoops = page.getByTestId(/loop-item|loop-card/).filter({ hasText: /running|active/i }).or(
page.locator('.loop-item').filter({ hasText: /running|active/i })
);
const runningLoops = page.locator('.loop-item').filter({ hasText: /running|active/i });
const count = await runningLoops.count();
if (count > 0) {
const firstLoop = runningLoops.first();
// Look for pause button
const pauseButton = firstLoop.getByRole('button', { name: /pause/i }).or(
firstLoop.getByTestId('pause-button')
// Look for terminate button
const terminateButton = firstLoop.getByRole('button', { name: /terminate|stop|end/i }).or(
firstLoop.getByTestId('loop-terminate-button')
);
const hasPauseButton = await pauseButton.isVisible().catch(() => false);
const hasTerminateButton = await terminateButton.isVisible().catch(() => false);
if (hasPauseButton) {
await pauseButton.click();
if (hasTerminateButton) {
await terminateButton.click();
// Verify loop is paused
const pausedIndicator = firstLoop.getByText(/paused/i);
const hasPaused = await pausedIndicator.isVisible().catch(() => false);
expect(hasPaused).toBe(true);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.4 - should resume paused loop', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Navigate to loops page
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Look for paused loop
const pausedLoops = page.getByTestId(/loop-item|loop-card/).filter({ hasText: /paused/i }).or(
page.locator('.loop-item').filter({ hasText: /paused/i })
);
const count = await pausedLoops.count();
if (count > 0) {
const firstLoop = pausedLoops.first();
// Look for resume button
const resumeButton = firstLoop.getByRole('button', { name: /resume|continue/i }).or(
firstLoop.getByTestId('resume-button')
);
const hasResumeButton = await resumeButton.isVisible().catch(() => false);
if (hasResumeButton) {
await resumeButton.click();
// Verify loop is resumed
const runningIndicator = firstLoop.getByText(/running|active/i);
const hasRunning = await runningIndicator.isVisible().catch(() => false);
expect(hasRunning).toBe(true);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.5 - should stop loop', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Navigate to loops page
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Look for active/paused loop
const activeLoops = page.locator('.loop-item').filter({ hasText: /running|paused|active/i });
const count = await activeLoops.count();
if (count > 0) {
const firstLoop = activeLoops.first();
// Look for stop button
const stopButton = firstLoop.getByRole('button', { name: /stop/i }).or(
firstLoop.getByTestId('stop-button')
);
const hasStopButton = await stopButton.isVisible().catch(() => false);
if (hasStopButton) {
await stopButton.click();
// Confirm stop if dialog appears
const confirmDialog = page.getByRole('dialog').filter({ hasText: /stop|confirm/i });
// Confirm if dialog appears
const confirmDialog = page.getByRole('dialog').filter({ hasText: /terminate|confirm/i });
const hasDialog = await confirmDialog.isVisible().catch(() => false);
if (hasDialog) {
const confirmButton = page.getByRole('button', { name: /stop|confirm|yes/i });
const confirmButton = confirmDialog.getByRole('button', { name: /terminate|confirm|yes/i });
await confirmButton.click();
}
// Verify loop is stopped
const stoppedIndicator = firstLoop.getByText(/stopped|completed/i);
const hasStopped = await stoppedIndicator.isVisible().catch(() => false);
expect(hasStopped).toBe(true);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.6 - should delete loop', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Navigate to loops page
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Look for existing loop
const loopItems = page.getByTestId(/loop-item|loop-card/).or(
page.locator('.loop-item')
);
const itemCount = await loopItems.count();
if (itemCount > 0) {
const firstLoop = loopItems.first();
// Look for delete button
const deleteButton = firstLoop.getByRole('button', { name: /delete|remove/i }).or(
firstLoop.getByTestId('delete-button')
);
const hasDeleteButton = await deleteButton.isVisible().catch(() => false);
if (hasDeleteButton) {
await deleteButton.click();
// Confirm delete if dialog appears
const confirmDialog = page.getByRole('dialog').filter({ hasText: /delete|confirm/i });
const hasDialog = await confirmDialog.isVisible().catch(() => false);
if (hasDialog) {
const confirmButton = page.getByRole('button', { name: /delete|confirm|yes/i });
await confirmButton.click();
}
// Verify success message
const successMessage = page.getByText(/deleted|success/i);
// Verify success
const successMessage = page.getByText(/terminated|stopped|success/i);
const hasSuccess = await successMessage.isVisible().catch(() => false);
expect(hasSuccess).toBe(true);
}
@@ -277,10 +201,23 @@ test.describe('[Loops] - Loop Management Tests', () => {
monitoring.stop();
});
test('L3.7 - should display loop status correctly', async ({ page }) => {
test('L3.17 - View loop execution logs', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Navigate to loops page
// Mock API for logs
await page.route('**/api/loops/*/logs', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
logs: [
{ timestamp: Date.now(), level: 'info', message: 'Loop started' },
{ timestamp: Date.now(), level: 'info', message: 'Processing step 1' }
]
})
});
});
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Look for loop items
@@ -290,51 +227,26 @@ test.describe('[Loops] - Loop Management Tests', () => {
const itemCount = await loopItems.count();
if (itemCount > 0) {
// Check each loop has a status indicator
for (let i = 0; i < Math.min(itemCount, 3); i++) {
const loop = loopItems.nth(i);
// Look for status indicator
const statusIndicator = loop.getByTestId('loop-status').or(
loop.locator('*').filter({ hasText: /running|paused|stopped|completed|created/i })
);
const hasStatus = await statusIndicator.isVisible().catch(() => false);
expect(hasStatus).toBe(true);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.8 - should display loop progress', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Navigate to loops page
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Look for loop items with progress
const loopItems = page.getByTestId(/loop-item|loop-card/).or(
page.locator('.loop-item')
);
const itemCount = await loopItems.count();
if (itemCount > 0) {
const firstLoop = loopItems.first();
// Look for progress bar or step indicator
const progressBar = firstLoop.getByTestId('loop-progress').or(
firstLoop.locator('*').filter({ hasText: /\d+\/\d+|step/i })
// Look for logs button/panel
const logsButton = firstLoop.getByRole('button', { name: /logs|view logs/i }).or(
firstLoop.getByTestId('view-logs-button')
);
const hasProgress = await progressBar.isVisible().catch(() => false);
const hasLogsButton = await logsButton.isVisible().catch(() => false);
// Progress is optional but if present should be visible
if (hasProgress) {
expect(progressBar).toBeVisible();
if (hasLogsButton) {
await logsButton.click();
// Look for logs panel
const logsPanel = page.getByTestId('loop-logs-panel').or(
page.getByRole('dialog').filter({ hasText: /logs/i })
);
const hasLogsPanel = await logsPanel.isVisible().catch(() => false);
expect(hasLogsPanel).toBe(true);
}
}
@@ -342,80 +254,96 @@ test.describe('[Loops] - Loop Management Tests', () => {
monitoring.stop();
});
test('L3.9 - should handle loop creation errors gracefully', async ({ page }) => {
test('L3.18 - i18n - Loop status in EN/ZH', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API failure
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Get language switcher
const languageSwitcher = page.getByRole('combobox', { name: /select language|language/i }).first();
const hasLanguageSwitcher = await languageSwitcher.isVisible().catch(() => false);
if (hasLanguageSwitcher) {
// Switch to Chinese
await switchLanguageAndVerify(page, 'zh', languageSwitcher);
// Verify loop status in Chinese
const statusText = page.getByText(/运行|停止|完成/i);
const hasChineseStatus = await statusText.isVisible().catch(() => false);
// Chinese status may or may not be visible depending on active loops
if (hasChineseStatus) {
expect(hasChineseStatus).toBe(true);
}
// Switch back to English
await switchLanguageAndVerify(page, 'en', languageSwitcher);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.19 - Error - Failed loop displays error', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for failed loop
await page.route('**/api/loops', (route) => {
route.fulfill({
status: 500,
status: 200,
contentType: 'application/json',
body: JSON.stringify({ error: 'Internal Server Error' }),
body: JSON.stringify({
loops: [
{
id: 'loop-failed',
name: 'Failed Loop',
status: 'failed',
error: 'Connection timeout'
}
]
})
});
});
// Navigate to loops page
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Look for create button
const createButton = page.getByRole('button', { name: /create|new|add/i });
const hasCreateButton = await createButton.isVisible().catch(() => false);
// Look for error indicator
const errorIndicator = page.getByText(/failed|error|timeout/i).or(
page.getByTestId('loop-error')
);
if (hasCreateButton) {
await createButton.click();
const hasError = await errorIndicator.isVisible().catch(() => false);
// Try to create loop
const promptInput = page.getByRole('textbox', { name: /prompt|description/i });
const hasPromptInput = await promptInput.isVisible().catch(() => false);
if (hasPromptInput) {
await promptInput.fill('Test prompt');
const submitButton = page.getByRole('button', { name: /create|save|submit/i });
await submitButton.click();
// Look for error message
const errorMessage = page.getByText(/error|failed|unable/i);
const hasError = await errorMessage.isVisible().catch(() => false);
expect(hasError).toBe(true);
}
if (hasError) {
expect(errorIndicator).toBeVisible();
}
// Restore routing
await page.unroute('**/api/loops');
monitoring.assertClean({ ignoreAPIPatterns: ['/api/loops'], allowWarnings: true });
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.10 - should support batch operations on loops', async ({ page }) => {
test('L3.20 - Edge - No loops available', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Navigate to loops page
// Mock API for empty loops
await page.route('**/api/loops', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ loops: [] })
});
});
await page.goto('/loops', { waitUntil: 'networkidle' as const });
// Look for batch operation controls
const selectAllCheckbox = page.getByRole('checkbox', { name: /select all/i }).or(
page.getByTestId('select-all-loops')
// Look for empty state
const emptyState = page.getByTestId('empty-state').or(
page.getByText(/no loops|empty|get started/i)
);
const hasSelectAll = await selectAllCheckbox.isVisible().catch(() => false);
if (hasSelectAll) {
await selectAllCheckbox.check();
// Look for batch action buttons
const batchStopButton = page.getByRole('button', { name: /stop selected|stop all/i }).or(
page.getByTestId('batch-stop-button')
);
const hasBatchStop = await batchStopButton.isVisible().catch(() => false);
if (hasBatchStop) {
expect(batchStopButton).toBeVisible();
}
}
const hasEmptyState = await emptyState.isVisible().catch(() => false);
expect(hasEmptyState).toBe(true);
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();

View File

@@ -0,0 +1,563 @@
// ========================================
// E2E Tests: Orchestrator - Workflow Canvas
// ========================================
// End-to-end tests for workflow orchestration with @xyflow/react canvas
import { test, expect } from '@playwright/test';
import { setupEnhancedMonitoring, switchLanguageAndVerify } from './helpers/i18n-helpers';
test.describe('[Orchestrator] - Workflow Canvas Tests', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/orchestrator', { waitUntil: 'networkidle' as const });
});
test('L3.01 - Canvas loads and displays nodes', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API response for workflows
await page.route('**/api/workflows', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
workflows: [
{
id: 'wf-1',
name: 'Test Workflow',
nodes: [
{ id: 'node-1', type: 'start', position: { x: 100, y: 100 } },
{ id: 'node-2', type: 'action', position: { x: 300, y: 100 } }
],
edges: [
{ id: 'edge-1', source: 'node-1', target: 'node-2' }
]
}
],
total: 1,
page: 1,
limit: 10
})
});
});
// Reload page to trigger API call
await page.reload({ waitUntil: 'networkidle' as const });
// Look for workflow canvas
const canvas = page.getByTestId('workflow-canvas').or(
page.locator('.react-flow')
);
const isCanvasVisible = await canvas.isVisible().catch(() => false);
if (isCanvasVisible) {
// Verify nodes are displayed
const nodes = page.locator('.react-flow-node').or(
page.getByTestId(/node-/)
);
const nodeCount = await nodes.count();
expect(nodeCount).toBeGreaterThan(0);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.02 - Create new node via drag-drop', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for node creation
await page.route('**/api/workflows', (route) => {
if (route.request().method() === 'POST') {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true, id: 'new-node-1' })
});
} else {
route.continue();
}
});
// Look for node library or create button
const nodeLibrary = page.getByTestId('node-library').or(
page.getByTestId('node-create-button')
);
const hasLibrary = await nodeLibrary.isVisible().catch(() => false);
if (hasLibrary) {
// Find a draggable node type
const nodeType = nodeLibrary.locator('[data-node-type]').first();
const hasNodeType = await nodeType.isVisible().catch(() => false);
if (hasNodeType) {
const canvas = page.getByTestId('workflow-canvas').or(
page.locator('.react-flow')
);
const canvasBox = await canvas.boundingBox();
if (canvasBox) {
// Simulate drag-drop
await nodeType.dragTo(canvas, {
targetPosition: { x: canvasBox.x + 200, y: canvasBox.y + 200 }
});
// Wait for node to appear
await page.waitForTimeout(500);
// Verify new node exists
const newNode = page.locator('.react-flow-node').or(
page.getByTestId(/node-/)
);
const nodeCount = await newNode.count();
expect(nodeCount).toBeGreaterThan(0);
}
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.03 - Connect nodes with edges', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for edge creation
await page.route('**/api/workflows/*', (route) => {
if (route.request().method() === 'PUT') {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true })
});
} else {
route.continue();
}
});
// Look for existing nodes
const nodes = page.locator('.react-flow-node').or(
page.getByTestId(/node-/)
);
const nodeCount = await nodes.count();
if (nodeCount >= 2) {
const sourceNode = nodes.first();
const targetNode = nodes.nth(1);
// Get node positions
const sourceBox = await sourceNode.boundingBox();
const targetBox = await targetNode.boundingBox();
if (sourceBox && targetBox) {
// Click and drag from source to target to create edge
await page.mouse.move(sourceBox.x + sourceBox.width, sourceBox.y + sourceBox.height / 2);
await page.mouse.down();
await page.mouse.move(targetBox.x, targetBox.y + targetBox.height / 2);
await page.mouse.up();
// Wait for edge to be created
await page.waitForTimeout(300);
// Verify edge exists
const edges = page.locator('.react-flow-edge').or(
page.getByTestId(/edge-/)
);
const edgeCount = await edges.count();
expect(edgeCount).toBeGreaterThan(0);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.04 - Delete node and verify edge removal', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for node deletion
await page.route('**/api/workflows/*', (route) => {
if (route.request().method() === 'DELETE') {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true })
});
} else {
route.continue();
}
});
// Look for nodes
const nodes = page.locator('.react-flow-node').or(
page.getByTestId(/node-/)
);
const nodeCount = await nodes.count();
if (nodeCount > 0) {
const firstNode = nodes.first();
// Get initial edge count
const edgesBefore = await page.locator('.react-flow-edge').count();
// Select node and look for delete button
await firstNode.click();
const deleteButton = page.getByRole('button', { name: /delete|remove/i }).or(
page.getByTestId('node-delete-button')
);
const hasDeleteButton = await deleteButton.isVisible().catch(() => false);
if (hasDeleteButton) {
await deleteButton.click();
// Wait for node to be removed
await page.waitForTimeout(300);
// Verify node count decreased
const nodesAfter = await page.locator('.react-flow-node').count();
expect(nodesAfter).toBeLessThan(nodeCount);
// Verify edges connected to deleted node are removed
const edgesAfter = await page.locator('.react-flow-edge').count();
expect(edgesAfter).toBeLessThanOrEqual(edgesBefore);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.05 - Zoom in/out functionality', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Look for zoom controls
const zoomControls = page.getByTestId('zoom-controls').or(
page.locator('.react-flow-controls')
);
const hasZoomControls = await zoomControls.isVisible().catch(() => false);
if (hasZoomControls) {
const zoomInButton = zoomControls.getByRole('button').first();
const zoomOutButton = zoomControls.getByRole('button').nth(1);
// Get initial zoom level
const initialZoom = await page.evaluate(() => {
const container = document.querySelector('.react-flow');
return container ? getComputedStyle(container).transform : 'none';
});
// Click zoom in
await zoomInButton.click();
await page.waitForTimeout(200);
// Click zoom out
await zoomOutButton.click();
await page.waitForTimeout(200);
// Verify controls are still functional
const isStillVisible = await zoomControls.isVisible();
expect(isStillVisible).toBe(true);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.06 - Pan canvas functionality', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Look for canvas
const canvas = page.getByTestId('workflow-canvas').or(
page.locator('.react-flow')
);
const isCanvasVisible = await canvas.isVisible().catch(() => false);
if (isCanvasVisible) {
const canvasBox = await canvas.boundingBox();
if (canvasBox) {
// Simulate panning by clicking and dragging on canvas
await page.mouse.move(canvasBox.x + 100, canvasBox.y + 100);
await page.mouse.down();
await page.mouse.move(canvasBox.x + 200, canvasBox.y + 150);
await page.mouse.up();
// Wait for pan to complete
await page.waitForTimeout(300);
// Verify canvas is still visible after pan
const isStillVisible = await canvas.isVisible();
expect(isStillVisible).toBe(true);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.07 - Save workflow state', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for saving workflow
await page.route('**/api/workflows/*', (route) => {
if (route.request().method() === 'PUT') {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ success: true, saved: true })
});
} else {
route.continue();
}
});
// Look for save button
const saveButton = page.getByRole('button', { name: /save/i }).or(
page.getByTestId('workflow-save-button')
);
const hasSaveButton = await saveButton.isVisible().catch(() => false);
if (hasSaveButton) {
await saveButton.click();
// Look for success indicator
const successMessage = page.getByText(/saved|success/i).or(
page.getByTestId('save-success')
);
const hasSuccess = await successMessage.isVisible().catch(() => false);
expect(hasSuccess).toBe(true);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.08 - Load existing workflow', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for loading workflows
await page.route('**/api/workflows', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
workflows: [
{
id: 'wf-existing',
name: 'Existing Workflow',
nodes: [
{ id: 'node-1', type: 'start', position: { x: 100, y: 100 } }
],
edges: []
}
],
total: 1,
page: 1,
limit: 10
})
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for workflow list selector
const workflowSelector = page.getByRole('combobox', { name: /workflow|select/i }).or(
page.getByTestId('workflow-selector')
);
const hasSelector = await workflowSelector.isVisible().catch(() => false);
if (hasSelector) {
const options = await workflowSelector.locator('option').count();
if (options > 0) {
await workflowSelector.selectOption({ index: 0 });
await page.waitForTimeout(500);
// Verify canvas has loaded content
const canvas = page.getByTestId('workflow-canvas').or(
page.locator('.react-flow')
);
const isCanvasVisible = await canvas.isVisible();
expect(isCanvasVisible).toBe(true);
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.09 - Export workflow configuration', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for export
await page.route('**/api/workflows/*/export', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
id: 'wf-1',
name: 'Exported Workflow',
nodes: [],
edges: []
})
});
});
// Look for export button
const exportButton = page.getByRole('button', { name: /export/i }).or(
page.getByTestId('workflow-export-button')
);
const hasExportButton = await exportButton.isVisible().catch(() => false);
if (hasExportButton) {
await exportButton.click();
// Look for export dialog or download
const exportDialog = page.getByRole('dialog').filter({ hasText: /export/i });
const hasDialog = await exportDialog.isVisible().catch(() => false);
if (hasDialog) {
const confirmButton = exportDialog.getByRole('button', { name: /export|download|save/i });
await confirmButton.click();
}
// Verify some indication of export
await page.waitForTimeout(500);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.10 - i18n - Node labels in EN/ZH', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Get language switcher
const languageSwitcher = page.getByRole('combobox', { name: /select language|language/i }).first();
const hasLanguageSwitcher = await languageSwitcher.isVisible().catch(() => false);
if (hasLanguageSwitcher) {
// Switch to Chinese
await switchLanguageAndVerify(page, 'zh', languageSwitcher);
// Verify canvas elements exist in Chinese context
const canvas = page.getByTestId('workflow-canvas').or(
page.locator('.react-flow')
);
const isCanvasVisible = await canvas.isVisible().catch(() => false);
expect(isCanvasVisible).toBe(true);
// Switch back to English
await switchLanguageAndVerify(page, 'en', languageSwitcher);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.11 - Error - Node with invalid configuration', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API error response
await page.route('**/api/workflows', (route) => {
if (route.request().method() === 'POST') {
route.fulfill({
status: 400,
contentType: 'application/json',
body: JSON.stringify({ error: 'Invalid node configuration' })
});
} else {
route.continue();
}
});
// Look for create button
const createButton = page.getByRole('button', { name: /create|add node/i }).or(
page.getByTestId('node-create-button')
);
const hasCreateButton = await createButton.isVisible().catch(() => false);
if (hasCreateButton) {
await createButton.click();
// Try to create node without required fields (this should trigger error)
const submitButton = page.getByRole('button', { name: /create|save|submit/i });
const hasSubmit = await submitButton.isVisible().catch(() => false);
if (hasSubmit) {
await submitButton.click();
// Look for error message
const errorMessage = page.getByText(/invalid|error|required/i).or(
page.getByTestId('error-message')
);
const hasError = await errorMessage.isVisible().catch(() => false);
// Error message may or may not appear depending on validation
}
}
monitoring.assertClean({ ignoreAPIPatterns: ['/api/workflows'], allowWarnings: true });
monitoring.stop();
});
test('L3.12 - Edge - Maximum nodes limit', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API to enforce limit
await page.route('**/api/workflows', (route) => {
if (route.request().method() === 'POST') {
route.fulfill({
status: 409,
contentType: 'application/json',
body: JSON.stringify({ error: 'Maximum node limit reached' })
});
} else {
route.continue();
}
});
// Try to create multiple nodes rapidly
const createButton = page.getByRole('button', { name: /create|add/i }).or(
page.getByTestId('node-create-button')
);
const hasCreateButton = await createButton.isVisible().catch(() => false);
if (hasCreateButton) {
// Attempt multiple creates
for (let i = 0; i < 5; i++) {
await createButton.click();
await page.waitForTimeout(100);
}
// Look for limit error message
const limitMessage = page.getByText(/limit|maximum|too many/i).or(
page.getByTestId('limit-message')
);
const hasLimitMessage = await limitMessage.isVisible().catch(() => false);
// Limit message may or may not appear
}
monitoring.assertClean({ ignoreAPIPatterns: ['/api/workflows'], allowWarnings: true });
monitoring.stop();
});
});

View File

@@ -0,0 +1,192 @@
// ========================================
// E2E Tests: Project - Development Timeline and Statistics
// ========================================
// End-to-end tests for project overview with statistics and timeline
import { test, expect } from '@playwright/test';
import { setupEnhancedMonitoring, switchLanguageAndVerify } from './helpers/i18n-helpers';
test.describe('[Project] - Development Statistics Tests', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/project', { waitUntil: 'networkidle' as const });
});
test('L3.39 - Page loads and displays project statistics', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for project stats
await page.route('**/api/project', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
stats: {
totalCommits: 150,
totalFiles: 450,
totalLines: 25000,
languages: ['TypeScript', 'JavaScript', 'CSS'],
contributors: 5
}
})
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for project stats
const projectStats = page.getByTestId('project-stats').or(
page.locator('.project-stats')
);
const isStatsVisible = await projectStats.isVisible().catch(() => false);
if (isStatsVisible) {
// Verify stat items are displayed
const statItems = page.getByTestId(/stat-|stat-card/).or(
page.locator('.stat-item')
);
const statCount = await statItems.count();
expect(statCount).toBeGreaterThan(0);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.40 - View development timeline', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Look for timeline section
const timeline = page.getByTestId('timeline-chart').or(
page.getByText(/timeline|activity|history/i)
);
const hasTimeline = await timeline.isVisible().catch(() => false);
if (hasTimeline) {
// Verify timeline has content
const timelineContent = timeline.locator('*').filter({ hasText: /\d+|commit|activity/i });
const hasContent = await timelineContent.isVisible().catch(() => false);
if (hasContent) {
expect(timelineContent).toBeVisible();
}
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.41 - View contribution graph', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Look for contribution graph
const contributionGraph = page.getByTestId('contribution-graph').or(
page.locator('.contribution-graph')
);
const hasGraph = await contributionGraph.isVisible().catch(() => false);
if (hasGraph) {
// Verify graph has visualization elements
const graphElements = contributionGraph.locator('*').filter({ hasText: /\d+/ });
const elementCount = await graphElements.count();
expect(elementCount).toBeGreaterThan(0);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.42 - i18n - Project stats in EN/ZH', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Get language switcher
const languageSwitcher = page.getByRole('combobox', { name: /select language|language/i }).first();
const hasLanguageSwitcher = await languageSwitcher.isVisible().catch(() => false);
if (hasLanguageSwitcher) {
// Switch to Chinese
await switchLanguageAndVerify(page, 'zh', languageSwitcher);
// Verify project stats are in Chinese
const pageContent = await page.content();
const hasChineseText = /[\u4e00-\u9fa5]/.test(pageContent);
expect(hasChineseText).toBe(true);
// Switch back to English
await switchLanguageAndVerify(page, 'en', languageSwitcher);
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
test('L3.43 - Error - Failed to load project data', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API error
await page.route('**/api/project', (route) => {
route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ error: 'Failed to load project data' })
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for error indicator
const errorIndicator = page.getByText(/error|failed|unable to load/i).or(
page.getByTestId('error-state')
);
const hasError = await errorIndicator.isVisible().catch(() => false);
if (hasError) {
expect(errorIndicator).toBeVisible();
}
monitoring.assertClean({ ignoreAPIPatterns: ['/api/project'], allowWarnings: true });
monitoring.stop();
});
test('L3.44 - Edge - No commits state', async ({ page }) => {
const monitoring = setupEnhancedMonitoring(page);
// Mock API for no commits
await page.route('**/api/project', (route) => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({
stats: {
totalCommits: 0,
totalFiles: 0,
totalLines: 0,
languages: [],
contributors: 0
}
})
});
});
// Reload to trigger API
await page.reload({ waitUntil: 'networkidle' as const });
// Look for empty state or zero stats
const emptyState = page.getByText(/no commits|no activity|empty/i);
const hasEmptyState = await emptyState.isVisible().catch(() => false);
if (hasEmptyState) {
expect(emptyState).toBeVisible();
}
monitoring.assertClean({ allowWarnings: true });
monitoring.stop();
});
});