From 5b48bcff64e4f7470b66c7541af4799764affd4e Mon Sep 17 00:00:00 2001 From: catlog22 Date: Fri, 6 Feb 2026 23:45:33 +0800 Subject: [PATCH] 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. --- .claude/skills/workflow-execute/SKILL.md | 639 ++++++++++++++++ .codex/skills/ccw-loop-b/README.md | 301 ++++++++ .codex/skills/ccw-loop-b/SKILL.md | 323 ++++++++ .../skills/ccw-loop-b/phases/orchestrator.md | 257 +++++++ .../skills/ccw-loop-b/phases/state-schema.md | 181 +++++ .../skills/ccw-loop-b/specs/action-catalog.md | 383 ++++++++++ .codex/skills/ccw-loop/README.md | 171 +++++ .codex/skills/ccw-loop/SKILL.md | 350 +++++++++ .../phases/actions/action-complete.md | 269 +++++++ .../ccw-loop/phases/actions/action-debug.md | 286 +++++++ .../ccw-loop/phases/actions/action-develop.md | 183 +++++ .../ccw-loop/phases/actions/action-init.md | 164 +++++ .../ccw-loop/phases/actions/action-menu.md | 205 ++++++ .../phases/actions/action-validate.md | 250 +++++++ .codex/skills/ccw-loop/phases/orchestrator.md | 416 +++++++++++ .codex/skills/ccw-loop/phases/state-schema.md | 388 ++++++++++ .../skills/ccw-loop/specs/action-catalog.md | 182 +++++ .codex/skills/parallel-dev-cycle/README.md | 385 ---------- .codex/skills/parallel-dev-cycle/SKILL.md | 358 ++++++--- .../phases/01-session-init.md | 159 ++++ .../phases/02-agent-execution.md | 309 ++++++++ .../phases/03-result-aggregation.md | 230 ++++++ .../phases/04-completion-summary.md | 109 +++ .../parallel-dev-cycle/phases/orchestrator.md | 696 ------------------ .../parallel-dev-cycle/phases/state-schema.md | 436 ----------- .../agents => roles}/code-developer.md | 0 .../agents => roles}/exploration-planner.md | 0 .../agents => roles}/requirements-analyst.md | 0 .../agents => roles}/validation-archivist.md | 0 .../specs/communication-optimization.md | 423 ----------- .../specs/coordination-protocol.md | 406 ---------- .../specs/versioning-strategy.md | 331 --------- .../.docusaurus/codeTranslations.json | 85 +-- .../default/__mdx-loader-dependency.json | 2 +- ...te-docs-commands-cli-cli-init-mdx-056.json | 6 +- ...ocs-commands-cli-codex-review-mdx-f1b.json | 6 +- ...mands-general-ccw-coordinator-mdx-d55.json | 6 +- ...cs-commands-general-ccw-debug-mdx-97c.json | 6 +- ...ite-docs-commands-general-ccw-mdx-f48.json | 8 +- ...ocs-commands-general-ccw-plan-mdx-04d.json | 6 +- ...ocs-commands-general-ccw-test-mdx-cce.json | 6 +- ...nds-general-codex-coordinator-mdx-f92.json | 6 +- ...-commands-general-flow-create-mdx-fab.json | 6 +- ...ds-issue-issue-convert-to-plan-md-5c7.json | 6 +- ...-commands-issue-issue-discover-md-1e3.json | 6 +- ...s-commands-issue-issue-execute-md-fe8.json | 6 +- ...ds-issue-issue-from-brainstorm-md-2ec.json | 6 +- ...-docs-commands-issue-issue-new-md-4ad.json | 6 +- ...docs-commands-issue-issue-plan-md-a6c.json | 6 +- ...ocs-commands-issue-issue-queue-md-1ba.json | 6 +- ...ommands-memory-memory-compact-mdx-7a1.json | 6 +- ...s-memory-memory-docs-full-cli-mdx-4cc.json | 6 +- ...emory-memory-docs-related-cli-mdx-60e.json | 6 +- ...s-commands-memory-memory-load-mdx-157.json | 6 +- ...nds-memory-memory-update-full-mdx-666.json | 6 +- ...-memory-memory-update-related-mdx-611.json | 6 +- .../.docusaurus/docusaurus.config.mjs | 8 +- ccw/docs-site/.docusaurus/globalData.json | 72 +- ccw/docs-site/.docusaurus/i18n.json | 2 +- ccw/docs-site/.docusaurus/registry.js | 84 ++- ccw/docs-site/.docusaurus/routes.js | 175 +++-- .../.docusaurus/routesChunkNames.json | 251 ++++--- ccw/docs-site/.docusaurus/site-metadata.json | 4 +- ccw/frontend/tests/e2e/api-settings.spec.ts | 480 ++++++++++++ .../tests/e2e/dashboard-charts.spec.ts | 14 +- .../tests/e2e/dashboard-redesign.spec.ts | 10 +- ccw/frontend/tests/e2e/explorer.spec.ts | 205 ++++++ ccw/frontend/tests/e2e/graph.spec.ts | 198 +++++ ccw/frontend/tests/e2e/history.spec.ts | 356 +++++++++ ccw/frontend/tests/e2e/loops.spec.ts | 512 ++++++------- ccw/frontend/tests/e2e/orchestrator.spec.ts | 563 ++++++++++++++ ccw/frontend/tests/e2e/project.spec.ts | 192 +++++ 72 files changed, 8645 insertions(+), 3492 deletions(-) create mode 100644 .claude/skills/workflow-execute/SKILL.md create mode 100644 .codex/skills/ccw-loop-b/README.md create mode 100644 .codex/skills/ccw-loop-b/SKILL.md create mode 100644 .codex/skills/ccw-loop-b/phases/orchestrator.md create mode 100644 .codex/skills/ccw-loop-b/phases/state-schema.md create mode 100644 .codex/skills/ccw-loop-b/specs/action-catalog.md create mode 100644 .codex/skills/ccw-loop/README.md create mode 100644 .codex/skills/ccw-loop/SKILL.md create mode 100644 .codex/skills/ccw-loop/phases/actions/action-complete.md create mode 100644 .codex/skills/ccw-loop/phases/actions/action-debug.md create mode 100644 .codex/skills/ccw-loop/phases/actions/action-develop.md create mode 100644 .codex/skills/ccw-loop/phases/actions/action-init.md create mode 100644 .codex/skills/ccw-loop/phases/actions/action-menu.md create mode 100644 .codex/skills/ccw-loop/phases/actions/action-validate.md create mode 100644 .codex/skills/ccw-loop/phases/orchestrator.md create mode 100644 .codex/skills/ccw-loop/phases/state-schema.md create mode 100644 .codex/skills/ccw-loop/specs/action-catalog.md delete mode 100644 .codex/skills/parallel-dev-cycle/README.md create mode 100644 .codex/skills/parallel-dev-cycle/phases/01-session-init.md create mode 100644 .codex/skills/parallel-dev-cycle/phases/02-agent-execution.md create mode 100644 .codex/skills/parallel-dev-cycle/phases/03-result-aggregation.md create mode 100644 .codex/skills/parallel-dev-cycle/phases/04-completion-summary.md delete mode 100644 .codex/skills/parallel-dev-cycle/phases/orchestrator.md delete mode 100644 .codex/skills/parallel-dev-cycle/phases/state-schema.md rename .codex/skills/parallel-dev-cycle/{phases/agents => roles}/code-developer.md (100%) rename .codex/skills/parallel-dev-cycle/{phases/agents => roles}/exploration-planner.md (100%) rename .codex/skills/parallel-dev-cycle/{phases/agents => roles}/requirements-analyst.md (100%) rename .codex/skills/parallel-dev-cycle/{phases/agents => roles}/validation-archivist.md (100%) delete mode 100644 .codex/skills/parallel-dev-cycle/specs/communication-optimization.md delete mode 100644 .codex/skills/parallel-dev-cycle/specs/coordination-protocol.md delete mode 100644 .codex/skills/parallel-dev-cycle/specs/versioning-strategy.md create mode 100644 ccw/frontend/tests/e2e/api-settings.spec.ts create mode 100644 ccw/frontend/tests/e2e/explorer.spec.ts create mode 100644 ccw/frontend/tests/e2e/graph.spec.ts create mode 100644 ccw/frontend/tests/e2e/history.spec.ts create mode 100644 ccw/frontend/tests/e2e/orchestrator.spec.ts create mode 100644 ccw/frontend/tests/e2e/project.spec.ts diff --git a/.claude/skills/workflow-execute/SKILL.md b/.claude/skills/workflow-execute/SKILL.md new file mode 100644 index 00000000..d082ef52 --- /dev/null +++ b/.claude/skills/workflow-execute/SKILL.md @@ -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="") +Skill(skill="workflow-execute", args="[FLAGS]") + +# Flags +-y, --yes Skip all confirmations (auto mode) +--resume-session="" 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 && git commit -m "" + └─ 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 && git commit -m ""` +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 && 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') +``` diff --git a/.codex/skills/ccw-loop-b/README.md b/.codex/skills/ccw-loop-b/README.md new file mode 100644 index 00000000..aeb708a4 --- /dev/null +++ b/.codex/skills/ccw-loop-b/README.md @@ -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 diff --git a/.codex/skills/ccw-loop-b/SKILL.md b/.codex/skills/ccw-loop-b/SKILL.md new file mode 100644 index 00000000..05554fb2 --- /dev/null +++ b/.codex/skills/ccw-loop-b/SKILL.md @@ -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="" [--loop-id=] [--mode=] +--- + +# 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) + +协调器展示菜单,用户选择 action,spawn 对应 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: +- files_changed: [list] +- next_suggestion: +- loop_back_to: + +DETAILED_OUTPUT: + +\`\`\` + +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 释放资源 diff --git a/.codex/skills/ccw-loop-b/phases/orchestrator.md b/.codex/skills/ccw-loop-b/phases/orchestrator.md new file mode 100644 index 00000000..3cc29752 --- /dev/null +++ b/.codex/skills/ccw-loop-b/phases/orchestrator.md @@ -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: +- files_changed: [] +- next_suggestion: +- loop_back_to: + +DETAILED_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 请求收敛,再超时则跳过 diff --git a/.codex/skills/ccw-loop-b/phases/state-schema.md b/.codex/skills/ccw-loop-b/phases/state-schema.md new file mode 100644 index 00000000..17dbf966 --- /dev/null +++ b/.codex/skills/ccw-loop-b/phases/state-schema.md @@ -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 +``` diff --git a/.codex/skills/ccw-loop-b/specs/action-catalog.md b/.codex/skills/ccw-loop-b/specs/action-catalog.md new file mode 100644 index 00000000..1d628d91 --- /dev/null +++ b/.codex/skills/ccw-loop-b/specs/action-catalog.md @@ -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 diff --git a/.codex/skills/ccw-loop/README.md b/.codex/skills/ccw-loop/README.md new file mode 100644 index 00000000..b3b18863 --- /dev/null +++ b/.codex/skills/ccw-loop/README.md @@ -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 diff --git a/.codex/skills/ccw-loop/SKILL.md b/.codex/skills/ccw-loop/SKILL.md new file mode 100644 index 00000000..de81d505 --- /dev/null +++ b/.codex/skills/ccw-loop/SKILL.md @@ -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="" [--loop-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 diff --git a/.codex/skills/ccw-loop/phases/actions/action-complete.md b/.codex/skills/ccw-loop/phases/actions/action-complete.md new file mode 100644 index 00000000..bd15d6e8 --- /dev/null +++ b/.codex/skills/ccw-loop/phases/actions/action-complete.md @@ -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) diff --git a/.codex/skills/ccw-loop/phases/actions/action-debug.md b/.codex/skills/ccw-loop/phases/actions/action-debug.md new file mode 100644 index 00000000..3f5fbeb0 --- /dev/null +++ b/.codex/skills/ccw-loop/phases/actions/action-debug.md @@ -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) diff --git a/.codex/skills/ccw-loop/phases/actions/action-develop.md b/.codex/skills/ccw-loop/phases/actions/action-develop.md new file mode 100644 index 00000000..0b23a3ba --- /dev/null +++ b/.codex/skills/ccw-loop/phases/actions/action-develop.md @@ -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) diff --git a/.codex/skills/ccw-loop/phases/actions/action-init.md b/.codex/skills/ccw-loop/phases/actions/action-init.md new file mode 100644 index 00000000..c9dec20d --- /dev/null +++ b/.codex/skills/ccw-loop/phases/actions/action-init.md @@ -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 diff --git a/.codex/skills/ccw-loop/phases/actions/action-menu.md b/.codex/skills/ccw-loop/phases/actions/action-menu.md new file mode 100644 index 00000000..d67e0cbe --- /dev/null +++ b/.codex/skills/ccw-loop/phases/actions/action-menu.md @@ -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. diff --git a/.codex/skills/ccw-loop/phases/actions/action-validate.md b/.codex/skills/ccw-loop/phases/actions/action-validate.md new file mode 100644 index 00000000..6ebf8109 --- /dev/null +++ b/.codex/skills/ccw-loop/phases/actions/action-validate.md @@ -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` diff --git a/.codex/skills/ccw-loop/phases/orchestrator.md b/.codex/skills/ccw-loop/phases/orchestrator.md new file mode 100644 index 00000000..e5a8d2cf --- /dev/null +++ b/.codex/skills/ccw-loop/phases/orchestrator.md @@ -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 diff --git a/.codex/skills/ccw-loop/phases/state-schema.md b/.codex/skills/ccw-loop/phases/state-schema.md new file mode 100644 index 00000000..6fc12d9f --- /dev/null +++ b/.codex/skills/ccw-loop/phases/state-schema.md @@ -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 | 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 diff --git a/.codex/skills/ccw-loop/specs/action-catalog.md b/.codex/skills/ccw-loop/specs/action-catalog.md new file mode 100644 index 00000000..8d325dbe --- /dev/null +++ b/.codex/skills/ccw-loop/specs/action-catalog.md @@ -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 diff --git a/.codex/skills/parallel-dev-cycle/README.md b/.codex/skills/parallel-dev-cycle/README.md deleted file mode 100644 index 4d9d0730..00000000 --- a/.codex/skills/parallel-dev-cycle/README.md +++ /dev/null @@ -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 diff --git a/.codex/skills/parallel-dev-cycle/SKILL.md b/.codex/skills/parallel-dev-cycle/SKILL.md index f0cfc776..26f7dda9 100644 --- a/.codex/skills/parallel-dev-cycle/SKILL.md +++ b/.codex/skills/parallel-dev-cycle/SKILL.md @@ -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="" | --cycle-id= [--extend=""] [--auto] [--parallel=] +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 | diff --git a/.codex/skills/parallel-dev-cycle/phases/01-session-init.md b/.codex/skills/parallel-dev-cycle/phases/01-session-init.md new file mode 100644 index 00000000..05271ac8 --- /dev/null +++ b/.codex/skills/parallel-dev-cycle/phases/01-session-init.md @@ -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). diff --git a/.codex/skills/parallel-dev-cycle/phases/02-agent-execution.md b/.codex/skills/parallel-dev-cycle/phases/02-agent-execution.md new file mode 100644 index 00000000..d654529f --- /dev/null +++ b/.codex/skills/parallel-dev-cycle/phases/02-agent-execution.md @@ -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). diff --git a/.codex/skills/parallel-dev-cycle/phases/03-result-aggregation.md b/.codex/skills/parallel-dev-cycle/phases/03-result-aggregation.md new file mode 100644 index 00000000..0913b5f7 --- /dev/null +++ b/.codex/skills/parallel-dev-cycle/phases/03-result-aggregation.md @@ -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). diff --git a/.codex/skills/parallel-dev-cycle/phases/04-completion-summary.md b/.codex/skills/parallel-dev-cycle/phases/04-completion-summary.md new file mode 100644 index 00000000..db610bce --- /dev/null +++ b/.codex/skills/parallel-dev-cycle/phases/04-completion-summary.md @@ -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" +``` diff --git a/.codex/skills/parallel-dev-cycle/phases/orchestrator.md b/.codex/skills/parallel-dev-cycle/phases/orchestrator.md deleted file mode 100644 index c28aa491..00000000 --- a/.codex/skills/parallel-dev-cycle/phases/orchestrator.md +++ /dev/null @@ -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 diff --git a/.codex/skills/parallel-dev-cycle/phases/state-schema.md b/.codex/skills/parallel-dev-cycle/phases/state-schema.md deleted file mode 100644 index 613bfcc1..00000000 --- a/.codex/skills/parallel-dev-cycle/phases/state-schema.md +++ /dev/null @@ -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 // 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 - 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 diff --git a/.codex/skills/parallel-dev-cycle/phases/agents/code-developer.md b/.codex/skills/parallel-dev-cycle/roles/code-developer.md similarity index 100% rename from .codex/skills/parallel-dev-cycle/phases/agents/code-developer.md rename to .codex/skills/parallel-dev-cycle/roles/code-developer.md diff --git a/.codex/skills/parallel-dev-cycle/phases/agents/exploration-planner.md b/.codex/skills/parallel-dev-cycle/roles/exploration-planner.md similarity index 100% rename from .codex/skills/parallel-dev-cycle/phases/agents/exploration-planner.md rename to .codex/skills/parallel-dev-cycle/roles/exploration-planner.md diff --git a/.codex/skills/parallel-dev-cycle/phases/agents/requirements-analyst.md b/.codex/skills/parallel-dev-cycle/roles/requirements-analyst.md similarity index 100% rename from .codex/skills/parallel-dev-cycle/phases/agents/requirements-analyst.md rename to .codex/skills/parallel-dev-cycle/roles/requirements-analyst.md diff --git a/.codex/skills/parallel-dev-cycle/phases/agents/validation-archivist.md b/.codex/skills/parallel-dev-cycle/roles/validation-archivist.md similarity index 100% rename from .codex/skills/parallel-dev-cycle/phases/agents/validation-archivist.md rename to .codex/skills/parallel-dev-cycle/roles/validation-archivist.md diff --git a/.codex/skills/parallel-dev-cycle/specs/communication-optimization.md b/.codex/skills/parallel-dev-cycle/specs/communication-optimization.md deleted file mode 100644 index ab18381a..00000000 --- a/.codex/skills/parallel-dev-cycle/specs/communication-optimization.md +++ /dev/null @@ -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 从引用路径自动读取所需文件。 - -### 第四步: 测试版本检测 - -确保版本不匹配时有警告。 diff --git a/.codex/skills/parallel-dev-cycle/specs/coordination-protocol.md b/.codex/skills/parallel-dev-cycle/specs/coordination-protocol.md deleted file mode 100644 index 7376283d..00000000 --- a/.codex/skills/parallel-dev-cycle/specs/coordination-protocol.md +++ /dev/null @@ -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 -``` diff --git a/.codex/skills/parallel-dev-cycle/specs/versioning-strategy.md b/.codex/skills/parallel-dev-cycle/specs/versioning-strategy.md deleted file mode 100644 index 2fa0335d..00000000 --- a/.codex/skills/parallel-dev-cycle/specs/versioning-strategy.md +++ /dev/null @@ -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). diff --git a/ccw/docs-site/.docusaurus/codeTranslations.json b/ccw/docs-site/.docusaurus/codeTranslations.json index 5de58a22..9e26dfee 100644 --- a/ccw/docs-site/.docusaurus/codeTranslations.json +++ b/ccw/docs-site/.docusaurus/codeTranslations.json @@ -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": "标签" -} \ No newline at end of file +{} \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/__mdx-loader-dependency.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/__mdx-loader-dependency.json index 44ca291c..1f53d98b 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/__mdx-loader-dependency.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/__mdx-loader-dependency.json @@ -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"}]} \ No newline at end of file +{"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"}]} \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-cli-cli-init-mdx-056.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-cli-cli-init-mdx-056.json index b61807e2..98324c27 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-cli-cli-init-mdx-056.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-cli-cli-init-mdx-056.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-cli-codex-review-mdx-f1b.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-cli-codex-review-mdx-f1b.json index ed61290c..0ff172cc 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-cli-codex-review-mdx-f1b.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-cli-codex-review-mdx-f1b.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-ccw-coordinator-mdx-d55.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-ccw-coordinator-mdx-d55.json index c723148a..43d3add3 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-ccw-coordinator-mdx-d55.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-ccw-coordinator-mdx-d55.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-ccw-debug-mdx-97c.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-ccw-debug-mdx-97c.json index a6160b72..52dc34aa 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-ccw-debug-mdx-97c.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-ccw-debug-mdx-97c.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-ccw-mdx-f48.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-ccw-mdx-f48.json index b15bcb3c..b95e3dd9 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-ccw-mdx-f48.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-ccw-mdx-f48.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-ccw-plan-mdx-04d.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-ccw-plan-mdx-04d.json index d17bf091..753a139a 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-ccw-plan-mdx-04d.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-ccw-plan-mdx-04d.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-ccw-test-mdx-cce.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-ccw-test-mdx-cce.json index 4e2720d3..867a837d 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-ccw-test-mdx-cce.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-ccw-test-mdx-cce.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-codex-coordinator-mdx-f92.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-codex-coordinator-mdx-f92.json index 06b81cab..f51432cd 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-codex-coordinator-mdx-f92.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-codex-coordinator-mdx-f92.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-flow-create-mdx-fab.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-flow-create-mdx-fab.json index 2432a1cd..af2ca45f 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-flow-create-mdx-fab.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-general-flow-create-mdx-fab.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-convert-to-plan-md-5c7.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-convert-to-plan-md-5c7.json index 45c9c689..357625e4 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-convert-to-plan-md-5c7.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-convert-to-plan-md-5c7.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-discover-md-1e3.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-discover-md-1e3.json index 3d22fdda..eb9f0d1a 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-discover-md-1e3.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-discover-md-1e3.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-execute-md-fe8.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-execute-md-fe8.json index bd853cc3..e5c33039 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-execute-md-fe8.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-execute-md-fe8.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-from-brainstorm-md-2ec.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-from-brainstorm-md-2ec.json index fcd79234..1b24145e 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-from-brainstorm-md-2ec.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-from-brainstorm-md-2ec.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-new-md-4ad.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-new-md-4ad.json index 8daa4bce..9e407718 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-new-md-4ad.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-new-md-4ad.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-plan-md-a6c.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-plan-md-a6c.json index 2abd3a33..e3b19693 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-plan-md-a6c.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-plan-md-a6c.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-queue-md-1ba.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-queue-md-1ba.json index 3c33214a..5abd0c80 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-queue-md-1ba.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-issue-issue-queue-md-1ba.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-compact-mdx-7a1.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-compact-mdx-7a1.json index a6749967..f4873aa5 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-compact-mdx-7a1.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-compact-mdx-7a1.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-docs-full-cli-mdx-4cc.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-docs-full-cli-mdx-4cc.json index ced106c4..f6394151 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-docs-full-cli-mdx-4cc.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-docs-full-cli-mdx-4cc.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-docs-related-cli-mdx-60e.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-docs-related-cli-mdx-60e.json index 52a7e910..3f4054ed 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-docs-related-cli-mdx-60e.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-docs-related-cli-mdx-60e.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-load-mdx-157.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-load-mdx-157.json index 3438f60c..ef887547 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-load-mdx-157.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-load-mdx-157.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-update-full-mdx-666.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-update-full-mdx-666.json index 9c9510d5..76cd168b 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-update-full-mdx-666.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-update-full-mdx-666.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-update-related-mdx-611.json b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-update-related-mdx-611.json index dbff395e..97bcd2d8 100644 --- a/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-update-related-mdx-611.json +++ b/ccw/docs-site/.docusaurus/docusaurus-plugin-content-docs/default/site-docs-commands-memory-memory-update-related-mdx-611.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/docusaurus.config.mjs b/ccw/docs-site/.docusaurus/docusaurus.config.mjs index d8173893..98db47c6 100644 --- a/ccw/docs-site/.docusaurus/docusaurus.config.mjs +++ b/ccw/docs-site/.docusaurus/docusaurus.config.mjs @@ -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": { diff --git a/ccw/docs-site/.docusaurus/globalData.json b/ccw/docs-site/.docusaurus/globalData.json index db17fcf0..d9132d1c 100644 --- a/ccw/docs-site/.docusaurus/globalData.json +++ b/ccw/docs-site/.docusaurus/globalData.json @@ -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" } } diff --git a/ccw/docs-site/.docusaurus/i18n.json b/ccw/docs-site/.docusaurus/i18n.json index e9a2547b..ea93de74 100644 --- a/ccw/docs-site/.docusaurus/i18n.json +++ b/ccw/docs-site/.docusaurus/i18n.json @@ -5,7 +5,7 @@ "zh" ], "path": "i18n", - "currentLocale": "zh", + "currentLocale": "en", "localeConfigs": { "en": { "label": "English", diff --git a/ccw/docs-site/.docusaurus/registry.js b/ccw/docs-site/.docusaurus/registry.js index 4cac6b1b..e1d71748 100644 --- a/ccw/docs-site/.docusaurus/registry.js +++ b/ccw/docs-site/.docusaurus/registry.js @@ -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")],}; diff --git a/ccw/docs-site/.docusaurus/routes.js b/ccw/docs-site/.docusaurus/routes.js index 7f74ea8a..88ac2bcd 100644 --- a/ccw/docs-site/.docusaurus/routes.js +++ b/ccw/docs-site/.docusaurus/routes.js @@ -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" } diff --git a/ccw/docs-site/.docusaurus/routesChunkNames.json b/ccw/docs-site/.docusaurus/routesChunkNames.json index 17b2e1a0..a3890cf0 100644 --- a/ccw/docs-site/.docusaurus/routesChunkNames.json +++ b/ccw/docs-site/.docusaurus/routesChunkNames.json @@ -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" } } \ No newline at end of file diff --git a/ccw/docs-site/.docusaurus/site-metadata.json b/ccw/docs-site/.docusaurus/site-metadata.json index 503133f5..085f2863 100644 --- a/ccw/docs-site/.docusaurus/site-metadata.json +++ b/ccw/docs-site/.docusaurus/site-metadata.json @@ -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": { diff --git a/ccw/frontend/tests/e2e/api-settings.spec.ts b/ccw/frontend/tests/e2e/api-settings.spec.ts new file mode 100644 index 00000000..8643525a --- /dev/null +++ b/ccw/frontend/tests/e2e/api-settings.spec.ts @@ -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(); + }); +}); diff --git a/ccw/frontend/tests/e2e/dashboard-charts.spec.ts b/ccw/frontend/tests/e2e/dashboard-charts.spec.ts index ac06ac02..a7865d75 100644 --- a/ccw/frontend/tests/e2e/dashboard-charts.spec.ts +++ b/ccw/frontend/tests/e2e/dashboard-charts.spec.ts @@ -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); diff --git a/ccw/frontend/tests/e2e/dashboard-redesign.spec.ts b/ccw/frontend/tests/e2e/dashboard-redesign.spec.ts index 38eb9d8d..1a1e794e 100644 --- a/ccw/frontend/tests/e2e/dashboard-redesign.spec.ts +++ b/ccw/frontend/tests/e2e/dashboard-redesign.spec.ts @@ -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); diff --git a/ccw/frontend/tests/e2e/explorer.spec.ts b/ccw/frontend/tests/e2e/explorer.spec.ts new file mode 100644 index 00000000..fb31b7d4 --- /dev/null +++ b/ccw/frontend/tests/e2e/explorer.spec.ts @@ -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(); + }); +}); diff --git a/ccw/frontend/tests/e2e/graph.spec.ts b/ccw/frontend/tests/e2e/graph.spec.ts new file mode 100644 index 00000000..dec10e2a --- /dev/null +++ b/ccw/frontend/tests/e2e/graph.spec.ts @@ -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(); + }); +}); diff --git a/ccw/frontend/tests/e2e/history.spec.ts b/ccw/frontend/tests/e2e/history.spec.ts new file mode 100644 index 00000000..6c2f5c9a --- /dev/null +++ b/ccw/frontend/tests/e2e/history.spec.ts @@ -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(); + }); +}); diff --git a/ccw/frontend/tests/e2e/loops.spec.ts b/ccw/frontend/tests/e2e/loops.spec.ts index 67c37c9f..06bfb22f 100644 --- a/ccw/frontend/tests/e2e/loops.spec.ts +++ b/ccw/frontend/tests/e2e/loops.spec.ts @@ -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(); diff --git a/ccw/frontend/tests/e2e/orchestrator.spec.ts b/ccw/frontend/tests/e2e/orchestrator.spec.ts new file mode 100644 index 00000000..f90b138c --- /dev/null +++ b/ccw/frontend/tests/e2e/orchestrator.spec.ts @@ -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(); + }); +}); diff --git a/ccw/frontend/tests/e2e/project.spec.ts b/ccw/frontend/tests/e2e/project.spec.ts new file mode 100644 index 00000000..d293463d --- /dev/null +++ b/ccw/frontend/tests/e2e/project.spec.ts @@ -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(); + }); +});