From d43696d7566bfb46ea194f918cadd17dc984ac64 Mon Sep 17 00:00:00 2001 From: catlog22 Date: Sat, 7 Feb 2026 19:28:33 +0800 Subject: [PATCH] feat: Implement phases 6 to 9 of the review cycle fix process, including discovery, batching, parallel planning, execution, and completion - Added Phase 6: Fix Discovery & Batching with intelligent grouping and batching of findings. - Added Phase 7: Fix Parallel Planning to launch planning agents for concurrent analysis and aggregation of partial plans. - Added Phase 8: Fix Execution for stage-based execution of fixes with conservative test verification. - Added Phase 9: Fix Completion to aggregate results, generate summary reports, and handle session completion. - Introduced new frontend components: ResizeHandle for draggable resizing of sidebar panels and useResizablePanel hook for managing panel sizes with localStorage persistence. - Added PowerShell script for checking TypeScript errors in source code, excluding test files. --- .claude/skills/review-cycle/SKILL.md | 337 +++++++++++ .../phases/01-discovery-initialization.md | 334 +++++++++++ .../review-cycle/phases/02-parallel-review.md | 473 +++++++++++++++ .../review-cycle/phases/03-aggregation.md | 74 +++ .../phases/04-iterative-deep-dive.md | 278 +++++++++ .../phases/05-review-completion.md | 176 ++++++ .../phases/06-fix-discovery-batching.md | 238 ++++++++ .../phases/07-fix-parallel-planning.md | 199 +++++++ .../review-cycle/phases/08-fix-execution.md | 221 +++++++ .../review-cycle/phases/09-fix-completion.md | 153 +++++ .codex/skills/review-cycle/SKILL.md | 412 +++++++++++++ .../phases/01-discovery-initialization.md | 341 +++++++++++ .../review-cycle/phases/02-parallel-review.md | 549 ++++++++++++++++++ .../review-cycle/phases/03-aggregation.md | 74 +++ .../phases/04-iterative-deep-dive.md | 333 +++++++++++ .../phases/05-review-completion.md | 173 ++++++ .../phases/06-fix-discovery-batching.md | 238 ++++++++ .../phases/07-fix-parallel-planning.md | 224 +++++++ .../review-cycle/phases/08-fix-execution.md | 239 ++++++++ .../review-cycle/phases/09-fix-completion.md | 141 +++++ ccw/frontend/_check.ps1 | 13 + ccw/frontend/analyze-errors.ps1 | 19 - ccw/frontend/analyze-errors2.ps1 | 28 - ccw/frontend/playwright-report/index.html | 2 +- .../orchestrator/ExecutionHeader.tsx | 160 +++++ .../orchestrator/ExecutionMonitor.example.tsx | 72 +++ .../orchestrator/NodeDetailPanel.tsx | 425 ++++++++++++++ .../orchestrator/NodeExecutionChain.tsx | 145 +++++ .../src/components/orchestrator/README.md | 110 ++++ .../components/orchestrator/ToolCallCard.tsx | 317 ++++++++++ .../orchestrator/ToolCallsTimeline.tsx | 233 ++++++++ .../src/components/orchestrator/index.ts | 8 + .../shared/LogBlock/LogBlockList.tsx | 5 +- .../src/components/shared/MarkdownModal.tsx | 2 +- .../src/components/shared/SessionCard.tsx | 4 +- .../src/components/ui/ContextAssembler.tsx | 8 +- .../workspace/WorkspaceSelector.tsx | 4 +- .../__tests__/chartHooksIntegration.test.tsx | 56 +- ccw/frontend/src/hooks/useCodexLens.test.tsx | 12 +- ccw/frontend/src/hooks/useCodexLens.ts | 2 +- ccw/frontend/src/hooks/useIssues.test.tsx | 10 +- ccw/frontend/src/hooks/useLocale.ts | 9 +- ccw/frontend/src/hooks/useSessions.ts | 2 +- ccw/frontend/src/hooks/useWebSocket.ts | 111 +++- ccw/frontend/src/lib/api.mcp.test.ts | 2 +- ccw/frontend/src/lib/api.ts | 13 +- .../__tests__/A2UIComponentRegistry.test.ts | 57 +- .../a2ui-runtime/__tests__/A2UIParser.test.ts | 8 +- .../packages/a2ui-runtime/core/A2UIParser.ts | 6 +- .../packages/a2ui-runtime/core/A2UITypes.ts | 4 +- .../a2ui-runtime/renderer/A2UIRenderer.tsx | 2 +- .../renderer/components/A2UICLIOutput.tsx | 6 +- .../renderer/components/A2UIText.tsx | 2 +- .../src/pages/CodexLensManagerPage.test.tsx | 36 +- .../src/pages/CommandsManagerPage.tsx | 16 + ccw/frontend/src/pages/LiteTaskDetailPage.tsx | 23 +- ccw/frontend/src/pages/LiteTasksPage.tsx | 4 +- ccw/frontend/src/pages/MemoryPage.tsx | 16 + ccw/frontend/src/pages/PromptHistoryPage.tsx | 8 +- ccw/frontend/src/pages/QueuePage.test.tsx | 8 +- ccw/frontend/src/pages/SessionDetailPage.tsx | 2 +- ccw/frontend/src/pages/SkillsManagerPage.tsx | 16 + .../pages/orchestrator/ExecutionMonitor.tsx | 294 +++++----- .../src/pages/orchestrator/FlowCanvas.tsx | 45 +- .../orchestrator/InteractionModeToggle.tsx | 51 ++ .../src/pages/orchestrator/LeftSidebar.tsx | 40 +- .../src/pages/orchestrator/NodeLibrary.tsx | 6 +- .../pages/orchestrator/OrchestratorPage.tsx | 45 +- .../src/pages/orchestrator/PropertyPanel.tsx | 21 +- .../src/pages/orchestrator/ResizeHandle.tsx | 39 ++ .../pages/orchestrator/useResizablePanel.ts | 136 +++++ ccw/frontend/src/stores/executionStore.ts | 269 ++++++++- ccw/frontend/src/stores/flowStore.ts | 65 ++- ccw/frontend/src/types/execution.ts | 37 ++ ccw/frontend/src/types/flow.ts | 7 + ccw/frontend/src/types/index.ts | 18 + ccw/frontend/src/types/toolCall.ts | 207 +++++++ ccw/frontend/tailwind.config.js | 10 + ccw/frontend/tests/e2e/api-settings.spec.ts | 10 +- ccw/frontend/tests/e2e/cli-config.spec.ts | 2 +- ccw/frontend/tests/e2e/cli-history.spec.ts | 2 +- .../tests/e2e/cli-installations.spec.ts | 2 +- .../tests/e2e/codexlens-manager.spec.ts | 2 +- ccw/frontend/tests/e2e/commands.spec.ts | 331 ++++++----- .../tests/e2e/helpers/i18n-helpers.ts | 72 ++- ccw/frontend/tests/e2e/mcp.spec.ts | 6 +- ccw/frontend/tests/e2e/memory.spec.ts | 6 +- ccw/frontend/tests/e2e/orchestrator.spec.ts | 8 +- ccw/frontend/tests/e2e/sessions-crud.spec.ts | 34 +- ccw/frontend/tests/e2e/skills.spec.ts | 120 ++-- 90 files changed, 8462 insertions(+), 616 deletions(-) create mode 100644 .claude/skills/review-cycle/SKILL.md create mode 100644 .claude/skills/review-cycle/phases/01-discovery-initialization.md create mode 100644 .claude/skills/review-cycle/phases/02-parallel-review.md create mode 100644 .claude/skills/review-cycle/phases/03-aggregation.md create mode 100644 .claude/skills/review-cycle/phases/04-iterative-deep-dive.md create mode 100644 .claude/skills/review-cycle/phases/05-review-completion.md create mode 100644 .claude/skills/review-cycle/phases/06-fix-discovery-batching.md create mode 100644 .claude/skills/review-cycle/phases/07-fix-parallel-planning.md create mode 100644 .claude/skills/review-cycle/phases/08-fix-execution.md create mode 100644 .claude/skills/review-cycle/phases/09-fix-completion.md create mode 100644 .codex/skills/review-cycle/SKILL.md create mode 100644 .codex/skills/review-cycle/phases/01-discovery-initialization.md create mode 100644 .codex/skills/review-cycle/phases/02-parallel-review.md create mode 100644 .codex/skills/review-cycle/phases/03-aggregation.md create mode 100644 .codex/skills/review-cycle/phases/04-iterative-deep-dive.md create mode 100644 .codex/skills/review-cycle/phases/05-review-completion.md create mode 100644 .codex/skills/review-cycle/phases/06-fix-discovery-batching.md create mode 100644 .codex/skills/review-cycle/phases/07-fix-parallel-planning.md create mode 100644 .codex/skills/review-cycle/phases/08-fix-execution.md create mode 100644 .codex/skills/review-cycle/phases/09-fix-completion.md create mode 100644 ccw/frontend/_check.ps1 delete mode 100644 ccw/frontend/analyze-errors.ps1 delete mode 100644 ccw/frontend/analyze-errors2.ps1 create mode 100644 ccw/frontend/src/components/orchestrator/ExecutionHeader.tsx create mode 100644 ccw/frontend/src/components/orchestrator/ExecutionMonitor.example.tsx create mode 100644 ccw/frontend/src/components/orchestrator/NodeDetailPanel.tsx create mode 100644 ccw/frontend/src/components/orchestrator/NodeExecutionChain.tsx create mode 100644 ccw/frontend/src/components/orchestrator/README.md create mode 100644 ccw/frontend/src/components/orchestrator/ToolCallCard.tsx create mode 100644 ccw/frontend/src/components/orchestrator/ToolCallsTimeline.tsx create mode 100644 ccw/frontend/src/components/orchestrator/index.ts create mode 100644 ccw/frontend/src/pages/orchestrator/InteractionModeToggle.tsx create mode 100644 ccw/frontend/src/pages/orchestrator/ResizeHandle.tsx create mode 100644 ccw/frontend/src/pages/orchestrator/useResizablePanel.ts create mode 100644 ccw/frontend/src/types/toolCall.ts diff --git a/.claude/skills/review-cycle/SKILL.md b/.claude/skills/review-cycle/SKILL.md new file mode 100644 index 00000000..8eb824ca --- /dev/null +++ b/.claude/skills/review-cycle/SKILL.md @@ -0,0 +1,337 @@ +--- +name: review-cycle +description: Unified multi-dimensional code review with automated fix orchestration. Supports session-based (git changes) and module-based (path patterns) review modes with 7-dimension parallel analysis, iterative deep-dive, and automated fix pipeline. Triggers on "workflow:review-cycle", "workflow:review-session-cycle", "workflow:review-module-cycle", "workflow:review-cycle-fix". +allowed-tools: Task, AskUserQuestion, TaskCreate, TaskUpdate, TaskList, Read, Write, Edit, Bash, Glob, Grep, Skill +--- + +# Review Cycle + +Unified multi-dimensional code review orchestrator with dual-mode (session/module) file discovery, 7-dimension parallel analysis, iterative deep-dive on critical findings, and optional automated fix pipeline with intelligent batching and parallel planning. + +## Architecture Overview + +``` +┌──────────────────────────────────────────────────────────────────────┐ +│ Review Cycle Orchestrator (SKILL.md) │ +│ → Pure coordinator: mode detection, phase dispatch, state tracking │ +└───────────────────────────────┬──────────────────────────────────────┘ + │ + ┌─────────────────────────────┼─────────────────────────────────┐ + │ Review Pipeline (Phase 1-5) │ + │ │ + │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ + │ │ Phase 1 │→ │ Phase 2 │→ │ Phase 3 │→ │ Phase 4 │→ │ Phase 5 │ + │ │Discovery│ │Parallel │ │Aggregate│ │Deep-Dive│ │Complete │ + │ │ Init │ │ Review │ │ │ │(cond.) │ │ │ + │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ + │ session| 7 agents severity N agents finalize + │ module ×cli-explore calc ×cli-explore state + │ ↕ loop + └────────────────────────────────────────────────────────────────┘ + │ + (optional --fix) + │ + ┌─────────────────────────────┼─────────────────────────────────┐ + │ Fix Pipeline (Phase 6-9) │ + │ │ + │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ + │ │ Phase 6 │→ │ Phase 7 │→ │ Phase 8 │→ │ Phase 9 │ + │ │Discovery│ │Parallel │ │Execution│ │Complete │ + │ │Batching │ │Planning │ │Orchestr.│ │ │ + │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ + │ grouping N agents M agents aggregate + │ + batch ×cli-plan ×cli-exec + summary + └────────────────────────────────────────────────────────────────┘ +``` + +## Key Design Principles + +1. **Dual-Mode Review**: Session-based (git changes) and module-based (path patterns) share the same review pipeline (Phase 2-5), differing only in file discovery (Phase 1) +2. **Pure Orchestrator**: Execute phases in sequence, parse outputs, pass context between them +3. **Progressive Phase Loading**: Phase docs are read on-demand when that phase executes, not all at once +4. **Auto-Continue**: All phases run autonomously without user intervention between phases +5. **Task Attachment Model**: Sub-tasks attached/collapsed dynamically in TaskCreate/TaskUpdate +6. **Optional Fix Pipeline**: Phase 6-9 triggered only by explicit `--fix` flag or user confirmation after Phase 5 +7. **Content Preservation**: All agent prompts, code, schemas preserved verbatim from source commands + +## Usage + +``` +# Review Pipeline (Phase 1-5) +Skill(skill="review-cycle", args="") # Module mode +Skill(skill="review-cycle", args="[session-id]") # Session mode +Skill(skill="review-cycle", args="[session-id|path-pattern] [FLAGS]") # With flags + +# Fix Pipeline (Phase 6-9) +Skill(skill="review-cycle", args="--fix ") # Fix mode +Skill(skill="review-cycle", args="--fix [FLAGS]") # Fix with flags + +# Flags +--dimensions=dim1,dim2,... Custom dimensions (default: all 7) +--max-iterations=N Max deep-dive iterations (default: 3) +--fix Enter fix pipeline after review or standalone +--resume Resume interrupted fix session +--batch-size=N Findings per planning batch (default: 5, fix mode only) + +# Examples +Skill(skill="review-cycle", args="src/auth/**") # Module: review auth +Skill(skill="review-cycle", args="src/auth/**,src/payment/**") # Module: multiple paths +Skill(skill="review-cycle", args="src/auth/** --dimensions=security,architecture") # Module: custom dims +Skill(skill="review-cycle", args="WFS-payment-integration") # Session: specific +Skill(skill="review-cycle", args="") # Session: auto-detect +Skill(skill="review-cycle", args="--fix .workflow/active/WFS-123/.review/") # Fix: from review dir +Skill(skill="review-cycle", args="--fix --resume") # Fix: resume session +``` + +## Mode Detection + +```javascript +// Input parsing logic (orchestrator responsibility) +function detectMode(args) { + if (args.includes('--fix')) return 'fix'; + if (args.match(/\*|\.ts|\.js|\.py|src\/|lib\//)) return 'module'; // glob/path patterns + if (args.match(/^WFS-/) || args.trim() === '') return 'session'; // session ID or empty + return 'session'; // default +} +``` + +| Input Pattern | Detected Mode | Phase Entry | +|---------------|---------------|-------------| +| `src/auth/**` | `module` | Phase 1 (module branch) | +| `WFS-payment-integration` | `session` | Phase 1 (session branch) | +| _(empty)_ | `session` | Phase 1 (session branch, auto-detect) | +| `--fix .review/` | `fix` | Phase 6 | +| `--fix --resume` | `fix` | Phase 6 (resume) | + +## Execution Flow + +``` +Input Parsing: + └─ Detect mode (session|module|fix) → route to appropriate phase entry + +Review Pipeline (session or module mode): + +Phase 1: Discovery & Initialization + └─ Ref: phases/01-discovery-initialization.md + ├─ Session mode: session discovery → git changed files → resolve + ├─ Module mode: path patterns → glob expand → resolve + └─ Common: create session, output dirs, review-state.json, review-progress.json + +Phase 2: Parallel Review Coordination + └─ Ref: phases/02-parallel-review.md + ├─ Launch 7 cli-explore-agent instances (Deep Scan mode) + ├─ Each produces dimensions/{dimension}.json + reports/{dimension}-analysis.md + └─ CLI fallback: Gemini → Qwen → Codex + +Phase 3: Aggregation + └─ Ref: phases/03-aggregation.md + ├─ Load dimension JSONs, calculate severity distribution + ├─ Identify cross-cutting concerns (files in 3+ dimensions) + └─ Decision: critical > 0 OR high > 5 OR critical files → Phase 4 + Else → Phase 5 + +Phase 4: Iterative Deep-Dive (conditional) + └─ Ref: phases/04-iterative-deep-dive.md + ├─ Select critical findings (max 5 per iteration) + ├─ Launch deep-dive agents for root cause analysis + ├─ Re-assess severity → loop back to Phase 3 aggregation + └─ Exit when: no critical findings OR max iterations reached + +Phase 5: Review Completion + └─ Ref: phases/05-review-completion.md + ├─ Finalize review-state.json + review-progress.json + ├─ Prompt user: "Run automated fixes? [Y/n]" + └─ If yes → Continue to Phase 6 + +Fix Pipeline (--fix mode or after Phase 5): + +Phase 6: Fix Discovery & Batching + └─ Ref: phases/06-fix-discovery-batching.md + ├─ Validate export file, create fix session + └─ Intelligent grouping by file+dimension similarity → batches + +Phase 7: Fix Parallel Planning + └─ Ref: phases/07-fix-parallel-planning.md + ├─ Launch N cli-planning-agent instances (≤10 parallel) + ├─ Each outputs partial-plan-{batch-id}.json + └─ Orchestrator aggregates → fix-plan.json + +Phase 8: Fix Execution + └─ Ref: phases/08-fix-execution.md + ├─ Stage-based execution per aggregated timeline + ├─ Each group: analyze → fix → test → commit/rollback + └─ 100% test pass rate required + +Phase 9: Fix Completion + └─ Ref: phases/09-fix-completion.md + ├─ Aggregate results → fix-summary.md + └─ Optional: complete workflow session if all fixes successful + +Complete: Review reports + optional fix results +``` + +**Phase Reference Documents** (read on-demand when phase executes): + +| Phase | Document | Load When | Source | +|-------|----------|-----------|--------| +| 1 | [phases/01-discovery-initialization.md](phases/01-discovery-initialization.md) | Review/Fix start | review-session-cycle + review-module-cycle Phase 1 (fused) | +| 2 | [phases/02-parallel-review.md](phases/02-parallel-review.md) | Phase 1 complete | Shared from both review commands Phase 2 | +| 3 | [phases/03-aggregation.md](phases/03-aggregation.md) | Phase 2 complete | Shared from both review commands Phase 3 | +| 4 | [phases/04-iterative-deep-dive.md](phases/04-iterative-deep-dive.md) | Aggregation triggers iteration | Shared from both review commands Phase 4 | +| 5 | [phases/05-review-completion.md](phases/05-review-completion.md) | No more iterations needed | Shared from both review commands Phase 5 | +| 6 | [phases/06-fix-discovery-batching.md](phases/06-fix-discovery-batching.md) | Fix mode entry | review-cycle-fix Phase 1 + 1.5 | +| 7 | [phases/07-fix-parallel-planning.md](phases/07-fix-parallel-planning.md) | Phase 6 complete | review-cycle-fix Phase 2 | +| 8 | [phases/08-fix-execution.md](phases/08-fix-execution.md) | Phase 7 complete | review-cycle-fix Phase 3 | +| 9 | [phases/09-fix-completion.md](phases/09-fix-completion.md) | Phase 8 complete | review-cycle-fix Phase 4 + 5 | + +## Core Rules + +1. **Start Immediately**: First action is TaskCreate initialization, second action is Phase 1 execution +2. **Mode Detection First**: Parse input to determine session/module/fix mode before Phase 1 +3. **Parse Every Output**: Extract required data from each phase for next phase +4. **Auto-Continue**: Check TaskList status to execute next pending phase automatically +5. **Progressive Phase Loading**: Read phase docs ONLY when that phase is about to execute +6. **DO NOT STOP**: Continuous multi-phase workflow until all applicable phases complete +7. **Conditional Phase 4**: Only execute if aggregation triggers iteration (critical > 0 OR high > 5 OR critical files) +8. **Fix Pipeline Optional**: Phase 6-9 only execute with explicit --fix flag or user confirmation + +## Data Flow + +``` +User Input (path-pattern | session-id | --fix export-file) + ↓ +[Mode Detection: session | module | fix] + ↓ +Phase 1: Discovery & Initialization + ↓ Output: sessionId, reviewId, resolvedFiles, reviewMode, outputDir + ↓ review-state.json, review-progress.json +Phase 2: Parallel Review Coordination + ↓ Output: dimensions/*.json, reports/*-analysis.md +Phase 3: Aggregation + ↓ Output: severityDistribution, criticalFiles, deepDiveFindings + ↓ Decision: iterate? → Phase 4 : Phase 5 +Phase 4: Iterative Deep-Dive (conditional, loops with Phase 3) + ↓ Output: iterations/*.json, reports/deep-dive-*.md + ↓ Loop: re-aggregate → check criteria → iterate or exit +Phase 5: Review Completion + ↓ Output: final review-state.json, review-progress.json + ↓ Decision: fix? → Phase 6 : END +Phase 6: Fix Discovery & Batching + ↓ Output: finding batches (in-memory) +Phase 7: Fix Parallel Planning + ↓ Output: partial-plan-*.json → fix-plan.json (aggregated) +Phase 8: Fix Execution + ↓ Output: fix-progress-*.json, git commits +Phase 9: Fix Completion + ↓ Output: fix-summary.md, fix-history.json +``` + +## TaskCreate/TaskUpdate Pattern + +**Review Pipeline Initialization**: +```javascript +TaskCreate({ subject: "Phase 1: Discovery & Initialization", activeForm: "Initializing review" }); +TaskCreate({ subject: "Phase 2: Parallel Reviews (7 dimensions)", activeForm: "Reviewing" }); +TaskCreate({ subject: "Phase 3: Aggregation", activeForm: "Aggregating findings" }); +TaskCreate({ subject: "Phase 4: Deep-dive (conditional)", activeForm: "Deep-diving" }); +TaskCreate({ subject: "Phase 5: Review Completion", activeForm: "Completing review" }); +``` + +**During Phase 2 (sub-tasks for each dimension)**: +```javascript +// Attach dimension sub-tasks +TaskCreate({ subject: " → Security review", activeForm: "Analyzing security" }); +TaskCreate({ subject: " → Architecture review", activeForm: "Analyzing architecture" }); +TaskCreate({ subject: " → Quality review", activeForm: "Analyzing quality" }); +// ... other dimensions +// Collapse: Mark all dimension tasks completed when Phase 2 finishes +``` + +**Fix Pipeline (added after Phase 5 if triggered)**: +```javascript +TaskCreate({ subject: "Phase 6: Fix Discovery & Batching", activeForm: "Batching findings" }); +TaskCreate({ subject: "Phase 7: Parallel Planning", activeForm: "Planning fixes" }); +TaskCreate({ subject: "Phase 8: Execution", activeForm: "Executing fixes" }); +TaskCreate({ subject: "Phase 9: Fix Completion", activeForm: "Completing fixes" }); +``` + +## Error Handling + +### Review Pipeline Errors + +| Phase | Error | Blocking? | Action | +|-------|-------|-----------|--------| +| Phase 1 | Session not found (session mode) | Yes | Error and exit | +| Phase 1 | No changed files (session mode) | Yes | Error and exit | +| Phase 1 | Invalid path pattern (module mode) | Yes | Error and exit | +| Phase 1 | No files matched (module mode) | Yes | Error and exit | +| Phase 2 | Single dimension fails | No | Log warning, continue other dimensions | +| Phase 2 | All dimensions fail | Yes | Error and exit | +| Phase 3 | Missing dimension JSON | No | Skip in aggregation, log warning | +| Phase 4 | Deep-dive agent fails | No | Skip finding, continue others | +| Phase 4 | Max iterations reached | No | Generate partial report | + +### Fix Pipeline Errors + +| Phase | Error | Blocking? | Action | +|-------|-------|-----------|--------| +| Phase 6 | Invalid export file | Yes | Abort with error | +| Phase 6 | Empty batches | No | Warn and skip empty | +| Phase 7 | Planning agent timeout | No | Mark batch failed, continue others | +| Phase 7 | All agents fail | Yes | Abort fix session | +| Phase 8 | Test failure after fix | No | Rollback, retry up to max_iterations | +| Phase 8 | Git operations fail | Yes | Abort, preserve state | +| Phase 9 | Aggregation error | No | Generate partial summary | + +### CLI Fallback Chain + +Gemini → Qwen → Codex → degraded mode + +**Fallback Triggers**: HTTP 429/5xx, connection timeout, invalid JSON output, low confidence < 0.4, analysis too brief (< 100 words) + +## Output File Structure + +``` +.workflow/active/WFS-{session-id}/.review/ +├── review-state.json # Orchestrator state machine +├── review-progress.json # Real-time progress +├── dimensions/ # Per-dimension results (Phase 2) +│ ├── security.json +│ ├── architecture.json +│ ├── quality.json +│ ├── action-items.json +│ ├── performance.json +│ ├── maintainability.json +│ └── best-practices.json +├── iterations/ # Deep-dive results (Phase 4) +│ ├── iteration-1-finding-{uuid}.json +│ └── iteration-2-finding-{uuid}.json +├── reports/ # Human-readable reports +│ ├── security-analysis.md +│ ├── security-cli-output.txt +│ ├── deep-dive-1-{uuid}.md +│ └── ... +└── fixes/{fix-session-id}/ # Fix results (Phase 6-9) + ├── partial-plan-*.json + ├── fix-plan.json + ├── fix-progress-*.json + ├── fix-summary.md + ├── active-fix-session.json + └── fix-history.json +``` + +## Related Commands + +### View Progress +```bash +ccw view +``` + +### Workflow Pipeline +```bash +# Step 1: Review (this skill) +Skill(skill="review-cycle", args="src/auth/**") + +# Step 2: Fix (continue or standalone) +Skill(skill="review-cycle", args="--fix .workflow/active/WFS-{session-id}/.review/") +``` diff --git a/.claude/skills/review-cycle/phases/01-discovery-initialization.md b/.claude/skills/review-cycle/phases/01-discovery-initialization.md new file mode 100644 index 00000000..95609d1c --- /dev/null +++ b/.claude/skills/review-cycle/phases/01-discovery-initialization.md @@ -0,0 +1,334 @@ +# Phase 1: Discovery & Initialization + +> Source: Fused from `commands/workflow/review-session-cycle.md` Phase 1 + `commands/workflow/review-module-cycle.md` Phase 1 + +## Overview + +Detect review mode (session or module), resolve target files, create workflow session, initialize output directory structure and state files. + +## Mode Detection + +The review mode is determined by the input arguments: + +- **Session mode**: No path pattern provided, OR a `WFS-*` session ID is provided. Reviews all changes within an existing workflow session (git-based change detection). +- **Module mode**: Glob/path patterns are provided (e.g., `src/auth/**`, `src/payment/processor.ts`). Reviews specific files/directories regardless of session history. + +--- + +## Session Mode (review-session-cycle) + +### Step 1.1: Session Discovery + +```javascript +// If session ID not provided, auto-detect +if (!providedSessionId) { + // Check for active sessions + const activeSessions = Glob('.workflow/active/WFS-*'); + if (activeSessions.length === 1) { + sessionId = activeSessions[0].match(/WFS-[^/]+/)[0]; + } else if (activeSessions.length > 1) { + // List sessions and prompt user + error("Multiple active sessions found. Please specify session ID."); + } else { + error("No active session found. Create session first with /workflow:session:start"); + } +} else { + sessionId = providedSessionId; +} + +// Validate session exists +Bash(`test -d .workflow/active/${sessionId} && echo "EXISTS"`); +``` + +### Step 1.2: Session Validation + +- Ensure session has implementation artifacts (check `.summaries/` or `.task/` directory) +- Extract session creation timestamp from `workflow-session.json` +- Use timestamp for git log filtering: `git log --since="${sessionCreatedAt}"` + +### Step 1.3: Changed Files Detection + +```bash +# Get files changed since session creation +git log --since="${sessionCreatedAt}" --name-only --pretty=format: | sort -u +``` + +--- + +## Module Mode (review-module-cycle) + +### Step 1.1: Session Creation + +```javascript +// Create workflow session for this review (type: review) +Skill(skill="workflow:session:start", args="--type review \"Code review for [target_pattern]\"") + +// Parse output +const sessionId = output.match(/SESSION_ID: (WFS-[^\s]+)/)[1]; +``` + +### Step 1.2: Path Resolution & Validation + +```bash +# Expand glob pattern to file list (relative paths from project root) +find . -path "./src/auth/**" -type f | sed 's|^\./||' + +# Validate files exist and are readable +for file in ${resolvedFiles[@]}; do + test -r "$file" || error "File not readable: $file" +done +``` + +- Parse and expand file patterns (glob support): `src/auth/**` -> actual file list +- Validation: Ensure all specified files exist and are readable +- Store as **relative paths** from project root (e.g., `src/auth/service.ts`) +- Agents construct absolute paths dynamically during execution + +**Syntax Rules**: +- All paths are **relative** from project root (e.g., `src/auth/**` not `/src/auth/**`) +- Multiple patterns: comma-separated, **no spaces** (e.g., `src/auth/**,src/payment/**`) +- Glob and specific files can be mixed (e.g., `src/auth/**,src/config.ts`) + +**Supported Patterns**: +| Pattern Type | Example | Description | +|--------------|---------|-------------| +| Glob directory | `src/auth/**` | All files under src/auth/ | +| Glob with extension | `src/**/*.ts` | All .ts files under src/ | +| Specific file | `src/payment/processor.ts` | Single file | +| Multiple patterns | `src/auth/**,src/payment/**` | Comma-separated (no spaces) | + +**Resolution Process**: +1. Parse input pattern (split by comma, trim whitespace) +2. Expand glob patterns to file list via `find` command +3. Validate all files exist and are readable +4. Error if pattern matches 0 files +5. Store resolved file list in review-state.json + +--- + +## Common Steps (Both Modes) + +### Step 1.4: Output Directory Setup + +- Output directory: `.workflow/active/${sessionId}/.review/` +- Create directory structure: + ```bash + mkdir -p ${sessionDir}/.review/{dimensions,iterations,reports} + ``` + +### Step 1.5: Initialize Review State + +- State initialization: Create `review-state.json` with metadata, dimensions, max_iterations (merged metadata + state) +- Session mode includes `git_changes` in metadata +- Module mode includes `target_pattern` and `resolved_files` in metadata +- Progress tracking: Create `review-progress.json` for progress tracking + +### Step 1.6: Initialize Review Progress + +- Create `review-progress.json` for real-time dashboard updates via polling +- See [Review Progress JSON](#review-progress-json) schema below + +### Step 1.7: TaskCreate Initialization + +- Set up progress tracking with hierarchical structure +- Mark Phase 1 completed, Phase 2 in_progress + +--- + +## Review State JSON (Session Mode) + +**Purpose**: Unified state machine and metadata (merged from metadata + state) + +```json +{ + "session_id": "WFS-payment-integration", + "review_id": "review-20250125-143022", + "review_type": "session", + "metadata": { + "created_at": "2025-01-25T14:30:22Z", + "git_changes": { + "commit_range": "abc123..def456", + "files_changed": 15, + "insertions": 342, + "deletions": 128 + }, + "dimensions": ["security", "architecture", "quality", "action-items", "performance", "maintainability", "best-practices"], + "max_iterations": 3 + }, + "phase": "parallel|aggregate|iterate|complete", + "current_iteration": 1, + "dimensions_reviewed": ["security", "architecture", "quality", "action-items", "performance", "maintainability", "best-practices"], + "selected_strategy": "comprehensive", + "next_action": "execute_parallel_reviews|aggregate_findings|execute_deep_dive|generate_final_report|complete", + "severity_distribution": { + "critical": 2, + "high": 5, + "medium": 12, + "low": 8 + }, + "critical_files": [ + { + "file": "src/payment/processor.ts", + "finding_count": 5, + "dimensions": ["security", "architecture", "quality"] + } + ], + "iterations": [ + { + "iteration": 1, + "findings_analyzed": ["uuid-1", "uuid-2"], + "findings_resolved": 1, + "findings_escalated": 1, + "severity_change": { + "before": {"critical": 2, "high": 5, "medium": 12, "low": 8}, + "after": {"critical": 1, "high": 6, "medium": 12, "low": 8} + }, + "timestamp": "2025-01-25T14:30:00Z" + } + ], + "completion_criteria": { + "target": "no_critical_findings_and_high_under_5", + "current_status": "in_progress", + "estimated_completion": "2 iterations remaining" + } +} +``` + +**Field Descriptions**: +- `phase`: Current execution phase (state machine pointer) +- `current_iteration`: Iteration counter (used for max check) +- `next_action`: Next step orchestrator should execute +- `severity_distribution`: Aggregated counts across all dimensions +- `critical_files`: Files appearing in 3+ dimensions with metadata +- `iterations[]`: Historical log for trend analysis + +## Review State JSON (Module Mode) + +**Purpose**: Unified state machine and metadata (merged from metadata + state) + +```json +{ + "review_id": "review-20250125-143022", + "review_type": "module", + "session_id": "WFS-auth-system", + "metadata": { + "created_at": "2025-01-25T14:30:22Z", + "target_pattern": "src/auth/**", + "resolved_files": [ + "src/auth/service.ts", + "src/auth/validator.ts", + "src/auth/middleware.ts" + ], + "dimensions": ["security", "architecture", "quality", "action-items", "performance", "maintainability", "best-practices"], + "max_iterations": 3 + }, + "phase": "parallel|aggregate|iterate|complete", + "current_iteration": 1, + "dimensions_reviewed": ["security", "architecture", "quality", "action-items", "performance", "maintainability", "best-practices"], + "selected_strategy": "comprehensive", + "next_action": "execute_parallel_reviews|aggregate_findings|execute_deep_dive|generate_final_report|complete", + "severity_distribution": { + "critical": 2, + "high": 5, + "medium": 12, + "low": 8 + }, + "critical_files": [...], + "iterations": [...], + "completion_criteria": {...} +} +``` + +## Review Progress JSON + +**Purpose**: Real-time dashboard updates via polling + +```json +{ + "review_id": "review-20250125-143022", + "last_update": "2025-01-25T14:35:10Z", + "phase": "parallel|aggregate|iterate|complete", + "current_iteration": 1, + "progress": { + "parallel_review": { + "total_dimensions": 7, + "completed": 5, + "in_progress": 2, + "percent_complete": 71 + }, + "deep_dive": { + "total_findings": 6, + "analyzed": 2, + "in_progress": 1, + "percent_complete": 33 + } + }, + "agent_status": [ + { + "agent_type": "review-agent", + "dimension": "security", + "status": "completed", + "started_at": "2025-01-25T14:30:00Z", + "completed_at": "2025-01-25T15:15:00Z", + "duration_ms": 2700000 + }, + { + "agent_type": "deep-dive-agent", + "finding_id": "sec-001-uuid", + "status": "in_progress", + "started_at": "2025-01-25T14:32:00Z" + } + ], + "estimated_completion": "2025-01-25T16:00:00Z" +} +``` + +--- + +## Output File Structure + +``` +.workflow/active/WFS-{session-id}/.review/ +├── review-state.json # Orchestrator state machine (includes metadata) +├── review-progress.json # Real-time progress for dashboard +├── dimensions/ # Per-dimension results +│ ├── security.json +│ ├── architecture.json +│ ├── quality.json +│ ├── action-items.json +│ ├── performance.json +│ ├── maintainability.json +│ └── best-practices.json +├── iterations/ # Deep-dive results +│ ├── iteration-1-finding-{uuid}.json +│ └── iteration-2-finding-{uuid}.json +└── reports/ # Human-readable reports + ├── security-analysis.md + ├── security-cli-output.txt + ├── deep-dive-1-{uuid}.md + └── ... +``` + +## Session Context + +``` +.workflow/active/WFS-{session-id}/ +├── workflow-session.json +├── IMPL_PLAN.md +├── TODO_LIST.md +├── .task/ +├── .summaries/ +└── .review/ # Review results (this command) + └── (structure above) +``` + +--- + +## Output + +- **Variables**: `sessionId`, `reviewId`, `resolvedFiles`, `reviewMode`, `outputDir` +- **Files**: `review-state.json`, `review-progress.json` + +## Next Phase + +Return to orchestrator, then auto-continue to [Phase 2: Parallel Review](02-parallel-review.md). diff --git a/.claude/skills/review-cycle/phases/02-parallel-review.md b/.claude/skills/review-cycle/phases/02-parallel-review.md new file mode 100644 index 00000000..f90ffc00 --- /dev/null +++ b/.claude/skills/review-cycle/phases/02-parallel-review.md @@ -0,0 +1,473 @@ +# Phase 2: Parallel Review Coordination + +> Source: Shared from `commands/workflow/review-session-cycle.md` + `commands/workflow/review-module-cycle.md` Phase 2 + +## Overview + +Launch 7 dimension-specific review agents simultaneously using cli-explore-agent in Deep Scan mode. + +## Review Dimensions Configuration + +**7 Specialized Dimensions** with priority-based allocation: + +| Dimension | Template | Priority | Timeout | +|-----------|----------|----------|---------| +| **Security** | 03-assess-security-risks.txt | 1 (Critical) | 60min | +| **Architecture** | 02-review-architecture.txt | 2 (High) | 60min | +| **Quality** | 02-review-code-quality.txt | 3 (Medium) | 40min | +| **Action-Items** | 02-analyze-code-patterns.txt | 2 (High) | 40min | +| **Performance** | 03-analyze-performance.txt | 3 (Medium) | 60min | +| **Maintainability** | 02-review-code-quality.txt* | 3 (Medium) | 40min | +| **Best-Practices** | 03-review-quality-standards.txt | 3 (Medium) | 40min | + +*Custom focus: "Assess technical debt and maintainability" + +**Category Definitions by Dimension**: + +```javascript +const CATEGORIES = { + security: ['injection', 'authentication', 'authorization', 'encryption', 'input-validation', 'access-control', 'data-exposure'], + architecture: ['coupling', 'cohesion', 'layering', 'dependency', 'pattern-violation', 'scalability', 'separation-of-concerns'], + quality: ['code-smell', 'duplication', 'complexity', 'naming', 'error-handling', 'testability', 'readability'], + 'action-items': ['requirement-coverage', 'acceptance-criteria', 'documentation', 'deployment-readiness', 'missing-functionality'], + performance: ['n-plus-one', 'inefficient-query', 'memory-leak', 'blocking-operation', 'caching', 'resource-usage'], + maintainability: ['technical-debt', 'magic-number', 'long-method', 'large-class', 'dead-code', 'commented-code'], + 'best-practices': ['convention-violation', 'anti-pattern', 'deprecated-api', 'missing-validation', 'inconsistent-style'] +}; +``` + +## Severity Assessment + +**Severity Levels**: +- **Critical**: Security vulnerabilities, data corruption risks, system-wide failures, authentication/authorization bypass +- **High**: Feature degradation, performance bottlenecks, architecture violations, significant technical debt +- **Medium**: Code smells, minor performance issues, style inconsistencies, maintainability concerns +- **Low**: Documentation gaps, minor refactoring opportunities, cosmetic issues + +**Iteration Trigger**: +- Critical findings > 0 OR +- High findings > 5 OR +- Critical files count > 0 + +## Orchestrator Responsibilities + +- Launch 7 @cli-explore-agent instances simultaneously (Deep Scan mode) +- Pass dimension-specific context (template, timeout, custom focus, **target files**) +- Monitor completion via review-progress.json updates +- TodoWrite updates: Mark dimensions as completed +- CLI tool fallback: Gemini → Qwen → Codex (on error/timeout) + +## Agent Output Schemas + +**Agent-produced JSON files follow standardized schemas**: + +1. **Dimension Results** (cli-explore-agent output from parallel reviews) + - Schema: `~/.claude/workflows/cli-templates/schemas/review-dimension-results-schema.json` + - Output: `{output-dir}/dimensions/{dimension}.json` + - Contains: findings array, summary statistics, cross_references + +2. **Deep-Dive Results** (cli-explore-agent output from iterations) + - Schema: `~/.claude/workflows/cli-templates/schemas/review-deep-dive-results-schema.json` + - Output: `{output-dir}/iterations/iteration-{N}-finding-{uuid}.json` + - Contains: root_cause, remediation_plan, impact_assessment, reassessed_severity + +## Review Agent Invocation Template + +### Module Mode + +**Review Agent** (parallel execution, 7 instances): + +```javascript +Task( + subagent_type="cli-explore-agent", + run_in_background=false, + description=`Execute ${dimension} review analysis via Deep Scan`, + prompt=` + ## Task Objective + Conduct comprehensive ${dimension} code exploration and analysis using Deep Scan mode (Bash + Gemini dual-source strategy) for specified module files + + ## Analysis Mode Selection + Use **Deep Scan mode** for this review: + - Phase 1: Bash structural scan for standard patterns (classes, functions, imports) + - Phase 2: Gemini semantic analysis for design intent, non-standard patterns, ${dimension}-specific concerns + - Phase 3: Synthesis with attribution (bash-discovered vs gemini-discovered findings) + + ## MANDATORY FIRST STEPS (Execute by Agent) + **You (cli-explore-agent) MUST execute these steps in order:** + 1. Read review state: ${reviewStateJsonPath} + 2. Get target files: Read resolved_files from review-state.json + 3. Validate file access: bash(ls -la ${targetFiles.join(' ')}) + 4. Execute: cat ~/.claude/workflows/cli-templates/schemas/review-dimension-results-schema.json (get output schema reference) + 5. Read: .workflow/project-tech.json (technology stack and architecture context) + 6. Read: .workflow/project-guidelines.json (user-defined constraints and conventions to validate against) + + ## Review Context + - Review Type: module (independent) + - Review Dimension: ${dimension} + - Review ID: ${reviewId} + - Target Pattern: ${targetPattern} + - Resolved Files: ${resolvedFiles.length} files + - Output Directory: ${outputDir} + + ## CLI Configuration + - Tool Priority: gemini → qwen → codex (fallback chain) + - Custom Focus: ${customFocus || 'Standard dimension analysis'} + - Mode: analysis (READ-ONLY) + - Context Pattern: ${targetFiles.map(f => `@${f}`).join(' ')} + + ## Expected Deliverables + + **Schema Reference**: Schema obtained in MANDATORY FIRST STEPS step 4, follow schema exactly + + 1. Dimension Results JSON: ${outputDir}/dimensions/${dimension}.json + + **⚠️ CRITICAL JSON STRUCTURE REQUIREMENTS**: + + Root structure MUST be array: \`[{ ... }]\` NOT \`{ ... }\` + + Required top-level fields: + - dimension, review_id, analysis_timestamp (NOT timestamp/analyzed_at) + - cli_tool_used (gemini|qwen|codex), model, analysis_duration_ms + - summary (FLAT structure), findings, cross_references + + Summary MUST be FLAT (NOT nested by_severity): + \`{ "total_findings": N, "critical": N, "high": N, "medium": N, "low": N, "files_analyzed": N, "lines_reviewed": N }\` + + Finding required fields: + - id: format \`{dim}-{seq}-{uuid8}\` e.g., \`sec-001-a1b2c3d4\` (lowercase) + - severity: lowercase only (critical|high|medium|low) + - snippet (NOT code_snippet), impact (NOT exploit_scenario) + - metadata, iteration (0), status (pending_remediation), cross_references + + 2. Analysis Report: ${outputDir}/reports/${dimension}-analysis.md + - Human-readable summary with recommendations + - Grouped by severity: critical → high → medium → low + - Include file:line references for all findings + + 3. CLI Output Log: ${outputDir}/reports/${dimension}-cli-output.txt + - Raw CLI tool output for debugging + - Include full analysis text + + ## Dimension-Specific Guidance + ${getDimensionGuidance(dimension)} + + ## Success Criteria + - [ ] Schema obtained via cat review-dimension-results-schema.json + - [ ] All target files analyzed for ${dimension} concerns + - [ ] All findings include file:line references with code snippets + - [ ] Severity assessment follows established criteria (see reference) + - [ ] Recommendations are actionable with code examples + - [ ] JSON output follows schema exactly + - [ ] Report is comprehensive and well-organized + ` +) +``` + +### Session Mode + +**Review Agent** (parallel execution, 7 instances): + +```javascript +Task( + subagent_type="cli-explore-agent", + run_in_background=false, + description=`Execute ${dimension} review analysis via Deep Scan`, + prompt=` + ## Task Objective + Conduct comprehensive ${dimension} code exploration and analysis using Deep Scan mode (Bash + Gemini dual-source strategy) for completed implementation in session ${sessionId} + + ## Analysis Mode Selection + Use **Deep Scan mode** for this review: + - Phase 1: Bash structural scan for standard patterns (classes, functions, imports) + - Phase 2: Gemini semantic analysis for design intent, non-standard patterns, ${dimension}-specific concerns + - Phase 3: Synthesis with attribution (bash-discovered vs gemini-discovered findings) + + ## MANDATORY FIRST STEPS (Execute by Agent) + **You (cli-explore-agent) MUST execute these steps in order:** + 1. Read session metadata: ${sessionMetadataPath} + 2. Read completed task summaries: bash(find ${summariesDir} -name "IMPL-*.md" -type f) + 3. Get changed files: bash(cd ${workflowDir} && git log --since="${sessionCreatedAt}" --name-only --pretty=format: | sort -u) + 4. Read review state: ${reviewStateJsonPath} + 5. Execute: cat ~/.claude/workflows/cli-templates/schemas/review-dimension-results-schema.json (get output schema reference) + 6. Read: .workflow/project-tech.json (technology stack and architecture context) + 7. Read: .workflow/project-guidelines.json (user-defined constraints and conventions to validate against) + + ## Session Context + - Session ID: ${sessionId} + - Review Dimension: ${dimension} + - Review ID: ${reviewId} + - Implementation Phase: Complete (all tests passing) + - Output Directory: ${outputDir} + + ## CLI Configuration + - Tool Priority: gemini → qwen → codex (fallback chain) + - Template: ~/.claude/workflows/cli-templates/prompts/analysis/${dimensionTemplate} + - Custom Focus: ${customFocus || 'Standard dimension analysis'} + - Timeout: ${timeout}ms + - Mode: analysis (READ-ONLY) + + ## Expected Deliverables + + **Schema Reference**: Schema obtained in MANDATORY FIRST STEPS step 5, follow schema exactly + + 1. Dimension Results JSON: ${outputDir}/dimensions/${dimension}.json + + **⚠️ CRITICAL JSON STRUCTURE REQUIREMENTS**: + + Root structure MUST be array: \`[{ ... }]\` NOT \`{ ... }\` + + Required top-level fields: + - dimension, review_id, analysis_timestamp (NOT timestamp/analyzed_at) + - cli_tool_used (gemini|qwen|codex), model, analysis_duration_ms + - summary (FLAT structure), findings, cross_references + + Summary MUST be FLAT (NOT nested by_severity): + \`{ "total_findings": N, "critical": N, "high": N, "medium": N, "low": N, "files_analyzed": N, "lines_reviewed": N }\` + + Finding required fields: + - id: format \`{dim}-{seq}-{uuid8}\` e.g., \`sec-001-a1b2c3d4\` (lowercase) + - severity: lowercase only (critical|high|medium|low) + - snippet (NOT code_snippet), impact (NOT exploit_scenario) + - metadata, iteration (0), status (pending_remediation), cross_references + + 2. Analysis Report: ${outputDir}/reports/${dimension}-analysis.md + - Human-readable summary with recommendations + - Grouped by severity: critical → high → medium → low + - Include file:line references for all findings + + 3. CLI Output Log: ${outputDir}/reports/${dimension}-cli-output.txt + - Raw CLI tool output for debugging + - Include full analysis text + + ## Dimension-Specific Guidance + ${getDimensionGuidance(dimension)} + + ## Success Criteria + - [ ] Schema obtained via cat review-dimension-results-schema.json + - [ ] All changed files analyzed for ${dimension} concerns + - [ ] All findings include file:line references with code snippets + - [ ] Severity assessment follows established criteria (see reference) + - [ ] Recommendations are actionable with code examples + - [ ] JSON output follows schema exactly + - [ ] Report is comprehensive and well-organized + ` +) +``` + +## Deep-Dive Agent Invocation Template + +**Deep-Dive Agent** (iteration execution): + +```javascript +Task( + subagent_type="cli-explore-agent", + run_in_background=false, + description=`Deep-dive analysis for critical finding: ${findingTitle} via Dependency Map + Deep Scan`, + prompt=` + ## Task Objective + Perform focused root cause analysis using Dependency Map mode (for impact analysis) + Deep Scan mode (for semantic understanding) to generate comprehensive remediation plan for critical ${dimension} issue + + ## Analysis Mode Selection + Use **Dependency Map mode** first to understand dependencies: + - Build dependency graph around ${file} to identify affected components + - Detect circular dependencies or tight coupling related to this finding + - Calculate change risk scores for remediation impact + + Then apply **Deep Scan mode** for semantic analysis: + - Understand design intent and architectural context + - Identify non-standard patterns or implicit dependencies + - Extract remediation insights from code structure + + ## Finding Context + - Finding ID: ${findingId} + - Original Dimension: ${dimension} + - Title: ${findingTitle} + - File: ${file}:${line} + - Severity: ${severity} + - Category: ${category} + - Original Description: ${description} + - Iteration: ${iteration} + + ## MANDATORY FIRST STEPS (Execute by Agent) + **You (cli-explore-agent) MUST execute these steps in order:** + 1. Read original finding: ${dimensionJsonPath} + 2. Read affected file: ${file} + 3. Identify related code: bash(grep -r "import.*${basename(file)}" ${projectDir}/src --include="*.ts") + 4. Read test files: bash(find ${projectDir}/tests -name "*${basename(file, '.ts')}*" -type f) + 5. Execute: cat ~/.claude/workflows/cli-templates/schemas/review-deep-dive-results-schema.json (get output schema reference) + 6. Read: .workflow/project-tech.json (technology stack and architecture context) + 7. Read: .workflow/project-guidelines.json (user-defined constraints for remediation compliance) + + ## CLI Configuration + - Tool Priority: gemini → qwen → codex + - Template: ~/.claude/workflows/cli-templates/prompts/analysis/01-diagnose-bug-root-cause.txt + - Mode: analysis (READ-ONLY) + + ## Expected Deliverables + + **Schema Reference**: Schema obtained in MANDATORY FIRST STEPS step 5, follow schema exactly + + 1. Deep-Dive Results JSON: ${outputDir}/iterations/iteration-${iteration}-finding-${findingId}.json + + **⚠️ CRITICAL JSON STRUCTURE REQUIREMENTS**: + + Root structure MUST be array: \`[{ ... }]\` NOT \`{ ... }\` + + Required top-level fields: + - finding_id, dimension, iteration, analysis_timestamp + - cli_tool_used, model, analysis_duration_ms + - original_finding, root_cause, remediation_plan + - impact_assessment, reassessed_severity, confidence_score, cross_references + + All nested objects must follow schema exactly - read schema for field names + + 2. Analysis Report: ${outputDir}/reports/deep-dive-${iteration}-${findingId}.md + - Detailed root cause analysis + - Step-by-step remediation plan + - Impact assessment and rollback strategy + + ## Success Criteria + - [ ] Schema obtained via cat review-deep-dive-results-schema.json + - [ ] Root cause clearly identified with supporting evidence + - [ ] Remediation plan is step-by-step actionable with exact file:line references + - [ ] Each step includes specific commands and validation tests + - [ ] Impact fully assessed (files, tests, breaking changes, dependencies) + - [ ] Severity re-evaluation justified with evidence + - [ ] Confidence score accurately reflects certainty of analysis + - [ ] JSON output follows schema exactly + - [ ] References include project-specific and external documentation + ` +) +``` + +## Dimension Guidance Reference + +```javascript +function getDimensionGuidance(dimension) { + const guidance = { + security: ` + Focus Areas: + - Input validation and sanitization + - Authentication and authorization mechanisms + - Data encryption (at-rest and in-transit) + - SQL/NoSQL injection vulnerabilities + - XSS, CSRF, and other web vulnerabilities + - Sensitive data exposure + - Access control and privilege escalation + + Severity Criteria: + - Critical: Authentication bypass, SQL injection, RCE, sensitive data exposure + - High: Missing authorization checks, weak encryption, exposed secrets + - Medium: Missing input validation, insecure defaults, weak password policies + - Low: Security headers missing, verbose error messages, outdated dependencies + `, + architecture: ` + Focus Areas: + - Layering and separation of concerns + - Coupling and cohesion + - Design pattern adherence + - Dependency management + - Scalability and extensibility + - Module boundaries + - API design consistency + + Severity Criteria: + - Critical: Circular dependencies, god objects, tight coupling across layers + - High: Violated architectural principles, scalability bottlenecks + - Medium: Missing abstractions, inconsistent patterns, suboptimal design + - Low: Minor coupling issues, documentation gaps, naming inconsistencies + `, + quality: ` + Focus Areas: + - Code duplication + - Complexity (cyclomatic, cognitive) + - Naming conventions + - Error handling patterns + - Code readability + - Comment quality + - Dead code + + Severity Criteria: + - Critical: Severe complexity (CC > 20), massive duplication (>50 lines) + - High: High complexity (CC > 10), significant duplication, poor error handling + - Medium: Moderate complexity (CC > 5), naming issues, code smells + - Low: Minor duplication, documentation gaps, cosmetic issues + `, + 'action-items': ` + Focus Areas: + - Requirements coverage verification + - Acceptance criteria met + - Documentation completeness + - Deployment readiness + - Missing functionality + - Test coverage gaps + - Configuration management + + Severity Criteria: + - Critical: Core requirements not met, deployment blockers + - High: Significant functionality missing, acceptance criteria not met + - Medium: Minor requirements gaps, documentation incomplete + - Low: Nice-to-have features missing, minor documentation gaps + `, + performance: ` + Focus Areas: + - N+1 query problems + - Inefficient algorithms (O(n^2) where O(n log n) possible) + - Memory leaks + - Blocking operations on main thread + - Missing caching opportunities + - Resource usage (CPU, memory, network) + - Database query optimization + + Severity Criteria: + - Critical: Memory leaks, O(n^2) in hot path, blocking main thread + - High: N+1 queries, missing indexes, inefficient algorithms + - Medium: Suboptimal caching, unnecessary computations, lazy loading issues + - Low: Minor optimization opportunities, redundant operations + `, + maintainability: ` + Focus Areas: + - Technical debt indicators + - Magic numbers and hardcoded values + - Long methods (>50 lines) + - Large classes (>500 lines) + - Dead code and commented code + - Code documentation + - Test coverage + + Severity Criteria: + - Critical: Massive methods (>200 lines), severe technical debt blocking changes + - High: Large methods (>100 lines), significant dead code, undocumented complex logic + - Medium: Magic numbers, moderate technical debt, missing tests + - Low: Minor refactoring opportunities, cosmetic improvements + `, + 'best-practices': ` + Focus Areas: + - Framework conventions adherence + - Language idioms + - Anti-patterns + - Deprecated API usage + - Coding standards compliance + - Error handling patterns + - Logging and monitoring + + Severity Criteria: + - Critical: Severe anti-patterns, deprecated APIs with security risks + - High: Major convention violations, poor error handling, missing logging + - Medium: Minor anti-patterns, style inconsistencies, suboptimal patterns + - Low: Cosmetic style issues, minor convention deviations + ` + }; + + return guidance[dimension] || 'Standard code review analysis'; +} +``` + +## Output + +- Files: `dimensions/{dimension}.json`, `reports/{dimension}-analysis.md`, `reports/{dimension}-cli-output.txt` +- TaskUpdate: Mark Phase 2 completed, Phase 3 in_progress + +## Next Phase + +Return to orchestrator, then auto-continue to [Phase 3: Aggregation](03-aggregation.md). diff --git a/.claude/skills/review-cycle/phases/03-aggregation.md b/.claude/skills/review-cycle/phases/03-aggregation.md new file mode 100644 index 00000000..1895a182 --- /dev/null +++ b/.claude/skills/review-cycle/phases/03-aggregation.md @@ -0,0 +1,74 @@ +# Phase 3: Aggregation + +> Source: Shared from `commands/workflow/review-session-cycle.md` + `commands/workflow/review-module-cycle.md` Phase 3 + +## Overview + +Load all dimension results, calculate severity distribution, identify cross-cutting concerns, and decide whether to enter iterative deep-dive (Phase 4) or proceed to completion (Phase 5). + +## Execution Steps + +### Step 3.1: Load Dimension Results + +- Load all dimension JSON files from `{outputDir}/dimensions/` +- Parse each file following review-dimension-results-schema.json +- Handle missing files gracefully (log warning, skip) + +### Step 3.2: Calculate Severity Distribution + +- Count findings by severity level: critical, high, medium, low +- Store in review-state.json `severity_distribution` field + +### Step 3.3: Cross-Cutting Concern Detection + +**Cross-Cutting Concern Detection**: +1. Files appearing in 3+ dimensions = **Critical Files** +2. Same issue pattern across dimensions = **Systemic Issue** +3. Severity clustering in specific files = **Hotspots** + +### Step 3.4: Deep-Dive Selection + +**Deep-Dive Selection Criteria**: +- All critical severity findings (priority 1) +- Top 3 high-severity findings in critical files (priority 2) +- Max 5 findings per iteration (prevent overwhelm) + +### Step 3.5: Decision Logic + +**Iteration Trigger**: +- Critical findings > 0 OR +- High findings > 5 OR +- Critical files count > 0 + +If any trigger condition is met, proceed to Phase 4 (Iterative Deep-Dive). Otherwise, skip to Phase 5 (Completion). + +### Step 3.6: Update State + +- Update review-state.json with aggregation results +- Update review-progress.json + +**Phase 3 Orchestrator Responsibilities**: +- Load all dimension JSON files from dimensions/ +- Calculate severity distribution: Count by critical/high/medium/low +- Identify cross-cutting concerns: Files in 3+ dimensions +- Select deep-dive findings: Critical + high in critical files (max 5) +- Decision logic: Iterate if critical > 0 OR high > 5 OR critical files exist +- Update review-state.json with aggregation results + +## Severity Assessment Reference + +**Severity Levels**: +- **Critical**: Security vulnerabilities, data corruption risks, system-wide failures, authentication/authorization bypass +- **High**: Feature degradation, performance bottlenecks, architecture violations, significant technical debt +- **Medium**: Code smells, minor performance issues, style inconsistencies, maintainability concerns +- **Low**: Documentation gaps, minor refactoring opportunities, cosmetic issues + +## Output + +- Variables: severityDistribution, criticalFiles, deepDiveFindings, shouldIterate (boolean) +- State: review-state.json updated with aggregation results + +## Next Phase + +- If shouldIterate: [Phase 4: Iterative Deep-Dive](04-iterative-deep-dive.md) +- Else: [Phase 5: Review Completion](05-review-completion.md) diff --git a/.claude/skills/review-cycle/phases/04-iterative-deep-dive.md b/.claude/skills/review-cycle/phases/04-iterative-deep-dive.md new file mode 100644 index 00000000..91675edd --- /dev/null +++ b/.claude/skills/review-cycle/phases/04-iterative-deep-dive.md @@ -0,0 +1,278 @@ +# Phase 4: Iterative Deep-Dive + +> Source: Shared from `commands/workflow/review-session-cycle.md` + `commands/workflow/review-module-cycle.md` Phase 4 + +## Overview + +Perform focused root cause analysis on critical findings. Select up to 5 findings per iteration, launch deep-dive agents, re-assess severity, and loop back to aggregation if needed. + +## Prerequisites + +- Phase 3 determined shouldIterate = true +- Available: severityDistribution, criticalFiles, deepDiveFindings + +## Execution Steps + +### Step 4.1: Check Iteration Limit + +- Check `current_iteration` < `max_iterations` (default 3) +- If exceeded: Log iteration limit reached, skip to Phase 5 +- Default iterations: 1 (deep-dive runs once; use --max-iterations=0 to skip entirely) + +### Step 4.2: Select Findings for Deep-Dive + +**Deep-Dive Selection Criteria**: +- All critical severity findings (priority 1) +- Top 3 high-severity findings in critical files (priority 2) +- Max 5 findings per iteration (prevent overwhelm) + +**Selection algorithm**: +1. Collect all findings with severity = critical -> add to selection +2. If selection < 5: add high-severity findings from critical files (files in 3+ dimensions), sorted by dimension count descending +3. Cap at 5 total findings + +### Step 4.3: Launch Deep-Dive Agents + +- Launch cli-explore-agent for each selected finding +- Use Dependency Map + Deep Scan mode +- Each agent runs independently (can be launched in parallel) +- Tool priority: gemini -> qwen -> codex (fallback on error/timeout) + +### Step 4.4: Collect Results + +- Parse iteration JSON files from `{outputDir}/iterations/iteration-{N}-finding-{uuid}.json` +- Extract reassessed severities from each result +- Collect remediation plans and impact assessments +- Handle agent failures gracefully (log warning, mark finding as unanalyzed) + +### Step 4.5: Re-Aggregate + +- Update severity distribution based on reassessments +- Record iteration in review-state.json `iterations[]` array: + +```json +{ + "iteration": 1, + "findings_analyzed": ["uuid-1", "uuid-2"], + "findings_resolved": 1, + "findings_escalated": 1, + "severity_change": { + "before": {"critical": 2, "high": 5, "medium": 12, "low": 8}, + "after": {"critical": 1, "high": 6, "medium": 12, "low": 8} + }, + "timestamp": "2025-01-25T14:30:00Z" +} +``` + +- Increment `current_iteration` in review-state.json +- Re-evaluate decision logic: Iterate if critical > 0 OR high > 5 OR critical files exist +- Loop back to Phase 3 aggregation check if conditions still met + +## Deep-Dive Agent Invocation Template + +### Module Mode + +```javascript +Task( + subagent_type="cli-explore-agent", + run_in_background=false, + description=`Deep-dive analysis for critical finding: ${findingTitle} via Dependency Map + Deep Scan`, + prompt=` + ## Task Objective + Perform focused root cause analysis using Dependency Map mode (for impact analysis) + Deep Scan mode (for semantic understanding) to generate comprehensive remediation plan for critical ${dimension} issue + + ## Analysis Mode Selection + Use **Dependency Map mode** first to understand dependencies: + - Build dependency graph around ${file} to identify affected components + - Detect circular dependencies or tight coupling related to this finding + - Calculate change risk scores for remediation impact + + Then apply **Deep Scan mode** for semantic analysis: + - Understand design intent and architectural context + - Identify non-standard patterns or implicit dependencies + - Extract remediation insights from code structure + + ## Finding Context + - Finding ID: ${findingId} + - Original Dimension: ${dimension} + - Title: ${findingTitle} + - File: ${file}:${line} + - Severity: ${severity} + - Category: ${category} + - Original Description: ${description} + - Iteration: ${iteration} + + ## MANDATORY FIRST STEPS (Execute by Agent) + **You (cli-explore-agent) MUST execute these steps in order:** + 1. Read original finding: ${dimensionJsonPath} + 2. Read affected file: ${file} + 3. Identify related code: bash(grep -r "import.*${basename(file)}" ${projectDir}/src --include="*.ts") + 4. Read test files: bash(find ${projectDir}/tests -name "*${basename(file, '.ts')}*" -type f) + 5. Execute: cat ~/.claude/workflows/cli-templates/schemas/review-deep-dive-results-schema.json (get output schema reference) + 6. Read: .workflow/project-tech.json (technology stack and architecture context) + 7. Read: .workflow/project-guidelines.json (user-defined constraints for remediation compliance) + + ## CLI Configuration + - Tool Priority: gemini → qwen → codex + - Template: ~/.claude/workflows/cli-templates/prompts/analysis/01-diagnose-bug-root-cause.txt + - Mode: analysis (READ-ONLY) + + ## Expected Deliverables + + **Schema Reference**: Schema obtained in MANDATORY FIRST STEPS step 5, follow schema exactly + + 1. Deep-Dive Results JSON: ${outputDir}/iterations/iteration-${iteration}-finding-${findingId}.json + + **⚠️ CRITICAL JSON STRUCTURE REQUIREMENTS**: + + Root structure MUST be array: \`[{ ... }]\` NOT \`{ ... }\` + + Required top-level fields: + - finding_id, dimension, iteration, analysis_timestamp + - cli_tool_used, model, analysis_duration_ms + - original_finding, root_cause, remediation_plan + - impact_assessment, reassessed_severity, confidence_score, cross_references + + All nested objects must follow schema exactly - read schema for field names + + 2. Analysis Report: ${outputDir}/reports/deep-dive-${iteration}-${findingId}.md + - Detailed root cause analysis + - Step-by-step remediation plan + - Impact assessment and rollback strategy + + ## Success Criteria + - [ ] Schema obtained via cat review-deep-dive-results-schema.json + - [ ] Root cause clearly identified with supporting evidence + - [ ] Remediation plan is step-by-step actionable with exact file:line references + - [ ] Each step includes specific commands and validation tests + - [ ] Impact fully assessed (files, tests, breaking changes, dependencies) + - [ ] Severity re-evaluation justified with evidence + - [ ] Confidence score accurately reflects certainty of analysis + - [ ] JSON output follows schema exactly + - [ ] References include project-specific and external documentation + ` +) +``` + +### Session Mode + +```javascript +Task( + subagent_type="cli-explore-agent", + run_in_background=false, + description=`Deep-dive analysis for critical finding: ${findingTitle} via Dependency Map + Deep Scan`, + prompt=` + ## Task Objective + Perform focused root cause analysis using Dependency Map mode (for impact analysis) + Deep Scan mode (for semantic understanding) to generate comprehensive remediation plan for critical ${dimension} issue + + ## Analysis Mode Selection + Use **Dependency Map mode** first to understand dependencies: + - Build dependency graph around ${file} to identify affected components + - Detect circular dependencies or tight coupling related to this finding + - Calculate change risk scores for remediation impact + + Then apply **Deep Scan mode** for semantic analysis: + - Understand design intent and architectural context + - Identify non-standard patterns or implicit dependencies + - Extract remediation insights from code structure + + ## Finding Context + - Finding ID: ${findingId} + - Original Dimension: ${dimension} + - Title: ${findingTitle} + - File: ${file}:${line} + - Severity: ${severity} + - Category: ${category} + - Original Description: ${description} + - Iteration: ${iteration} + + ## MANDATORY FIRST STEPS (Execute by Agent) + **You (cli-explore-agent) MUST execute these steps in order:** + 1. Read original finding: ${dimensionJsonPath} + 2. Read affected file: ${file} + 3. Identify related code: bash(grep -r "import.*${basename(file)}" ${workflowDir}/src --include="*.ts") + 4. Read test files: bash(find ${workflowDir}/tests -name "*${basename(file, '.ts')}*" -type f) + 5. Execute: cat ~/.claude/workflows/cli-templates/schemas/review-deep-dive-results-schema.json (get output schema reference) + 6. Read: .workflow/project-tech.json (technology stack and architecture context) + 7. Read: .workflow/project-guidelines.json (user-defined constraints for remediation compliance) + + ## CLI Configuration + - Tool Priority: gemini → qwen → codex + - Template: ~/.claude/workflows/cli-templates/prompts/analysis/01-diagnose-bug-root-cause.txt + - Timeout: 2400000ms (40 minutes) + - Mode: analysis (READ-ONLY) + + ## Expected Deliverables + + **Schema Reference**: Schema obtained in MANDATORY FIRST STEPS step 5, follow schema exactly + + 1. Deep-Dive Results JSON: ${outputDir}/iterations/iteration-${iteration}-finding-${findingId}.json + + **⚠️ CRITICAL JSON STRUCTURE REQUIREMENTS**: + + Root structure MUST be array: \`[{ ... }]\` NOT \`{ ... }\` + + Required top-level fields: + - finding_id, dimension, iteration, analysis_timestamp + - cli_tool_used, model, analysis_duration_ms + - original_finding, root_cause, remediation_plan + - impact_assessment, reassessed_severity, confidence_score, cross_references + + All nested objects must follow schema exactly - read schema for field names + + 2. Analysis Report: ${outputDir}/reports/deep-dive-${iteration}-${findingId}.md + - Detailed root cause analysis + - Step-by-step remediation plan + - Impact assessment and rollback strategy + + ## Success Criteria + - [ ] Schema obtained via cat review-deep-dive-results-schema.json + - [ ] Root cause clearly identified with supporting evidence + - [ ] Remediation plan is step-by-step actionable with exact file:line references + - [ ] Each step includes specific commands and validation tests + - [ ] Impact fully assessed (files, tests, breaking changes, dependencies) + - [ ] Severity re-evaluation justified with evidence + - [ ] Confidence score accurately reflects certainty of analysis + - [ ] JSON output follows schema exactly + - [ ] References include project-specific and external documentation + ` +) +``` + +## Key Differences Between Modes + +| Aspect | Module Mode | Session Mode | +|--------|-------------|--------------| +| MANDATORY STEP 3 | `${projectDir}/src` | `${workflowDir}/src` | +| MANDATORY STEP 4 | `${projectDir}/tests` | `${workflowDir}/tests` | +| CLI Timeout | (not specified) | 2400000ms (40 minutes) | + +## Iteration Control + +**Phase 4 Orchestrator Responsibilities**: +- Check iteration count < max_iterations (default 3) +- Launch deep-dive agents for selected findings +- Collect remediation plans and re-assessed severities +- Update severity distribution based on re-assessments +- Record iteration in review-state.json +- Loop back to aggregation if still have critical/high findings + +**Termination Conditions** (any one stops iteration): +1. `current_iteration` >= `max_iterations` +2. No critical findings remaining AND high findings <= 5 AND no critical files +3. No findings selected for deep-dive (all resolved or downgraded) + +**State Updates Per Iteration**: +- `review-state.json`: Increment `current_iteration`, append to `iterations[]`, update `severity_distribution`, set `next_action` +- `review-progress.json`: Update `deep_dive.analyzed` count, `deep_dive.percent_complete`, `phase` + +## Output + +- Files: `iterations/iteration-{N}-finding-{uuid}.json`, `reports/deep-dive-{N}-{uuid}.md` +- State: review-state.json `iterations[]` updated +- Decision: Re-enter Phase 3 aggregation or proceed to Phase 5 + +## Next Phase + +- If still has critical findings AND iterations < max: Loop to [Phase 3: Aggregation](03-aggregation.md) +- Else: [Phase 5: Review Completion](05-review-completion.md) diff --git a/.claude/skills/review-cycle/phases/05-review-completion.md b/.claude/skills/review-cycle/phases/05-review-completion.md new file mode 100644 index 00000000..6596616a --- /dev/null +++ b/.claude/skills/review-cycle/phases/05-review-completion.md @@ -0,0 +1,176 @@ +# Phase 5: Review Completion + +> Source: Shared from `commands/workflow/review-session-cycle.md` + `commands/workflow/review-module-cycle.md` Phase 5 + +## Overview + +Finalize review state, generate completion statistics, and optionally prompt for automated fix pipeline. + +## Execution Steps + +### Step 5.1: Finalize State + +**Phase 5 Orchestrator Responsibilities**: +- Finalize review-progress.json with completion statistics +- Update review-state.json with completion_time and phase=complete +- TaskUpdate completion: Mark all tasks done + +**review-state.json updates**: +```json +{ + "phase": "complete", + "completion_time": "2025-01-25T15:00:00Z", + "next_action": "none" +} +``` + +**review-progress.json updates**: +```json +{ + "phase": "complete", + "overall_percent": 100, + "completion_time": "2025-01-25T15:00:00Z", + "final_severity_distribution": { + "critical": 0, + "high": 3, + "medium": 12, + "low": 8 + } +} +``` + +### Step 5.2: Evaluate Completion Status + +**Full Success**: +- All dimensions reviewed +- Critical findings = 0 +- High findings <= 5 +- Action: Generate final report, mark phase=complete + +**Partial Success**: +- All dimensions reviewed +- Max iterations reached +- Still have critical/high findings +- Action: Generate report with warnings, recommend follow-up + +### Step 5.3: TaskUpdate Completion + +```javascript +TodoWrite({ + todos: [ + { content: "Phase 1: Discovery & Initialization", status: "completed", activeForm: "Initializing" }, + { content: "Phase 2: Parallel Reviews (7 dimensions)", status: "completed", activeForm: "Reviewing" }, + { content: " -> Security review", status: "completed", activeForm: "Analyzing security" }, + // ... other dimensions as sub-items + { content: "Phase 3: Aggregation", status: "completed", activeForm: "Aggregating" }, + { content: "Phase 4: Deep-dive", status: "completed", activeForm: "Deep-diving" }, + { content: "Phase 5: Completion", status: "completed", activeForm: "Completing" } + ] +}); +``` + +### Step 5.4: Fix Pipeline Prompt + +- Ask user: "Run automated fixes on findings? [Y/n]" +- If confirmed AND --fix flag: Continue to Phase 6 +- Display summary of findings by severity: + +``` +Review Complete - Summary: + Critical: 0 High: 3 Medium: 12 Low: 8 + Total findings: 23 + Dimensions reviewed: 7/7 + Iterations completed: 2/3 + +Run automated fixes on findings? [Y/n] +``` + +## Completion Conditions + +**Full Success**: +- All dimensions reviewed +- Critical findings = 0 +- High findings <= 5 +- Action: Generate final report, mark phase=complete + +**Partial Success**: +- All dimensions reviewed +- Max iterations reached +- Still have critical/high findings +- Action: Generate report with warnings, recommend follow-up + +## Error Handling Reference + +### Phase-Level Error Matrix + +| Phase | Error | Blocking? | Action | +|-------|-------|-----------|--------| +| Phase 1 | Invalid path pattern / Session not found | Yes | Error and exit | +| Phase 1 | No files matched / No completed tasks | Yes | Error and exit | +| Phase 1 | Files not readable / No changed files | Yes | Error and exit | +| Phase 2 | Single dimension fails | No | Log warning, continue other dimensions | +| Phase 2 | All dimensions fail | Yes | Error and exit | +| Phase 3 | Missing dimension JSON | No | Skip in aggregation, log warning | +| Phase 4 | Deep-dive agent fails | No | Skip finding, continue others | +| Phase 4 | Max iterations reached | No | Generate partial report | + +### CLI Fallback Chain + +Gemini -> Qwen -> Codex -> degraded mode + +### Fallback Triggers + +1. HTTP 429, 5xx errors, connection timeout +2. Invalid JSON output (parse error, missing required fields) +3. Low confidence score < 0.4 +4. Analysis too brief (< 100 words in report) + +### Fallback Behavior + +- On trigger: Retry with next tool in chain +- After Codex fails: Enter degraded mode (skip analysis, log error) +- Degraded mode: Continue workflow with available results + +## Best Practices + +1. **Start Specific**: Begin with focused module patterns for faster results +2. **Expand Gradually**: Add more modules based on initial findings +3. **Use Glob Wisely**: `src/auth/**` is more efficient than `src/**` with lots of irrelevant files +4. **Trust Aggregation Logic**: Auto-selection based on proven heuristics +5. **Monitor Logs**: Check reports/ directory for CLI analysis insights + +## Related Commands + +### View Review Progress + +Use `ccw view` to open the review dashboard in browser: + +```bash +ccw view +``` + +### Automated Fix Workflow + +After completing a review, use the generated findings JSON for automated fixing: + +```bash +# Step 1: Complete review (this command) +/workflow:review-module-cycle src/auth/** +# OR +/workflow:review-session-cycle + +# Step 2: Run automated fixes using dimension findings +/workflow:review-cycle-fix .workflow/active/WFS-{session-id}/.review/ +``` + +See `/workflow:review-cycle-fix` for automated fixing with smart grouping, parallel execution, and test verification. + +## Output + +- State: review-state.json (phase=complete), review-progress.json (final) +- Decision: fix pipeline or end + +## Next Phase + +- If fix requested: [Phase 6: Fix Discovery & Batching](06-fix-discovery-batching.md) +- Else: Workflow complete diff --git a/.claude/skills/review-cycle/phases/06-fix-discovery-batching.md b/.claude/skills/review-cycle/phases/06-fix-discovery-batching.md new file mode 100644 index 00000000..e46387f0 --- /dev/null +++ b/.claude/skills/review-cycle/phases/06-fix-discovery-batching.md @@ -0,0 +1,238 @@ +# Phase 6: Fix Discovery & Batching + +> Source: `commands/workflow/review-cycle-fix.md` Phase 1 + Phase 1.5 + +## Overview + +Validate fix input source, create fix session structure, and perform intelligent grouping of findings into batches for parallel planning. + +## Quick Start + +```bash +# Fix from exported findings file (session-based path) +Skill(skill="review-cycle", args="--fix .workflow/active/WFS-123/.review/fix-export-1706184622000.json") + +# Fix from review directory (auto-discovers latest export) +Skill(skill="review-cycle", args="--fix .workflow/active/WFS-123/.review/") + +# Resume interrupted fix session +Skill(skill="review-cycle", args="--fix --resume") + +# Custom max retry attempts per finding +Skill(skill="review-cycle", args="--fix .workflow/active/WFS-123/.review/ --max-iterations=5") + +# Custom batch size for parallel planning (default: 5 findings per batch) +Skill(skill="review-cycle", args="--fix .workflow/active/WFS-123/.review/ --batch-size=3") +``` + +**Fix Source**: Exported findings from review cycle dashboard +**Output Directory**: `{review-dir}/fixes/{fix-session-id}/` (within session .review/) +**Default Max Iterations**: 3 (per finding, adjustable) +**Default Batch Size**: 5 (findings per planning batch, adjustable) +**Max Parallel Agents**: 10 (concurrent planning agents) +**CLI Tools**: @cli-planning-agent (planning), @cli-execute-agent (fixing) + +## Core Concept + +Automated fix orchestrator with **parallel planning architecture**: Multiple AI agents analyze findings concurrently in batches, then coordinate parallel/serial execution. Generates fix timeline with intelligent grouping and dependency analysis, executes fixes with conservative test verification. + +**Fix Process**: +- **Batching Phase (1.5)**: Orchestrator groups findings by file+dimension similarity, creates batches +- **Planning Phase (2)**: Up to 10 agents plan batches in parallel, generate partial plans, orchestrator aggregates +- **Execution Phase (3)**: Main orchestrator coordinates agents per aggregated timeline stages +- **Parallel Efficiency**: Customizable batch size (default: 5), MAX_PARALLEL=10 agents +- **No rigid structure**: Adapts to task requirements, not bound to fixed JSON format + +**vs Manual Fixing**: +- **Manual**: Developer reviews findings one-by-one, fixes sequentially +- **Automated**: AI groups related issues, multiple agents plan in parallel, executes in optimal parallel/serial order with automatic test verification + +### Value Proposition +1. **Parallel Planning**: Multiple agents analyze findings concurrently, reducing planning time for large batches (10+ findings) +2. **Intelligent Batching**: Semantic similarity grouping ensures related findings are analyzed together +3. **Multi-stage Coordination**: Supports complex parallel + serial execution with cross-batch dependency management +4. **Conservative Safety**: Mandatory test verification with automatic rollback on failure +5. **Resume Support**: Checkpoint-based recovery for interrupted sessions + +### Orchestrator Boundary (CRITICAL) +- **ONLY command** for automated review finding fixes +- Manages: Intelligent batching (Phase 1.5), parallel planning coordination (launch N agents), plan aggregation (merge partial plans, resolve cross-batch dependencies), stage-based execution scheduling, agent scheduling, progress tracking +- Delegates: Batch planning to @cli-planning-agent, fix execution to @cli-execute-agent + +## Fix Process Overview + +``` +Phase 1: Discovery & Initialization + └─ Validate export file, create fix session structure, initialize state files + +Phase 1.5: Intelligent Grouping & Batching + ├─ Analyze findings metadata (file, dimension, severity) + ├─ Group by semantic similarity (file proximity + dimension affinity) + ├─ Create batches respecting --batch-size (default: 5) + └─ Output: Finding batches for parallel planning + +Phase 2: Parallel Planning Coordination (@cli-planning-agent × N) + ├─ Launch MAX_PARALLEL planning agents concurrently (default: 10) + ├─ Each agent processes one batch: + │ ├─ Analyze findings for patterns and dependencies + │ ├─ Group by file + dimension + root cause similarity + │ ├─ Determine execution strategy (parallel/serial/hybrid) + │ ├─ Generate fix timeline with stages + │ └─ Output: partial-plan-{batch-id}.json + ├─ Collect results from all agents + └─ Aggregate: Merge partial plans → fix-plan.json (resolve cross-batch dependencies) + +Phase 3: Execution Orchestration (Stage-based) + For each timeline stage: + ├─ Load groups for this stage + ├─ If parallel: Launch all group agents simultaneously + ├─ If serial: Execute groups sequentially + ├─ Each agent: + │ ├─ Analyze code context + │ ├─ Apply fix per strategy + │ ├─ Run affected tests + │ ├─ On test failure: Rollback, retry up to max_iterations + │ └─ On success: Commit, update fix-progress-{N}.json + └─ Advance to next stage + +Phase 4: Completion & Aggregation + └─ Aggregate results → Generate fix-summary.md → Update history → Output summary + +Phase 5: Session Completion (Optional) + └─ If all fixes successful → Prompt to complete workflow session +``` + +## Agent Roles + +| Agent | Responsibility | +|-------|---------------| +| **Orchestrator** | Input validation, session management, intelligent batching (Phase 1.5), parallel planning coordination (launch N agents), plan aggregation (merge partial plans, resolve cross-batch dependencies), stage-based execution scheduling, progress tracking, result aggregation | +| **@cli-planning-agent** | Batch findings analysis, intelligent grouping (file+dimension+root cause), execution strategy determination (parallel/serial/hybrid), timeline generation with dependency mapping, partial plan output | +| **@cli-execute-agent** | Fix execution per group, code context analysis, Edit tool operations, test verification, git rollback on failure, completion JSON generation | + +## Parallel Planning Architecture + +**Batch Processing Strategy**: + +| Phase | Agent Count | Input | Output | Purpose | +|-------|-------------|-------|--------|---------| +| **Batching (1.5)** | Orchestrator | All findings | Finding batches | Semantic grouping by file+dimension, respecting --batch-size | +| **Planning (2)** | N agents (≤10) | 1 batch each | partial-plan-{batch-id}.json | Analyze batch in parallel, generate execution groups and timeline | +| **Aggregation (2)** | Orchestrator | All partial plans | fix-plan.json | Merge timelines, resolve cross-batch dependencies | +| **Execution (3)** | M agents (dynamic) | 1 group each | fix-progress-{N}.json | Execute fixes per aggregated plan with test verification | + +**Benefits**: +- **Speed**: N agents plan concurrently, reducing planning time for large batches +- **Scalability**: MAX_PARALLEL=10 prevents resource exhaustion +- **Flexibility**: Batch size customizable via --batch-size (default: 5) +- **Isolation**: Each planning agent focuses on related findings (semantic grouping) +- **Reusable**: Aggregated plan can be re-executed without re-planning + +## Intelligent Grouping Strategy + +**Three-Level Grouping**: + +```javascript +// Level 1: Primary grouping by file + dimension +{file: "auth.ts", dimension: "security"} → Group A +{file: "auth.ts", dimension: "quality"} → Group B +{file: "query-builder.ts", dimension: "security"} → Group C + +// Level 2: Secondary grouping by root cause similarity +Group A findings → Semantic similarity analysis (threshold 0.7) + → Sub-group A1: "missing-input-validation" (findings 1, 2) + → Sub-group A2: "insecure-crypto" (finding 3) + +// Level 3: Dependency analysis +Sub-group A1 creates validation utilities +Sub-group C4 depends on those utilities +→ A1 must execute before C4 (serial stage dependency) +``` + +**Similarity Computation**: +- Combine: `description + recommendation + category` +- Vectorize: TF-IDF or LLM embedding +- Cluster: Greedy algorithm with cosine similarity > 0.7 + +## Phase 1: Discovery & Initialization (Orchestrator) + +**Phase 1 Orchestrator Responsibilities**: +- Input validation: Check export file exists and is valid JSON +- Auto-discovery: If review-dir provided, find latest `*-fix-export.json` +- Session creation: Generate fix-session-id (`fix-{timestamp}`) +- Directory structure: Create `{review-dir}/fixes/{fix-session-id}/` with subdirectories +- State files: Initialize active-fix-session.json (session marker) +- TodoWrite initialization: Set up 5-phase tracking (including Phase 1.5) + +## Phase 1.5: Intelligent Grouping & Batching (Orchestrator) + +- Load all findings metadata (id, file, dimension, severity, title) +- Semantic similarity analysis: + - Primary: Group by file proximity (same file or related modules) + - Secondary: Group by dimension affinity (same review dimension) + - Tertiary: Analyze title/description similarity (root cause clustering) +- Create batches respecting --batch-size (default: 5 findings per batch) +- Balance workload: Distribute high-severity findings across batches +- Output: Array of finding batches for parallel planning + +```javascript +// Load findings +const findings = JSON.parse(Read(exportFile)); +const batchSize = flags.batchSize || 5; + +// Semantic similarity analysis: group by file+dimension +const batches = []; +const grouped = new Map(); // key: "${file}:${dimension}" + +for (const finding of findings) { + const key = `${finding.file || 'unknown'}:${finding.dimension || 'general'}`; + if (!grouped.has(key)) grouped.set(key, []); + grouped.get(key).push(finding); +} + +// Create batches respecting batchSize +for (const [key, group] of grouped) { + while (group.length > 0) { + const batch = group.splice(0, batchSize); + batches.push({ + batch_id: batches.length + 1, + findings: batch, + metadata: { primary_file: batch[0].file, primary_dimension: batch[0].dimension } + }); + } +} + +console.log(`Created ${batches.length} batches (${batchSize} findings per batch)`); +``` + +## Output File Structure + +``` +.workflow/active/WFS-{session-id}/.review/ +├── fix-export-{timestamp}.json # Exported findings (input) +└── fixes/{fix-session-id}/ + ├── partial-plan-1.json # Batch 1 partial plan (planning agent 1 output) + ├── partial-plan-2.json # Batch 2 partial plan (planning agent 2 output) + ├── partial-plan-N.json # Batch N partial plan (planning agent N output) + ├── fix-plan.json # Aggregated execution plan (orchestrator merges partials) + ├── fix-progress-1.json # Group 1 progress (planning agent init → agent updates) + ├── fix-progress-2.json # Group 2 progress (planning agent init → agent updates) + ├── fix-progress-3.json # Group 3 progress (planning agent init → agent updates) + ├── fix-summary.md # Final report (orchestrator generates) + ├── active-fix-session.json # Active session marker + └── fix-history.json # All sessions history +``` + +**File Producers**: +- **Orchestrator**: Batches findings (Phase 1.5), aggregates partial plans → `fix-plan.json` (Phase 2), launches parallel planning agents +- **Planning Agents (N)**: Each outputs `partial-plan-{batch-id}.json` + initializes `fix-progress-*.json` for assigned groups +- **Execution Agents (M)**: Update assigned `fix-progress-{N}.json` in real-time + +## Output + +- Variables: batches (array), fixSessionId, sessionDir +- Files: active-fix-session.json, directory structure created + +## Next Phase + +Return to orchestrator, then auto-continue to [Phase 7: Fix Parallel Planning](07-fix-parallel-planning.md). diff --git a/.claude/skills/review-cycle/phases/07-fix-parallel-planning.md b/.claude/skills/review-cycle/phases/07-fix-parallel-planning.md new file mode 100644 index 00000000..632e72ac --- /dev/null +++ b/.claude/skills/review-cycle/phases/07-fix-parallel-planning.md @@ -0,0 +1,199 @@ +# Phase 7: Fix Parallel Planning + +> Source: `commands/workflow/review-cycle-fix.md` Phase 2 + +## Overview +Launch N planning agents (up to MAX_PARALLEL=10) to analyze finding batches concurrently. Each agent outputs a partial plan. Orchestrator aggregates partial plans into unified fix-plan.json. + +## Execution Strategy Determination + +**Strategy Types**: + +| Strategy | When to Use | Stage Structure | +|----------|-------------|-----------------| +| **Parallel** | All groups independent, different files | Single stage, all groups in parallel | +| **Serial** | Strong dependencies, shared resources | Multiple stages, one group per stage | +| **Hybrid** | Mixed dependencies | Multiple stages, parallel within stages | + +**Dependency Detection**: +- Shared file modifications +- Utility creation + usage patterns +- Test dependency chains +- Risk level clustering (high-risk groups isolated) + +## Phase 2: Parallel Planning Coordination (Orchestrator) + +```javascript +const MAX_PARALLEL = 10; +const partialPlans = []; + +// Process batches in chunks of MAX_PARALLEL +for (let i = 0; i < batches.length; i += MAX_PARALLEL) { + const chunk = batches.slice(i, i + MAX_PARALLEL); + const taskIds = []; + + // Launch agents in parallel (run_in_background=true) + for (const batch of chunk) { + const taskId = Task({ + subagent_type: "cli-planning-agent", + run_in_background: true, + description: `Plan batch ${batch.batch_id}: ${batch.findings.length} findings`, + prompt: planningPrompt(batch) // See Planning Agent template below + }); + taskIds.push({ taskId, batch }); + } + + console.log(`Launched ${taskIds.length} planning agents...`); + + // Collect results from this chunk (blocking) + for (const { taskId, batch } of taskIds) { + const result = TaskOutput({ task_id: taskId, block: true }); + const partialPlan = JSON.parse(Read(`${sessionDir}/partial-plan-${batch.batch_id}.json`)); + partialPlans.push(partialPlan); + updateTodo(`Batch ${batch.batch_id}`, 'completed'); + } +} + +// Aggregate partial plans → fix-plan.json +let groupCounter = 1; +const groupIdMap = new Map(); + +for (const partial of partialPlans) { + for (const group of partial.groups) { + const newGroupId = `G${groupCounter}`; + groupIdMap.set(`${partial.batch_id}:${group.group_id}`, newGroupId); + aggregatedPlan.groups.push({ ...group, group_id: newGroupId, progress_file: `fix-progress-${groupCounter}.json` }); + groupCounter++; + } +} + +// Merge timelines, resolve cross-batch conflicts (shared files → serialize) +let stageCounter = 1; +for (const partial of partialPlans) { + for (const stage of partial.timeline) { + aggregatedPlan.timeline.push({ + ...stage, stage_id: stageCounter, + groups: stage.groups.map(gid => groupIdMap.get(`${partial.batch_id}:${gid}`)) + }); + stageCounter++; + } +} + +// Write aggregated plan + initialize progress files +Write(`${sessionDir}/fix-plan.json`, JSON.stringify(aggregatedPlan, null, 2)); +for (let i = 1; i <= aggregatedPlan.groups.length; i++) { + Write(`${sessionDir}/fix-progress-${i}.json`, JSON.stringify(initProgressFile(aggregatedPlan.groups[i-1]), null, 2)); +} +``` + +## Planning Agent Template (Batch Mode) + +```javascript +Task({ + subagent_type: "cli-planning-agent", + run_in_background: true, + description: `Plan batch ${batch.batch_id}: ${batch.findings.length} findings`, + prompt: ` +## Task Objective +Analyze code review findings in batch ${batch.batch_id} and generate **partial** execution plan. + +## Input Data +Review Session: ${reviewId} +Fix Session ID: ${fixSessionId} +Batch ID: ${batch.batch_id} +Batch Findings: ${batch.findings.length} + +Findings: +${JSON.stringify(batch.findings, null, 2)} + +Project Context: +- Structure: ${projectStructure} +- Test Framework: ${testFramework} +- Git Status: ${gitStatus} + +## Output Requirements + +### 1. partial-plan-${batch.batch_id}.json +Generate partial execution plan with structure: +{ + "batch_id": ${batch.batch_id}, + "groups": [...], // Groups created from batch findings (use local IDs: G1, G2, ...) + "timeline": [...], // Local timeline for this batch only + "metadata": { + "findings_count": ${batch.findings.length}, + "groups_count": N, + "created_at": "ISO-8601-timestamp" + } +} + +**Key Generation Rules**: +- **Groups**: Create groups with local IDs (G1, G2, ...) using intelligent grouping (file+dimension+root cause) +- **Timeline**: Define stages for this batch only (local dependencies within batch) +- **Progress Files**: DO NOT generate fix-progress-*.json here (orchestrator handles after aggregation) + +## Analysis Requirements + +### Intelligent Grouping Strategy +Group findings using these criteria (in priority order): + +1. **File Proximity**: Findings in same file or related files +2. **Dimension Affinity**: Same dimension (security, performance, etc.) +3. **Root Cause Similarity**: Similar underlying issues +4. **Fix Approach Commonality**: Can be fixed with similar approach + +**Grouping Guidelines**: +- Optimal group size: 2-5 findings per group +- Avoid cross-cutting concerns in same group +- Consider test isolation (different test suites → different groups) +- Balance workload across groups for parallel execution + +### Execution Strategy Determination (Local Only) + +**Parallel Mode**: Use when groups are independent, no shared files +**Serial Mode**: Use when groups have dependencies or shared resources +**Hybrid Mode**: Use for mixed dependency graphs (recommended for most cases) + +**Dependency Analysis**: +- Identify shared files between groups +- Detect test dependency chains +- Evaluate risk of concurrent modifications + +### Risk Assessment + +For each group, evaluate: +- **Complexity**: Based on code structure, file size, existing tests +- **Impact Scope**: Number of files affected, API surface changes +- **Rollback Feasibility**: Ease of reverting changes if tests fail + +### Test Strategy + +For each group, determine: +- **Test Pattern**: Glob pattern matching affected tests +- **Pass Criteria**: All tests must pass (100% pass rate) +- **Test Command**: Infer from project (package.json, pytest.ini, etc.) + +## Output Files + +Write to ${sessionDir}: +- ./partial-plan-${batch.batch_id}.json + +## Quality Checklist + +Before finalizing outputs: +- All batch findings assigned to exactly one group +- Group dependencies (within batch) correctly identified +- Timeline stages respect local dependencies +- Test patterns are valid and specific +- Risk assessments are realistic + ` +}) +``` + +## Output + +- Files: `partial-plan-{batch-id}.json` (per agent), `fix-plan.json` (aggregated), `fix-progress-*.json` (initialized) +- TaskUpdate: Mark Phase 7 completed, Phase 8 in_progress + +## Next Phase + +Return to orchestrator, then auto-continue to [Phase 8: Fix Execution](08-fix-execution.md). diff --git a/.claude/skills/review-cycle/phases/08-fix-execution.md b/.claude/skills/review-cycle/phases/08-fix-execution.md new file mode 100644 index 00000000..19299ea1 --- /dev/null +++ b/.claude/skills/review-cycle/phases/08-fix-execution.md @@ -0,0 +1,221 @@ +# Phase 8: Fix Execution + +> Source: `commands/workflow/review-cycle-fix.md` Phase 3 + +## Overview +Stage-based execution using aggregated fix-plan.json timeline. Each group gets a cli-execute-agent that applies fixes, runs tests, and commits on success or rolls back on failure. + +## Conservative Test Verification + +**Test Strategy** (per fix): + +```javascript +// 1. Identify affected tests +const testPattern = identifyTestPattern(finding.file); +// e.g., "tests/auth/**/*.test.*" for src/auth/service.ts + +// 2. Run tests +const result = await runTests(testPattern); + +// 3. Evaluate +if (result.passRate < 100%) { + // Rollback + await gitCheckout(finding.file); + + // Retry with failure context + if (attempts < maxIterations) { + const fixContext = analyzeFailure(result.stderr); + regenerateFix(finding, fixContext); + retry(); + } else { + markFailed(finding.id); + } +} else { + // Commit + await gitCommit(`Fix: ${finding.title} [${finding.id}]`); + markFixed(finding.id); +} +``` + +**Pass Criteria**: 100% test pass rate (no partial fixes) + +## Phase 3: Execution Orchestration (Orchestrator) + +- Load fix-plan.json timeline stages +- For each stage: + - If parallel mode: Launch all group agents via `Promise.all()` + - If serial mode: Execute groups sequentially with `await` + - Assign agent IDs (agents update their fix-progress-{N}.json) +- Handle agent failures gracefully (mark group as failed, continue) +- Advance to next stage only when current stage complete + +## Execution Agent Template (Per Group) + +```javascript +Task({ + subagent_type: "cli-execute-agent", + description: `Fix ${group.findings.length} issues: ${group.group_name}`, + prompt: ` +## Task Objective +Execute fixes for code review findings in group ${group.group_id}. Update progress file in real-time with flow control tracking. + +## Assignment +- Group ID: ${group.group_id} +- Group Name: ${group.group_name} +- Progress File: ${sessionDir}/${group.progress_file} +- Findings Count: ${group.findings.length} +- Max Iterations: ${maxIterations} (per finding) + +## Fix Strategy +${JSON.stringify(group.fix_strategy, null, 2)} + +## Risk Assessment +${JSON.stringify(group.risk_assessment, null, 2)} + +## Execution Flow + +### Initialization (Before Starting) + +1. Read ${group.progress_file} to load initial state +2. Update progress file: + - assigned_agent: "${agentId}" + - status: "in-progress" + - started_at: Current ISO 8601 timestamp + - last_update: Current ISO 8601 timestamp +3. Write updated state back to ${group.progress_file} + +### Main Execution Loop + +For EACH finding in ${group.progress_file}.findings: + +#### Step 1: Analyze Context + +**Before Step**: +- Update finding: status→"in-progress", started_at→now() +- Update current_finding: Populate with finding details, status→"analyzing", action→"Reading file and understanding code structure" +- Update phase→"analyzing" +- Update flow_control: Add "analyze_context" step to implementation_approach (status→"in-progress"), set current_step→"analyze_context" +- Update last_update→now(), write to ${group.progress_file} + +**Action**: +- Read file: finding.file +- Understand code structure around line: finding.line +- Analyze surrounding context (imports, dependencies, related functions) +- Review recommendations: finding.recommendations + +**After Step**: +- Update flow_control: Mark "analyze_context" step as "completed" with completed_at→now() +- Update last_update→now(), write to ${group.progress_file} + +#### Step 2: Apply Fix + +**Before Step**: +- Update current_finding: status→"fixing", action→"Applying code changes per recommendations" +- Update phase→"fixing" +- Update flow_control: Add "apply_fix" step to implementation_approach (status→"in-progress"), set current_step→"apply_fix" +- Update last_update→now(), write to ${group.progress_file} + +**Action**: +- Use Edit tool to implement code changes per finding.recommendations +- Follow fix_strategy.approach +- Maintain code style and existing patterns + +**After Step**: +- Update flow_control: Mark "apply_fix" step as "completed" with completed_at→now() +- Update last_update→now(), write to ${group.progress_file} + +#### Step 3: Test Verification + +**Before Step**: +- Update current_finding: status→"testing", action→"Running test suite to verify fix" +- Update phase→"testing" +- Update flow_control: Add "run_tests" step to implementation_approach (status→"in-progress"), set current_step→"run_tests" +- Update last_update→now(), write to ${group.progress_file} + +**Action**: +- Run tests using fix_strategy.test_pattern +- Require 100% pass rate +- Capture test output + +**On Test Failure**: +- Git rollback: \`git checkout -- \${finding.file}\` +- Increment finding.attempts +- Update flow_control: Mark "run_tests" step as "failed" with completed_at→now() +- Update errors: Add entry (finding_id, error_type→"test_failure", message, timestamp) +- If finding.attempts < ${maxIterations}: + - Reset flow_control: implementation_approach→[], current_step→null + - Retry from Step 1 +- Else: + - Update finding: status→"completed", result→"failed", error_message→"Max iterations reached", completed_at→now() + - Update summary counts, move to next finding + +**On Test Success**: +- Update flow_control: Mark "run_tests" step as "completed" with completed_at→now() +- Update last_update→now(), write to ${group.progress_file} +- Proceed to Step 4 + +#### Step 4: Commit Changes + +**Before Step**: +- Update current_finding: status→"committing", action→"Creating git commit for successful fix" +- Update phase→"committing" +- Update flow_control: Add "commit_changes" step to implementation_approach (status→"in-progress"), set current_step→"commit_changes" +- Update last_update→now(), write to ${group.progress_file} + +**Action**: +- Git commit: \`git commit -m "fix(${finding.dimension}): ${finding.title} [${finding.id}]"\` +- Capture commit hash + +**After Step**: +- Update finding: status→"completed", result→"fixed", commit_hash→, test_passed→true, completed_at→now() +- Update flow_control: Mark "commit_changes" step as "completed" with completed_at→now() +- Update last_update→now(), write to ${group.progress_file} + +#### After Each Finding + +- Update summary: Recalculate counts (pending/in_progress/fixed/failed) and percent_complete +- If all findings completed: Clear current_finding, reset flow_control +- Update last_update→now(), write to ${group.progress_file} + +### Final Completion + +When all findings processed: +- Update status→"completed", phase→"done", summary.percent_complete→100.0 +- Update last_update→now(), write final state to ${group.progress_file} + +## Critical Requirements + +### Progress File Updates +- **MUST update after every significant action** (before/after each step) +- **Always maintain complete structure** - never write partial updates +- **Use ISO 8601 timestamps** - e.g., "2025-01-25T14:36:00Z" + +### Flow Control Format +Follow action-planning-agent flow_control.implementation_approach format: +- step: Identifier (e.g., "analyze_context", "apply_fix") +- action: Human-readable description +- status: "pending" | "in-progress" | "completed" | "failed" +- started_at: ISO 8601 timestamp or null +- completed_at: ISO 8601 timestamp or null + +### Error Handling +- Capture all errors in errors[] array +- Never leave progress file in invalid state +- Always write complete updates, never partial +- On unrecoverable error: Mark group as failed, preserve state + +## Test Patterns +Use fix_strategy.test_pattern to run affected tests: +- Pattern: ${group.fix_strategy.test_pattern} +- Command: Infer from project (npm test, pytest, etc.) +- Pass Criteria: 100% pass rate required + ` +}) +``` + +## Output +- Files: fix-progress-{N}.json (updated per group), git commits +- TaskUpdate: Mark Phase 8 completed, Phase 9 in_progress + +## Next Phase +Return to orchestrator, then auto-continue to [Phase 9: Fix Completion](09-fix-completion.md). diff --git a/.claude/skills/review-cycle/phases/09-fix-completion.md b/.claude/skills/review-cycle/phases/09-fix-completion.md new file mode 100644 index 00000000..b7cbd46e --- /dev/null +++ b/.claude/skills/review-cycle/phases/09-fix-completion.md @@ -0,0 +1,153 @@ +# Phase 9: Fix Completion + +> Source: `commands/workflow/review-cycle-fix.md` Phase 4 + Phase 5 + +## Overview +Aggregate fix results, generate summary report, update history, and optionally complete workflow session. + +## Phase 4: Completion & Aggregation (Orchestrator) + +- Collect final status from all fix-progress-{N}.json files +- Generate fix-summary.md with timeline and results +- Update fix-history.json with new session entry +- Remove active-fix-session.json +- TodoWrite completion: Mark all phases done +- Output summary to user + +## Phase 5: Session Completion (Orchestrator) + +- If all findings fixed successfully (no failures): + - Prompt user: "All fixes complete. Complete workflow session? [Y/n]" + - If confirmed: Execute `Skill(skill="workflow:session:complete")` to archive session with lessons learned +- If partial success (some failures): + - Output: "Some findings failed. Review fix-summary.md before completing session." + - Do NOT auto-complete session + +## Error Handling + +### Batching Failures (Phase 1.5) + +- Invalid findings data -> Abort with error message +- Empty batches after grouping -> Warn and skip empty batches + +### Planning Failures (Phase 2) + +- Planning agent timeout -> Mark batch as failed, continue with other batches +- Partial plan missing -> Skip batch, warn user +- Agent crash -> Collect available partial plans, proceed with aggregation +- All agents fail -> Abort entire fix session with error +- Aggregation conflicts -> Apply conflict resolution (serialize conflicting groups) + +### Execution Failures (Phase 3) + +- Agent crash -> Mark group as failed, continue with other groups +- Test command not found -> Skip test verification, warn user +- Git operations fail -> Abort with error, preserve state + +### Rollback Scenarios + +- Test failure after fix -> Automatic `git checkout` rollback +- Max iterations reached -> Leave file unchanged, mark as failed +- Unrecoverable error -> Rollback entire group, save checkpoint + +## TodoWrite Structures + +### Initialization (after Phase 1.5 batching) + +```javascript +TodoWrite({ + todos: [ + {content: "Phase 1: Discovery & Initialization", status: "completed", activeForm: "Discovering"}, + {content: "Phase 1.5: Intelligent Batching", status: "completed", activeForm: "Batching"}, + {content: "Phase 2: Parallel Planning", status: "in_progress", activeForm: "Planning"}, + {content: " → Batch 1: 4 findings (auth.ts:security)", status: "pending", activeForm: "Planning batch 1"}, + {content: " → Batch 2: 3 findings (query.ts:security)", status: "pending", activeForm: "Planning batch 2"}, + {content: " → Batch 3: 2 findings (config.ts:quality)", status: "pending", activeForm: "Planning batch 3"}, + {content: "Phase 3: Execution", status: "pending", activeForm: "Executing"}, + {content: "Phase 4: Completion", status: "pending", activeForm: "Completing"} + ] +}); +``` + +### During Planning (parallel agents running) + +```javascript +TodoWrite({ + todos: [ + {content: "Phase 1: Discovery & Initialization", status: "completed", activeForm: "Discovering"}, + {content: "Phase 1.5: Intelligent Batching", status: "completed", activeForm: "Batching"}, + {content: "Phase 2: Parallel Planning", status: "in_progress", activeForm: "Planning"}, + {content: " → Batch 1: 4 findings (auth.ts:security)", status: "completed", activeForm: "Planning batch 1"}, + {content: " → Batch 2: 3 findings (query.ts:security)", status: "in_progress", activeForm: "Planning batch 2"}, + {content: " → Batch 3: 2 findings (config.ts:quality)", status: "in_progress", activeForm: "Planning batch 3"}, + {content: "Phase 3: Execution", status: "pending", activeForm: "Executing"}, + {content: "Phase 4: Completion", status: "pending", activeForm: "Completing"} + ] +}); +``` + +### During Execution + +```javascript +TodoWrite({ + todos: [ + {content: "Phase 1: Discovery & Initialization", status: "completed", activeForm: "Discovering"}, + {content: "Phase 1.5: Intelligent Batching", status: "completed", activeForm: "Batching"}, + {content: "Phase 2: Parallel Planning (3 batches → 5 groups)", status: "completed", activeForm: "Planning"}, + {content: "Phase 3: Execution", status: "in_progress", activeForm: "Executing"}, + {content: " → Stage 1: Parallel execution (3 groups)", status: "completed", activeForm: "Executing stage 1"}, + {content: " • Group G1: Auth validation (2 findings)", status: "completed", activeForm: "Fixing G1"}, + {content: " • Group G2: Query security (3 findings)", status: "completed", activeForm: "Fixing G2"}, + {content: " • Group G3: Config quality (1 finding)", status: "completed", activeForm: "Fixing G3"}, + {content: " → Stage 2: Serial execution (1 group)", status: "in_progress", activeForm: "Executing stage 2"}, + {content: " • Group G4: Dependent fixes (2 findings)", status: "in_progress", activeForm: "Fixing G4"}, + {content: "Phase 4: Completion", status: "pending", activeForm: "Completing"} + ] +}); +``` + +### Update Rules + +- Add batch items dynamically during Phase 1.5 +- Mark batch items completed as parallel agents return results +- Add stage/group items dynamically after Phase 2 plan aggregation +- Mark completed immediately after each group finishes +- Update parent phase status when all child items complete + +## Post-Completion Expansion + +After completion, ask user whether to expand into issues (test/enhance/refactor/doc). For selected items, invoke `Skill(skill="issue:new", args="{summary} - {dimension}")`. + +## Best Practices + +1. **Leverage Parallel Planning**: For 10+ findings, parallel batching significantly reduces planning time +2. **Tune Batch Size**: Use `--batch-size` to control granularity (smaller batches = more parallelism, larger = better grouping context) +3. **Conservative Approach**: Test verification is mandatory - no fixes kept without passing tests +4. **Parallel Efficiency**: MAX_PARALLEL=10 for planning agents, 3 concurrent execution agents per stage +5. **Resume Support**: Fix sessions can resume from checkpoints after interruption +6. **Manual Review**: Always review failed fixes manually - may require architectural changes +7. **Incremental Fixing**: Start with small batches (5-10 findings) before large-scale fixes + +## Related Commands + +### View Fix Progress +Use `ccw view` to open the workflow dashboard in browser: + +```bash +ccw view +``` + +### Re-run Fix Pipeline +``` +Skill(skill="review-cycle", args="--fix ...") +``` + +## Output + +- Files: fix-summary.md, fix-history.json +- State: active-fix-session.json removed +- Optional: workflow session completed via `Skill(skill="workflow:session:complete")` + +## Completion + +Review Cycle fix pipeline complete. Review fix-summary.md for results. diff --git a/.codex/skills/review-cycle/SKILL.md b/.codex/skills/review-cycle/SKILL.md new file mode 100644 index 00000000..ec73344f --- /dev/null +++ b/.codex/skills/review-cycle/SKILL.md @@ -0,0 +1,412 @@ +--- +name: review-cycle +description: Unified multi-dimensional code review with automated fix orchestration. Supports session-based (git changes) and module-based (path patterns) review modes with 7-dimension parallel analysis, iterative deep-dive, and automated fix pipeline. Triggers on "workflow:review-cycle", "workflow:review-session-cycle", "workflow:review-module-cycle", "workflow:review-cycle-fix". +allowed-tools: spawn_agent, wait, send_input, close_agent, AskUserQuestion, Read, Write, Edit, Bash, Glob, Grep +--- + +# Review Cycle + +Unified multi-dimensional code review orchestrator with dual-mode (session/module) file discovery, 7-dimension parallel analysis, iterative deep-dive on critical findings, and optional automated fix pipeline with intelligent batching and parallel planning. + +## Architecture Overview + +``` +┌──────────────────────────────────────────────────────────────────────┐ +│ Review Cycle Orchestrator (SKILL.md) │ +│ → Pure coordinator: mode detection, phase dispatch, state tracking │ +└───────────────────────────────┬──────────────────────────────────────┘ + │ + ┌─────────────────────────────┼─────────────────────────────────┐ + │ Review Pipeline (Phase 1-5) │ + │ │ + │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ + │ │ Phase 1 │→ │ Phase 2 │→ │ Phase 3 │→ │ Phase 4 │→ │ Phase 5 │ + │ │Discovery│ │Parallel │ │Aggregate│ │Deep-Dive│ │Complete │ + │ │ Init │ │ Review │ │ │ │(cond.) │ │ │ + │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ + │ session| 7 agents severity N agents finalize + │ module ×cli-explore calc ×cli-explore state + │ ↕ loop + └────────────────────────────────────────────────────────────────┘ + │ + (optional --fix) + │ + ┌─────────────────────────────┼─────────────────────────────────┐ + │ Fix Pipeline (Phase 6-9) │ + │ │ + │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ + │ │ Phase 6 │→ │ Phase 7 │→ │ Phase 8 │→ │ Phase 9 │ + │ │Discovery│ │Parallel │ │Execution│ │Complete │ + │ │Batching │ │Planning │ │Orchestr.│ │ │ + │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ + │ grouping N agents M agents aggregate + │ + batch ×cli-plan ×cli-exec + summary + └────────────────────────────────────────────────────────────────┘ +``` + +## Key Design Principles + +1. **Dual-Mode Review**: Session-based (git changes) and module-based (path patterns) share the same review pipeline (Phase 2-5), differing only in file discovery (Phase 1) +2. **Pure Orchestrator**: Execute phases in sequence, parse outputs, pass context between them +3. **Progressive Phase Loading**: Phase docs are read on-demand when that phase executes, not all at once +4. **Auto-Continue**: All phases run autonomously without user intervention between phases +5. **Subagent Lifecycle**: Explicit lifecycle management with spawn_agent → wait → close_agent +6. **Role Path Loading**: Subagent roles loaded via path reference in MANDATORY FIRST STEPS +7. **Optional Fix Pipeline**: Phase 6-9 triggered only by explicit `--fix` flag or user confirmation after Phase 5 +8. **Content Preservation**: All agent prompts, code, schemas preserved verbatim from source commands + +## Usage + +``` +# Review Pipeline (Phase 1-5) +review-cycle # Module mode +review-cycle [session-id] # Session mode +review-cycle [session-id|path-pattern] [FLAGS] # With flags + +# Fix Pipeline (Phase 6-9) +review-cycle --fix # Fix mode +review-cycle --fix [FLAGS] # Fix with flags + +# Flags +--dimensions=dim1,dim2,... Custom dimensions (default: all 7) +--max-iterations=N Max deep-dive iterations (default: 3) +--fix Enter fix pipeline after review or standalone +--resume Resume interrupted fix session +--batch-size=N Findings per planning batch (default: 5, fix mode only) + +# Examples +review-cycle src/auth/** # Module: review auth +review-cycle src/auth/**,src/payment/** # Module: multiple paths +review-cycle src/auth/** --dimensions=security,architecture # Module: custom dims +review-cycle WFS-payment-integration # Session: specific +review-cycle # Session: auto-detect +review-cycle --fix .workflow/active/WFS-123/.review/ # Fix: from review dir +review-cycle --fix --resume # Fix: resume session +``` + +## Mode Detection + +```javascript +// Input parsing logic (orchestrator responsibility) +function detectMode(args) { + if (args.includes('--fix')) return 'fix'; + if (args.match(/\*|\.ts|\.js|\.py|src\/|lib\//)) return 'module'; // glob/path patterns + if (args.match(/^WFS-/) || args.trim() === '') return 'session'; // session ID or empty + return 'session'; // default +} +``` + +| Input Pattern | Detected Mode | Phase Entry | +|---------------|---------------|-------------| +| `src/auth/**` | `module` | Phase 1 (module branch) | +| `WFS-payment-integration` | `session` | Phase 1 (session branch) | +| _(empty)_ | `session` | Phase 1 (session branch, auto-detect) | +| `--fix .review/` | `fix` | Phase 6 | +| `--fix --resume` | `fix` | Phase 6 (resume) | + +## Execution Flow + +``` +Input Parsing: + └─ Detect mode (session|module|fix) → route to appropriate phase entry + +Review Pipeline (session or module mode): + +Phase 1: Discovery & Initialization + └─ Ref: phases/01-discovery-initialization.md + ├─ Session mode: session discovery → git changed files → resolve + ├─ Module mode: path patterns → glob expand → resolve + └─ Common: create session, output dirs, review-state.json, review-progress.json + +Phase 2: Parallel Review Coordination + └─ Ref: phases/02-parallel-review.md + ├─ Spawn 7 cli-explore-agent instances (Deep Scan mode) + ├─ Each produces dimensions/{dimension}.json + reports/{dimension}-analysis.md + ├─ Lifecycle: spawn_agent → batch wait → close_agent + └─ CLI fallback: Gemini → Qwen → Codex + +Phase 3: Aggregation + └─ Ref: phases/03-aggregation.md + ├─ Load dimension JSONs, calculate severity distribution + ├─ Identify cross-cutting concerns (files in 3+ dimensions) + └─ Decision: critical > 0 OR high > 5 OR critical files → Phase 4 + Else → Phase 5 + +Phase 4: Iterative Deep-Dive (conditional) + └─ Ref: phases/04-iterative-deep-dive.md + ├─ Select critical findings (max 5 per iteration) + ├─ Spawn deep-dive agents for root cause analysis + ├─ Re-assess severity → loop back to Phase 3 aggregation + └─ Exit when: no critical findings OR max iterations reached + +Phase 5: Review Completion + └─ Ref: phases/05-review-completion.md + ├─ Finalize review-state.json + review-progress.json + ├─ Prompt user: "Run automated fixes? [Y/n]" + └─ If yes → Continue to Phase 6 + +Fix Pipeline (--fix mode or after Phase 5): + +Phase 6: Fix Discovery & Batching + └─ Ref: phases/06-fix-discovery-batching.md + ├─ Validate export file, create fix session + └─ Intelligent grouping by file+dimension similarity → batches + +Phase 7: Fix Parallel Planning + └─ Ref: phases/07-fix-parallel-planning.md + ├─ Spawn N cli-planning-agent instances (≤10 parallel) + ├─ Each outputs partial-plan-{batch-id}.json + ├─ Lifecycle: spawn_agent → batch wait → close_agent + └─ Orchestrator aggregates → fix-plan.json + +Phase 8: Fix Execution + └─ Ref: phases/08-fix-execution.md + ├─ Stage-based execution per aggregated timeline + ├─ Each group: analyze → fix → test → commit/rollback + ├─ Lifecycle: spawn_agent → wait → close_agent per group + └─ 100% test pass rate required + +Phase 9: Fix Completion + └─ Ref: phases/09-fix-completion.md + ├─ Aggregate results → fix-summary.md + └─ Optional: complete workflow session if all fixes successful + +Complete: Review reports + optional fix results +``` + +**Phase Reference Documents** (read on-demand when phase executes): + +| Phase | Document | Load When | Source | +|-------|----------|-----------|--------| +| 1 | [phases/01-discovery-initialization.md](phases/01-discovery-initialization.md) | Review/Fix start | review-session-cycle + review-module-cycle Phase 1 (fused) | +| 2 | [phases/02-parallel-review.md](phases/02-parallel-review.md) | Phase 1 complete | Shared from both review commands Phase 2 | +| 3 | [phases/03-aggregation.md](phases/03-aggregation.md) | Phase 2 complete | Shared from both review commands Phase 3 | +| 4 | [phases/04-iterative-deep-dive.md](phases/04-iterative-deep-dive.md) | Aggregation triggers iteration | Shared from both review commands Phase 4 | +| 5 | [phases/05-review-completion.md](phases/05-review-completion.md) | No more iterations needed | Shared from both review commands Phase 5 | +| 6 | [phases/06-fix-discovery-batching.md](phases/06-fix-discovery-batching.md) | Fix mode entry | review-cycle-fix Phase 1 + 1.5 | +| 7 | [phases/07-fix-parallel-planning.md](phases/07-fix-parallel-planning.md) | Phase 6 complete | review-cycle-fix Phase 2 | +| 8 | [phases/08-fix-execution.md](phases/08-fix-execution.md) | Phase 7 complete | review-cycle-fix Phase 3 | +| 9 | [phases/09-fix-completion.md](phases/09-fix-completion.md) | Phase 8 complete | review-cycle-fix Phase 4 + 5 | + +## Core Rules + +1. **Start Immediately**: First action is progress tracking initialization, second action is Phase 1 execution +2. **Mode Detection First**: Parse input to determine session/module/fix mode before Phase 1 +3. **Parse Every Output**: Extract required data from each phase for next phase +4. **Auto-Continue**: Check progress status to execute next pending phase automatically +5. **Progressive Phase Loading**: Read phase docs ONLY when that phase is about to execute +6. **DO NOT STOP**: Continuous multi-phase workflow until all applicable phases complete +7. **Conditional Phase 4**: Only execute if aggregation triggers iteration (critical > 0 OR high > 5 OR critical files) +8. **Fix Pipeline Optional**: Phase 6-9 only execute with explicit --fix flag or user confirmation +9. **Explicit Lifecycle**: Always close_agent after wait completes to free resources + +## Data Flow + +``` +User Input (path-pattern | session-id | --fix export-file) + ↓ +[Mode Detection: session | module | fix] + ↓ +Phase 1: Discovery & Initialization + ↓ Output: sessionId, reviewId, resolvedFiles, reviewMode, outputDir + ↓ review-state.json, review-progress.json +Phase 2: Parallel Review Coordination + ↓ Output: dimensions/*.json, reports/*-analysis.md +Phase 3: Aggregation + ↓ Output: severityDistribution, criticalFiles, deepDiveFindings + ↓ Decision: iterate? → Phase 4 : Phase 5 +Phase 4: Iterative Deep-Dive (conditional, loops with Phase 3) + ↓ Output: iterations/*.json, reports/deep-dive-*.md + ↓ Loop: re-aggregate → check criteria → iterate or exit +Phase 5: Review Completion + ↓ Output: final review-state.json, review-progress.json + ↓ Decision: fix? → Phase 6 : END +Phase 6: Fix Discovery & Batching + ↓ Output: finding batches (in-memory) +Phase 7: Fix Parallel Planning + ↓ Output: partial-plan-*.json → fix-plan.json (aggregated) +Phase 8: Fix Execution + ↓ Output: fix-progress-*.json, git commits +Phase 9: Fix Completion + ↓ Output: fix-summary.md, fix-history.json +``` + +## Subagent API Reference + +### spawn_agent + +Create a new subagent with task assignment. + +```javascript +const agentId = spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/{agent-type}.md (MUST read first) +2. Read: .workflow/project-tech.json +3. Read: .workflow/project-guidelines.json + +--- + +## TASK CONTEXT +${taskContext} + +## DELIVERABLES +${deliverables} +` +}) +``` + +### wait + +Get results from subagent (only way to retrieve results). + +```javascript +const result = wait({ + ids: [agentId], + timeout_ms: 600000 // 10 minutes +}) + +if (result.timed_out) { + // Handle timeout - can continue waiting or send_input to prompt completion +} + +// Check completion status +if (result.status[agentId].completed) { + const output = result.status[agentId].completed; +} +``` + +### send_input + +Continue interaction with active subagent (for clarification or follow-up). + +```javascript +send_input({ + id: agentId, + message: ` +## CLARIFICATION ANSWERS +${answers} + +## NEXT STEP +Continue with analysis generation. +` +}) +``` + +### close_agent + +Clean up subagent resources (irreversible). + +```javascript +close_agent({ id: agentId }) +``` + +## Progress Tracking Pattern + +**Review Pipeline Initialization**: +``` +Phase 1: Discovery & Initialization → pending +Phase 2: Parallel Reviews (7 dimensions) → pending +Phase 3: Aggregation → pending +Phase 4: Deep-dive (conditional) → pending +Phase 5: Review Completion → pending +``` + +**During Phase 2 (sub-tasks for each dimension)**: +``` + → Security review → in_progress / completed + → Architecture review → in_progress / completed + → Quality review → in_progress / completed + ... other dimensions +``` + +**Fix Pipeline (added after Phase 5 if triggered)**: +``` +Phase 6: Fix Discovery & Batching → pending +Phase 7: Parallel Planning → pending +Phase 8: Execution → pending +Phase 9: Fix Completion → pending +``` + +## Error Handling + +### Review Pipeline Errors + +| Phase | Error | Blocking? | Action | +|-------|-------|-----------|--------| +| Phase 1 | Session not found (session mode) | Yes | Error and exit | +| Phase 1 | No changed files (session mode) | Yes | Error and exit | +| Phase 1 | Invalid path pattern (module mode) | Yes | Error and exit | +| Phase 1 | No files matched (module mode) | Yes | Error and exit | +| Phase 2 | Single dimension fails | No | Log warning, continue other dimensions | +| Phase 2 | All dimensions fail | Yes | Error and exit | +| Phase 3 | Missing dimension JSON | No | Skip in aggregation, log warning | +| Phase 4 | Deep-dive agent fails | No | Skip finding, continue others | +| Phase 4 | Max iterations reached | No | Generate partial report | + +### Fix Pipeline Errors + +| Phase | Error | Blocking? | Action | +|-------|-------|-----------|--------| +| Phase 6 | Invalid export file | Yes | Abort with error | +| Phase 6 | Empty batches | No | Warn and skip empty | +| Phase 7 | Planning agent timeout | No | Mark batch failed, continue others | +| Phase 7 | All agents fail | Yes | Abort fix session | +| Phase 8 | Test failure after fix | No | Rollback, retry up to max_iterations | +| Phase 8 | Git operations fail | Yes | Abort, preserve state | +| Phase 9 | Aggregation error | No | Generate partial summary | + +### CLI Fallback Chain + +Gemini → Qwen → Codex → degraded mode + +**Fallback Triggers**: HTTP 429/5xx, connection timeout, invalid JSON output, low confidence < 0.4, analysis too brief (< 100 words) + +## Output File Structure + +``` +.workflow/active/WFS-{session-id}/.review/ +├── review-state.json # Orchestrator state machine +├── review-progress.json # Real-time progress +├── dimensions/ # Per-dimension results (Phase 2) +│ ├── security.json +│ ├── architecture.json +│ ├── quality.json +│ ├── action-items.json +│ ├── performance.json +│ ├── maintainability.json +│ └── best-practices.json +├── iterations/ # Deep-dive results (Phase 4) +│ ├── iteration-1-finding-{uuid}.json +│ └── iteration-2-finding-{uuid}.json +├── reports/ # Human-readable reports +│ ├── security-analysis.md +│ ├── security-cli-output.txt +│ ├── deep-dive-1-{uuid}.md +│ └── ... +└── fixes/{fix-session-id}/ # Fix results (Phase 6-9) + ├── partial-plan-*.json + ├── fix-plan.json + ├── fix-progress-*.json + ├── fix-summary.md + ├── active-fix-session.json + └── fix-history.json +``` + +## Related Commands + +### View Progress +```bash +ccw view +``` + +### Workflow Pipeline +```bash +# Step 1: Review (this skill) +review-cycle src/auth/** + +# Step 2: Fix (continue or standalone) +review-cycle --fix .workflow/active/WFS-{session-id}/.review/ +``` diff --git a/.codex/skills/review-cycle/phases/01-discovery-initialization.md b/.codex/skills/review-cycle/phases/01-discovery-initialization.md new file mode 100644 index 00000000..affdd368 --- /dev/null +++ b/.codex/skills/review-cycle/phases/01-discovery-initialization.md @@ -0,0 +1,341 @@ +# Phase 1: Discovery & Initialization + +> Source: Fused from `commands/workflow/review-session-cycle.md` Phase 1 + `commands/workflow/review-module-cycle.md` Phase 1 + +## Overview + +Detect review mode (session or module), resolve target files, create workflow session, initialize output directory structure and state files. + +## Mode Detection + +The review mode is determined by the input arguments: + +- **Session mode**: No path pattern provided, OR a `WFS-*` session ID is provided. Reviews all changes within an existing workflow session (git-based change detection). +- **Module mode**: Glob/path patterns are provided (e.g., `src/auth/**`, `src/payment/processor.ts`). Reviews specific files/directories regardless of session history. + +--- + +## Session Mode (review-session-cycle) + +### Step 1.1: Session Discovery + +```javascript +// If session ID not provided, auto-detect +if (!providedSessionId) { + // Check for active sessions + const activeSessions = Glob('.workflow/active/WFS-*'); + if (activeSessions.length === 1) { + sessionId = activeSessions[0].match(/WFS-[^/]+/)[0]; + } else if (activeSessions.length > 1) { + // List sessions and prompt user + error("Multiple active sessions found. Please specify session ID."); + } else { + error("No active session found. Create session first."); + } +} else { + sessionId = providedSessionId; +} + +// Validate session exists +Bash(`test -d .workflow/active/${sessionId} && echo "EXISTS"`); +``` + +### Step 1.2: Session Validation + +- Ensure session has implementation artifacts (check `.summaries/` or `.task/` directory) +- Extract session creation timestamp from `workflow-session.json` +- Use timestamp for git log filtering: `git log --since="${sessionCreatedAt}"` + +### Step 1.3: Changed Files Detection + +```bash +# Get files changed since session creation +git log --since="${sessionCreatedAt}" --name-only --pretty=format: | sort -u +``` + +--- + +## Module Mode (review-module-cycle) + +### Step 1.1: Session Creation + +```javascript +// Create workflow session for this review (type: review) +// Orchestrator handles session creation directly +Bash(`mkdir -p .workflow/active/WFS-review-${Date.now()}`); + +// Initialize workflow-session.json +const sessionId = `WFS-review-${Date.now()}`; +Write(`.workflow/active/${sessionId}/workflow-session.json`, JSON.stringify({ + session_id: sessionId, + type: "review", + description: `Code review for ${targetPattern}`, + created_at: new Date().toISOString() +}, null, 2)); +``` + +### Step 1.2: Path Resolution & Validation + +```bash +# Expand glob pattern to file list (relative paths from project root) +find . -path "./src/auth/**" -type f | sed 's|^\./||' + +# Validate files exist and are readable +for file in ${resolvedFiles[@]}; do + test -r "$file" || error "File not readable: $file" +done +``` + +- Parse and expand file patterns (glob support): `src/auth/**` -> actual file list +- Validation: Ensure all specified files exist and are readable +- Store as **relative paths** from project root (e.g., `src/auth/service.ts`) +- Agents construct absolute paths dynamically during execution + +**Syntax Rules**: +- All paths are **relative** from project root (e.g., `src/auth/**` not `/src/auth/**`) +- Multiple patterns: comma-separated, **no spaces** (e.g., `src/auth/**,src/payment/**`) +- Glob and specific files can be mixed (e.g., `src/auth/**,src/config.ts`) + +**Supported Patterns**: +| Pattern Type | Example | Description | +|--------------|---------|-------------| +| Glob directory | `src/auth/**` | All files under src/auth/ | +| Glob with extension | `src/**/*.ts` | All .ts files under src/ | +| Specific file | `src/payment/processor.ts` | Single file | +| Multiple patterns | `src/auth/**,src/payment/**` | Comma-separated (no spaces) | + +**Resolution Process**: +1. Parse input pattern (split by comma, trim whitespace) +2. Expand glob patterns to file list via `find` command +3. Validate all files exist and are readable +4. Error if pattern matches 0 files +5. Store resolved file list in review-state.json + +--- + +## Common Steps (Both Modes) + +### Step 1.4: Output Directory Setup + +- Output directory: `.workflow/active/${sessionId}/.review/` +- Create directory structure: + ```bash + mkdir -p ${sessionDir}/.review/{dimensions,iterations,reports} + ``` + +### Step 1.5: Initialize Review State + +- State initialization: Create `review-state.json` with metadata, dimensions, max_iterations (merged metadata + state) +- Session mode includes `git_changes` in metadata +- Module mode includes `target_pattern` and `resolved_files` in metadata +- Progress tracking: Create `review-progress.json` for progress tracking + +### Step 1.6: Initialize Review Progress + +- Create `review-progress.json` for real-time dashboard updates via polling +- See [Review Progress JSON](#review-progress-json) schema below + +### Step 1.7: Progress Tracking Initialization + +- Set up progress tracking with hierarchical structure +- Mark Phase 1 completed, Phase 2 in_progress + +--- + +## Review State JSON (Session Mode) + +**Purpose**: Unified state machine and metadata (merged from metadata + state) + +```json +{ + "session_id": "WFS-payment-integration", + "review_id": "review-20250125-143022", + "review_type": "session", + "metadata": { + "created_at": "2025-01-25T14:30:22Z", + "git_changes": { + "commit_range": "abc123..def456", + "files_changed": 15, + "insertions": 342, + "deletions": 128 + }, + "dimensions": ["security", "architecture", "quality", "action-items", "performance", "maintainability", "best-practices"], + "max_iterations": 3 + }, + "phase": "parallel|aggregate|iterate|complete", + "current_iteration": 1, + "dimensions_reviewed": ["security", "architecture", "quality", "action-items", "performance", "maintainability", "best-practices"], + "selected_strategy": "comprehensive", + "next_action": "execute_parallel_reviews|aggregate_findings|execute_deep_dive|generate_final_report|complete", + "severity_distribution": { + "critical": 2, + "high": 5, + "medium": 12, + "low": 8 + }, + "critical_files": [ + { + "file": "src/payment/processor.ts", + "finding_count": 5, + "dimensions": ["security", "architecture", "quality"] + } + ], + "iterations": [ + { + "iteration": 1, + "findings_analyzed": ["uuid-1", "uuid-2"], + "findings_resolved": 1, + "findings_escalated": 1, + "severity_change": { + "before": {"critical": 2, "high": 5, "medium": 12, "low": 8}, + "after": {"critical": 1, "high": 6, "medium": 12, "low": 8} + }, + "timestamp": "2025-01-25T14:30:00Z" + } + ], + "completion_criteria": { + "target": "no_critical_findings_and_high_under_5", + "current_status": "in_progress", + "estimated_completion": "2 iterations remaining" + } +} +``` + +**Field Descriptions**: +- `phase`: Current execution phase (state machine pointer) +- `current_iteration`: Iteration counter (used for max check) +- `next_action`: Next step orchestrator should execute +- `severity_distribution`: Aggregated counts across all dimensions +- `critical_files`: Files appearing in 3+ dimensions with metadata +- `iterations[]`: Historical log for trend analysis + +## Review State JSON (Module Mode) + +**Purpose**: Unified state machine and metadata (merged from metadata + state) + +```json +{ + "review_id": "review-20250125-143022", + "review_type": "module", + "session_id": "WFS-auth-system", + "metadata": { + "created_at": "2025-01-25T14:30:22Z", + "target_pattern": "src/auth/**", + "resolved_files": [ + "src/auth/service.ts", + "src/auth/validator.ts", + "src/auth/middleware.ts" + ], + "dimensions": ["security", "architecture", "quality", "action-items", "performance", "maintainability", "best-practices"], + "max_iterations": 3 + }, + "phase": "parallel|aggregate|iterate|complete", + "current_iteration": 1, + "dimensions_reviewed": ["security", "architecture", "quality", "action-items", "performance", "maintainability", "best-practices"], + "selected_strategy": "comprehensive", + "next_action": "execute_parallel_reviews|aggregate_findings|execute_deep_dive|generate_final_report|complete", + "severity_distribution": { + "critical": 2, + "high": 5, + "medium": 12, + "low": 8 + }, + "critical_files": [...], + "iterations": [...], + "completion_criteria": {...} +} +``` + +## Review Progress JSON + +**Purpose**: Real-time dashboard updates via polling + +```json +{ + "review_id": "review-20250125-143022", + "last_update": "2025-01-25T14:35:10Z", + "phase": "parallel|aggregate|iterate|complete", + "current_iteration": 1, + "progress": { + "parallel_review": { + "total_dimensions": 7, + "completed": 5, + "in_progress": 2, + "percent_complete": 71 + }, + "deep_dive": { + "total_findings": 6, + "analyzed": 2, + "in_progress": 1, + "percent_complete": 33 + } + }, + "agent_status": [ + { + "agent_type": "review-agent", + "dimension": "security", + "status": "completed", + "started_at": "2025-01-25T14:30:00Z", + "completed_at": "2025-01-25T15:15:00Z", + "duration_ms": 2700000 + }, + { + "agent_type": "deep-dive-agent", + "finding_id": "sec-001-uuid", + "status": "in_progress", + "started_at": "2025-01-25T14:32:00Z" + } + ], + "estimated_completion": "2025-01-25T16:00:00Z" +} +``` + +--- + +## Output File Structure + +``` +.workflow/active/WFS-{session-id}/.review/ +├── review-state.json # Orchestrator state machine (includes metadata) +├── review-progress.json # Real-time progress for dashboard +├── dimensions/ # Per-dimension results +│ ├── security.json +│ ├── architecture.json +│ ├── quality.json +│ ├── action-items.json +│ ├── performance.json +│ ├── maintainability.json +│ └── best-practices.json +├── iterations/ # Deep-dive results +│ ├── iteration-1-finding-{uuid}.json +│ └── iteration-2-finding-{uuid}.json +└── reports/ # Human-readable reports + ├── security-analysis.md + ├── security-cli-output.txt + ├── deep-dive-1-{uuid}.md + └── ... +``` + +## Session Context + +``` +.workflow/active/WFS-{session-id}/ +├── workflow-session.json +├── IMPL_PLAN.md +├── TODO_LIST.md +├── .task/ +├── .summaries/ +└── .review/ # Review results (this command) + └── (structure above) +``` + +--- + +## Output + +- **Variables**: `sessionId`, `reviewId`, `resolvedFiles`, `reviewMode`, `outputDir` +- **Files**: `review-state.json`, `review-progress.json` + +## Next Phase + +Return to orchestrator, then auto-continue to [Phase 2: Parallel Review](02-parallel-review.md). diff --git a/.codex/skills/review-cycle/phases/02-parallel-review.md b/.codex/skills/review-cycle/phases/02-parallel-review.md new file mode 100644 index 00000000..42dd27bf --- /dev/null +++ b/.codex/skills/review-cycle/phases/02-parallel-review.md @@ -0,0 +1,549 @@ +# Phase 2: Parallel Review Coordination + +> Source: Shared from `commands/workflow/review-session-cycle.md` + `commands/workflow/review-module-cycle.md` Phase 2 + +## Overview + +Launch 7 dimension-specific review agents simultaneously using cli-explore-agent in Deep Scan mode. + +## Review Dimensions Configuration + +**7 Specialized Dimensions** with priority-based allocation: + +| Dimension | Template | Priority | Timeout | +|-----------|----------|----------|---------| +| **Security** | 03-assess-security-risks.txt | 1 (Critical) | 60min | +| **Architecture** | 02-review-architecture.txt | 2 (High) | 60min | +| **Quality** | 02-review-code-quality.txt | 3 (Medium) | 40min | +| **Action-Items** | 02-analyze-code-patterns.txt | 2 (High) | 40min | +| **Performance** | 03-analyze-performance.txt | 3 (Medium) | 60min | +| **Maintainability** | 02-review-code-quality.txt* | 3 (Medium) | 40min | +| **Best-Practices** | 03-review-quality-standards.txt | 3 (Medium) | 40min | + +*Custom focus: "Assess technical debt and maintainability" + +**Category Definitions by Dimension**: + +```javascript +const CATEGORIES = { + security: ['injection', 'authentication', 'authorization', 'encryption', 'input-validation', 'access-control', 'data-exposure'], + architecture: ['coupling', 'cohesion', 'layering', 'dependency', 'pattern-violation', 'scalability', 'separation-of-concerns'], + quality: ['code-smell', 'duplication', 'complexity', 'naming', 'error-handling', 'testability', 'readability'], + 'action-items': ['requirement-coverage', 'acceptance-criteria', 'documentation', 'deployment-readiness', 'missing-functionality'], + performance: ['n-plus-one', 'inefficient-query', 'memory-leak', 'blocking-operation', 'caching', 'resource-usage'], + maintainability: ['technical-debt', 'magic-number', 'long-method', 'large-class', 'dead-code', 'commented-code'], + 'best-practices': ['convention-violation', 'anti-pattern', 'deprecated-api', 'missing-validation', 'inconsistent-style'] +}; +``` + +## Severity Assessment + +**Severity Levels**: +- **Critical**: Security vulnerabilities, data corruption risks, system-wide failures, authentication/authorization bypass +- **High**: Feature degradation, performance bottlenecks, architecture violations, significant technical debt +- **Medium**: Code smells, minor performance issues, style inconsistencies, maintainability concerns +- **Low**: Documentation gaps, minor refactoring opportunities, cosmetic issues + +**Iteration Trigger**: +- Critical findings > 0 OR +- High findings > 5 OR +- Critical files count > 0 + +## Orchestrator Responsibilities + +- Spawn 7 @cli-explore-agent instances simultaneously (Deep Scan mode) +- Pass dimension-specific context (template, timeout, custom focus, **target files**) +- Monitor completion via review-progress.json updates +- Progress tracking: Mark dimensions as completed +- CLI tool fallback: Gemini → Qwen → Codex (on error/timeout) +- Lifecycle: spawn_agent → batch wait → close_agent for all 7 agents + +## Agent Output Schemas + +**Agent-produced JSON files follow standardized schemas**: + +1. **Dimension Results** (cli-explore-agent output from parallel reviews) + - Schema: `~/.codex/workflows/cli-templates/schemas/review-dimension-results-schema.json` + - Output: `{output-dir}/dimensions/{dimension}.json` + - Contains: findings array, summary statistics, cross_references + +2. **Deep-Dive Results** (cli-explore-agent output from iterations) + - Schema: `~/.codex/workflows/cli-templates/schemas/review-deep-dive-results-schema.json` + - Output: `{output-dir}/iterations/iteration-{N}-finding-{uuid}.json` + - Contains: root_cause, remediation_plan, impact_assessment, reassessed_severity + +## Review Agent Invocation Template + +### Module Mode + +**Review Agent** (parallel execution, 7 instances): + +```javascript +// Step 1: Spawn 7 agents in parallel +const reviewAgents = []; +const dimensions = ['security', 'architecture', 'quality', 'action-items', 'performance', 'maintainability', 'best-practices']; + +dimensions.forEach(dimension => { + const agentId = spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/cli-explore-agent.md (MUST read first) +2. Read review state: ${reviewStateJsonPath} +3. Get target files: Read resolved_files from review-state.json +4. Validate file access: bash(ls -la ${targetFiles.join(' ')}) +5. Execute: cat ~/.codex/workflows/cli-templates/schemas/review-dimension-results-schema.json (get output schema reference) +6. Read: .workflow/project-tech.json (technology stack and architecture context) +7. Read: .workflow/project-guidelines.json (user-defined constraints and conventions to validate against) + +--- + +## Task Objective +Conduct comprehensive ${dimension} code exploration and analysis using Deep Scan mode (Bash + Gemini dual-source strategy) for specified module files + +## Analysis Mode Selection +Use **Deep Scan mode** for this review: +- Phase 1: Bash structural scan for standard patterns (classes, functions, imports) +- Phase 2: Gemini semantic analysis for design intent, non-standard patterns, ${dimension}-specific concerns +- Phase 3: Synthesis with attribution (bash-discovered vs gemini-discovered findings) + +## Review Context +- Review Type: module (independent) +- Review Dimension: ${dimension} +- Review ID: ${reviewId} +- Target Pattern: ${targetPattern} +- Resolved Files: ${resolvedFiles.length} files +- Output Directory: ${outputDir} + +## CLI Configuration +- Tool Priority: gemini → qwen → codex (fallback chain) +- Custom Focus: ${customFocus || 'Standard dimension analysis'} +- Mode: analysis (READ-ONLY) +- Context Pattern: ${targetFiles.map(f => '@' + f).join(' ')} + +## Expected Deliverables + +**Schema Reference**: Schema obtained in MANDATORY FIRST STEPS step 5, follow schema exactly + +1. Dimension Results JSON: ${outputDir}/dimensions/${dimension}.json + + **⚠️ CRITICAL JSON STRUCTURE REQUIREMENTS**: + + Root structure MUST be array: \`[{ ... }]\` NOT \`{ ... }\` + + Required top-level fields: + - dimension, review_id, analysis_timestamp (NOT timestamp/analyzed_at) + - cli_tool_used (gemini|qwen|codex), model, analysis_duration_ms + - summary (FLAT structure), findings, cross_references + + Summary MUST be FLAT (NOT nested by_severity): + \`{ "total_findings": N, "critical": N, "high": N, "medium": N, "low": N, "files_analyzed": N, "lines_reviewed": N }\` + + Finding required fields: + - id: format \`{dim}-{seq}-{uuid8}\` e.g., \`sec-001-a1b2c3d4\` (lowercase) + - severity: lowercase only (critical|high|medium|low) + - snippet (NOT code_snippet), impact (NOT exploit_scenario) + - metadata, iteration (0), status (pending_remediation), cross_references + +2. Analysis Report: ${outputDir}/reports/${dimension}-analysis.md + - Human-readable summary with recommendations + - Grouped by severity: critical → high → medium → low + - Include file:line references for all findings + +3. CLI Output Log: ${outputDir}/reports/${dimension}-cli-output.txt + - Raw CLI tool output for debugging + - Include full analysis text + +## Dimension-Specific Guidance +${getDimensionGuidance(dimension)} + +## Success Criteria +- [ ] Schema obtained via cat review-dimension-results-schema.json +- [ ] All target files analyzed for ${dimension} concerns +- [ ] All findings include file:line references with code snippets +- [ ] Severity assessment follows established criteria (see reference) +- [ ] Recommendations are actionable with code examples +- [ ] JSON output follows schema exactly +- [ ] Report is comprehensive and well-organized +` + }); + + reviewAgents.push(agentId); +}); + +// Step 2: Batch wait for all 7 agents +const reviewResults = wait({ + ids: reviewAgents, + timeout_ms: 3600000 // 60 minutes +}); + +// Step 3: Check results and handle timeouts +if (reviewResults.timed_out) { + console.log('Some dimension reviews timed out, continuing with completed results'); +} + +reviewAgents.forEach((agentId, index) => { + const dimension = dimensions[index]; + if (reviewResults.status[agentId].completed) { + console.log(`${dimension} review completed`); + } else { + console.log(`${dimension} review failed or timed out`); + } +}); + +// Step 4: Cleanup all agents +reviewAgents.forEach(id => close_agent({ id })); +``` + +### Session Mode + +**Review Agent** (parallel execution, 7 instances): + +```javascript +// Step 1: Spawn 7 agents in parallel +const reviewAgents = []; +const dimensions = ['security', 'architecture', 'quality', 'action-items', 'performance', 'maintainability', 'best-practices']; + +dimensions.forEach(dimension => { + const agentId = spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/cli-explore-agent.md (MUST read first) +2. Read session metadata: ${sessionMetadataPath} +3. Read completed task summaries: bash(find ${summariesDir} -name "IMPL-*.md" -type f) +4. Get changed files: bash(cd ${workflowDir} && git log --since="${sessionCreatedAt}" --name-only --pretty=format: | sort -u) +5. Read review state: ${reviewStateJsonPath} +6. Execute: cat ~/.codex/workflows/cli-templates/schemas/review-dimension-results-schema.json (get output schema reference) +7. Read: .workflow/project-tech.json (technology stack and architecture context) +8. Read: .workflow/project-guidelines.json (user-defined constraints and conventions to validate against) + +--- + +## Task Objective +Conduct comprehensive ${dimension} code exploration and analysis using Deep Scan mode (Bash + Gemini dual-source strategy) for completed implementation in session ${sessionId} + +## Analysis Mode Selection +Use **Deep Scan mode** for this review: +- Phase 1: Bash structural scan for standard patterns (classes, functions, imports) +- Phase 2: Gemini semantic analysis for design intent, non-standard patterns, ${dimension}-specific concerns +- Phase 3: Synthesis with attribution (bash-discovered vs gemini-discovered findings) + +## Session Context +- Session ID: ${sessionId} +- Review Dimension: ${dimension} +- Review ID: ${reviewId} +- Implementation Phase: Complete (all tests passing) +- Output Directory: ${outputDir} + +## CLI Configuration +- Tool Priority: gemini → qwen → codex (fallback chain) +- Template: ~/.codex/workflows/cli-templates/prompts/analysis/${dimensionTemplate} +- Custom Focus: ${customFocus || 'Standard dimension analysis'} +- Timeout: ${timeout}ms +- Mode: analysis (READ-ONLY) + +## Expected Deliverables + +**Schema Reference**: Schema obtained in MANDATORY FIRST STEPS step 6, follow schema exactly + +1. Dimension Results JSON: ${outputDir}/dimensions/${dimension}.json + + **⚠️ CRITICAL JSON STRUCTURE REQUIREMENTS**: + + Root structure MUST be array: \`[{ ... }]\` NOT \`{ ... }\` + + Required top-level fields: + - dimension, review_id, analysis_timestamp (NOT timestamp/analyzed_at) + - cli_tool_used (gemini|qwen|codex), model, analysis_duration_ms + - summary (FLAT structure), findings, cross_references + + Summary MUST be FLAT (NOT nested by_severity): + \`{ "total_findings": N, "critical": N, "high": N, "medium": N, "low": N, "files_analyzed": N, "lines_reviewed": N }\` + + Finding required fields: + - id: format \`{dim}-{seq}-{uuid8}\` e.g., \`sec-001-a1b2c3d4\` (lowercase) + - severity: lowercase only (critical|high|medium|low) + - snippet (NOT code_snippet), impact (NOT exploit_scenario) + - metadata, iteration (0), status (pending_remediation), cross_references + +2. Analysis Report: ${outputDir}/reports/${dimension}-analysis.md + - Human-readable summary with recommendations + - Grouped by severity: critical → high → medium → low + - Include file:line references for all findings + +3. CLI Output Log: ${outputDir}/reports/${dimension}-cli-output.txt + - Raw CLI tool output for debugging + - Include full analysis text + +## Dimension-Specific Guidance +${getDimensionGuidance(dimension)} + +## Success Criteria +- [ ] Schema obtained via cat review-dimension-results-schema.json +- [ ] All changed files analyzed for ${dimension} concerns +- [ ] All findings include file:line references with code snippets +- [ ] Severity assessment follows established criteria (see reference) +- [ ] Recommendations are actionable with code examples +- [ ] JSON output follows schema exactly +- [ ] Report is comprehensive and well-organized +` + }); + + reviewAgents.push(agentId); +}); + +// Step 2: Batch wait for all 7 agents +const reviewResults = wait({ + ids: reviewAgents, + timeout_ms: 3600000 // 60 minutes +}); + +// Step 3: Check results and handle timeouts +if (reviewResults.timed_out) { + console.log('Some dimension reviews timed out, continuing with completed results'); +} + +reviewAgents.forEach((agentId, index) => { + const dimension = dimensions[index]; + if (reviewResults.status[agentId].completed) { + console.log(`${dimension} review completed`); + } else { + console.log(`${dimension} review failed or timed out`); + } +}); + +// Step 4: Cleanup all agents +reviewAgents.forEach(id => close_agent({ id })); +``` + +## Deep-Dive Agent Invocation Template + +**Deep-Dive Agent** (iteration execution): + +```javascript +// Spawn deep-dive agent +const deepDiveAgentId = spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/cli-explore-agent.md (MUST read first) +2. Read original finding: ${dimensionJsonPath} +3. Read affected file: ${file} +4. Identify related code: bash(grep -r "import.*${basename(file)}" ${projectDir}/src --include="*.ts") +5. Read test files: bash(find ${projectDir}/tests -name "*${basename(file, '.ts')}*" -type f) +6. Execute: cat ~/.codex/workflows/cli-templates/schemas/review-deep-dive-results-schema.json (get output schema reference) +7. Read: .workflow/project-tech.json (technology stack and architecture context) +8. Read: .workflow/project-guidelines.json (user-defined constraints for remediation compliance) + +--- + +## Task Objective +Perform focused root cause analysis using Dependency Map mode (for impact analysis) + Deep Scan mode (for semantic understanding) to generate comprehensive remediation plan for critical ${dimension} issue + +## Analysis Mode Selection +Use **Dependency Map mode** first to understand dependencies: +- Build dependency graph around ${file} to identify affected components +- Detect circular dependencies or tight coupling related to this finding +- Calculate change risk scores for remediation impact + +Then apply **Deep Scan mode** for semantic analysis: +- Understand design intent and architectural context +- Identify non-standard patterns or implicit dependencies +- Extract remediation insights from code structure + +## Finding Context +- Finding ID: ${findingId} +- Original Dimension: ${dimension} +- Title: ${findingTitle} +- File: ${file}:${line} +- Severity: ${severity} +- Category: ${category} +- Original Description: ${description} +- Iteration: ${iteration} + +## CLI Configuration +- Tool Priority: gemini → qwen → codex +- Template: ~/.codex/workflows/cli-templates/prompts/analysis/01-diagnose-bug-root-cause.txt +- Mode: analysis (READ-ONLY) + +## Expected Deliverables + +**Schema Reference**: Schema obtained in MANDATORY FIRST STEPS step 6, follow schema exactly + +1. Deep-Dive Results JSON: ${outputDir}/iterations/iteration-${iteration}-finding-${findingId}.json + + **⚠️ CRITICAL JSON STRUCTURE REQUIREMENTS**: + + Root structure MUST be array: \`[{ ... }]\` NOT \`{ ... }\` + + Required top-level fields: + - finding_id, dimension, iteration, analysis_timestamp + - cli_tool_used, model, analysis_duration_ms + - original_finding, root_cause, remediation_plan + - impact_assessment, reassessed_severity, confidence_score, cross_references + + All nested objects must follow schema exactly - read schema for field names + +2. Analysis Report: ${outputDir}/reports/deep-dive-${iteration}-${findingId}.md + - Detailed root cause analysis + - Step-by-step remediation plan + - Impact assessment and rollback strategy + +## Success Criteria +- [ ] Schema obtained via cat review-deep-dive-results-schema.json +- [ ] Root cause clearly identified with supporting evidence +- [ ] Remediation plan is step-by-step actionable with exact file:line references +- [ ] Each step includes specific commands and validation tests +- [ ] Impact fully assessed (files, tests, breaking changes, dependencies) +- [ ] Severity re-evaluation justified with evidence +- [ ] Confidence score accurately reflects certainty of analysis +- [ ] JSON output follows schema exactly +- [ ] References include project-specific and external documentation +` +}); + +// Wait for completion +const deepDiveResult = wait({ + ids: [deepDiveAgentId], + timeout_ms: 2400000 // 40 minutes +}); + +// Cleanup +close_agent({ id: deepDiveAgentId }); +``` + +## Dimension Guidance Reference + +```javascript +function getDimensionGuidance(dimension) { + const guidance = { + security: ` + Focus Areas: + - Input validation and sanitization + - Authentication and authorization mechanisms + - Data encryption (at-rest and in-transit) + - SQL/NoSQL injection vulnerabilities + - XSS, CSRF, and other web vulnerabilities + - Sensitive data exposure + - Access control and privilege escalation + + Severity Criteria: + - Critical: Authentication bypass, SQL injection, RCE, sensitive data exposure + - High: Missing authorization checks, weak encryption, exposed secrets + - Medium: Missing input validation, insecure defaults, weak password policies + - Low: Security headers missing, verbose error messages, outdated dependencies + `, + architecture: ` + Focus Areas: + - Layering and separation of concerns + - Coupling and cohesion + - Design pattern adherence + - Dependency management + - Scalability and extensibility + - Module boundaries + - API design consistency + + Severity Criteria: + - Critical: Circular dependencies, god objects, tight coupling across layers + - High: Violated architectural principles, scalability bottlenecks + - Medium: Missing abstractions, inconsistent patterns, suboptimal design + - Low: Minor coupling issues, documentation gaps, naming inconsistencies + `, + quality: ` + Focus Areas: + - Code duplication + - Complexity (cyclomatic, cognitive) + - Naming conventions + - Error handling patterns + - Code readability + - Comment quality + - Dead code + + Severity Criteria: + - Critical: Severe complexity (CC > 20), massive duplication (>50 lines) + - High: High complexity (CC > 10), significant duplication, poor error handling + - Medium: Moderate complexity (CC > 5), naming issues, code smells + - Low: Minor duplication, documentation gaps, cosmetic issues + `, + 'action-items': ` + Focus Areas: + - Requirements coverage verification + - Acceptance criteria met + - Documentation completeness + - Deployment readiness + - Missing functionality + - Test coverage gaps + - Configuration management + + Severity Criteria: + - Critical: Core requirements not met, deployment blockers + - High: Significant functionality missing, acceptance criteria not met + - Medium: Minor requirements gaps, documentation incomplete + - Low: Nice-to-have features missing, minor documentation gaps + `, + performance: ` + Focus Areas: + - N+1 query problems + - Inefficient algorithms (O(n^2) where O(n log n) possible) + - Memory leaks + - Blocking operations on main thread + - Missing caching opportunities + - Resource usage (CPU, memory, network) + - Database query optimization + + Severity Criteria: + - Critical: Memory leaks, O(n^2) in hot path, blocking main thread + - High: N+1 queries, missing indexes, inefficient algorithms + - Medium: Suboptimal caching, unnecessary computations, lazy loading issues + - Low: Minor optimization opportunities, redundant operations + `, + maintainability: ` + Focus Areas: + - Technical debt indicators + - Magic numbers and hardcoded values + - Long methods (>50 lines) + - Large classes (>500 lines) + - Dead code and commented code + - Code documentation + - Test coverage + + Severity Criteria: + - Critical: Massive methods (>200 lines), severe technical debt blocking changes + - High: Large methods (>100 lines), significant dead code, undocumented complex logic + - Medium: Magic numbers, moderate technical debt, missing tests + - Low: Minor refactoring opportunities, cosmetic improvements + `, + 'best-practices': ` + Focus Areas: + - Framework conventions adherence + - Language idioms + - Anti-patterns + - Deprecated API usage + - Coding standards compliance + - Error handling patterns + - Logging and monitoring + + Severity Criteria: + - Critical: Severe anti-patterns, deprecated APIs with security risks + - High: Major convention violations, poor error handling, missing logging + - Medium: Minor anti-patterns, style inconsistencies, suboptimal patterns + - Low: Cosmetic style issues, minor convention deviations + ` + }; + + return guidance[dimension] || 'Standard code review analysis'; +} +``` + +## Output + +- Files: `dimensions/{dimension}.json`, `reports/{dimension}-analysis.md`, `reports/{dimension}-cli-output.txt` +- Progress: Mark Phase 2 completed, Phase 3 in_progress + +## Next Phase + +Return to orchestrator, then auto-continue to [Phase 3: Aggregation](03-aggregation.md). diff --git a/.codex/skills/review-cycle/phases/03-aggregation.md b/.codex/skills/review-cycle/phases/03-aggregation.md new file mode 100644 index 00000000..1895a182 --- /dev/null +++ b/.codex/skills/review-cycle/phases/03-aggregation.md @@ -0,0 +1,74 @@ +# Phase 3: Aggregation + +> Source: Shared from `commands/workflow/review-session-cycle.md` + `commands/workflow/review-module-cycle.md` Phase 3 + +## Overview + +Load all dimension results, calculate severity distribution, identify cross-cutting concerns, and decide whether to enter iterative deep-dive (Phase 4) or proceed to completion (Phase 5). + +## Execution Steps + +### Step 3.1: Load Dimension Results + +- Load all dimension JSON files from `{outputDir}/dimensions/` +- Parse each file following review-dimension-results-schema.json +- Handle missing files gracefully (log warning, skip) + +### Step 3.2: Calculate Severity Distribution + +- Count findings by severity level: critical, high, medium, low +- Store in review-state.json `severity_distribution` field + +### Step 3.3: Cross-Cutting Concern Detection + +**Cross-Cutting Concern Detection**: +1. Files appearing in 3+ dimensions = **Critical Files** +2. Same issue pattern across dimensions = **Systemic Issue** +3. Severity clustering in specific files = **Hotspots** + +### Step 3.4: Deep-Dive Selection + +**Deep-Dive Selection Criteria**: +- All critical severity findings (priority 1) +- Top 3 high-severity findings in critical files (priority 2) +- Max 5 findings per iteration (prevent overwhelm) + +### Step 3.5: Decision Logic + +**Iteration Trigger**: +- Critical findings > 0 OR +- High findings > 5 OR +- Critical files count > 0 + +If any trigger condition is met, proceed to Phase 4 (Iterative Deep-Dive). Otherwise, skip to Phase 5 (Completion). + +### Step 3.6: Update State + +- Update review-state.json with aggregation results +- Update review-progress.json + +**Phase 3 Orchestrator Responsibilities**: +- Load all dimension JSON files from dimensions/ +- Calculate severity distribution: Count by critical/high/medium/low +- Identify cross-cutting concerns: Files in 3+ dimensions +- Select deep-dive findings: Critical + high in critical files (max 5) +- Decision logic: Iterate if critical > 0 OR high > 5 OR critical files exist +- Update review-state.json with aggregation results + +## Severity Assessment Reference + +**Severity Levels**: +- **Critical**: Security vulnerabilities, data corruption risks, system-wide failures, authentication/authorization bypass +- **High**: Feature degradation, performance bottlenecks, architecture violations, significant technical debt +- **Medium**: Code smells, minor performance issues, style inconsistencies, maintainability concerns +- **Low**: Documentation gaps, minor refactoring opportunities, cosmetic issues + +## Output + +- Variables: severityDistribution, criticalFiles, deepDiveFindings, shouldIterate (boolean) +- State: review-state.json updated with aggregation results + +## Next Phase + +- If shouldIterate: [Phase 4: Iterative Deep-Dive](04-iterative-deep-dive.md) +- Else: [Phase 5: Review Completion](05-review-completion.md) diff --git a/.codex/skills/review-cycle/phases/04-iterative-deep-dive.md b/.codex/skills/review-cycle/phases/04-iterative-deep-dive.md new file mode 100644 index 00000000..75add650 --- /dev/null +++ b/.codex/skills/review-cycle/phases/04-iterative-deep-dive.md @@ -0,0 +1,333 @@ +# Phase 4: Iterative Deep-Dive + +> Source: Shared from `commands/workflow/review-session-cycle.md` + `commands/workflow/review-module-cycle.md` Phase 4 + +## Overview + +Perform focused root cause analysis on critical findings. Select up to 5 findings per iteration, launch deep-dive agents, re-assess severity, and loop back to aggregation if needed. + +## Prerequisites + +- Phase 3 determined shouldIterate = true +- Available: severityDistribution, criticalFiles, deepDiveFindings + +## Execution Steps + +### Step 4.1: Check Iteration Limit + +- Check `current_iteration` < `max_iterations` (default 3) +- If exceeded: Log iteration limit reached, skip to Phase 5 +- Default iterations: 1 (deep-dive runs once; use --max-iterations=0 to skip entirely) + +### Step 4.2: Select Findings for Deep-Dive + +**Deep-Dive Selection Criteria**: +- All critical severity findings (priority 1) +- Top 3 high-severity findings in critical files (priority 2) +- Max 5 findings per iteration (prevent overwhelm) + +**Selection algorithm**: +1. Collect all findings with severity = critical -> add to selection +2. If selection < 5: add high-severity findings from critical files (files in 3+ dimensions), sorted by dimension count descending +3. Cap at 5 total findings + +### Step 4.3: Launch Deep-Dive Agents + +- Spawn cli-explore-agent for each selected finding +- Use Dependency Map + Deep Scan mode +- Each agent runs independently (can be launched in parallel) +- Tool priority: gemini -> qwen -> codex (fallback on error/timeout) +- Lifecycle: spawn_agent → batch wait → close_agent + +### Step 4.4: Collect Results + +- Parse iteration JSON files from `{outputDir}/iterations/iteration-{N}-finding-{uuid}.json` +- Extract reassessed severities from each result +- Collect remediation plans and impact assessments +- Handle agent failures gracefully (log warning, mark finding as unanalyzed) + +### Step 4.5: Re-Aggregate + +- Update severity distribution based on reassessments +- Record iteration in review-state.json `iterations[]` array: + +```json +{ + "iteration": 1, + "findings_analyzed": ["uuid-1", "uuid-2"], + "findings_resolved": 1, + "findings_escalated": 1, + "severity_change": { + "before": {"critical": 2, "high": 5, "medium": 12, "low": 8}, + "after": {"critical": 1, "high": 6, "medium": 12, "low": 8} + }, + "timestamp": "2025-01-25T14:30:00Z" +} +``` + +- Increment `current_iteration` in review-state.json +- Re-evaluate decision logic: Iterate if critical > 0 OR high > 5 OR critical files exist +- Loop back to Phase 3 aggregation check if conditions still met + +## Deep-Dive Agent Invocation Template + +### Module Mode + +```javascript +// Step 1: Spawn deep-dive agents in parallel +const deepDiveAgents = []; + +selectedFindings.forEach(finding => { + const agentId = spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/cli-explore-agent.md (MUST read first) +2. Read original finding: ${dimensionJsonPath} +3. Read affected file: ${finding.file} +4. Identify related code: bash(grep -r "import.*${basename(finding.file)}" ${projectDir}/src --include="*.ts") +5. Read test files: bash(find ${projectDir}/tests -name "*${basename(finding.file, '.ts')}*" -type f) +6. Execute: cat ~/.codex/workflows/cli-templates/schemas/review-deep-dive-results-schema.json (get output schema reference) +7. Read: .workflow/project-tech.json (technology stack and architecture context) +8. Read: .workflow/project-guidelines.json (user-defined constraints for remediation compliance) + +--- + +## Task Objective +Perform focused root cause analysis using Dependency Map mode (for impact analysis) + Deep Scan mode (for semantic understanding) to generate comprehensive remediation plan for critical ${finding.dimension} issue + +## Analysis Mode Selection +Use **Dependency Map mode** first to understand dependencies: +- Build dependency graph around ${finding.file} to identify affected components +- Detect circular dependencies or tight coupling related to this finding +- Calculate change risk scores for remediation impact + +Then apply **Deep Scan mode** for semantic analysis: +- Understand design intent and architectural context +- Identify non-standard patterns or implicit dependencies +- Extract remediation insights from code structure + +## Finding Context +- Finding ID: ${finding.id} +- Original Dimension: ${finding.dimension} +- Title: ${finding.title} +- File: ${finding.file}:${finding.line} +- Severity: ${finding.severity} +- Category: ${finding.category} +- Original Description: ${finding.description} +- Iteration: ${iteration} + +## CLI Configuration +- Tool Priority: gemini → qwen → codex +- Template: ~/.codex/workflows/cli-templates/prompts/analysis/01-diagnose-bug-root-cause.txt +- Mode: analysis (READ-ONLY) + +## Expected Deliverables + +**Schema Reference**: Schema obtained in MANDATORY FIRST STEPS step 6, follow schema exactly + +1. Deep-Dive Results JSON: ${outputDir}/iterations/iteration-${iteration}-finding-${finding.id}.json + + **⚠️ CRITICAL JSON STRUCTURE REQUIREMENTS**: + + Root structure MUST be array: \`[{ ... }]\` NOT \`{ ... }\` + + Required top-level fields: + - finding_id, dimension, iteration, analysis_timestamp + - cli_tool_used, model, analysis_duration_ms + - original_finding, root_cause, remediation_plan + - impact_assessment, reassessed_severity, confidence_score, cross_references + + All nested objects must follow schema exactly - read schema for field names + +2. Analysis Report: ${outputDir}/reports/deep-dive-${iteration}-${finding.id}.md + - Detailed root cause analysis + - Step-by-step remediation plan + - Impact assessment and rollback strategy + +## Success Criteria +- [ ] Schema obtained via cat review-deep-dive-results-schema.json +- [ ] Root cause clearly identified with supporting evidence +- [ ] Remediation plan is step-by-step actionable with exact file:line references +- [ ] Each step includes specific commands and validation tests +- [ ] Impact fully assessed (files, tests, breaking changes, dependencies) +- [ ] Severity re-evaluation justified with evidence +- [ ] Confidence score accurately reflects certainty of analysis +- [ ] JSON output follows schema exactly +- [ ] References include project-specific and external documentation +` + }); + + deepDiveAgents.push(agentId); +}); + +// Step 2: Batch wait for all deep-dive agents +const deepDiveResults = wait({ + ids: deepDiveAgents, + timeout_ms: 2400000 // 40 minutes +}); + +// Step 3: Collect results +deepDiveAgents.forEach((agentId, index) => { + const finding = selectedFindings[index]; + if (deepDiveResults.status[agentId].completed) { + console.log(`Deep-dive completed for ${finding.id}`); + } else { + console.log(`Deep-dive failed/timed out for ${finding.id}`); + } +}); + +// Step 4: Cleanup all agents +deepDiveAgents.forEach(id => close_agent({ id })); +``` + +### Session Mode + +```javascript +// Step 1: Spawn deep-dive agents in parallel +const deepDiveAgents = []; + +selectedFindings.forEach(finding => { + const agentId = spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/cli-explore-agent.md (MUST read first) +2. Read original finding: ${dimensionJsonPath} +3. Read affected file: ${finding.file} +4. Identify related code: bash(grep -r "import.*${basename(finding.file)}" ${workflowDir}/src --include="*.ts") +5. Read test files: bash(find ${workflowDir}/tests -name "*${basename(finding.file, '.ts')}*" -type f) +6. Execute: cat ~/.codex/workflows/cli-templates/schemas/review-deep-dive-results-schema.json (get output schema reference) +7. Read: .workflow/project-tech.json (technology stack and architecture context) +8. Read: .workflow/project-guidelines.json (user-defined constraints for remediation compliance) + +--- + +## Task Objective +Perform focused root cause analysis using Dependency Map mode (for impact analysis) + Deep Scan mode (for semantic understanding) to generate comprehensive remediation plan for critical ${finding.dimension} issue + +## Analysis Mode Selection +Use **Dependency Map mode** first to understand dependencies: +- Build dependency graph around ${finding.file} to identify affected components +- Detect circular dependencies or tight coupling related to this finding +- Calculate change risk scores for remediation impact + +Then apply **Deep Scan mode** for semantic analysis: +- Understand design intent and architectural context +- Identify non-standard patterns or implicit dependencies +- Extract remediation insights from code structure + +## Finding Context +- Finding ID: ${finding.id} +- Original Dimension: ${finding.dimension} +- Title: ${finding.title} +- File: ${finding.file}:${finding.line} +- Severity: ${finding.severity} +- Category: ${finding.category} +- Original Description: ${finding.description} +- Iteration: ${iteration} + +## CLI Configuration +- Tool Priority: gemini → qwen → codex +- Template: ~/.codex/workflows/cli-templates/prompts/analysis/01-diagnose-bug-root-cause.txt +- Timeout: 2400000ms (40 minutes) +- Mode: analysis (READ-ONLY) + +## Expected Deliverables + +**Schema Reference**: Schema obtained in MANDATORY FIRST STEPS step 6, follow schema exactly + +1. Deep-Dive Results JSON: ${outputDir}/iterations/iteration-${iteration}-finding-${finding.id}.json + + **⚠️ CRITICAL JSON STRUCTURE REQUIREMENTS**: + + Root structure MUST be array: \`[{ ... }]\` NOT \`{ ... }\` + + Required top-level fields: + - finding_id, dimension, iteration, analysis_timestamp + - cli_tool_used, model, analysis_duration_ms + - original_finding, root_cause, remediation_plan + - impact_assessment, reassessed_severity, confidence_score, cross_references + + All nested objects must follow schema exactly - read schema for field names + +2. Analysis Report: ${outputDir}/reports/deep-dive-${iteration}-${finding.id}.md + - Detailed root cause analysis + - Step-by-step remediation plan + - Impact assessment and rollback strategy + +## Success Criteria +- [ ] Schema obtained via cat review-deep-dive-results-schema.json +- [ ] Root cause clearly identified with supporting evidence +- [ ] Remediation plan is step-by-step actionable with exact file:line references +- [ ] Each step includes specific commands and validation tests +- [ ] Impact fully assessed (files, tests, breaking changes, dependencies) +- [ ] Severity re-evaluation justified with evidence +- [ ] Confidence score accurately reflects certainty of analysis +- [ ] JSON output follows schema exactly +- [ ] References include project-specific and external documentation +` + }); + + deepDiveAgents.push(agentId); +}); + +// Step 2: Batch wait for all deep-dive agents +const deepDiveResults = wait({ + ids: deepDiveAgents, + timeout_ms: 2400000 // 40 minutes +}); + +// Step 3: Collect results +deepDiveAgents.forEach((agentId, index) => { + const finding = selectedFindings[index]; + if (deepDiveResults.status[agentId].completed) { + console.log(`Deep-dive completed for ${finding.id}`); + } else { + console.log(`Deep-dive failed/timed out for ${finding.id}`); + } +}); + +// Step 4: Cleanup all agents +deepDiveAgents.forEach(id => close_agent({ id })); +``` + +## Key Differences Between Modes + +| Aspect | Module Mode | Session Mode | +|--------|-------------|--------------| +| MANDATORY STEP 4 | `${projectDir}/src` | `${workflowDir}/src` | +| MANDATORY STEP 5 | `${projectDir}/tests` | `${workflowDir}/tests` | +| CLI Timeout | (not specified) | 2400000ms (40 minutes) | + +## Iteration Control + +**Phase 4 Orchestrator Responsibilities**: +- Check iteration count < max_iterations (default 3) +- Spawn deep-dive agents for selected findings +- Collect remediation plans and re-assessed severities +- Update severity distribution based on re-assessments +- Record iteration in review-state.json +- Loop back to aggregation if still have critical/high findings + +**Termination Conditions** (any one stops iteration): +1. `current_iteration` >= `max_iterations` +2. No critical findings remaining AND high findings <= 5 AND no critical files +3. No findings selected for deep-dive (all resolved or downgraded) + +**State Updates Per Iteration**: +- `review-state.json`: Increment `current_iteration`, append to `iterations[]`, update `severity_distribution`, set `next_action` +- `review-progress.json`: Update `deep_dive.analyzed` count, `deep_dive.percent_complete`, `phase` + +## Output + +- Files: `iterations/iteration-{N}-finding-{uuid}.json`, `reports/deep-dive-{N}-{uuid}.md` +- State: review-state.json `iterations[]` updated +- Decision: Re-enter Phase 3 aggregation or proceed to Phase 5 + +## Next Phase + +- If still has critical findings AND iterations < max: Loop to [Phase 3: Aggregation](03-aggregation.md) +- Else: [Phase 5: Review Completion](05-review-completion.md) diff --git a/.codex/skills/review-cycle/phases/05-review-completion.md b/.codex/skills/review-cycle/phases/05-review-completion.md new file mode 100644 index 00000000..80ca356b --- /dev/null +++ b/.codex/skills/review-cycle/phases/05-review-completion.md @@ -0,0 +1,173 @@ +# Phase 5: Review Completion + +> Source: Shared from `commands/workflow/review-session-cycle.md` + `commands/workflow/review-module-cycle.md` Phase 5 + +## Overview + +Finalize review state, generate completion statistics, and optionally prompt for automated fix pipeline. + +## Execution Steps + +### Step 5.1: Finalize State + +**Phase 5 Orchestrator Responsibilities**: +- Finalize review-progress.json with completion statistics +- Update review-state.json with completion_time and phase=complete +- Progress tracking: Mark all tasks done + +**review-state.json updates**: +```json +{ + "phase": "complete", + "completion_time": "2025-01-25T15:00:00Z", + "next_action": "none" +} +``` + +**review-progress.json updates**: +```json +{ + "phase": "complete", + "overall_percent": 100, + "completion_time": "2025-01-25T15:00:00Z", + "final_severity_distribution": { + "critical": 0, + "high": 3, + "medium": 12, + "low": 8 + } +} +``` + +### Step 5.2: Evaluate Completion Status + +**Full Success**: +- All dimensions reviewed +- Critical findings = 0 +- High findings <= 5 +- Action: Generate final report, mark phase=complete + +**Partial Success**: +- All dimensions reviewed +- Max iterations reached +- Still have critical/high findings +- Action: Generate report with warnings, recommend follow-up + +### Step 5.3: Progress Tracking Completion + +Update progress tracking to reflect all phases completed: +``` +Phase 1: Discovery & Initialization → completed +Phase 2: Parallel Reviews (7 dimensions) → completed + → Security review → completed + → Architecture review → completed + → Quality review → completed + ... other dimensions as sub-items +Phase 3: Aggregation → completed +Phase 4: Deep-dive → completed +Phase 5: Completion → completed +``` + +### Step 5.4: Fix Pipeline Prompt + +- Ask user: "Run automated fixes on findings? [Y/n]" +- If confirmed AND --fix flag: Continue to Phase 6 +- Display summary of findings by severity: + +``` +Review Complete - Summary: + Critical: 0 High: 3 Medium: 12 Low: 8 + Total findings: 23 + Dimensions reviewed: 7/7 + Iterations completed: 2/3 + +Run automated fixes on findings? [Y/n] +``` + +## Completion Conditions + +**Full Success**: +- All dimensions reviewed +- Critical findings = 0 +- High findings <= 5 +- Action: Generate final report, mark phase=complete + +**Partial Success**: +- All dimensions reviewed +- Max iterations reached +- Still have critical/high findings +- Action: Generate report with warnings, recommend follow-up + +## Error Handling Reference + +### Phase-Level Error Matrix + +| Phase | Error | Blocking? | Action | +|-------|-------|-----------|--------| +| Phase 1 | Invalid path pattern / Session not found | Yes | Error and exit | +| Phase 1 | No files matched / No completed tasks | Yes | Error and exit | +| Phase 1 | Files not readable / No changed files | Yes | Error and exit | +| Phase 2 | Single dimension fails | No | Log warning, continue other dimensions | +| Phase 2 | All dimensions fail | Yes | Error and exit | +| Phase 3 | Missing dimension JSON | No | Skip in aggregation, log warning | +| Phase 4 | Deep-dive agent fails | No | Skip finding, continue others | +| Phase 4 | Max iterations reached | No | Generate partial report | + +### CLI Fallback Chain + +Gemini -> Qwen -> Codex -> degraded mode + +### Fallback Triggers + +1. HTTP 429, 5xx errors, connection timeout +2. Invalid JSON output (parse error, missing required fields) +3. Low confidence score < 0.4 +4. Analysis too brief (< 100 words in report) + +### Fallback Behavior + +- On trigger: Retry with next tool in chain +- After Codex fails: Enter degraded mode (skip analysis, log error) +- Degraded mode: Continue workflow with available results + +## Best Practices + +1. **Start Specific**: Begin with focused module patterns for faster results +2. **Expand Gradually**: Add more modules based on initial findings +3. **Use Glob Wisely**: `src/auth/**` is more efficient than `src/**` with lots of irrelevant files +4. **Trust Aggregation Logic**: Auto-selection based on proven heuristics +5. **Monitor Logs**: Check reports/ directory for CLI analysis insights + +## Related Commands + +### View Review Progress + +Use `ccw view` to open the review dashboard in browser: + +```bash +ccw view +``` + +### Automated Fix Workflow + +After completing a review, use the generated findings JSON for automated fixing: + +```bash +# Step 1: Complete review (this skill) +review-cycle src/auth/** +# OR +review-cycle + +# Step 2: Run automated fixes using dimension findings +review-cycle --fix .workflow/active/WFS-{session-id}/.review/ +``` + +## Output + +- State: review-state.json (phase=complete), review-progress.json (final) +- Decision: fix pipeline or end + +## Next Phase + +- If fix requested: [Phase 6: Fix Discovery & Batching](06-fix-discovery-batching.md) +- Else: Workflow complete diff --git a/.codex/skills/review-cycle/phases/06-fix-discovery-batching.md b/.codex/skills/review-cycle/phases/06-fix-discovery-batching.md new file mode 100644 index 00000000..d968d258 --- /dev/null +++ b/.codex/skills/review-cycle/phases/06-fix-discovery-batching.md @@ -0,0 +1,238 @@ +# Phase 6: Fix Discovery & Batching + +> Source: `commands/workflow/review-cycle-fix.md` Phase 1 + Phase 1.5 + +## Overview + +Validate fix input source, create fix session structure, and perform intelligent grouping of findings into batches for parallel planning. + +## Quick Start + +```bash +# Fix from exported findings file (session-based path) +review-cycle --fix .workflow/active/WFS-123/.review/fix-export-1706184622000.json + +# Fix from review directory (auto-discovers latest export) +review-cycle --fix .workflow/active/WFS-123/.review/ + +# Resume interrupted fix session +review-cycle --fix --resume + +# Custom max retry attempts per finding +review-cycle --fix .workflow/active/WFS-123/.review/ --max-iterations=5 + +# Custom batch size for parallel planning (default: 5 findings per batch) +review-cycle --fix .workflow/active/WFS-123/.review/ --batch-size=3 +``` + +**Fix Source**: Exported findings from review cycle dashboard +**Output Directory**: `{review-dir}/fixes/{fix-session-id}/` (within session .review/) +**Default Max Iterations**: 3 (per finding, adjustable) +**Default Batch Size**: 5 (findings per planning batch, adjustable) +**Max Parallel Agents**: 10 (concurrent planning agents) +**CLI Tools**: @cli-planning-agent (planning), @cli-execute-agent (fixing) + +## Core Concept + +Automated fix orchestrator with **parallel planning architecture**: Multiple AI agents analyze findings concurrently in batches, then coordinate parallel/serial execution. Generates fix timeline with intelligent grouping and dependency analysis, executes fixes with conservative test verification. + +**Fix Process**: +- **Batching Phase (1.5)**: Orchestrator groups findings by file+dimension similarity, creates batches +- **Planning Phase (2)**: Up to 10 agents plan batches in parallel, generate partial plans, orchestrator aggregates +- **Execution Phase (3)**: Main orchestrator coordinates agents per aggregated timeline stages +- **Parallel Efficiency**: Customizable batch size (default: 5), MAX_PARALLEL=10 agents +- **No rigid structure**: Adapts to task requirements, not bound to fixed JSON format + +**vs Manual Fixing**: +- **Manual**: Developer reviews findings one-by-one, fixes sequentially +- **Automated**: AI groups related issues, multiple agents plan in parallel, executes in optimal parallel/serial order with automatic test verification + +### Value Proposition +1. **Parallel Planning**: Multiple agents analyze findings concurrently, reducing planning time for large batches (10+ findings) +2. **Intelligent Batching**: Semantic similarity grouping ensures related findings are analyzed together +3. **Multi-stage Coordination**: Supports complex parallel + serial execution with cross-batch dependency management +4. **Conservative Safety**: Mandatory test verification with automatic rollback on failure +5. **Resume Support**: Checkpoint-based recovery for interrupted sessions + +### Orchestrator Boundary (CRITICAL) +- **ONLY command** for automated review finding fixes +- Manages: Intelligent batching (Phase 1.5), parallel planning coordination (spawn N agents), plan aggregation (merge partial plans, resolve cross-batch dependencies), stage-based execution scheduling, agent scheduling, progress tracking +- Delegates: Batch planning to @cli-planning-agent, fix execution to @cli-execute-agent + +## Fix Process Overview + +``` +Phase 1: Discovery & Initialization + └─ Validate export file, create fix session structure, initialize state files + +Phase 1.5: Intelligent Grouping & Batching + ├─ Analyze findings metadata (file, dimension, severity) + ├─ Group by semantic similarity (file proximity + dimension affinity) + ├─ Create batches respecting --batch-size (default: 5) + └─ Output: Finding batches for parallel planning + +Phase 2: Parallel Planning Coordination (@cli-planning-agent × N) + ├─ Spawn MAX_PARALLEL planning agents concurrently (default: 10) + ├─ Each agent processes one batch: + │ ├─ Analyze findings for patterns and dependencies + │ ├─ Group by file + dimension + root cause similarity + │ ├─ Determine execution strategy (parallel/serial/hybrid) + │ ├─ Generate fix timeline with stages + │ └─ Output: partial-plan-{batch-id}.json + ├─ Collect results from all agents + └─ Aggregate: Merge partial plans → fix-plan.json (resolve cross-batch dependencies) + +Phase 3: Execution Orchestration (Stage-based) + For each timeline stage: + ├─ Load groups for this stage + ├─ If parallel: Spawn all group agents simultaneously + ├─ If serial: Execute groups sequentially + ├─ Each agent: + │ ├─ Analyze code context + │ ├─ Apply fix per strategy + │ ├─ Run affected tests + │ ├─ On test failure: Rollback, retry up to max_iterations + │ └─ On success: Commit, update fix-progress-{N}.json + └─ Advance to next stage + +Phase 4: Completion & Aggregation + └─ Aggregate results → Generate fix-summary.md → Update history → Output summary + +Phase 5: Session Completion (Optional) + └─ If all fixes successful → Prompt to complete workflow session +``` + +## Agent Roles + +| Agent | Responsibility | +|-------|---------------| +| **Orchestrator** | Input validation, session management, intelligent batching (Phase 1.5), parallel planning coordination (spawn N agents), plan aggregation (merge partial plans, resolve cross-batch dependencies), stage-based execution scheduling, progress tracking, result aggregation | +| **@cli-planning-agent** | Batch findings analysis, intelligent grouping (file+dimension+root cause), execution strategy determination (parallel/serial/hybrid), timeline generation with dependency mapping, partial plan output | +| **@cli-execute-agent** | Fix execution per group, code context analysis, Edit tool operations, test verification, git rollback on failure, completion JSON generation | + +## Parallel Planning Architecture + +**Batch Processing Strategy**: + +| Phase | Agent Count | Input | Output | Purpose | +|-------|-------------|-------|--------|---------| +| **Batching (1.5)** | Orchestrator | All findings | Finding batches | Semantic grouping by file+dimension, respecting --batch-size | +| **Planning (2)** | N agents (≤10) | 1 batch each | partial-plan-{batch-id}.json | Analyze batch in parallel, generate execution groups and timeline | +| **Aggregation (2)** | Orchestrator | All partial plans | fix-plan.json | Merge timelines, resolve cross-batch dependencies | +| **Execution (3)** | M agents (dynamic) | 1 group each | fix-progress-{N}.json | Execute fixes per aggregated plan with test verification | + +**Benefits**: +- **Speed**: N agents plan concurrently, reducing planning time for large batches +- **Scalability**: MAX_PARALLEL=10 prevents resource exhaustion +- **Flexibility**: Batch size customizable via --batch-size (default: 5) +- **Isolation**: Each planning agent focuses on related findings (semantic grouping) +- **Reusable**: Aggregated plan can be re-executed without re-planning + +## Intelligent Grouping Strategy + +**Three-Level Grouping**: + +```javascript +// Level 1: Primary grouping by file + dimension +{file: "auth.ts", dimension: "security"} → Group A +{file: "auth.ts", dimension: "quality"} → Group B +{file: "query-builder.ts", dimension: "security"} → Group C + +// Level 2: Secondary grouping by root cause similarity +Group A findings → Semantic similarity analysis (threshold 0.7) + → Sub-group A1: "missing-input-validation" (findings 1, 2) + → Sub-group A2: "insecure-crypto" (finding 3) + +// Level 3: Dependency analysis +Sub-group A1 creates validation utilities +Sub-group C4 depends on those utilities +→ A1 must execute before C4 (serial stage dependency) +``` + +**Similarity Computation**: +- Combine: `description + recommendation + category` +- Vectorize: TF-IDF or LLM embedding +- Cluster: Greedy algorithm with cosine similarity > 0.7 + +## Phase 1: Discovery & Initialization (Orchestrator) + +**Phase 1 Orchestrator Responsibilities**: +- Input validation: Check export file exists and is valid JSON +- Auto-discovery: If review-dir provided, find latest `*-fix-export.json` +- Session creation: Generate fix-session-id (`fix-{timestamp}`) +- Directory structure: Create `{review-dir}/fixes/{fix-session-id}/` with subdirectories +- State files: Initialize active-fix-session.json (session marker) +- Progress tracking initialization: Set up 5-phase tracking (including Phase 1.5) + +## Phase 1.5: Intelligent Grouping & Batching (Orchestrator) + +- Load all findings metadata (id, file, dimension, severity, title) +- Semantic similarity analysis: + - Primary: Group by file proximity (same file or related modules) + - Secondary: Group by dimension affinity (same review dimension) + - Tertiary: Analyze title/description similarity (root cause clustering) +- Create batches respecting --batch-size (default: 5 findings per batch) +- Balance workload: Distribute high-severity findings across batches +- Output: Array of finding batches for parallel planning + +```javascript +// Load findings +const findings = JSON.parse(Read(exportFile)); +const batchSize = flags.batchSize || 5; + +// Semantic similarity analysis: group by file+dimension +const batches = []; +const grouped = new Map(); // key: "${file}:${dimension}" + +for (const finding of findings) { + const key = `${finding.file || 'unknown'}:${finding.dimension || 'general'}`; + if (!grouped.has(key)) grouped.set(key, []); + grouped.get(key).push(finding); +} + +// Create batches respecting batchSize +for (const [key, group] of grouped) { + while (group.length > 0) { + const batch = group.splice(0, batchSize); + batches.push({ + batch_id: batches.length + 1, + findings: batch, + metadata: { primary_file: batch[0].file, primary_dimension: batch[0].dimension } + }); + } +} + +console.log(`Created ${batches.length} batches (${batchSize} findings per batch)`); +``` + +## Output File Structure + +``` +.workflow/active/WFS-{session-id}/.review/ +├── fix-export-{timestamp}.json # Exported findings (input) +└── fixes/{fix-session-id}/ + ├── partial-plan-1.json # Batch 1 partial plan (planning agent 1 output) + ├── partial-plan-2.json # Batch 2 partial plan (planning agent 2 output) + ├── partial-plan-N.json # Batch N partial plan (planning agent N output) + ├── fix-plan.json # Aggregated execution plan (orchestrator merges partials) + ├── fix-progress-1.json # Group 1 progress (planning agent init → agent updates) + ├── fix-progress-2.json # Group 2 progress (planning agent init → agent updates) + ├── fix-progress-3.json # Group 3 progress (planning agent init → agent updates) + ├── fix-summary.md # Final report (orchestrator generates) + ├── active-fix-session.json # Active session marker + └── fix-history.json # All sessions history +``` + +**File Producers**: +- **Orchestrator**: Batches findings (Phase 1.5), aggregates partial plans → `fix-plan.json` (Phase 2), spawns parallel planning agents +- **Planning Agents (N)**: Each outputs `partial-plan-{batch-id}.json` + initializes `fix-progress-*.json` for assigned groups +- **Execution Agents (M)**: Update assigned `fix-progress-{N}.json` in real-time + +## Output + +- Variables: batches (array), fixSessionId, sessionDir +- Files: active-fix-session.json, directory structure created + +## Next Phase + +Return to orchestrator, then auto-continue to [Phase 7: Fix Parallel Planning](07-fix-parallel-planning.md). diff --git a/.codex/skills/review-cycle/phases/07-fix-parallel-planning.md b/.codex/skills/review-cycle/phases/07-fix-parallel-planning.md new file mode 100644 index 00000000..e51264e5 --- /dev/null +++ b/.codex/skills/review-cycle/phases/07-fix-parallel-planning.md @@ -0,0 +1,224 @@ +# Phase 7: Fix Parallel Planning + +> Source: `commands/workflow/review-cycle-fix.md` Phase 2 + +## Overview +Launch N planning agents (up to MAX_PARALLEL=10) to analyze finding batches concurrently. Each agent outputs a partial plan. Orchestrator aggregates partial plans into unified fix-plan.json. + +## Execution Strategy Determination + +**Strategy Types**: + +| Strategy | When to Use | Stage Structure | +|----------|-------------|-----------------| +| **Parallel** | All groups independent, different files | Single stage, all groups in parallel | +| **Serial** | Strong dependencies, shared resources | Multiple stages, one group per stage | +| **Hybrid** | Mixed dependencies | Multiple stages, parallel within stages | + +**Dependency Detection**: +- Shared file modifications +- Utility creation + usage patterns +- Test dependency chains +- Risk level clustering (high-risk groups isolated) + +## Phase 2: Parallel Planning Coordination (Orchestrator) + +```javascript +const MAX_PARALLEL = 10; +const partialPlans = []; + +// Process batches in chunks of MAX_PARALLEL +for (let i = 0; i < batches.length; i += MAX_PARALLEL) { + const chunk = batches.slice(i, i + MAX_PARALLEL); + const agentIds = []; + + // Step 1: Spawn agents in parallel + for (const batch of chunk) { + const agentId = spawn_agent({ + message: planningPrompt(batch) // See Planning Agent template below + }); + agentIds.push({ agentId, batch }); + } + + console.log(`Spawned ${agentIds.length} planning agents...`); + + // Step 2: Batch wait for all agents in this chunk + const chunkResults = wait({ + ids: agentIds.map(a => a.agentId), + timeout_ms: 600000 // 10 minutes + }); + + // Step 3: Collect results from this chunk + for (const { agentId, batch } of agentIds) { + if (chunkResults.status[agentId].completed) { + const partialPlan = JSON.parse(Read(`${sessionDir}/partial-plan-${batch.batch_id}.json`)); + partialPlans.push(partialPlan); + console.log(`Batch ${batch.batch_id} planning completed`); + } else { + console.log(`Batch ${batch.batch_id} planning failed/timed out`); + } + } + + // Step 4: Cleanup agents in this chunk + agentIds.forEach(({ agentId }) => close_agent({ id: agentId })); +} + +// Aggregate partial plans → fix-plan.json +let groupCounter = 1; +const groupIdMap = new Map(); + +for (const partial of partialPlans) { + for (const group of partial.groups) { + const newGroupId = `G${groupCounter}`; + groupIdMap.set(`${partial.batch_id}:${group.group_id}`, newGroupId); + aggregatedPlan.groups.push({ ...group, group_id: newGroupId, progress_file: `fix-progress-${groupCounter}.json` }); + groupCounter++; + } +} + +// Merge timelines, resolve cross-batch conflicts (shared files → serialize) +let stageCounter = 1; +for (const partial of partialPlans) { + for (const stage of partial.timeline) { + aggregatedPlan.timeline.push({ + ...stage, stage_id: stageCounter, + groups: stage.groups.map(gid => groupIdMap.get(`${partial.batch_id}:${gid}`)) + }); + stageCounter++; + } +} + +// Write aggregated plan + initialize progress files +Write(`${sessionDir}/fix-plan.json`, JSON.stringify(aggregatedPlan, null, 2)); +for (let i = 1; i <= aggregatedPlan.groups.length; i++) { + Write(`${sessionDir}/fix-progress-${i}.json`, JSON.stringify(initProgressFile(aggregatedPlan.groups[i-1]), null, 2)); +} +``` + +## Planning Agent Template (Batch Mode) + +```javascript +// Spawn planning agent for a batch +const agentId = spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/cli-planning-agent.md (MUST read first) +2. Read: .workflow/project-tech.json +3. Read: .workflow/project-guidelines.json + +--- + +## Task Objective +Analyze code review findings in batch ${batch.batch_id} and generate **partial** execution plan. + +## Input Data +Review Session: ${reviewId} +Fix Session ID: ${fixSessionId} +Batch ID: ${batch.batch_id} +Batch Findings: ${batch.findings.length} + +Findings: +${JSON.stringify(batch.findings, null, 2)} + +Project Context: +- Structure: ${projectStructure} +- Test Framework: ${testFramework} +- Git Status: ${gitStatus} + +## Output Requirements + +### 1. partial-plan-${batch.batch_id}.json +Generate partial execution plan with structure: +{ + "batch_id": ${batch.batch_id}, + "groups": [...], // Groups created from batch findings (use local IDs: G1, G2, ...) + "timeline": [...], // Local timeline for this batch only + "metadata": { + "findings_count": ${batch.findings.length}, + "groups_count": N, + "created_at": "ISO-8601-timestamp" + } +} + +**Key Generation Rules**: +- **Groups**: Create groups with local IDs (G1, G2, ...) using intelligent grouping (file+dimension+root cause) +- **Timeline**: Define stages for this batch only (local dependencies within batch) +- **Progress Files**: DO NOT generate fix-progress-*.json here (orchestrator handles after aggregation) + +## Analysis Requirements + +### Intelligent Grouping Strategy +Group findings using these criteria (in priority order): + +1. **File Proximity**: Findings in same file or related files +2. **Dimension Affinity**: Same dimension (security, performance, etc.) +3. **Root Cause Similarity**: Similar underlying issues +4. **Fix Approach Commonality**: Can be fixed with similar approach + +**Grouping Guidelines**: +- Optimal group size: 2-5 findings per group +- Avoid cross-cutting concerns in same group +- Consider test isolation (different test suites → different groups) +- Balance workload across groups for parallel execution + +### Execution Strategy Determination (Local Only) + +**Parallel Mode**: Use when groups are independent, no shared files +**Serial Mode**: Use when groups have dependencies or shared resources +**Hybrid Mode**: Use for mixed dependency graphs (recommended for most cases) + +**Dependency Analysis**: +- Identify shared files between groups +- Detect test dependency chains +- Evaluate risk of concurrent modifications + +### Risk Assessment + +For each group, evaluate: +- **Complexity**: Based on code structure, file size, existing tests +- **Impact Scope**: Number of files affected, API surface changes +- **Rollback Feasibility**: Ease of reverting changes if tests fail + +### Test Strategy + +For each group, determine: +- **Test Pattern**: Glob pattern matching affected tests +- **Pass Criteria**: All tests must pass (100% pass rate) +- **Test Command**: Infer from project (package.json, pytest.ini, etc.) + +## Output Files + +Write to ${sessionDir}: +- ./partial-plan-${batch.batch_id}.json + +## Quality Checklist + +Before finalizing outputs: +- All batch findings assigned to exactly one group +- Group dependencies (within batch) correctly identified +- Timeline stages respect local dependencies +- Test patterns are valid and specific +- Risk assessments are realistic +` +}); + +// Wait for completion +const result = wait({ + ids: [agentId], + timeout_ms: 600000 // 10 minutes +}); + +// Cleanup +close_agent({ id: agentId }); +``` + +## Output + +- Files: `partial-plan-{batch-id}.json` (per agent), `fix-plan.json` (aggregated), `fix-progress-*.json` (initialized) +- Progress: Mark Phase 7 completed, Phase 8 in_progress + +## Next Phase + +Return to orchestrator, then auto-continue to [Phase 8: Fix Execution](08-fix-execution.md). diff --git a/.codex/skills/review-cycle/phases/08-fix-execution.md b/.codex/skills/review-cycle/phases/08-fix-execution.md new file mode 100644 index 00000000..31cb9a94 --- /dev/null +++ b/.codex/skills/review-cycle/phases/08-fix-execution.md @@ -0,0 +1,239 @@ +# Phase 8: Fix Execution + +> Source: `commands/workflow/review-cycle-fix.md` Phase 3 + +## Overview +Stage-based execution using aggregated fix-plan.json timeline. Each group gets a cli-execute-agent that applies fixes, runs tests, and commits on success or rolls back on failure. + +## Conservative Test Verification + +**Test Strategy** (per fix): + +```javascript +// 1. Identify affected tests +const testPattern = identifyTestPattern(finding.file); +// e.g., "tests/auth/**/*.test.*" for src/auth/service.ts + +// 2. Run tests +const result = await runTests(testPattern); + +// 3. Evaluate +if (result.passRate < 100%) { + // Rollback + await gitCheckout(finding.file); + + // Retry with failure context + if (attempts < maxIterations) { + const fixContext = analyzeFailure(result.stderr); + regenerateFix(finding, fixContext); + retry(); + } else { + markFailed(finding.id); + } +} else { + // Commit + await gitCommit(`Fix: ${finding.title} [${finding.id}]`); + markFixed(finding.id); +} +``` + +**Pass Criteria**: 100% test pass rate (no partial fixes) + +## Phase 3: Execution Orchestration (Orchestrator) + +- Load fix-plan.json timeline stages +- For each stage: + - If parallel mode: Spawn all group agents, batch wait + - If serial mode: Spawn groups sequentially with wait between each + - Assign agent IDs (agents update their fix-progress-{N}.json) +- Handle agent failures gracefully (mark group as failed, continue) +- Advance to next stage only when current stage complete +- Lifecycle: spawn_agent → wait → close_agent per group/batch + +## Execution Agent Template (Per Group) + +```javascript +// Spawn execution agent for a group +const execAgentId = spawn_agent({ + message: ` +## TASK ASSIGNMENT + +### MANDATORY FIRST STEPS (Agent Execute) +1. **Read role definition**: ~/.codex/agents/cli-execution-agent.md (MUST read first) +2. Read: .workflow/project-tech.json +3. Read: .workflow/project-guidelines.json + +--- + +## Task Objective +Execute fixes for code review findings in group ${group.group_id}. Update progress file in real-time with flow control tracking. + +## Assignment +- Group ID: ${group.group_id} +- Group Name: ${group.group_name} +- Progress File: ${sessionDir}/${group.progress_file} +- Findings Count: ${group.findings.length} +- Max Iterations: ${maxIterations} (per finding) + +## Fix Strategy +${JSON.stringify(group.fix_strategy, null, 2)} + +## Risk Assessment +${JSON.stringify(group.risk_assessment, null, 2)} + +## Execution Flow + +### Initialization (Before Starting) + +1. Read ${group.progress_file} to load initial state +2. Update progress file: + - assigned_agent: "${agentId}" + - status: "in-progress" + - started_at: Current ISO 8601 timestamp + - last_update: Current ISO 8601 timestamp +3. Write updated state back to ${group.progress_file} + +### Main Execution Loop + +For EACH finding in ${group.progress_file}.findings: + +#### Step 1: Analyze Context + +**Before Step**: +- Update finding: status→"in-progress", started_at→now() +- Update current_finding: Populate with finding details, status→"analyzing", action→"Reading file and understanding code structure" +- Update phase→"analyzing" +- Update flow_control: Add "analyze_context" step to implementation_approach (status→"in-progress"), set current_step→"analyze_context" +- Update last_update→now(), write to ${group.progress_file} + +**Action**: +- Read file: finding.file +- Understand code structure around line: finding.line +- Analyze surrounding context (imports, dependencies, related functions) +- Review recommendations: finding.recommendations + +**After Step**: +- Update flow_control: Mark "analyze_context" step as "completed" with completed_at→now() +- Update last_update→now(), write to ${group.progress_file} + +#### Step 2: Apply Fix + +**Before Step**: +- Update current_finding: status→"fixing", action→"Applying code changes per recommendations" +- Update phase→"fixing" +- Update flow_control: Add "apply_fix" step to implementation_approach (status→"in-progress"), set current_step→"apply_fix" +- Update last_update→now(), write to ${group.progress_file} + +**Action**: +- Use Edit tool to implement code changes per finding.recommendations +- Follow fix_strategy.approach +- Maintain code style and existing patterns + +**After Step**: +- Update flow_control: Mark "apply_fix" step as "completed" with completed_at→now() +- Update last_update→now(), write to ${group.progress_file} + +#### Step 3: Test Verification + +**Before Step**: +- Update current_finding: status→"testing", action→"Running test suite to verify fix" +- Update phase→"testing" +- Update flow_control: Add "run_tests" step to implementation_approach (status→"in-progress"), set current_step→"run_tests" +- Update last_update→now(), write to ${group.progress_file} + +**Action**: +- Run tests using fix_strategy.test_pattern +- Require 100% pass rate +- Capture test output + +**On Test Failure**: +- Git rollback: \`git checkout -- \${finding.file}\` +- Increment finding.attempts +- Update flow_control: Mark "run_tests" step as "failed" with completed_at→now() +- Update errors: Add entry (finding_id, error_type→"test_failure", message, timestamp) +- If finding.attempts < ${maxIterations}: + - Reset flow_control: implementation_approach→[], current_step→null + - Retry from Step 1 +- Else: + - Update finding: status→"completed", result→"failed", error_message→"Max iterations reached", completed_at→now() + - Update summary counts, move to next finding + +**On Test Success**: +- Update flow_control: Mark "run_tests" step as "completed" with completed_at→now() +- Update last_update→now(), write to ${group.progress_file} +- Proceed to Step 4 + +#### Step 4: Commit Changes + +**Before Step**: +- Update current_finding: status→"committing", action→"Creating git commit for successful fix" +- Update phase→"committing" +- Update flow_control: Add "commit_changes" step to implementation_approach (status→"in-progress"), set current_step→"commit_changes" +- Update last_update→now(), write to ${group.progress_file} + +**Action**: +- Git commit: \`git commit -m "fix(${finding.dimension}): ${finding.title} [${finding.id}]"\` +- Capture commit hash + +**After Step**: +- Update finding: status→"completed", result→"fixed", commit_hash→, test_passed→true, completed_at→now() +- Update flow_control: Mark "commit_changes" step as "completed" with completed_at→now() +- Update last_update→now(), write to ${group.progress_file} + +#### After Each Finding + +- Update summary: Recalculate counts (pending/in_progress/fixed/failed) and percent_complete +- If all findings completed: Clear current_finding, reset flow_control +- Update last_update→now(), write to ${group.progress_file} + +### Final Completion + +When all findings processed: +- Update status→"completed", phase→"done", summary.percent_complete→100.0 +- Update last_update→now(), write final state to ${group.progress_file} + +## Critical Requirements + +### Progress File Updates +- **MUST update after every significant action** (before/after each step) +- **Always maintain complete structure** - never write partial updates +- **Use ISO 8601 timestamps** - e.g., "2025-01-25T14:36:00Z" + +### Flow Control Format +Follow action-planning-agent flow_control.implementation_approach format: +- step: Identifier (e.g., "analyze_context", "apply_fix") +- action: Human-readable description +- status: "pending" | "in-progress" | "completed" | "failed" +- started_at: ISO 8601 timestamp or null +- completed_at: ISO 8601 timestamp or null + +### Error Handling +- Capture all errors in errors[] array +- Never leave progress file in invalid state +- Always write complete updates, never partial +- On unrecoverable error: Mark group as failed, preserve state + +## Test Patterns +Use fix_strategy.test_pattern to run affected tests: +- Pattern: ${group.fix_strategy.test_pattern} +- Command: Infer from project (npm test, pytest, etc.) +- Pass Criteria: 100% pass rate required +` +}); + +// Wait for completion +const execResult = wait({ + ids: [execAgentId], + timeout_ms: 1200000 // 20 minutes per group +}); + +// Cleanup +close_agent({ id: execAgentId }); +``` + +## Output +- Files: fix-progress-{N}.json (updated per group), git commits +- Progress: Mark Phase 8 completed, Phase 9 in_progress + +## Next Phase +Return to orchestrator, then auto-continue to [Phase 9: Fix Completion](09-fix-completion.md). diff --git a/.codex/skills/review-cycle/phases/09-fix-completion.md b/.codex/skills/review-cycle/phases/09-fix-completion.md new file mode 100644 index 00000000..20c637c1 --- /dev/null +++ b/.codex/skills/review-cycle/phases/09-fix-completion.md @@ -0,0 +1,141 @@ +# Phase 9: Fix Completion + +> Source: `commands/workflow/review-cycle-fix.md` Phase 4 + Phase 5 + +## Overview +Aggregate fix results, generate summary report, update history, and optionally complete workflow session. + +## Phase 4: Completion & Aggregation (Orchestrator) + +- Collect final status from all fix-progress-{N}.json files +- Generate fix-summary.md with timeline and results +- Update fix-history.json with new session entry +- Remove active-fix-session.json +- Progress tracking: Mark all phases done +- Output summary to user + +## Phase 5: Session Completion (Orchestrator) + +- If all findings fixed successfully (no failures): + - Prompt user: "All fixes complete. Complete workflow session? [Y/n]" + - If confirmed: Archive session with lessons learned +- If partial success (some failures): + - Output: "Some findings failed. Review fix-summary.md before completing session." + - Do NOT auto-complete session + +## Error Handling + +### Batching Failures (Phase 1.5) + +- Invalid findings data -> Abort with error message +- Empty batches after grouping -> Warn and skip empty batches + +### Planning Failures (Phase 2) + +- Planning agent timeout -> Mark batch as failed, continue with other batches +- Partial plan missing -> Skip batch, warn user +- Agent crash -> Collect available partial plans, proceed with aggregation +- All agents fail -> Abort entire fix session with error +- Aggregation conflicts -> Apply conflict resolution (serialize conflicting groups) + +### Execution Failures (Phase 3) + +- Agent crash -> Mark group as failed, continue with other groups +- Test command not found -> Skip test verification, warn user +- Git operations fail -> Abort with error, preserve state + +### Rollback Scenarios + +- Test failure after fix -> Automatic `git checkout` rollback +- Max iterations reached -> Leave file unchanged, mark as failed +- Unrecoverable error -> Rollback entire group, save checkpoint + +## Progress Tracking Structures + +### Initialization (after Phase 1.5 batching) + +``` +Phase 1: Discovery & Initialization → completed +Phase 1.5: Intelligent Batching → completed +Phase 2: Parallel Planning → in_progress + → Batch 1: 4 findings (auth.ts:security) → pending + → Batch 2: 3 findings (query.ts:security) → pending + → Batch 3: 2 findings (config.ts:quality) → pending +Phase 3: Execution → pending +Phase 4: Completion → pending +``` + +### During Planning (parallel agents running) + +``` +Phase 1: Discovery & Initialization → completed +Phase 1.5: Intelligent Batching → completed +Phase 2: Parallel Planning → in_progress + → Batch 1: 4 findings (auth.ts:security) → completed + → Batch 2: 3 findings (query.ts:security) → in_progress + → Batch 3: 2 findings (config.ts:quality) → in_progress +Phase 3: Execution → pending +Phase 4: Completion → pending +``` + +### During Execution + +``` +Phase 1: Discovery & Initialization → completed +Phase 1.5: Intelligent Batching → completed +Phase 2: Parallel Planning (3 batches → 5 groups) → completed +Phase 3: Execution → in_progress + → Stage 1: Parallel execution (3 groups) → completed + • Group G1: Auth validation (2 findings) → completed + • Group G2: Query security (3 findings) → completed + • Group G3: Config quality (1 finding) → completed + → Stage 2: Serial execution (1 group) → in_progress + • Group G4: Dependent fixes (2 findings) → in_progress +Phase 4: Completion → pending +``` + +### Update Rules + +- Add batch items dynamically during Phase 1.5 +- Mark batch items completed as parallel agents return results +- Add stage/group items dynamically after Phase 2 plan aggregation +- Mark completed immediately after each group finishes +- Update parent phase status when all child items complete + +## Post-Completion Expansion + +After completion, ask user whether to expand into issues (test/enhance/refactor/doc). For selected items, create structured issues with summary and dimension context. + +## Best Practices + +1. **Leverage Parallel Planning**: For 10+ findings, parallel batching significantly reduces planning time +2. **Tune Batch Size**: Use `--batch-size` to control granularity (smaller batches = more parallelism, larger = better grouping context) +3. **Conservative Approach**: Test verification is mandatory - no fixes kept without passing tests +4. **Parallel Efficiency**: MAX_PARALLEL=10 for planning agents, 3 concurrent execution agents per stage +5. **Resume Support**: Fix sessions can resume from checkpoints after interruption +6. **Manual Review**: Always review failed fixes manually - may require architectural changes +7. **Incremental Fixing**: Start with small batches (5-10 findings) before large-scale fixes + +## Related Commands + +### View Fix Progress +Use `ccw view` to open the workflow dashboard in browser: + +```bash +ccw view +``` + +### Re-run Fix Pipeline +``` +review-cycle --fix ... +``` + +## Output + +- Files: fix-summary.md, fix-history.json +- State: active-fix-session.json removed +- Optional: workflow session completed and archived + +## Completion + +Review Cycle fix pipeline complete. Review fix-summary.md for results. diff --git a/ccw/frontend/_check.ps1 b/ccw/frontend/_check.ps1 new file mode 100644 index 00000000..4361d84e --- /dev/null +++ b/ccw/frontend/_check.ps1 @@ -0,0 +1,13 @@ +Set-Location 'D:\Claude_dms3\ccw\frontend' +$output = npx tsc --noEmit 2>&1 +$errors = $output | Select-String 'error TS' + +# Real errors only +$real = $errors | Where-Object { $_.Line -notmatch 'TS6133' -and $_.Line -notmatch 'TS1149' -and $_.Line -notmatch 'TS6196' -and $_.Line -notmatch 'TS6192' } + +# Source code errors (non-test) +$src = $real | Where-Object { $_.Line -notmatch '\.test\.' -and $_.Line -notmatch '__tests__' } +Write-Host "=== SOURCE CODE ERRORS (non-test) ===" +Write-Host "Count: $($src.Count)" +Write-Host "" +foreach ($e in $src) { Write-Host $e.Line } diff --git a/ccw/frontend/analyze-errors.ps1 b/ccw/frontend/analyze-errors.ps1 deleted file mode 100644 index 62dbd6ae..00000000 --- a/ccw/frontend/analyze-errors.ps1 +++ /dev/null @@ -1,19 +0,0 @@ -Set-Location 'D:\Claude_dms3\ccw\frontend' -$output = npx tsc --noEmit 2>&1 -$errorLines = $output | Select-String 'error TS' - -Write-Host "=== TOTAL ERRORS ===" -Write-Host $errorLines.Count - -Write-Host "`n=== BY ERROR CODE ===" -$errorLines | ForEach-Object { - if ($_.Line -match 'error (TS\d+)') { $Matches[1] } -} | Group-Object | Sort-Object Count -Descending | Select-Object -First 15 | Format-Table Name, Count -AutoSize - -Write-Host "`n=== BY FILE (top 25) ===" -$errorLines | ForEach-Object { - ($_.Line -split '\(')[0] -} | Group-Object | Sort-Object Count -Descending | Select-Object -First 25 | Format-Table Name, Count -AutoSize - -Write-Host "`n=== NON-TS6133 ERRORS (real issues, not unused vars) ===" -$errorLines | Where-Object { $_.Line -notmatch 'TS6133' -and $_.Line -notmatch 'TS1149' } | ForEach-Object { $_.Line } | Select-Object -First 60 diff --git a/ccw/frontend/analyze-errors2.ps1 b/ccw/frontend/analyze-errors2.ps1 deleted file mode 100644 index 1d10d173..00000000 --- a/ccw/frontend/analyze-errors2.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -Set-Location 'D:\Claude_dms3\ccw\frontend' -$output = npx tsc --noEmit 2>&1 -$errorLines = $output | Select-String 'error TS' - -Write-Host "=== NON-TS6133/TS1149/TS6196/TS6192 ERRORS (real issues) ===" -$real = $errorLines | Where-Object { $_.Line -notmatch 'TS6133' -and $_.Line -notmatch 'TS1149' -and $_.Line -notmatch 'TS6196' -and $_.Line -notmatch 'TS6192' } -Write-Host "Count: $($real.Count)" -Write-Host "" - -Write-Host "=== GROUPED BY FILE ===" -$real | ForEach-Object { - ($_.Line -split '\(')[0] -} | Group-Object | Sort-Object Count -Descending | Format-Table Name, Count -AutoSize - -Write-Host "`n=== ROUTER.TSX ERRORS ===" -$errorLines | Where-Object { $_.Line -match 'src/router\.tsx' } | ForEach-Object { $_.Line } - -Write-Host "`n=== STORES/INDEX.TS ERRORS ===" -$errorLines | Where-Object { $_.Line -match 'src/stores/index\.ts' } | ForEach-Object { $_.Line } - -Write-Host "`n=== TYPES/INDEX.TS ERRORS ===" -$errorLines | Where-Object { $_.Line -match 'src/types/index\.ts' } | ForEach-Object { $_.Line } - -Write-Host "`n=== SHARED/INDEX.TS ERRORS ===" -$errorLines | Where-Object { $_.Line -match 'src/components/shared/index\.ts' } | ForEach-Object { $_.Line } - -Write-Host "`n=== HOOKS/INDEX.TS ERRORS ===" -$errorLines | Where-Object { $_.Line -match 'src/hooks/index\.ts' } | ForEach-Object { $_.Line } diff --git a/ccw/frontend/playwright-report/index.html b/ccw/frontend/playwright-report/index.html index 543f5e89..b688bc4b 100644 --- a/ccw/frontend/playwright-report/index.html +++ b/ccw/frontend/playwright-report/index.html @@ -82,4 +82,4 @@ Error generating stack: `+n.message+`
- \ No newline at end of file + \ No newline at end of file diff --git a/ccw/frontend/src/components/orchestrator/ExecutionHeader.tsx b/ccw/frontend/src/components/orchestrator/ExecutionHeader.tsx new file mode 100644 index 00000000..4078f9ea --- /dev/null +++ b/ccw/frontend/src/components/orchestrator/ExecutionHeader.tsx @@ -0,0 +1,160 @@ +// ======================================== +// Execution Header Component +// ======================================== +// Displays execution overview with status badge, progress bar, duration, and current node + +import { Clock, ArrowRight, AlertCircle } from 'lucide-react'; +import { cn } from '@/lib/utils'; +import type { ExecutionState } from '@/types/execution'; +import type { NodeExecutionState } from '@/types/execution'; + +interface ExecutionHeaderProps { + /** Current execution state */ + execution: ExecutionState | null; + /** Node execution states keyed by node ID */ + nodeStates: Record; +} + +/** + * Status badge component showing execution status + */ +function StatusBadge({ status }: { status: ExecutionState['status'] }) { + const config = { + pending: { + label: 'Pending', + className: 'bg-muted text-muted-foreground border-border', + }, + running: { + label: 'Running', + className: 'bg-primary/10 text-primary border-primary/50', + }, + paused: { + label: 'Paused', + className: 'bg-amber-500/10 text-amber-500 border-amber-500/50', + }, + completed: { + label: 'Completed', + className: 'bg-green-500/10 text-green-500 border-green-500/50', + }, + failed: { + label: 'Failed', + className: 'bg-destructive/10 text-destructive border-destructive/50', + }, + }; + + const { label, className } = config[status]; + + return ( + + {label} + + ); +} + +/** + * Format duration in milliseconds to human-readable string + */ +function formatDuration(ms: number): string { + if (ms < 1000) return `${ms}ms`; + if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`; + const minutes = Math.floor(ms / 60000); + const seconds = Math.floor((ms % 60000) / 1000); + return `${minutes}m ${seconds}s`; +} + +/** + * ExecutionHeader component displays the execution overview + * + * Shows: + * - Status badge (pending/running/completed/failed) + * - Progress bar with completion percentage + * - Elapsed time + * - Current executing node (if any) + * - Error message (if failed) + */ +export function ExecutionHeader({ execution, nodeStates }: ExecutionHeaderProps) { + if (!execution) { + return ( +
+

+ No execution in progress +

+
+ ); + } + + // Calculate progress + const completedCount = Object.values(nodeStates).filter( + (n) => n.status === 'completed' + ).length; + const totalCount = Object.keys(nodeStates).length; + const progress = totalCount > 0 ? (completedCount / totalCount) * 100 : 0; + + // Get current node info + const currentNodeState = execution.currentNodeId + ? nodeStates[execution.currentNodeId] + : null; + + return ( +
+ {/* Status and Progress */} +
+ + + {/* Progress bar */} +
+
+
+
+
+ + {/* Completion count */} + + {completedCount}/{totalCount} + +
+ + {/* Details: Duration, Current Node, Error */} +
+ {/* Elapsed time */} +
+ + {formatDuration(execution.elapsedMs)} +
+ + {/* Current node */} + {currentNodeState && execution.status === 'running' && ( +
+ + Current: + {execution.currentNodeId} +
+ )} + + {/* Error message */} + {execution.status === 'failed' && ( +
+ + Execution failed +
+ )} +
+
+ ); +} + +ExecutionHeader.displayName = 'ExecutionHeader'; diff --git a/ccw/frontend/src/components/orchestrator/ExecutionMonitor.example.tsx b/ccw/frontend/src/components/orchestrator/ExecutionMonitor.example.tsx new file mode 100644 index 00000000..d6583008 --- /dev/null +++ b/ccw/frontend/src/components/orchestrator/ExecutionMonitor.example.tsx @@ -0,0 +1,72 @@ +// ======================================== +// ExecutionMonitor Integration Example +// ======================================== +// This file demonstrates how to use ExecutionHeader and NodeExecutionChain components +// in a typical execution monitoring scenario + +import { useExecutionStore } from '@/stores/executionStore'; +import { useFlowStore } from '@/stores'; +import { ExecutionHeader, NodeExecutionChain } from '@/components/orchestrator'; + +/** + * Example execution monitor component + * + * This example shows how to integrate ExecutionHeader and NodeExecutionChain + * with the executionStore and flowStore. + */ +export function ExecutionMonitorExample() { + // Get execution state from executionStore + const currentExecution = useExecutionStore((state) => state.currentExecution); + const nodeStates = useExecutionStore((state) => state.nodeStates); + const selectedNodeId = useExecutionStore((state) => state.selectedNodeId); + const selectNode = useExecutionStore((state) => state.selectNode); + + // Get flow nodes from flowStore + const nodes = useFlowStore((state) => state.nodes); + + return ( +
+ {/* Execution Overview Header */} + + + {/* Node Execution Chain */} + + + {/* Rest of the monitor UI would go here */} + {/* - Node Detail Panel */} + {/* - Tool Calls Timeline */} + {/* - Global Logs */} +
+ ); +} + +/** + * Integration Notes: + * + * 1. ExecutionHeader requires: + * - execution: ExecutionState from executionStore.currentExecution + * - nodeStates: Record from executionStore.nodeStates + * + * 2. NodeExecutionChain requires: + * - nodes: FlowNode[] from flowStore.nodes + * - nodeStates: Record from executionStore.nodeStates + * - selectedNodeId: string | null from executionStore.selectedNodeId + * - onNodeSelect: (nodeId: string) => void (use executionStore.selectNode) + * + * 3. Data flow: + * - WebSocket messages update executionStore + * - ExecutionHeader reacts to execution state changes + * - NodeExecutionChain reacts to node state changes + * - Clicking a node calls selectNode, updating selectedNodeId + * - Selected node can be used to show detail panel + */ + +export default ExecutionMonitorExample; diff --git a/ccw/frontend/src/components/orchestrator/NodeDetailPanel.tsx b/ccw/frontend/src/components/orchestrator/NodeDetailPanel.tsx new file mode 100644 index 00000000..a915f71f --- /dev/null +++ b/ccw/frontend/src/components/orchestrator/NodeDetailPanel.tsx @@ -0,0 +1,425 @@ +// ======================================== +// Node Detail Panel Component +// ======================================== +// Tab panel displaying node execution details: Output, Tool Calls, Logs, Variables + +import { useState, useCallback, useMemo, useRef, useEffect } from 'react'; +import { + Terminal, + Wrench, + FileText, + Database, + FileText as FileTextIcon, + Circle, + Loader2, + CheckCircle2, + XCircle, + AlertTriangle, +} from 'lucide-react'; +import { cn } from '@/lib/utils'; +import { StreamingOutput } from '@/components/shared/StreamingOutput'; +import { ToolCallsTimeline } from './ToolCallsTimeline'; +import type { ExecutionLog, NodeExecutionOutput, NodeExecutionState } from '@/types/execution'; +import type { ToolCallExecution } from '@/types/toolCall'; +import type { CliOutputLine } from '@/stores/cliStreamStore'; +import type { FlowNode } from '@/types/flow'; + +// ========== Tab Types ========== + +type DetailTabId = 'output' | 'toolCalls' | 'logs' | 'variables'; + +interface DetailTab { + id: DetailTabId; + label: string; + icon: React.ComponentType<{ className?: string }>; +} + +const DETAIL_TABS: DetailTab[] = [ + { id: 'output', label: 'Output Stream', icon: Terminal }, + { id: 'toolCalls', label: 'Tool Calls', icon: Wrench }, + { id: 'logs', label: 'Logs', icon: FileText }, + { id: 'variables', label: 'Variables', icon: Database }, +]; + +// ========== Helper Functions ========== + +/** + * Get log level color class + */ +function getLogLevelColor(level: ExecutionLog['level']): string { + switch (level) { + case 'error': + return 'text-red-500 bg-red-500/10'; + case 'warn': + return 'text-yellow-600 bg-yellow-500/10 dark:text-yellow-500'; + case 'info': + return 'text-blue-500 bg-blue-500/10'; + case 'debug': + return 'text-gray-500 bg-gray-500/10'; + default: + return 'text-foreground bg-muted'; + } +} + +/** + * Format timestamp to locale time string + */ +function formatLogTimestamp(timestamp: string): string { + return new Date(timestamp).toLocaleTimeString('zh-CN', { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: false, + }); +} + +// ========== Tab Components ========== + +interface OutputTabProps { + outputs: CliOutputLine[]; + isStreaming: boolean; +} + +function OutputTab({ outputs, isStreaming }: OutputTabProps) { + return ( +
+ +
+ ); +} + +interface ToolCallsTabProps { + toolCalls: ToolCallExecution[]; + onToggleExpand: (callId: string) => void; +} + +function ToolCallsTab({ toolCalls, onToggleExpand }: ToolCallsTabProps) { + return ( +
+ +
+ ); +} + +interface LogsTabProps { + logs: ExecutionLog[]; +} + +function LogsTab({ logs }: LogsTabProps) { + const containerRef = useRef(null); + const logsEndRef = useRef(null); + + // Auto-scroll to bottom when new logs arrive + useEffect(() => { + if (logsEndRef.current) { + logsEndRef.current.scrollIntoView({ behavior: 'smooth' }); + } + }, [logs]); + + if (logs.length === 0) { + return ( +
+
+ +

No logs for this node

+
+
+ ); + } + + return ( +
+
+ {logs.map((log, index) => ( +
+ + {formatLogTimestamp(log.timestamp)} + + + [{log.level}] + + {log.message} +
+ ))} +
+
+
+ ); +} + +interface VariablesTabProps { + node: FlowNode; + nodeOutput: NodeExecutionOutput | undefined; + nodeState: NodeExecutionState | undefined; +} + +function VariablesTab({ node, nodeOutput, nodeState }: VariablesTabProps) { + const variables = useMemo(() => { + const vars: Record = {}; + + // Add outputName if available + if (node.data.outputName) { + vars[`{{${node.data.outputName}}}`] = nodeState?.result ?? ''; + } else if (nodeState?.result) { + // Also add result if available even without outputName + vars['result'] = nodeState.result; + } + + // Add any variables stored in nodeOutput + if (nodeOutput?.variables) { + Object.entries(nodeOutput.variables).forEach(([key, value]) => { + vars[`{{${key}}}`] = value; + }); + } + + // Add execution metadata + if (nodeOutput) { + vars['_execution'] = { + startTime: new Date(nodeOutput.startTime).toISOString(), + endTime: nodeOutput.endTime ? new Date(nodeOutput.endTime).toISOString() : '', + outputCount: nodeOutput.outputs.length, + toolCallCount: nodeOutput.toolCalls.length, + logCount: nodeOutput.logs.length, + }; + } + + return vars; + }, [node, nodeOutput, nodeState]); + + const variableEntries = useMemo(() => { + return Object.entries(variables).sort(([a], [b]) => a.localeCompare(b)); + }, [variables]); + + if (variableEntries.length === 0) { + return ( +
+
+ +

No variables defined

+
+
+ ); + } + + return ( +
+
+ {variableEntries.map(([key, value]) => ( +
+
+ + {key} + +
+
+              {typeof value === 'object'
+                ? JSON.stringify(value, null, 2)
+                : String(value)}
+            
+
+ ))} +
+
+ ); +} + +// ========== Main Component ========== + +interface NodeDetailPanelProps { + /** Currently selected node */ + node: FlowNode | null; + /** Node execution output data */ + nodeOutput: NodeExecutionOutput | undefined; + /** Node execution state */ + nodeState: NodeExecutionState | undefined; + /** Tool calls for this node */ + toolCalls: ToolCallExecution[]; + /** Whether the node is currently executing */ + isExecuting: boolean; + /** Callback to toggle tool call expand */ + onToggleToolCallExpand: (callId: string) => void; + /** Optional CSS class name */ + className?: string; +} + +/** + * NodeDetailPanel displays detailed information about a selected node + * + * Features: + * - Tab-based layout (Output/Tool Calls/Logs/Variables) + * - Auto-scroll to bottom for output/logs + * - Expandable tool call cards + * - Variable inspection + */ +export function NodeDetailPanel({ + node, + nodeOutput, + nodeState, + toolCalls, + isExecuting, + onToggleToolCallExpand, + className, +}: NodeDetailPanelProps) { + const [activeTab, setActiveTab] = useState('output'); + + // Reset to output tab when node changes + useEffect(() => { + setActiveTab('output'); + }, [node?.id]); + + // Handle tab change + const handleTabChange = useCallback((tabId: DetailTabId) => { + setActiveTab(tabId); + }, []); + + // Handle toggle tool call expand + const handleToggleToolCallExpand = useCallback( + (callId: string) => { + onToggleToolCallExpand(callId); + }, + [onToggleToolCallExpand] + ); + + // If no node selected, show empty state + if (!node) { + return ( +
+
+ +

Select a node to view details

+
+
+ ); + } + + const outputs = nodeOutput?.outputs ?? []; + const logs = nodeOutput?.logs ?? []; + + // Render active tab content + const renderTabContent = () => { + switch (activeTab) { + case 'output': + return ; + case 'toolCalls': + return ( + + ); + case 'logs': + return ; + case 'variables': + return ; + } + }; + + // Get tab counts for badges + const tabCounts = { + output: outputs.length, + toolCalls: toolCalls.length, + logs: logs.length, + variables: 1, // At least outputName or execution metadata + }; + + return ( +
+ {/* Tab Headers */} +
+ {DETAIL_TABS.map((tab) => { + const Icon = tab.icon; + const isActive = activeTab === tab.id; + const count = tabCounts[tab.id]; + + return ( + + ); + })} +
+ + {/* Node status indicator */} + {nodeState && ( +
+
+ {nodeState.status === 'running' && ( + + )} + {nodeState.status === 'completed' && ( + + )} + {nodeState.status === 'failed' && ( + + )} + + Status: {nodeState.status} + +
+ {nodeState.error && ( +
+ + {nodeState.error} +
+ )} +
+ )} + + {/* Tab Content */} +
+ {renderTabContent()} +
+
+ ); +} + +NodeDetailPanel.displayName = 'NodeDetailPanel'; + +export default NodeDetailPanel; diff --git a/ccw/frontend/src/components/orchestrator/NodeExecutionChain.tsx b/ccw/frontend/src/components/orchestrator/NodeExecutionChain.tsx new file mode 100644 index 00000000..e40f58f7 --- /dev/null +++ b/ccw/frontend/src/components/orchestrator/NodeExecutionChain.tsx @@ -0,0 +1,145 @@ +// ======================================== +// Node Execution Chain Component +// ======================================== +// Horizontal chain display of all nodes with execution status + +import { Circle, Loader2, CheckCircle2, XCircle, ChevronRight } from 'lucide-react'; +import { cn } from '@/lib/utils'; +import type { FlowNode } from '@/types/flow'; +import type { NodeExecutionState } from '@/types/execution'; + +interface NodeExecutionChainProps { + /** All nodes in the flow */ + nodes: FlowNode[]; + /** Node execution states keyed by node ID */ + nodeStates: Record; + /** Currently selected node ID */ + selectedNodeId: string | null; + /** Callback when a node is clicked */ + onNodeSelect: (nodeId: string) => void; +} + +/** + * Get status icon for a node + */ +function getNodeStatusIcon(status?: NodeExecutionState['status']) { + switch (status) { + case 'pending': + return ; + case 'running': + return ; + case 'completed': + return ; + case 'failed': + return ; + default: + return ; + } +} + +/** + * Get node card styles based on execution state + */ +function getNodeCardStyles( + state: NodeExecutionState | undefined, + isSelected: boolean +): string { + const baseStyles = cn( + 'px-3 py-2 rounded-lg border transition-all duration-200', + 'hover:bg-muted/50 hover:border-primary/50', + 'min-w-[140px] max-w-[180px]' + ); + + const stateStyles = cn( + !state && 'border-border opacity-50', + state?.status === 'running' && + 'border-primary bg-primary/5 animate-pulse shadow-sm shadow-primary/20', + state?.status === 'completed' && + 'border-green-500/50 bg-green-500/5', + state?.status === 'failed' && + 'border-destructive bg-destructive/10', + state?.status === 'pending' && + 'border-muted-foreground/30' + ); + + const selectedStyles = isSelected + ? 'border-primary bg-primary/10 ring-2 ring-primary/20' + : ''; + + return cn(baseStyles, stateStyles, selectedStyles); +} + +/** + * NodeExecutionChain displays a horizontal chain of all nodes + * + * Features: + * - Nodes arranged horizontally with arrow connectors + * - Visual status indicators (pending/running/completed/failed) + * - Pulse animation for running nodes + * - Click to select a node + * - Selected node highlighting + */ +export function NodeExecutionChain({ + nodes, + nodeStates, + selectedNodeId, + onNodeSelect, +}: NodeExecutionChainProps) { + if (nodes.length === 0) { + return ( +
+

+ No nodes in flow +

+
+ ); + } + + return ( +
+
+ {nodes.map((node, index) => { + const state = nodeStates[node.id]; + const isSelected = selectedNodeId === node.id; + const nodeLabel = node.data.label || node.id; + + return ( +
+ {/* Node card */} + + + {/* Connector arrow */} + {index < nodes.length - 1 && ( +
+ ); + })} +
+
+ ); +} + +NodeExecutionChain.displayName = 'NodeExecutionChain'; diff --git a/ccw/frontend/src/components/orchestrator/README.md b/ccw/frontend/src/components/orchestrator/README.md new file mode 100644 index 00000000..33b2c6c0 --- /dev/null +++ b/ccw/frontend/src/components/orchestrator/README.md @@ -0,0 +1,110 @@ +# Orchestrator Components + +## Tool Call Timeline Components + +### Components + +#### `ToolCallCard` +Expandable card displaying tool call details with status, output, and results. + +**Props:** +- `toolCall`: `ToolCallExecution` - Tool call execution data +- `isExpanded`: `boolean` - Whether the card is expanded +- `onToggle`: `() => void` - Callback when toggle expand/collapse +- `className?`: `string` - Optional CSS class name + +**Features:** +- Status icon (pending/executing/success/error/canceled) +- Kind icon (execute/patch/thinking/web_search/mcp_tool/file_operation) +- Duration display +- Expand/collapse animation +- stdout/stderr output with syntax highlighting +- Exit code badge +- Error message display +- Result display + +#### `ToolCallsTimeline` +Vertical timeline displaying tool calls in chronological order. + +**Props:** +- `toolCalls`: `ToolCallExecution[]` - Array of tool call executions +- `onToggleExpand`: `(callId: string) => void` - Callback when tool call toggled +- `className?`: `string` - Optional CSS class name + +**Features:** +- Chronological sorting by start time +- Timeline dot with status color +- Auto-expand executing tool calls +- Auto-scroll to executing tool call +- Empty state with icon +- Summary statistics (total/success/error/running) +- Loading indicator when tools are executing + +### Usage Example + +```tsx +import { ToolCallsTimeline } from '@/components/orchestrator'; +import { useExecutionStore } from '@/stores/executionStore'; + +function ToolCallsTab({ nodeId }: { nodeId: string }) { + const toolCalls = useExecutionStore((state) => + state.getToolCallsForNode(nodeId) + ); + const toggleToolCallExpanded = useExecutionStore( + (state) => state.toggleToolCallExpanded + ); + + return ( +
+ toggleToolCallExpanded(nodeId, callId)} + /> +
+ ); +} +``` + +### Integration with ExecutionStore + +The components integrate with `executionStore` for state management: + +```tsx +// Get tool calls for a node +const toolCalls = useExecutionStore((state) => + state.getToolCallsForNode(nodeId) +); + +// Toggle expand state +const handleToggle = (callId: string) => { + useExecutionStore.getState().toggleToolCallExpanded(nodeId, callId); +}; +``` + +### Data Flow + +``` +WebSocket Message + │ + ▼ +useWebSocket (parsing) + │ + ▼ +executionStore.startToolCall() + │ + ▼ +ToolCallsTimeline (re-render) + │ + ▼ +ToolCallCard (display) +``` + +### Styling + +Components use Tailwind CSS with the following conventions: +- `border-border` - Border color +- `bg-muted` - Muted background +- `text-destructive` - Error text color +- `text-green-500` - Success text color +- `text-primary` - Primary text color +- `animate-pulse` - Pulse animation for executing status diff --git a/ccw/frontend/src/components/orchestrator/ToolCallCard.tsx b/ccw/frontend/src/components/orchestrator/ToolCallCard.tsx new file mode 100644 index 00000000..eada10dc --- /dev/null +++ b/ccw/frontend/src/components/orchestrator/ToolCallCard.tsx @@ -0,0 +1,317 @@ +// ======================================== +// Tool Call Card Component +// ======================================== +// Expandable card displaying tool call details with status, output, and results + +import { memo } from 'react'; +import { + ChevronDown, + ChevronUp, + Loader2, + CheckCircle2, + AlertCircle, + Clock, + XCircle, + Terminal, + Wrench, + FileEdit, + Brain, + Search, +} from 'lucide-react'; +import { cn } from '@/lib/utils'; +import type { ToolCallExecution } from '@/types/toolCall'; + +// ========== Helper Functions ========== + +/** + * Get status icon for tool call + */ +function getToolCallStatusIcon(status: ToolCallExecution['status']) { + const iconClassName = 'h-4 w-4'; + + switch (status) { + case 'pending': + return ; + case 'executing': + return ; + case 'success': + return ; + case 'error': + return ; + case 'canceled': + return ; + } +} + +/** + * Get kind icon for tool call + */ +function getToolCallKindIcon(kind: ToolCallExecution['kind']) { + const iconClassName = 'h-3.5 w-3.5'; + + switch (kind) { + case 'execute': + return ; + case 'patch': + return ; + case 'thinking': + return ; + case 'web_search': + return ; + case 'mcp_tool': + return ; + case 'file_operation': + return ; + } +} + +/** + * Get kind label for tool call + */ +function getToolCallKindLabel(kind: ToolCallExecution['kind']): string { + switch (kind) { + case 'execute': + return 'Execute'; + case 'patch': + return 'Patch'; + case 'thinking': + return 'Thinking'; + case 'web_search': + return 'Web Search'; + case 'mcp_tool': + return 'MCP Tool'; + case 'file_operation': + return 'File Operation'; + } +} + +/** + * Get border color class for tool call status + */ +function getToolCallBorderClass(status: ToolCallExecution['status']): string { + switch (status) { + case 'pending': + return 'border-l-2 border-l-yellow-500/50'; + case 'executing': + return 'border-l-2 border-l-blue-500'; + case 'success': + return 'border-l-2 border-l-green-500'; + case 'error': + return 'border-l-2 border-l-destructive'; + case 'canceled': + return 'border-l-2 border-l-muted-foreground/50'; + } +} + +/** + * Format duration in milliseconds to human readable string + */ +function formatDuration(ms: number): string { + if (ms < 1000) return `${ms}ms`; + const seconds = Math.floor(ms / 1000); + if (seconds < 60) return `${seconds}s`; + const minutes = Math.floor(seconds / 60); + const remainingSeconds = seconds % 60; + return `${minutes}m ${remainingSeconds}s`; +} + +/** + * Calculate duration from start and end time + */ +function calculateDuration(startTime: number, endTime?: number): string { + const duration = (endTime || Date.now()) - startTime; + return formatDuration(duration); +} + +/** + * Format timestamp to locale time string + */ +function formatTimestamp(timestamp: number): string { + return new Date(timestamp).toLocaleTimeString('zh-CN', { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: false, + }); +} + +// ========== Component Interfaces ========== + +export interface ToolCallCardProps { + /** Tool call execution data */ + toolCall: ToolCallExecution; + /** Whether the card is expanded */ + isExpanded?: boolean; + /** Callback when toggle expand/collapse */ + onToggle: () => void; + /** Optional CSS class name */ + className?: string; +} + +// ========== Component ========== + +export const ToolCallCard = memo(function ToolCallCard({ + toolCall, + isExpanded = false, + onToggle, + className, +}: ToolCallCardProps) { + const duration = calculateDuration(toolCall.startTime, toolCall.endTime); + + return ( +
+ {/* Header */} +
+ {/* Expand/Collapse Icon */} +
+ {isExpanded ? ( + + ) : ( + + )} +
+ + {/* Status Icon */} +
{getToolCallStatusIcon(toolCall.status)}
+ + {/* Kind Icon */} +
+ {getToolCallKindIcon(toolCall.kind)} +
+ + {/* Description */} +
+

{toolCall.description}

+
+

+ {getToolCallKindLabel(toolCall.kind)} +

+ {toolCall.subtype && ( + <> + · +

{toolCall.subtype}

+ + )} +
+
+ + {/* Duration */} +
+ {duration} +
+ + {/* Exit Code Badge (if completed) */} + {toolCall.exitCode !== undefined && ( +
+ Exit: {toolCall.exitCode} +
+ )} +
+ + {/* Expanded Content */} + {isExpanded && ( +
+ {/* Metadata */} +
+ Started: {formatTimestamp(toolCall.startTime)} + {toolCall.endTime && ( + Ended: {formatTimestamp(toolCall.endTime)} + )} + Duration: {duration} +
+ + {/* stdout Output */} + {toolCall.outputBuffer.stdout && ( +
+
+ + Output (stdout): +
+
+                {toolCall.outputBuffer.stdout}
+              
+
+ )} + + {/* stderr Output */} + {toolCall.outputBuffer.stderr && ( +
+
+ + Error Output (stderr): +
+
+                {toolCall.outputBuffer.stderr}
+              
+
+ )} + + {/* Error Message */} + {toolCall.error && ( +
+ Error: + {toolCall.error} +
+ )} + + {/* Result Display (if available) */} + {toolCall.result !== undefined && ( +
+
+ Result: +
+
+                {typeof toolCall.result === 'string'
+                  ? toolCall.result
+                  : JSON.stringify(toolCall.result, null, 2)}
+              
+
+ )} + + {/* Output Lines Count */} + {toolCall.outputLines.length > 0 && ( +
+ {toolCall.outputLines.length} output line{toolCall.outputLines.length !== 1 ? 's' : ''} captured +
+ )} +
+ )} +
+ ); +}, (prevProps, nextProps) => { + // Custom comparison for performance optimization + return ( + prevProps.isExpanded === nextProps.isExpanded && + prevProps.className === nextProps.className && + prevProps.toolCall.callId === nextProps.toolCall.callId && + prevProps.toolCall.status === nextProps.toolCall.status && + prevProps.toolCall.description === nextProps.toolCall.description && + prevProps.toolCall.endTime === nextProps.toolCall.endTime && + prevProps.toolCall.exitCode === nextProps.toolCall.exitCode && + prevProps.toolCall.error === nextProps.toolCall.error && + prevProps.toolCall.outputBuffer.stdout === nextProps.toolCall.outputBuffer.stdout && + prevProps.toolCall.outputBuffer.stderr === nextProps.toolCall.outputBuffer.stderr + ); +}); + +export default ToolCallCard; diff --git a/ccw/frontend/src/components/orchestrator/ToolCallsTimeline.tsx b/ccw/frontend/src/components/orchestrator/ToolCallsTimeline.tsx new file mode 100644 index 00000000..aebc586e --- /dev/null +++ b/ccw/frontend/src/components/orchestrator/ToolCallsTimeline.tsx @@ -0,0 +1,233 @@ +// ======================================== +// Tool Calls Timeline Component +// ======================================== +// Vertical timeline displaying tool calls in chronological order + +import React, { memo, useMemo, useCallback, useEffect, useRef } from 'react'; +import { Wrench, Loader2 } from 'lucide-react'; +import { cn } from '@/lib/utils'; +import { ToolCallCard } from './ToolCallCard'; +import type { ToolCallExecution } from '@/types/toolCall'; + +// ========== Helper Functions ========== + +/** + * Format timestamp to locale time string + */ +function formatTimestamp(timestamp: number): string { + return new Date(timestamp).toLocaleTimeString('zh-CN', { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: false, + }); +} + +/** + * Get timeline dot color based on tool call status + */ +function getTimelineDotClass(status: ToolCallExecution['status']): string { + switch (status) { + case 'pending': + return 'bg-yellow-500'; + case 'executing': + return 'bg-blue-500 animate-ping'; + case 'success': + return 'bg-green-500'; + case 'error': + return 'bg-destructive'; + case 'canceled': + return 'bg-muted-foreground'; + } +} + +/** + * Check if tool call is currently executing + */ +function isExecutingToolCall(toolCall: ToolCallExecution): boolean { + return toolCall.status === 'executing' || toolCall.status === 'pending'; +} + +// ========== Component Interfaces ========== + +export interface ToolCallsTimelineProps { + /** Array of tool call executions to display */ + toolCalls: ToolCallExecution[]; + /** Callback when a tool call is toggled (expanded/collapsed) */ + onToggleExpand: (callId: string) => void; + /** Optional CSS class name */ + className?: string; +} + +// ========== Internal Components ========== + +interface ToolCallTimelineItemProps { + /** Tool call execution data */ + call: ToolCallExecution; + /** Callback when toggle expand/collapse */ + onToggle: () => void; + /** Whether this is the last item in timeline */ + isLast: boolean; +} + +/** + * Individual timeline item with timestamp and tool call card + */ +const ToolCallTimelineItem = memo(function ToolCallTimelineItem({ + call, + onToggle, + isLast, +}: ToolCallTimelineItemProps) { + const itemRef = useRef(null); + + // Auto-scroll to this item if it's executing + useEffect(() => { + if (isExecutingToolCall(call) && itemRef.current) { + itemRef.current.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + }, [call.status]); + + return ( +
+ {/* Timeline vertical line */} +
+ + {/* Timeline dot */} +
+ + {/* Timestamp */} +
+ {formatTimestamp(call.startTime)} +
+ + {/* Tool Call Card */} + +
+ ); +}, (prevProps, nextProps) => { + // Custom comparison for performance + return ( + prevProps.isLast === nextProps.isLast && + prevProps.call.callId === nextProps.call.callId && + prevProps.call.status === nextProps.call.status && + prevProps.call.isExpanded === nextProps.call.isExpanded && + prevProps.call.endTime === nextProps.call.endTime + ); +}); + +// ========== Main Component ========== + +export function ToolCallsTimeline({ + toolCalls, + onToggleExpand, + className, +}: ToolCallsTimelineProps) { + // Auto-expand executing tool calls + const adjustedToolCalls = useMemo(() => { + return toolCalls.map((call) => { + // Auto-expand if executing + if (isExecutingToolCall(call) && !call.isExpanded) { + return { ...call, isExpanded: true }; + } + return call; + }); + }, [toolCalls]); + + // Handle toggle expand + const handleToggleExpand = useCallback( + (callId: string) => { + onToggleExpand(callId); + }, + [onToggleExpand] + ); + + // Sort tool calls by start time (chronological order) + const sortedToolCalls = useMemo(() => { + return [...adjustedToolCalls].sort((a, b) => a.startTime - b.startTime); + }, [adjustedToolCalls]); + + // Empty state + if (sortedToolCalls.length === 0) { + return ( +
+
+ +

暂无工具调用

+

等待执行开始...

+
+
+ ); + } + + // Loading state (executing calls present) + const hasExecutingCalls = sortedToolCalls.some((call) => + isExecutingToolCall(call) + ); + + return ( +
+ {/* Status indicator */} + {hasExecutingCalls && ( +
+ + 工具执行中... +
+ )} + + {/* Timeline items */} + {sortedToolCalls.map((call, index) => ( + handleToggleExpand(call.callId)} + isLast={index === sortedToolCalls.length - 1} + /> + ))} + + {/* Summary stats */} + {sortedToolCalls.length > 0 && ( +
+
+ Total: {sortedToolCalls.length} tool call{sortedToolCalls.length !== 1 ? 's' : ''} +
+ + + Success: {sortedToolCalls.filter((c) => c.status === 'success').length} + + + + Error: {sortedToolCalls.filter((c) => c.status === 'error').length} + + {hasExecutingCalls && ( + + + Running: {sortedToolCalls.filter((c) => isExecutingToolCall(c)).length} + + )} +
+
+
+ )} +
+ ); +} + +export default ToolCallsTimeline; + +// Re-export ToolCallCard for direct usage +export { ToolCallCard } from './ToolCallCard'; diff --git a/ccw/frontend/src/components/orchestrator/index.ts b/ccw/frontend/src/components/orchestrator/index.ts new file mode 100644 index 00000000..e3b436c2 --- /dev/null +++ b/ccw/frontend/src/components/orchestrator/index.ts @@ -0,0 +1,8 @@ +// ======================================== +// Orchestrator Components Export +// ======================================== + +export { ExecutionHeader } from './ExecutionHeader'; +export { NodeExecutionChain } from './NodeExecutionChain'; +export { ToolCallCard, type ToolCallCardProps } from './ToolCallCard'; +export { ToolCallsTimeline, type ToolCallsTimelineProps } from './ToolCallsTimeline'; diff --git a/ccw/frontend/src/components/shared/LogBlock/LogBlockList.tsx b/ccw/frontend/src/components/shared/LogBlock/LogBlockList.tsx index e7346ee3..d5049b7d 100644 --- a/ccw/frontend/src/components/shared/LogBlock/LogBlockList.tsx +++ b/ccw/frontend/src/components/shared/LogBlock/LogBlockList.tsx @@ -28,9 +28,8 @@ export function LogBlockList({ executionId, className }: LogBlockListProps) { // Get blocks directly from store using the getBlocks selector // This avoids duplicate logic and leverages store-side caching const blocks = useCliStreamStore( - (state) => executionId ? state.getBlocks(executionId) : [], - (a: LogBlockData[], b: LogBlockData[]) => a === b // Shallow comparison - arrays are cached in store - ); + (state) => executionId ? state.getBlocks(executionId) : [] + ) as LogBlockData[]; // Get execution status for empty state display const currentExecution = useCliStreamStore((state) => diff --git a/ccw/frontend/src/components/shared/MarkdownModal.tsx b/ccw/frontend/src/components/shared/MarkdownModal.tsx index 73e67def..2eaca446 100644 --- a/ccw/frontend/src/components/shared/MarkdownModal.tsx +++ b/ccw/frontend/src/components/shared/MarkdownModal.tsx @@ -47,7 +47,7 @@ export interface ModalAction { label: string; icon?: React.ComponentType<{ className?: string }>; onClick: (content: string) => void | Promise; - variant?: 'default' | 'outline' | 'ghost' | 'destructive' | 'success'; + variant?: 'default' | 'outline' | 'ghost' | 'destructive' | 'secondary'; disabled?: boolean; } diff --git a/ccw/frontend/src/components/shared/SessionCard.tsx b/ccw/frontend/src/components/shared/SessionCard.tsx index d0339f38..a4fc8370 100644 --- a/ccw/frontend/src/components/shared/SessionCard.tsx +++ b/ccw/frontend/src/components/shared/SessionCard.tsx @@ -78,7 +78,7 @@ const statusLabelKeys: Record = { // Type variant configuration for session type badges (unique colors for each type) const typeVariantConfig: Record< - SessionMetadata['type'], + NonNullable, { variant: 'default' | 'secondary' | 'destructive' | 'success' | 'warning' | 'info' | 'review'; icon: React.ElementType } > = { review: { variant: 'review', icon: Search }, // Purple @@ -91,7 +91,7 @@ const typeVariantConfig: Record< }; // Type label keys for i18n -const typeLabelKeys: Record = { +const typeLabelKeys: Record, string> = { review: 'sessions.type.review', tdd: 'sessions.type.tdd', test: 'sessions.type.test', diff --git a/ccw/frontend/src/components/ui/ContextAssembler.tsx b/ccw/frontend/src/components/ui/ContextAssembler.tsx index b8ef29f0..3f25cf17 100644 --- a/ccw/frontend/src/components/ui/ContextAssembler.tsx +++ b/ccw/frontend/src/components/ui/ContextAssembler.tsx @@ -44,9 +44,9 @@ const ContextAssembler = React.forwardRef const nodeRegex = /\{\{node:([^}]+)\}\}/g; const varRegex = /\{\{var:([^}]+)\}\}/g; - let match; + let match: RegExpExecArray | null; while ((match = nodeRegex.exec(value)) !== null) { - const node = availableNodes.find((n) => n.id === match[1]); + const node = availableNodes.find((n) => n.id === match![1]); extracted.push({ nodeId: match[1], label: node?.label, @@ -98,7 +98,7 @@ const ContextAssembler = React.forwardRef const addNode = (nodeId: string) => { const node = availableNodes.find((n) => n.id === nodeId); if (node && !rules.find((r) => r.nodeId === nodeId)) { - const newRules = [...rules, { nodeId, label: node.label, variable: node.outputVariable, includeOutput: true, transform: "raw" }]; + const newRules: ContextRule[] = [...rules, { nodeId, label: node.label, variable: node.outputVariable, includeOutput: true, transform: "raw" as const }]; setRules(newRules); updateTemplate(newRules); } @@ -106,7 +106,7 @@ const ContextAssembler = React.forwardRef const addVariable = (variableName: string) => { if (!rules.find((r) => r.variable === variableName && !r.nodeId)) { - const newRules = [...rules, { nodeId: "", variable: variableName, includeOutput: true, transform: "raw" }]; + const newRules: ContextRule[] = [...rules, { nodeId: "", variable: variableName, includeOutput: true, transform: "raw" as const }]; setRules(newRules); updateTemplate(newRules); } diff --git a/ccw/frontend/src/components/workspace/WorkspaceSelector.tsx b/ccw/frontend/src/components/workspace/WorkspaceSelector.tsx index 8f2f4756..a45628ac 100644 --- a/ccw/frontend/src/components/workspace/WorkspaceSelector.tsx +++ b/ccw/frontend/src/components/workspace/WorkspaceSelector.tsx @@ -291,11 +291,11 @@ export function WorkspaceSelector({ className }: WorkspaceSelectorProps) { {/* Hidden file input for folder selection */} + {/* eslint-disable-next-line @typescript-eslint/no-explicit-any */} { const { result } = renderHook(() => useWorkflowStatusCounts(), { wrapper }); await waitFor(() => { - expect(result.current.isSuccess).toBe(true); + expect((result.current as any).isSuccess).toBe(true); }); expect(result.current.data).toEqual(mockData); @@ -71,12 +71,12 @@ describe('Chart Hooks Integration Tests', () => { mockApi.get.mockResolvedValue({ data: mockData }); const { result } = renderHook( - () => useWorkflowStatusCounts({ projectPath: '/test/workspace' }), + () => useWorkflowStatusCounts({ projectPath: '/test/workspace' } as any), { wrapper } ); await waitFor(() => { - expect(result.current.isSuccess).toBe(true); + expect((result.current as any).isSuccess).toBe(true); }); expect(mockApi.get).toHaveBeenCalledWith('/api/session-status-counts', { @@ -90,7 +90,7 @@ describe('Chart Hooks Integration Tests', () => { const { result } = renderHook(() => useWorkflowStatusCounts(), { wrapper }); await waitFor(() => { - expect(result.current.isError).toBe(true); + expect((result.current as any).isError).toBe(true); }); expect(result.current.error).toBeDefined(); @@ -102,13 +102,13 @@ describe('Chart Hooks Integration Tests', () => { mockApi.get.mockResolvedValue({ data: mockData }); const { result: result1 } = renderHook(() => useWorkflowStatusCounts(), { wrapper }); - await waitFor(() => expect(result1.current.isSuccess).toBe(true)); + await waitFor(() => expect((result1.current as any).isSuccess).toBe(true)); // Second render should use cache const { result: result2 } = renderHook(() => useWorkflowStatusCounts(), { wrapper }); await waitFor(() => { - expect(result2.current.isSuccess).toBe(true); + expect((result2.current as any).isSuccess).toBe(true); }); // API should only be called once (cached) @@ -122,7 +122,7 @@ describe('Chart Hooks Integration Tests', () => { const { result } = renderHook(() => useWorkflowStatusCounts(), { wrapper }); - await waitFor(() => expect(result.current.isSuccess).toBe(true)); + await waitFor(() => expect((result.current as any).isSuccess).toBe(true)); // Refetch await result.current.refetch(); @@ -143,7 +143,7 @@ describe('Chart Hooks Integration Tests', () => { const { result } = renderHook(() => useActivityTimeline(), { wrapper }); await waitFor(() => { - expect(result.current.isSuccess).toBe(true); + expect((result.current as any).isSuccess).toBe(true); }); expect(result.current.data).toEqual(mockData); @@ -159,10 +159,10 @@ describe('Chart Hooks Integration Tests', () => { end: new Date('2026-01-31'), }; - const { result } = renderHook(() => useActivityTimeline(dateRange), { wrapper }); + const { result } = renderHook(() => (useActivityTimeline as any)(dateRange), { wrapper }); await waitFor(() => { - expect(result.current.isSuccess).toBe(true); + expect((result.current as any).isSuccess).toBe(true); }); expect(mockApi.get).toHaveBeenCalledWith('/api/activity-timeline', { @@ -179,7 +179,7 @@ describe('Chart Hooks Integration Tests', () => { const { result } = renderHook(() => useActivityTimeline(), { wrapper }); await waitFor(() => { - expect(result.current.isSuccess).toBe(true); + expect((result.current as any).isSuccess).toBe(true); }); expect(result.current.data).toEqual([]); @@ -190,12 +190,12 @@ describe('Chart Hooks Integration Tests', () => { mockApi.get.mockResolvedValue({ data: mockData }); const { result } = renderHook( - () => useActivityTimeline(undefined, '/test/workspace'), + () => (useActivityTimeline as any)(undefined, '/test/workspace'), { wrapper } ); await waitFor(() => { - expect(result.current.isSuccess).toBe(true); + expect((result.current as any).isSuccess).toBe(true); }); expect(mockApi.get).toHaveBeenCalledWith('/api/activity-timeline', { @@ -210,11 +210,11 @@ describe('Chart Hooks Integration Tests', () => { mockApi.get.mockResolvedValueOnce({ data: mockData1 }); const { result, rerender } = renderHook( - ({ workspace }: { workspace?: string }) => useActivityTimeline(undefined, workspace), + ({ workspace }: { workspace?: string }) => (useActivityTimeline as any)(undefined, workspace), { wrapper, initialProps: { workspace: '/workspace1' } } ); - await waitFor(() => expect(result.current.isSuccess).toBe(true)); + await waitFor(() => expect((result.current as any).isSuccess).toBe(true)); expect(result.current.data).toEqual(mockData1); // Change workspace @@ -242,7 +242,7 @@ describe('Chart Hooks Integration Tests', () => { const { result } = renderHook(() => useTaskTypeCounts(), { wrapper }); await waitFor(() => { - expect(result.current.isSuccess).toBe(true); + expect((result.current as any).isSuccess).toBe(true); }); expect(result.current.data).toEqual(mockData); @@ -254,12 +254,12 @@ describe('Chart Hooks Integration Tests', () => { mockApi.get.mockResolvedValue({ data: mockData }); const { result } = renderHook( - () => useTaskTypeCounts({ projectPath: '/test/workspace' }), + () => useTaskTypeCounts({ projectPath: '/test/workspace' } as any), { wrapper } ); await waitFor(() => { - expect(result.current.isSuccess).toBe(true); + expect((result.current as any).isSuccess).toBe(true); }); expect(mockApi.get).toHaveBeenCalledWith('/api/task-type-counts', { @@ -278,7 +278,7 @@ describe('Chart Hooks Integration Tests', () => { const { result } = renderHook(() => useTaskTypeCounts(), { wrapper }); await waitFor(() => { - expect(result.current.isSuccess).toBe(true); + expect((result.current as any).isSuccess).toBe(true); }); expect(result.current.data).toEqual(mockData); @@ -294,7 +294,7 @@ describe('Chart Hooks Integration Tests', () => { ); await waitFor(() => { - expect(result.current.isSuccess).toBe(true); + expect((result.current as any).isSuccess).toBe(true); }); // Data should be fresh for 30s @@ -318,9 +318,9 @@ describe('Chart Hooks Integration Tests', () => { const { result: result3 } = renderHook(() => useTaskTypeCounts(), { wrapper }); await waitFor(() => { - expect(result1.current.isSuccess).toBe(true); - expect(result2.current.isSuccess).toBe(true); - expect(result3.current.isSuccess).toBe(true); + expect((result1.current as any).isSuccess).toBe(true); + expect((result2.current as any).isSuccess).toBe(true); + expect((result3.current as any).isSuccess).toBe(true); }); expect(mockApi.get).toHaveBeenCalledTimes(3); @@ -343,9 +343,9 @@ describe('Chart Hooks Integration Tests', () => { const { result: result3 } = renderHook(() => useTaskTypeCounts(), { wrapper }); await waitFor(() => { - expect(result1.current.isError).toBe(true); - expect(result2.current.isSuccess).toBe(true); - expect(result3.current.isSuccess).toBe(true); + expect((result1.current as any).isError).toBe(true); + expect((result2.current as any).isSuccess).toBe(true); + expect((result3.current as any).isSuccess).toBe(true); }); }); @@ -355,13 +355,13 @@ describe('Chart Hooks Integration Tests', () => { // First component const { result: result1 } = renderHook(() => useWorkflowStatusCounts(), { wrapper }); - await waitFor(() => expect(result1.current.isSuccess).toBe(true)); + await waitFor(() => expect((result1.current as any).isSuccess).toBe(true)); // Second component should use cache const { result: result2 } = renderHook(() => useWorkflowStatusCounts(), { wrapper }); await waitFor(() => { - expect(result2.current.isSuccess).toBe(true); + expect((result2.current as any).isSuccess).toBe(true); }); // Only one API call diff --git a/ccw/frontend/src/hooks/useCodexLens.test.tsx b/ccw/frontend/src/hooks/useCodexLens.test.tsx index 771c6de1..216a7fa0 100644 --- a/ccw/frontend/src/hooks/useCodexLens.test.tsx +++ b/ccw/frontend/src/hooks/useCodexLens.test.tsx @@ -183,7 +183,7 @@ describe('useCodexLens Hook', () => { describe('useCodexLensModels', () => { it('should fetch and filter models by type', async () => { - vi.mocked(api.fetchCodexLensModels).mockResolvedValue(mockModelsData); + vi.mocked(api.fetchCodexLensModels).mockResolvedValue(mockModelsData as any); const { result } = renderHook(() => useCodexLensModels(), { wrapper }); @@ -203,7 +203,7 @@ describe('useCodexLens Hook', () => { settings: { SETTING1: 'setting1' }, raw: 'KEY1=value1\nKEY2=value2', }; - vi.mocked(api.fetchCodexLensEnv).mockResolvedValue(mockEnv); + vi.mocked(api.fetchCodexLensEnv).mockResolvedValue(mockEnv as any); const { result } = renderHook(() => useCodexLensEnv(), { wrapper }); @@ -225,8 +225,8 @@ describe('useCodexLens Hook', () => { ], selected_device_id: 0, }; - vi.mocked(api.fetchCodexLensGpuDetect).mockResolvedValue(mockDetect); - vi.mocked(api.fetchCodexLensGpuList).mockResolvedValue(mockList); + vi.mocked(api.fetchCodexLensGpuDetect).mockResolvedValue(mockDetect as any); + vi.mocked(api.fetchCodexLensGpuList).mockResolvedValue(mockList as any); const { result } = renderHook(() => useCodexLensGpu(), { wrapper }); @@ -366,13 +366,13 @@ describe('useCodexLens Hook', () => { env: { KEY1: 'newvalue' }, settings: {}, raw: 'KEY1=newvalue', - }); + } as any); const { result } = renderHook(() => useUpdateCodexLensEnv(), { wrapper }); const updateResult = await result.current.updateEnv({ raw: 'KEY1=newvalue', - }); + } as any); expect(api.updateCodexLensEnv).toHaveBeenCalledWith({ raw: 'KEY1=newvalue' }); expect(updateResult.success).toBe(true); diff --git a/ccw/frontend/src/hooks/useCodexLens.ts b/ccw/frontend/src/hooks/useCodexLens.ts index 04f218b4..2d3f4789 100644 --- a/ccw/frontend/src/hooks/useCodexLens.ts +++ b/ccw/frontend/src/hooks/useCodexLens.ts @@ -1000,7 +1000,7 @@ export function useCodexLensIndexingStatus(): UseCodexLensIndexingStatusReturn { queryKey: codexLensKeys.indexingStatus(), queryFn: checkCodexLensIndexingStatus, staleTime: STALE_TIME_SHORT, - refetchInterval: (data) => (data?.inProgress ? 2000 : false), // Poll every 2s when indexing + refetchInterval: (query) => ((query.state.data as any)?.inProgress ? 2000 : false), // Poll every 2s when indexing retry: false, }); diff --git a/ccw/frontend/src/hooks/useIssues.test.tsx b/ccw/frontend/src/hooks/useIssues.test.tsx index 78078b74..decf0e30 100644 --- a/ccw/frontend/src/hooks/useIssues.test.tsx +++ b/ccw/frontend/src/hooks/useIssues.test.tsx @@ -67,7 +67,7 @@ describe('useIssueQueue', () => { grouped_items: { 'parallel-group': ['task1', 'task2'] }, }; - vi.mocked(api.fetchIssueQueue).mockResolvedValue(mockQueue); + vi.mocked(api.fetchIssueQueue).mockResolvedValue(mockQueue as any); const { result } = renderHook(() => useIssueQueue(), { wrapper: createWrapper(), @@ -192,7 +192,7 @@ describe('useIssueDiscovery', () => { vi.mocked(api.fetchDiscoveries).mockResolvedValue([ { id: '1', name: 'Session 1', status: 'completed' as const, progress: 100, findings_count: 2, created_at: '2024-01-01T00:00:00Z' }, ]); - vi.mocked(api.fetchDiscoveryFindings).mockResolvedValue(mockFindings); + vi.mocked(api.fetchDiscoveryFindings).mockResolvedValue(mockFindings as any); const { result } = renderHook(() => useIssueDiscovery(), { wrapper: createWrapper(), @@ -228,7 +228,7 @@ describe('useIssueDiscovery', () => { vi.mocked(api.fetchDiscoveries).mockResolvedValue([ { id: '1', name: 'Session 1', status: 'completed' as const, progress: 100, findings_count: 2, created_at: '2024-01-01T00:00:00Z' }, ]); - vi.mocked(api.fetchDiscoveryFindings).mockResolvedValue(mockFindings); + vi.mocked(api.fetchDiscoveryFindings).mockResolvedValue(mockFindings as any); const { result } = renderHook(() => useIssueDiscovery(), { wrapper: createWrapper(), @@ -264,7 +264,7 @@ describe('useIssueDiscovery', () => { vi.mocked(api.fetchDiscoveries).mockResolvedValue([ { id: '1', name: 'Session 1', status: 'completed' as const, progress: 100, findings_count: 2, created_at: '2024-01-01T00:00:00Z' }, ]); - vi.mocked(api.fetchDiscoveryFindings).mockResolvedValue(mockFindings); + vi.mocked(api.fetchDiscoveryFindings).mockResolvedValue(mockFindings as any); const { result } = renderHook(() => useIssueDiscovery(), { wrapper: createWrapper(), @@ -299,7 +299,7 @@ describe('useIssueDiscovery', () => { vi.mocked(api.fetchDiscoveries).mockResolvedValue([ { id: '1', name: 'Session 1', status: 'completed' as const, progress: 100, findings_count: 1, created_at: '2024-01-01T00:00:00Z' }, ]); - vi.mocked(api.fetchDiscoveryFindings).mockResolvedValue(mockFindings); + vi.mocked(api.fetchDiscoveryFindings).mockResolvedValue(mockFindings as any); const { result } = renderHook(() => useIssueDiscovery(), { wrapper: createWrapper(), diff --git a/ccw/frontend/src/hooks/useLocale.ts b/ccw/frontend/src/hooks/useLocale.ts index 3d27e898..98b75819 100644 --- a/ccw/frontend/src/hooks/useLocale.ts +++ b/ccw/frontend/src/hooks/useLocale.ts @@ -56,6 +56,10 @@ export function useLocale(): UseLocaleReturn { * Hook to format i18n messages with the current locale * @returns A formatMessage function for translating message IDs * + * Supports both string and react-intl descriptor formats: + * - formatMessage('home.title') + * - formatMessage({ id: 'home.title' }) + * * @example * ```tsx * const formatMessage = useFormatMessage(); @@ -63,12 +67,13 @@ export function useLocale(): UseLocaleReturn { * ``` */ export function useFormatMessage(): ( - id: string, + idOrDescriptor: string | { id: string; defaultMessage?: string }, values?: Record ) => string { // Use useMemo to avoid recreating the function on each render return useMemo(() => { - return (id: string, values?: Record) => { + return (idOrDescriptor: string | { id: string; defaultMessage?: string }, values?: Record) => { + const id = typeof idOrDescriptor === 'string' ? idOrDescriptor : idOrDescriptor.id; return formatMessage(id, values); }; }, []); diff --git a/ccw/frontend/src/hooks/useSessions.ts b/ccw/frontend/src/hooks/useSessions.ts index e604ab71..b7abb5ca 100644 --- a/ccw/frontend/src/hooks/useSessions.ts +++ b/ccw/frontend/src/hooks/useSessions.ts @@ -298,7 +298,7 @@ export function usePrefetchSessions() { return (filter?: SessionsFilter) => { queryClient.prefetchQuery({ queryKey: sessionsKeys.list(filter), - queryFn: fetchSessions, + queryFn: () => fetchSessions(), staleTime: STALE_TIME, }); }; diff --git a/ccw/frontend/src/hooks/useWebSocket.ts b/ccw/frontend/src/hooks/useWebSocket.ts index c8a5aaaf..32e06dd8 100644 --- a/ccw/frontend/src/hooks/useWebSocket.ts +++ b/ccw/frontend/src/hooks/useWebSocket.ts @@ -14,6 +14,7 @@ import { type ExecutionLog, } from '../types/execution'; import { SurfaceUpdateSchema } from '../packages/a2ui-runtime/core/A2UITypes'; +import type { ToolCallKind } from '../types/toolCall'; // Constants const RECONNECT_DELAY_BASE = 1000; // 1 second @@ -42,6 +43,15 @@ function getStoreState() { addLog: execution.addLog, completeExecution: execution.completeExecution, currentExecution: execution.currentExecution, + // Tool call actions + startToolCall: execution.startToolCall, + updateToolCall: execution.updateToolCall, + completeToolCall: execution.completeToolCall, + toggleToolCallExpanded: execution.toggleToolCallExpanded, + // Tool call getters + getToolCallsForNode: execution.getToolCallsForNode, + // Node output actions + addNodeOutput: execution.addNodeOutput, // Flow store updateNode: flow.updateNode, // CLI stream store @@ -60,6 +70,61 @@ export interface UseWebSocketReturn { reconnect: () => void; } +// ========== Tool Call Parsing Helpers ========== + +/** + * Parse tool call metadata from content + * Expected format: "[Tool] toolName(args)" + */ +function parseToolCallMetadata(content: string): { toolName: string; args: string } | null { + // Handle string content + if (typeof content === 'string') { + const match = content.match(/^\[Tool\]\s+(\w+)\((.*)\)$/); + if (match) { + return { toolName: match[1], args: match[2] || '' }; + } + } + + // Handle object content with toolName field + try { + const parsed = typeof content === 'string' ? JSON.parse(content) : content; + if (parsed && typeof parsed === 'object' && 'toolName' in parsed) { + return { + toolName: String(parsed.toolName), + args: parsed.parameters ? JSON.stringify(parsed.parameters) : '', + }; + } + } catch { + // Not valid JSON, return null + } + + return null; +} + +/** + * Infer tool call kind from tool name + */ +function inferToolCallKind(toolName: string): ToolCallKind { + const name = toolName.toLowerCase(); + + if (name === 'exec_command' || name === 'execute') return 'execute'; + if (name === 'apply_patch' || name === 'patch') return 'patch'; + if (name === 'web_search' || name === 'exa_search') return 'web_search'; + if (name.startsWith('mcp_') || name.includes('mcp')) return 'mcp_tool'; + if (name.includes('file') || name.includes('read') || name.includes('write')) return 'file_operation'; + if (name.includes('think') || name.includes('reason')) return 'thinking'; + + // Default to execute + return 'execute'; +} + +/** + * Generate unique tool call ID + */ +function generateToolCallId(): string { + return `tool_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`; +} + export function useWebSocket(options: UseWebSocketOptions = {}): UseWebSocketReturn { const { enabled = true, onMessage } = options; @@ -105,7 +170,7 @@ export function useWebSocket(options: UseWebSocketOptions = {}): UseWebSocketRet const unitContent = unit?.content || outputData; const unitType = unit?.type || chunkType; - // Special handling for tool_call type + // Convert content to string for display let content: string; if (unitType === 'tool_call' && typeof unitContent === 'object' && unitContent !== null) { // Format tool_call display @@ -114,7 +179,49 @@ export function useWebSocket(options: UseWebSocketOptions = {}): UseWebSocketRet content = typeof unitContent === 'string' ? unitContent : JSON.stringify(unitContent); } - // Split by lines and add each line to store + // ========== Tool Call Processing ========== + // Parse and start new tool call if this is a tool_call type + if (unitType === 'tool_call') { + const metadata = parseToolCallMetadata(content); + if (metadata) { + const callId = generateToolCallId(); + const currentNodeId = stores.currentExecution?.currentNodeId; + + if (currentNodeId) { + stores.startToolCall(currentNodeId, callId, { + kind: inferToolCallKind(metadata.toolName), + description: metadata.args + ? `${metadata.toolName}(${metadata.args})` + : metadata.toolName, + }); + + // Also add to node output for streaming display + stores.addNodeOutput(currentNodeId, { + type: 'tool_call', + content, + timestamp: Date.now(), + }); + } + } + } + + // ========== Stream Processing ========== + // Update tool call output buffer if we have an active tool call for this node + const currentNodeId = stores.currentExecution?.currentNodeId; + if (currentNodeId && (unitType === 'stdout' || unitType === 'stderr')) { + const toolCalls = stores.getToolCallsForNode?.(currentNodeId); + const activeCall = toolCalls?.find(c => c.status === 'executing'); + + if (activeCall) { + stores.updateToolCall(currentNodeId, activeCall.callId, { + outputChunk: content, + stream: unitType === 'stderr' ? 'stderr' : 'stdout', + }); + } + } + + // ========== Legacy CLI Stream Output ========== + // Split by lines and add each line to cliStreamStore const lines = content.split('\n'); lines.forEach((line: string) => { // Add non-empty lines, or single line if that's all we have diff --git a/ccw/frontend/src/lib/api.mcp.test.ts b/ccw/frontend/src/lib/api.mcp.test.ts index 546a3d37..d4c26680 100644 --- a/ccw/frontend/src/lib/api.mcp.test.ts +++ b/ccw/frontend/src/lib/api.mcp.test.ts @@ -20,7 +20,7 @@ function jsonResponse(body: unknown, init: ResponseInit = {}) { }); } -function getLastFetchCall(fetchMock: ReturnType) { +function getLastFetchCall(fetchMock: any) { const calls = fetchMock.mock.calls; return calls[calls.length - 1] as [RequestInfo | URL, RequestInit | undefined]; } diff --git a/ccw/frontend/src/lib/api.ts b/ccw/frontend/src/lib/api.ts index 7a1310ea..f1ea971e 100644 --- a/ccw/frontend/src/lib/api.ts +++ b/ccw/frontend/src/lib/api.ts @@ -6,7 +6,7 @@ import type { SessionMetadata, TaskData, IndexStatus, IndexRebuildRequest, Rule, RuleCreateInput, RulesResponse, Prompt, PromptInsight, Pattern, Suggestion, McpTemplate, McpTemplateInstallRequest, AllProjectsResponse, OtherProjectsServersResponse, CrossCliCopyRequest, CrossCliCopyResponse } from '../types/store'; // Re-export types for backward compatibility -export type { IndexStatus, IndexRebuildRequest, Rule, RuleCreateInput, RulesResponse, Prompt, PromptInsight, Pattern, Suggestion }; +export type { IndexStatus, IndexRebuildRequest, Rule, RuleCreateInput, RulesResponse, Prompt, PromptInsight, Pattern, Suggestion, McpTemplate, McpTemplateInstallRequest, AllProjectsResponse, OtherProjectsServersResponse, CrossCliCopyRequest, CrossCliCopyResponse }; /** @@ -1152,14 +1152,9 @@ export async function fetchCommands(projectPath?: string): Promise { describe('register()', () => { it('should register a component renderer', () => { - registry.register('TestComponent', mockRenderer); - expect(registry.has('TestComponent')).toBe(true); + registry.register('TestComponent' as any, mockRenderer); + expect(registry.has('TestComponent' as any)).toBe(true); }); it('should allow overriding existing renderer', () => { - registry.register('TestComponent', mockRenderer); - registry.register('TestComponent', anotherMockRenderer); - - const retrieved = registry.get('TestComponent'); + registry.register('TestComponent' as any, mockRenderer); + registry.register('TestComponent' as any, anotherMockRenderer); + + const retrieved = registry.get('TestComponent' as any); expect(retrieved).toBe(anotherMockRenderer); }); @@ -49,57 +50,57 @@ describe('A2UIComponentRegistry', () => { describe('unregister()', () => { it('should remove a registered renderer', () => { - registry.register('TestComponent', mockRenderer); - expect(registry.has('TestComponent')).toBe(true); + registry.register('TestComponent' as any, mockRenderer); + expect(registry.has('TestComponent' as any)).toBe(true); - registry.unregister('TestComponent'); - expect(registry.has('TestComponent')).toBe(false); + registry.unregister('TestComponent' as any); + expect(registry.has('TestComponent' as any)).toBe(false); }); it('should be idempotent for non-existent components', () => { - expect(() => registry.unregister('NonExistent')).not.toThrow(); - expect(registry.has('NonExistent')).toBe(false); + expect(() => registry.unregister('NonExistent' as any)).not.toThrow(); + expect(registry.has('NonExistent' as any)).toBe(false); }); }); describe('get()', () => { it('should return registered renderer', () => { - registry.register('TestComponent', mockRenderer); - const retrieved = registry.get('TestComponent'); + registry.register('TestComponent' as any, mockRenderer); + const retrieved = registry.get('TestComponent' as any); expect(retrieved).toBe(mockRenderer); }); it('should return undefined for unregistered component', () => { - const retrieved = registry.get('NonExistent'); + const retrieved = registry.get('NonExistent' as any); expect(retrieved).toBeUndefined(); }); it('should return correct renderer after multiple registrations', () => { - registry.register('First', mockRenderer); - registry.register('Second', anotherMockRenderer); + registry.register('First' as any, mockRenderer); + registry.register('Second' as any, anotherMockRenderer); - expect(registry.get('First')).toBe(mockRenderer); - expect(registry.get('Second')).toBe(anotherMockRenderer); + expect(registry.get('First' as any)).toBe(mockRenderer); + expect(registry.get('Second' as any)).toBe(anotherMockRenderer); }); }); describe('has()', () => { it('should return true for registered components', () => { - registry.register('TestComponent', mockRenderer); - expect(registry.has('TestComponent')).toBe(true); + registry.register('TestComponent' as any, mockRenderer); + expect(registry.has('TestComponent' as any)).toBe(true); }); it('should return false for unregistered components', () => { - expect(registry.has('NonExistent')).toBe(false); + expect(registry.has('NonExistent' as any)).toBe(false); }); it('should return false after unregistering', () => { - registry.register('TestComponent', mockRenderer); - expect(registry.has('TestComponent')).toBe(true); + registry.register('TestComponent' as any, mockRenderer); + expect(registry.has('TestComponent' as any)).toBe(true); - registry.unregister('TestComponent'); - expect(registry.has('TestComponent')).toBe(false); + registry.unregister('TestComponent' as any); + expect(registry.has('TestComponent' as any)).toBe(false); }); }); @@ -149,7 +150,7 @@ describe('A2UIComponentRegistry', () => { }); it('should be idempotent', () => { - registry.register('Test', mockRenderer); + registry.register('Test' as any, mockRenderer); registry.clear(); expect(registry.size).toBe(0); diff --git a/ccw/frontend/src/packages/a2ui-runtime/__tests__/A2UIParser.test.ts b/ccw/frontend/src/packages/a2ui-runtime/__tests__/A2UIParser.test.ts index 6cfebb16..4683bb6b 100644 --- a/ccw/frontend/src/packages/a2ui-runtime/__tests__/A2UIParser.test.ts +++ b/ccw/frontend/src/packages/a2ui-runtime/__tests__/A2UIParser.test.ts @@ -376,15 +376,13 @@ describe('A2UIParser', () => { code: z.ZodIssueCode.invalid_type, path: ['components', 0, 'id'], expected: 'string', - received: 'undefined', message: 'Required', - }, + } as any, { - code: z.ZodIssueCode.invalid_string, + code: 'invalid_format' as any, path: ['surfaceId'], - validation: 'uuid', message: 'Invalid format', - }, + } as any, ]); const parseError = new A2UIParseError('Validation failed', zodError); diff --git a/ccw/frontend/src/packages/a2ui-runtime/core/A2UIParser.ts b/ccw/frontend/src/packages/a2ui-runtime/core/A2UIParser.ts index 6a199d0a..e5752059 100644 --- a/ccw/frontend/src/packages/a2ui-runtime/core/A2UIParser.ts +++ b/ccw/frontend/src/packages/a2ui-runtime/core/A2UIParser.ts @@ -105,14 +105,14 @@ export class A2UIParser { * @param json - JSON string to parse * @returns Result object with success flag and data or error */ - safeParse(json: string): z.SafeParseReturnType { + safeParse(json: string): ReturnType { try { const data = JSON.parse(json); return SurfaceUpdateSchema.safeParse(data); } catch (error) { return { success: false as const, - error: error as z.ZodError, + error: error as any, }; } } @@ -122,7 +122,7 @@ export class A2UIParser { * @param data - Object to validate * @returns Result object with success flag and data or error */ - safeParseObject(data: unknown): z.SafeParseReturnType { + safeParseObject(data: unknown): ReturnType { return SurfaceUpdateSchema.safeParse(data); } diff --git a/ccw/frontend/src/packages/a2ui-runtime/core/A2UITypes.ts b/ccw/frontend/src/packages/a2ui-runtime/core/A2UITypes.ts index 1d1c44d2..2358578d 100644 --- a/ccw/frontend/src/packages/a2ui-runtime/core/A2UITypes.ts +++ b/ccw/frontend/src/packages/a2ui-runtime/core/A2UITypes.ts @@ -41,7 +41,7 @@ export const BooleanContentSchema = z.union([ /** Action trigger */ export const ActionSchema = z.object({ actionId: z.string(), - parameters: z.record(z.unknown()).optional(), + parameters: z.record(z.string(), z.unknown()).optional(), }); /** Text component */ @@ -196,7 +196,7 @@ export const DisplayModeSchema = z.enum(['popup', 'panel']); export const SurfaceUpdateSchema = z.object({ surfaceId: z.string(), components: z.array(SurfaceComponentSchema), - initialState: z.record(z.unknown()).optional(), + initialState: z.record(z.string(), z.unknown()).optional(), /** Display mode: 'popup' for centered dialog, 'panel' for notification panel */ displayMode: DisplayModeSchema.optional(), }); diff --git a/ccw/frontend/src/packages/a2ui-runtime/renderer/A2UIRenderer.tsx b/ccw/frontend/src/packages/a2ui-runtime/renderer/A2UIRenderer.tsx index 043c007d..294cee8d 100644 --- a/ccw/frontend/src/packages/a2ui-runtime/renderer/A2UIRenderer.tsx +++ b/ccw/frontend/src/packages/a2ui-runtime/renderer/A2UIRenderer.tsx @@ -162,7 +162,7 @@ export function resolveLiteralOrBinding( const value = resolveBinding(content as Binding); // Return resolved value or empty string as fallback - return value ?? ''; + return (value ?? '') as string | number | boolean; } /** diff --git a/ccw/frontend/src/packages/a2ui-runtime/renderer/components/A2UICLIOutput.tsx b/ccw/frontend/src/packages/a2ui-runtime/renderer/components/A2UICLIOutput.tsx index d2f1f0a2..5af129ec 100644 --- a/ccw/frontend/src/packages/a2ui-runtime/renderer/components/A2UICLIOutput.tsx +++ b/ccw/frontend/src/packages/a2ui-runtime/renderer/components/A2UICLIOutput.tsx @@ -16,7 +16,7 @@ function highlightSyntax(output: string, language?: string): React.ReactNode { const lines = output.split('\n'); // Define syntax patterns by language - const patterns: Record = { + const patterns: Record = { bash: [ { regex: /^(\$|>|\s)(\s*)/gm, className: 'text-muted-foreground' }, // Prompt { regex: /\b(error|fail|failed|failure)\b/gi, className: 'text-destructive font-semibold' }, @@ -55,8 +55,8 @@ function highlightSyntax(output: string, language?: string): React.ReactNode { for (const pattern of langPatterns) { if (typeof result === 'string') { - const parts = result.split(pattern.regex); - result = parts.map((part, i) => { + const parts: string[] = result.split(pattern.regex); + result = parts.map((part: string, i: number) => { if (pattern.regex.test(part)) { return ( diff --git a/ccw/frontend/src/packages/a2ui-runtime/renderer/components/A2UIText.tsx b/ccw/frontend/src/packages/a2ui-runtime/renderer/components/A2UIText.tsx index 245b2605..1d9f30b4 100644 --- a/ccw/frontend/src/packages/a2ui-runtime/renderer/components/A2UIText.tsx +++ b/ccw/frontend/src/packages/a2ui-runtime/renderer/components/A2UIText.tsx @@ -23,7 +23,7 @@ export const A2UIText: ComponentRenderer = ({ component, state, onAction, resolv const { Text } = component as { Text: { text: unknown; usageHint?: string } }; // Resolve text content - const text = resolveTextContent(Text.text, resolveBinding); + const text = resolveTextContent(Text.text as { literalString: string } | { path: string }, resolveBinding); const usageHint = Text.usageHint || 'span'; // Map usageHint to HTML elements diff --git a/ccw/frontend/src/pages/CodexLensManagerPage.test.tsx b/ccw/frontend/src/pages/CodexLensManagerPage.test.tsx index a0502d7e..b58a6b47 100644 --- a/ccw/frontend/src/pages/CodexLensManagerPage.test.tsx +++ b/ccw/frontend/src/pages/CodexLensManagerPage.test.tsx @@ -87,7 +87,7 @@ describe('CodexLensManagerPage', () => { describe('when installed', () => { beforeEach(() => { - vi.mocked(useCodexLensDashboard).mockReturnValue({ + (vi.mocked(useCodexLensDashboard) as any).mockReturnValue({ installed: true, status: mockDashboardData.status, config: mockDashboardData.config, @@ -97,7 +97,7 @@ describe('CodexLensManagerPage', () => { error: null, refetch: vi.fn(), }); - vi.mocked(useCodexLensMutations).mockReturnValue(mockMutations); + (vi.mocked(useCodexLensMutations) as any).mockReturnValue(mockMutations); }); it('should render page title and description', () => { @@ -134,7 +134,7 @@ describe('CodexLensManagerPage', () => { it('should call refresh on button click', async () => { const refetch = vi.fn(); - vi.mocked(useCodexLensDashboard).mockReturnValue({ + (vi.mocked(useCodexLensDashboard) as any).mockReturnValue({ installed: true, status: mockDashboardData.status, config: mockDashboardData.config, @@ -157,7 +157,7 @@ describe('CodexLensManagerPage', () => { describe('when not installed', () => { beforeEach(() => { - vi.mocked(useCodexLensDashboard).mockReturnValue({ + (vi.mocked(useCodexLensDashboard) as any).mockReturnValue({ installed: false, status: undefined, config: undefined, @@ -167,7 +167,7 @@ describe('CodexLensManagerPage', () => { error: null, refetch: vi.fn(), }); - vi.mocked(useCodexLensMutations).mockReturnValue(mockMutations); + (vi.mocked(useCodexLensMutations) as any).mockReturnValue(mockMutations); }); it('should show bootstrap button', () => { @@ -184,7 +184,7 @@ describe('CodexLensManagerPage', () => { it('should call bootstrap on button click', async () => { const bootstrap = vi.fn().mockResolvedValue({ success: true }); - vi.mocked(useCodexLensMutations).mockReturnValue({ + (vi.mocked(useCodexLensMutations) as any).mockReturnValue({ ...mockMutations, bootstrap, }); @@ -203,7 +203,7 @@ describe('CodexLensManagerPage', () => { describe('uninstall flow', () => { beforeEach(() => { - vi.mocked(useCodexLensDashboard).mockReturnValue({ + (vi.mocked(useCodexLensDashboard) as any).mockReturnValue({ installed: true, status: mockDashboardData.status, config: mockDashboardData.config, @@ -217,7 +217,7 @@ describe('CodexLensManagerPage', () => { it('should show confirmation dialog on uninstall', async () => { const uninstall = vi.fn().mockResolvedValue({ success: true }); - vi.mocked(useCodexLensMutations).mockReturnValue({ + (vi.mocked(useCodexLensMutations) as any).mockReturnValue({ ...mockMutations, uninstall, }); @@ -233,7 +233,7 @@ describe('CodexLensManagerPage', () => { it('should call uninstall when confirmed', async () => { const uninstall = vi.fn().mockResolvedValue({ success: true }); - vi.mocked(useCodexLensMutations).mockReturnValue({ + (vi.mocked(useCodexLensMutations) as any).mockReturnValue({ ...mockMutations, uninstall, }); @@ -252,7 +252,7 @@ describe('CodexLensManagerPage', () => { it('should not call uninstall when cancelled', async () => { (global.confirm as ReturnType).mockReturnValue(false); const uninstall = vi.fn().mockResolvedValue({ success: true }); - vi.mocked(useCodexLensMutations).mockReturnValue({ + (vi.mocked(useCodexLensMutations) as any).mockReturnValue({ ...mockMutations, uninstall, }); @@ -269,7 +269,7 @@ describe('CodexLensManagerPage', () => { describe('loading states', () => { it('should show loading skeleton when loading', () => { - vi.mocked(useCodexLensDashboard).mockReturnValue({ + (vi.mocked(useCodexLensDashboard) as any).mockReturnValue({ installed: false, status: undefined, config: undefined, @@ -279,7 +279,7 @@ describe('CodexLensManagerPage', () => { error: null, refetch: vi.fn(), }); - vi.mocked(useCodexLensMutations).mockReturnValue(mockMutations); + (vi.mocked(useCodexLensMutations) as any).mockReturnValue(mockMutations); render(); @@ -289,7 +289,7 @@ describe('CodexLensManagerPage', () => { }); it('should disable refresh button when fetching', () => { - vi.mocked(useCodexLensDashboard).mockReturnValue({ + (vi.mocked(useCodexLensDashboard) as any).mockReturnValue({ installed: true, status: mockDashboardData.status, config: mockDashboardData.config, @@ -299,7 +299,7 @@ describe('CodexLensManagerPage', () => { error: null, refetch: vi.fn(), }); - vi.mocked(useCodexLensMutations).mockReturnValue(mockMutations); + (vi.mocked(useCodexLensMutations) as any).mockReturnValue(mockMutations); render(); @@ -310,7 +310,7 @@ describe('CodexLensManagerPage', () => { describe('i18n - Chinese locale', () => { beforeEach(() => { - vi.mocked(useCodexLensDashboard).mockReturnValue({ + (vi.mocked(useCodexLensDashboard) as any).mockReturnValue({ installed: true, status: mockDashboardData.status, config: mockDashboardData.config, @@ -320,7 +320,7 @@ describe('CodexLensManagerPage', () => { error: null, refetch: vi.fn(), }); - vi.mocked(useCodexLensMutations).mockReturnValue(mockMutations); + (vi.mocked(useCodexLensMutations) as any).mockReturnValue(mockMutations); }); it('should display translated text in Chinese', () => { @@ -343,7 +343,7 @@ describe('CodexLensManagerPage', () => { describe('error states', () => { it('should handle API errors gracefully', () => { - vi.mocked(useCodexLensDashboard).mockReturnValue({ + (vi.mocked(useCodexLensDashboard) as any).mockReturnValue({ installed: false, status: undefined, config: undefined, @@ -353,7 +353,7 @@ describe('CodexLensManagerPage', () => { error: new Error('API Error'), refetch: vi.fn(), }); - vi.mocked(useCodexLensMutations).mockReturnValue(mockMutations); + (vi.mocked(useCodexLensMutations) as any).mockReturnValue(mockMutations); render(); diff --git a/ccw/frontend/src/pages/CommandsManagerPage.tsx b/ccw/frontend/src/pages/CommandsManagerPage.tsx index 0f3878d4..bc815a7d 100644 --- a/ccw/frontend/src/pages/CommandsManagerPage.tsx +++ b/ccw/frontend/src/pages/CommandsManagerPage.tsx @@ -15,6 +15,7 @@ import { XCircle, Folder, User, + AlertCircle, } from 'lucide-react'; import { Card } from '@/components/ui/Card'; import { Button } from '@/components/ui/Button'; @@ -47,6 +48,7 @@ export function CommandsManagerPage() { disabledCount, isLoading, isFetching, + error, refetch, } = useCommands({ filter: { @@ -121,6 +123,20 @@ export function CommandsManagerPage() {
+ {/* Error alert */} + {error && ( +
+ +
+

{formatMessage({ id: 'common.errors.loadFailed' })}

+

{error.message}

+
+ +
+ )} + {/* Location Tabs - styled like LiteTasksPage */} {isLitePlan ? : isLiteFix ? : } - {formatMessage({ id: isLitePlan ? 'liteTasks.type.plan' : isLiteFix ? 'liteTasks.type.fix' : 'liteTasks.type.multiCli' })} + {formatMessage({ id: isLitePlan ? 'liteTasks.type.plan' : isLiteFix ? 'liteTasks.type.fix' : 'liteTasks.type.multiCli' }) as React.ReactNode}
@@ -564,7 +579,7 @@ export function LiteTaskDetailPage() { )} {/* Tech Stack from Session Metadata */} - {session.metadata?.tech_stack && ( + {!!session.metadata?.tech_stack && (
@@ -579,7 +594,7 @@ export function LiteTaskDetailPage() { )} {/* Conventions from Session Metadata */} - {session.metadata?.conventions && ( + {!!session.metadata?.conventions && (
@@ -604,7 +619,7 @@ export function LiteTaskDetailPage() {
{/* Session-Level Explorations (if available) */} - {session.metadata?.explorations && ( + {!!session.metadata?.explorations && ( diff --git a/ccw/frontend/src/pages/LiteTasksPage.tsx b/ccw/frontend/src/pages/LiteTasksPage.tsx index eae9fa4e..7f387490 100644 --- a/ccw/frontend/src/pages/LiteTasksPage.tsx +++ b/ccw/frontend/src/pages/LiteTasksPage.tsx @@ -183,7 +183,7 @@ function ExpandedSessionPanel({ {depsCount > 0 && (
- {task.context.depends_on.map((depId, idx) => ( + {task.context?.depends_on?.map((depId, idx) => ( {depId} @@ -514,7 +514,7 @@ function ExpandedMultiCliPanel({ {depsCount > 0 && (
- {task.context.depends_on.map((depId, idx) => ( + {task.context?.depends_on?.map((depId, idx) => ( {depId} diff --git a/ccw/frontend/src/pages/MemoryPage.tsx b/ccw/frontend/src/pages/MemoryPage.tsx index cae2de4d..6506ac72 100644 --- a/ccw/frontend/src/pages/MemoryPage.tsx +++ b/ccw/frontend/src/pages/MemoryPage.tsx @@ -23,6 +23,7 @@ import { Star, Archive, ArchiveRestore, + AlertCircle, } from 'lucide-react'; import { Card } from '@/components/ui/Card'; import { Button } from '@/components/ui/Button'; @@ -394,6 +395,7 @@ export function MemoryPage() { allTags, isLoading, isFetching, + error, refetch, } = useMemory({ filter: { @@ -551,6 +553,20 @@ export function MemoryPage() { ]} /> + {/* Error alert */} + {error && ( +
+ +
+

{formatMessage({ id: 'common.errors.loadFailed' })}

+

{error.message}

+
+ +
+ )} + {/* Stats Cards */}
diff --git a/ccw/frontend/src/pages/PromptHistoryPage.tsx b/ccw/frontend/src/pages/PromptHistoryPage.tsx index a7c5729c..d635a3d9 100644 --- a/ccw/frontend/src/pages/PromptHistoryPage.tsx +++ b/ccw/frontend/src/pages/PromptHistoryPage.tsx @@ -170,15 +170,15 @@ export function PromptHistoryPage() { setSelectedInsight(null); // Show success toast const successMessage = locale === 'zh' ? '洞察已删除' : 'Insight deleted'; - if (window.showToast) { - window.showToast(successMessage, 'success'); + if ((window as any).showToast) { + (window as any).showToast(successMessage, 'success'); } } catch (err) { console.error('Failed to delete insight:', err); // Show error toast const errorMessage = locale === 'zh' ? '删除洞察失败' : 'Failed to delete insight'; - if (window.showToast) { - window.showToast(errorMessage, 'error'); + if ((window as any).showToast) { + (window as any).showToast(errorMessage, 'error'); } } }; diff --git a/ccw/frontend/src/pages/QueuePage.test.tsx b/ccw/frontend/src/pages/QueuePage.test.tsx index 644d6ca6..df8e2ae3 100644 --- a/ccw/frontend/src/pages/QueuePage.test.tsx +++ b/ccw/frontend/src/pages/QueuePage.test.tsx @@ -10,13 +10,13 @@ import { useWorkflowStore } from '@/stores/workflowStore'; import type { IssueQueue } from '@/lib/api'; // Mock queue data -const mockQueueData: IssueQueue = { +const mockQueueData = { tasks: ['task1', 'task2'], solutions: ['solution1'], conflicts: [], - execution_groups: { 'group-1': ['task1', 'task2'] }, - grouped_items: { 'parallel-group': ['task1', 'task2'] }, -}; + execution_groups: ['group-1'], + grouped_items: { 'parallel-group': [] as any[] }, +} satisfies IssueQueue; // Mock hooks at top level vi.mock('@/hooks', () => ({ diff --git a/ccw/frontend/src/pages/SessionDetailPage.tsx b/ccw/frontend/src/pages/SessionDetailPage.tsx index 07465e38..30ca3b29 100644 --- a/ccw/frontend/src/pages/SessionDetailPage.tsx +++ b/ccw/frontend/src/pages/SessionDetailPage.tsx @@ -221,7 +221,7 @@ export function SessionDetailPage() { {activeTab === 'impl-plan' && (
- +
)} diff --git a/ccw/frontend/src/pages/SkillsManagerPage.tsx b/ccw/frontend/src/pages/SkillsManagerPage.tsx index 17ecb0fa..f9921aba 100644 --- a/ccw/frontend/src/pages/SkillsManagerPage.tsx +++ b/ccw/frontend/src/pages/SkillsManagerPage.tsx @@ -20,6 +20,7 @@ import { Grid3x3, Folder, User, + AlertCircle, } from 'lucide-react'; import { Card } from '@/components/ui/Card'; import { Button } from '@/components/ui/Button'; @@ -129,6 +130,7 @@ export function SkillsManagerPage() { userSkills, isLoading, isFetching, + error, refetch, } = useSkills({ filter: { @@ -248,6 +250,20 @@ export function SkillsManagerPage() {
+ {/* Error alert */} + {error && ( +
+ +
+

{formatMessage({ id: 'common.errors.loadFailed' })}

+

{error.message}

+
+ +
+ )} + {/* Location Tabs - styled like LiteTasksPage */} ; - case 'paused': - return ; - case 'completed': - return ; - case 'failed': - return ; - default: - return ; - } -} - function getLogLevelColor(level: LogLevel): string { switch (level) { case 'error': @@ -95,23 +61,21 @@ interface ExecutionMonitorProps { } export function ExecutionMonitor({ className }: ExecutionMonitorProps) { - const logsEndRef = useRef(null); - const logsContainerRef = useRef(null); - const [isUserScrolling, setIsUserScrolling] = useState(false); const { formatMessage } = useIntl(); // Execution store state const currentExecution = useExecutionStore((state) => state.currentExecution); const logs = useExecutionStore((state) => state.logs); const nodeStates = useExecutionStore((state) => state.nodeStates); + const selectedNodeId = useExecutionStore((state) => state.selectedNodeId); + const nodeOutputs = useExecutionStore((state) => state.nodeOutputs); + const nodeToolCalls = useExecutionStore((state) => state.nodeToolCalls); const isMonitorPanelOpen = useExecutionStore((state) => state.isMonitorPanelOpen); - const autoScrollLogs = useExecutionStore((state) => state.autoScrollLogs); const setMonitorPanelOpen = useExecutionStore((state) => state.setMonitorPanelOpen); + const selectNode = useExecutionStore((state) => state.selectNode); + const toggleToolCallExpanded = useExecutionStore((state) => state.toggleToolCallExpanded); const startExecution = useExecutionStore((state) => state.startExecution); - // Local state for elapsed time - const [elapsedMs, setElapsedMs] = useState(0); - // Flow store state const currentFlow = useFlowStore((state) => state.currentFlow); const nodes = useFlowStore((state) => state.nodes); @@ -122,6 +86,12 @@ export function ExecutionMonitor({ className }: ExecutionMonitorProps) { const resumeExecution = useResumeExecution(); const stopExecution = useStopExecution(); + // Local state + const [elapsedMs, setElapsedMs] = useState(0); + const [isUserScrollingLogs, setIsUserScrollingLogs] = useState(false); + const logsContainerRef = useRef(null); + const logsEndRef = useRef(null); + // Update elapsed time every second while running useEffect(() => { if (currentExecution?.status === 'running' && currentExecution.startedAt) { @@ -139,25 +109,32 @@ export function ExecutionMonitor({ className }: ExecutionMonitorProps) { } }, [currentExecution?.status, currentExecution?.startedAt, currentExecution?.completedAt, currentExecution?.elapsedMs]); - // Auto-scroll logs + // Auto-scroll global logs useEffect(() => { - if (autoScrollLogs && !isUserScrolling && logsEndRef.current) { + if (!isUserScrollingLogs && logsEndRef.current) { logsEndRef.current.scrollIntoView({ behavior: 'smooth' }); } - }, [logs, autoScrollLogs, isUserScrolling]); + }, [logs, isUserScrollingLogs]); + + // Auto-select current executing node + useEffect(() => { + if (currentExecution?.currentNodeId && currentExecution.status === 'running') { + selectNode(currentExecution.currentNodeId); + } + }, [currentExecution?.currentNodeId, currentExecution?.status, selectNode]); // Handle scroll to detect user scrolling const handleScroll = useCallback(() => { if (!logsContainerRef.current) return; const { scrollTop, scrollHeight, clientHeight } = logsContainerRef.current; const isAtBottom = scrollHeight - scrollTop - clientHeight < 50; - setIsUserScrolling(!isAtBottom); + setIsUserScrollingLogs(!isAtBottom); }, []); // Scroll to bottom handler const scrollToBottom = useCallback(() => { logsEndRef.current?.scrollIntoView({ behavior: 'smooth' }); - setIsUserScrolling(false); + setIsUserScrollingLogs(false); }, []); // Handle execute @@ -201,12 +178,30 @@ export function ExecutionMonitor({ className }: ExecutionMonitorProps) { } }, [currentExecution, stopExecution]); - // Calculate node progress - const completedNodes = Object.values(nodeStates).filter( - (state) => state.status === 'completed' - ).length; - const totalNodes = nodes.length; - const progressPercent = totalNodes > 0 ? (completedNodes / totalNodes) * 100 : 0; + // Handle node select + const handleNodeSelect = useCallback( + (nodeId: string) => { + selectNode(nodeId); + }, + [selectNode] + ); + + // Handle toggle tool call expand + const handleToggleToolCallExpand = useCallback( + (callId: string) => { + if (selectedNodeId) { + toggleToolCallExpanded(selectedNodeId, callId); + } + }, + [selectedNodeId, toggleToolCallExpanded] + ); + + // Get selected node data + const selectedNode = nodes.find((n) => n.id === selectedNodeId) ?? null; + const selectedNodeOutput = selectedNodeId ? nodeOutputs[selectedNodeId] : undefined; + const selectedNodeState = selectedNodeId ? nodeStates[selectedNodeId] : undefined; + const selectedNodeToolCalls = selectedNodeId ? (nodeToolCalls[selectedNodeId] ?? []) : []; + const isNodeExecuting = selectedNodeId ? nodeStates[selectedNodeId]?.status === 'running' : false; const isExecuting = currentExecution?.status === 'running'; const isPaused = currentExecution?.status === 'paused'; @@ -228,11 +223,21 @@ export function ExecutionMonitor({ className }: ExecutionMonitorProps) { {formatMessage({ id: 'orchestrator.monitor.title' })} {currentExecution && ( - - - {getStatusIcon(currentExecution.status)} - {formatMessage({ id: `orchestrator.status.${currentExecution.status}` })} - + + {formatMessage({ id: `orchestrator.status.${currentExecution.status}` })} )}
@@ -313,98 +318,85 @@ export function ExecutionMonitor({ className }: ExecutionMonitorProps) { )}
- {/* Progress bar */} - {currentExecution && ( -
+ {/* Multi-Panel Layout */} +
+ {/* 1. Execution Overview */} + + + {/* 2. Node Execution Chain */} + + + {/* 3. Node Detail Panel */} + + + {/* 4. Global Logs */} +
+
+ + + Global Logs ({logs.length}) + +
-
- )} - - {/* Node status */} - {currentExecution && Object.keys(nodeStates).length > 0 && ( -
-
- {formatMessage({ id: 'orchestrator.node.statusCount' }, { completed: completedNodes, total: totalNodes })} -
-
- {Object.entries(nodeStates).map(([nodeId, state]) => ( -
- {state.status === 'running' && ( - - )} - {state.status === 'completed' && ( - - )} - {state.status === 'failed' && ( - - )} - {state.status === 'pending' && ( - - )} - - {nodeId.slice(0, 24)} - + ref={logsContainerRef} + className="flex-1 overflow-y-auto p-3 font-mono text-xs" + onScroll={handleScroll} + > + {logs.length === 0 ? ( +
+ {currentExecution + ? formatMessage({ id: 'orchestrator.monitor.waitingForLogs' }) + : formatMessage({ id: 'orchestrator.monitor.clickExecuteToStart' })}
- ))} + ) : ( +
+ {logs.map((log, index) => ( +
+ + {new Date(log.timestamp).toLocaleTimeString()} + + + [{log.level}] + + + {log.message} + +
+ ))} +
+
+ )}
-
- )} - {/* Logs */} -
-
- {logs.length === 0 ? ( -
- {currentExecution - ? formatMessage({ id: 'orchestrator.monitor.waitingForLogs' }) - : formatMessage({ id: 'orchestrator.monitor.clickExecuteToStart' })} -
- ) : ( -
- {logs.map((log, index) => ( -
- - {new Date(log.timestamp).toLocaleTimeString()} - - - [{log.level}] - - - {log.message} - -
- ))} -
-
+ {/* Scroll to bottom button */} + {isUserScrollingLogs && logs.length > 0 && ( + )}
- - {/* Scroll to bottom button */} - {isUserScrolling && logs.length > 0 && ( - - )}
); diff --git a/ccw/frontend/src/pages/orchestrator/FlowCanvas.tsx b/ccw/frontend/src/pages/orchestrator/FlowCanvas.tsx index b5d2c5c1..fe48d0fe 100644 --- a/ccw/frontend/src/pages/orchestrator/FlowCanvas.tsx +++ b/ccw/frontend/src/pages/orchestrator/FlowCanvas.tsx @@ -3,7 +3,7 @@ // ======================================== // React Flow canvas with minimap, controls, and background -import { useCallback, useRef, DragEvent } from 'react'; +import { useCallback, useRef, useState, useEffect, DragEvent } from 'react'; import { useIntl } from 'react-intl'; import { ReactFlow, @@ -20,6 +20,7 @@ import { Edge, ReactFlowProvider, useReactFlow, + Panel, } from '@xyflow/react'; import '@xyflow/react/dist/style.css'; @@ -29,6 +30,7 @@ import type { FlowNode, FlowEdge } from '@/types/flow'; // Custom node types (enhanced with execution status in IMPL-A8) import { nodeTypes } from './nodes'; +import { InteractionModeToggle } from './InteractionModeToggle'; interface FlowCanvasProps { className?: string; @@ -53,6 +55,42 @@ function FlowCanvasInner({ className }: FlowCanvasProps) { const setSelectedEdgeId = useFlowStore((state) => state.setSelectedEdgeId); const markModified = useFlowStore((state) => state.markModified); + // Interaction mode from store + const interactionMode = useFlowStore((state) => state.interactionMode); + + // Ctrl key state for temporary mode reversal + const [isCtrlPressed, setIsCtrlPressed] = useState(false); + + // Listen for Ctrl/Meta key press for temporary mode reversal + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Control' || e.key === 'Meta') { + setIsCtrlPressed(true); + } + }; + const handleKeyUp = (e: KeyboardEvent) => { + if (e.key === 'Control' || e.key === 'Meta') { + setIsCtrlPressed(false); + } + }; + // Reset on blur (user switches window) + const handleBlur = () => setIsCtrlPressed(false); + + window.addEventListener('keydown', handleKeyDown); + window.addEventListener('keyup', handleKeyUp); + window.addEventListener('blur', handleBlur); + return () => { + window.removeEventListener('keydown', handleKeyDown); + window.removeEventListener('keyup', handleKeyUp); + window.removeEventListener('blur', handleBlur); + }; + }, []); + + // Calculate effective mode (Ctrl reverses the current mode) + const effectiveMode = isCtrlPressed + ? (interactionMode === 'pan' ? 'selection' : 'pan') + : interactionMode; + // Handle node changes (position, selection, etc.) const onNodesChange = useCallback( (changes: NodeChange[]) => { @@ -163,6 +201,8 @@ function FlowCanvasInner({ className }: FlowCanvasProps) { onDragOver={onDragOver} onDrop={onDrop} nodeTypes={nodeTypes} + panOnDrag={effectiveMode === 'pan'} + selectionOnDrag={effectiveMode === 'selection'} nodesDraggable={!isExecuting} nodesConnectable={!isExecuting} elementsSelectable={!isExecuting} @@ -172,6 +212,9 @@ function FlowCanvasInner({ className }: FlowCanvasProps) { snapGrid={[15, 15]} className="bg-background" > + + + state.interactionMode); + const toggleInteractionMode = useFlowStore((state) => state.toggleInteractionMode); + + return ( +
+ + +
+ ); +} diff --git a/ccw/frontend/src/pages/orchestrator/LeftSidebar.tsx b/ccw/frontend/src/pages/orchestrator/LeftSidebar.tsx index e21fb1f4..ea711342 100644 --- a/ccw/frontend/src/pages/orchestrator/LeftSidebar.tsx +++ b/ccw/frontend/src/pages/orchestrator/LeftSidebar.tsx @@ -4,12 +4,14 @@ // Container with tab switching between NodeLibrary and InlineTemplatePanel import { useIntl } from 'react-intl'; -import { ChevronRight, ChevronDown } from 'lucide-react'; +import { ChevronDown } from 'lucide-react'; import { cn } from '@/lib/utils'; import { Button } from '@/components/ui/Button'; import { useFlowStore } from '@/stores'; import { NodeLibrary } from './NodeLibrary'; import { InlineTemplatePanel } from './InlineTemplatePanel'; +import { useResizablePanel } from './useResizablePanel'; +import { ResizeHandle } from './ResizeHandle'; // ========== Tab Configuration ========== @@ -30,30 +32,27 @@ interface LeftSidebarProps { */ export function LeftSidebar({ className }: LeftSidebarProps) { const { formatMessage } = useIntl(); - const isPaletteOpen = useFlowStore((state) => state.isPaletteOpen); const setIsPaletteOpen = useFlowStore((state) => state.setIsPaletteOpen); const leftPanelTab = useFlowStore((state) => state.leftPanelTab); const setLeftPanelTab = useFlowStore((state) => state.setLeftPanelTab); - // Collapsed state - if (!isPaletteOpen) { - return ( -
- -
- ); - } + const { width, isResizing, handleMouseDown } = useResizablePanel({ + minWidth: 200, + maxWidth: 400, + defaultWidth: 288, // w-72 = 18rem = 288px + storageKey: 'ccw-orchestrator.leftSidebar.width', + direction: 'right', + }); - // Expanded state return ( -
+
{/* Header */}

{formatMessage({ id: 'orchestrator.leftSidebar.workbench' })}

@@ -100,6 +99,9 @@ export function LeftSidebar({ className }: LeftSidebarProps) { {formatMessage({ id: 'orchestrator.leftSidebar.tipLabel' })} {formatMessage({ id: 'orchestrator.leftSidebar.dragOrDoubleClick' })}
+ + {/* Resize handle on right edge */} +
); } diff --git a/ccw/frontend/src/pages/orchestrator/NodeLibrary.tsx b/ccw/frontend/src/pages/orchestrator/NodeLibrary.tsx index aa28ca95..afacd2af 100644 --- a/ccw/frontend/src/pages/orchestrator/NodeLibrary.tsx +++ b/ccw/frontend/src/pages/orchestrator/NodeLibrary.tsx @@ -117,8 +117,7 @@ function QuickTemplateCard({ }; const onDoubleClick = () => { - const position = { x: 100 + Math.random() * 200, y: 100 + Math.random() * 200 }; - useFlowStore.getState().addNodeFromTemplate(template.id, position); + useFlowStore.getState().addNodeFromTemplate(template.id, { x: 250, y: 200 }); }; return ( @@ -166,8 +165,7 @@ function BasicTemplateCard() { }; const onDoubleClick = () => { - const position = { x: 100 + Math.random() * 200, y: 100 + Math.random() * 200 }; - useFlowStore.getState().addNode(position); + useFlowStore.getState().addNode({ x: 250, y: 200 }); }; return ( diff --git a/ccw/frontend/src/pages/orchestrator/OrchestratorPage.tsx b/ccw/frontend/src/pages/orchestrator/OrchestratorPage.tsx index 4c654dd3..0cb9189b 100644 --- a/ccw/frontend/src/pages/orchestrator/OrchestratorPage.tsx +++ b/ccw/frontend/src/pages/orchestrator/OrchestratorPage.tsx @@ -4,8 +4,11 @@ // Visual workflow editor with React Flow, drag-drop node palette, and property panel import { useEffect, useState, useCallback } from 'react'; +import * as Collapsible from '@radix-ui/react-collapsible'; +import { ChevronRight, Settings } from 'lucide-react'; import { useFlowStore } from '@/stores'; import { useExecutionStore } from '@/stores/executionStore'; +import { Button } from '@/components/ui/Button'; import { FlowCanvas } from './FlowCanvas'; import { LeftSidebar } from './LeftSidebar'; import { PropertyPanel } from './PropertyPanel'; @@ -15,6 +18,10 @@ import { ExecutionMonitor } from './ExecutionMonitor'; export function OrchestratorPage() { const fetchFlows = useFlowStore((state) => state.fetchFlows); + const isPaletteOpen = useFlowStore((state) => state.isPaletteOpen); + const setIsPaletteOpen = useFlowStore((state) => state.setIsPaletteOpen); + const isPropertyPanelOpen = useFlowStore((state) => state.isPropertyPanelOpen); + const setIsPropertyPanelOpen = useFlowStore((state) => state.setIsPropertyPanelOpen); const isMonitorPanelOpen = useExecutionStore((state) => state.isMonitorPanelOpen); const [isTemplateLibraryOpen, setIsTemplateLibraryOpen] = useState(false); @@ -35,16 +42,42 @@ export function OrchestratorPage() { {/* Main Content Area */}
- {/* Left Sidebar (Templates + Nodes) */} - + {/* Left Sidebar with collapse toggle */} + {!isPaletteOpen && ( +
+ +
+ )} + + + + + - {/* Flow Canvas (Center) */} + {/* Flow Canvas (Center) + PropertyPanel Overlay */}
-
- {/* Property Panel (Right) - hidden when monitor is open */} - {!isMonitorPanelOpen && } + {/* Property Panel as overlay - hidden when monitor is open */} + {!isMonitorPanelOpen && ( +
+ {!isPropertyPanelOpen && ( +
+ +
+ )} + + + + + +
+ )} +
{/* Execution Monitor Panel (Right) */} diff --git a/ccw/frontend/src/pages/orchestrator/PropertyPanel.tsx b/ccw/frontend/src/pages/orchestrator/PropertyPanel.tsx index 0726fd75..0699ebc4 100644 --- a/ccw/frontend/src/pages/orchestrator/PropertyPanel.tsx +++ b/ccw/frontend/src/pages/orchestrator/PropertyPanel.tsx @@ -1238,7 +1238,6 @@ export function PropertyPanel({ className }: PropertyPanelProps) { const nodes = useFlowStore((state) => state.nodes); const updateNode = useFlowStore((state) => state.updateNode); const removeNode = useFlowStore((state) => state.removeNode); - const isPropertyPanelOpen = useFlowStore((state) => state.isPropertyPanelOpen); const setIsPropertyPanelOpen = useFlowStore((state) => state.setIsPropertyPanelOpen); const selectedNode = nodes.find((n) => n.id === selectedNodeId); @@ -1258,26 +1257,10 @@ export function PropertyPanel({ className }: PropertyPanelProps) { } }, [selectedNodeId, removeNode]); - // Collapsed state - if (!isPropertyPanelOpen) { - return ( -
- -
- ); - } - // No node selected if (!selectedNode) { return ( -
+

{formatMessage({ id: 'orchestrator.propertyPanel.title' })}