From 696141ee66647de20103feaca9a76ca967fe18ec Mon Sep 17 00:00:00 2001 From: catlog22 Date: Fri, 13 Feb 2026 22:58:47 +0800 Subject: [PATCH] feat: merge 10 team commands into unified team-lifecycle skill Consolidate coordinate, plan, execute, test, review, spec-coordinate, spec-analyst, spec-writer, spec-discuss, spec-reviewer into a single team-lifecycle skill with role-based routing (Pattern B architecture). - SKILL.md: role router with 8 roles, shared message bus, 3-mode pipeline - coordinator: unified orchestrator for spec-only/impl-only/full-lifecycle - 7 worker roles: analyst, writer, discussant, planner, executor, tester, reviewer - reviewer: dual-prefix (REVIEW-*/QUALITY-*) auto-switching code/spec review - Each role: 5-phase execution, message bus with CLI fallback, error handling --- .claude/commands/team/coordinate.md | 228 -------- .claude/commands/team/execute.md | 373 ------------- .claude/commands/team/plan.md | 422 --------------- .claude/commands/team/review.md | 394 -------------- .claude/commands/team/spec-analyst.md | 321 ----------- .claude/commands/team/spec-coordinate.md | 352 ------------ .claude/commands/team/spec-discuss.md | 498 ----------------- .claude/commands/team/spec-reviewer.md | 492 ----------------- .claude/commands/team/spec-writer.md | 492 ----------------- .claude/skills/team-lifecycle/SKILL.md | 320 +++++++++++ .../skills/team-lifecycle/roles/analyst.md | 207 +++++++ .../team-lifecycle/roles/coordinator.md | 326 +++++++++++ .../skills/team-lifecycle/roles/discussant.md | 223 ++++++++ .../skills/team-lifecycle/roles/executor.md | 235 ++++++++ .../skills/team-lifecycle/roles/planner.md | 274 ++++++++++ .../skills/team-lifecycle/roles/reviewer.md | 508 ++++++++++++++++++ .../team-lifecycle/roles/tester.md} | 222 ++------ .claude/skills/team-lifecycle/roles/writer.md | 192 +++++++ .../team-lifecycle/specs/team-config.json | 78 +++ 19 files changed, 2420 insertions(+), 3737 deletions(-) delete mode 100644 .claude/commands/team/coordinate.md delete mode 100644 .claude/commands/team/execute.md delete mode 100644 .claude/commands/team/plan.md delete mode 100644 .claude/commands/team/review.md delete mode 100644 .claude/commands/team/spec-analyst.md delete mode 100644 .claude/commands/team/spec-coordinate.md delete mode 100644 .claude/commands/team/spec-discuss.md delete mode 100644 .claude/commands/team/spec-reviewer.md delete mode 100644 .claude/commands/team/spec-writer.md create mode 100644 .claude/skills/team-lifecycle/SKILL.md create mode 100644 .claude/skills/team-lifecycle/roles/analyst.md create mode 100644 .claude/skills/team-lifecycle/roles/coordinator.md create mode 100644 .claude/skills/team-lifecycle/roles/discussant.md create mode 100644 .claude/skills/team-lifecycle/roles/executor.md create mode 100644 .claude/skills/team-lifecycle/roles/planner.md create mode 100644 .claude/skills/team-lifecycle/roles/reviewer.md rename .claude/{commands/team/test.md => skills/team-lifecycle/roles/tester.md} (52%) create mode 100644 .claude/skills/team-lifecycle/roles/writer.md create mode 100644 .claude/skills/team-lifecycle/specs/team-config.json diff --git a/.claude/commands/team/coordinate.md b/.claude/commands/team/coordinate.md deleted file mode 100644 index 9055e9cf..00000000 --- a/.claude/commands/team/coordinate.md +++ /dev/null @@ -1,228 +0,0 @@ ---- -name: coordinate -description: Team coordinator - 需求澄清、MVP路线图、创建持久化agent team、跨阶段协调plan/execute/test/review -argument-hint: "[--team-name=NAME] \"task description\"" -allowed-tools: TeamCreate(*), TeamDelete(*), SendMessage(*), TaskCreate(*), TaskUpdate(*), TaskList(*), TaskGet(*), Task(*), AskUserQuestion(*), TodoWrite(*), Read(*), Bash(*), Glob(*), Grep(*) -group: team ---- - -# Team Coordinate Command (/team:coordinate) - -纯调度协调器。需求澄清 → 创建 Team → 创建任务链 → 协调消息 → 持久循环。具体工作逻辑由各 teammate 调用自己的 skill 完成。 - -## 消息总线 - -所有 teammate 在 SendMessage 的**同时**必须调用 `mcp__ccw-tools__team_msg` 记录消息,实现持久化和用户可观测: - -```javascript -// 记录消息(每个 teammate 发 SendMessage 前调用) -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "planner", to: "coordinator", type: "plan_ready", summary: "Plan就绪: 3个task", ref: ".workflow/.team-plan/auth-team/plan.json" }) - -// Coordinator 查看全部消息 -mcp__ccw-tools__team_msg({ operation: "list", team: teamName }) - -// 按角色过滤 -mcp__ccw-tools__team_msg({ operation: "list", team: teamName, from: "tester", last: 5 }) - -// 查看团队状态 -mcp__ccw-tools__team_msg({ operation: "status", team: teamName }) - -// 读取特定消息 -mcp__ccw-tools__team_msg({ operation: "read", team: teamName, id: "MSG-003" }) -``` - -**日志位置**: `.workflow/.team-msg/{team-name}/messages.jsonl` -**消息类型**: `plan_ready | plan_approved | plan_revision | task_unblocked | impl_complete | impl_progress | test_result | review_result | fix_required | error | shutdown` - -### CLI 回退 - -当 `mcp__ccw-tools__team_msg` MCP 不可用时,使用 `ccw team` CLI 作为等效回退: - -```javascript -// 回退: 将 MCP 调用替换为 Bash CLI(参数一一对应) -// log -Bash(`ccw team log --team "${teamName}" --from "coordinator" --to "planner" --type "plan_approved" --summary "Plan已批准" --json`) -// list -Bash(`ccw team list --team "${teamName}" --last 10 --json`) -// list (带过滤) -Bash(`ccw team list --team "${teamName}" --from "tester" --last 5 --json`) -// status -Bash(`ccw team status --team "${teamName}" --json`) -// read -Bash(`ccw team read --team "${teamName}" --id "MSG-003" --json`) -``` - -**参数映射**: `team_msg(params)` → `ccw team --team [--from/--to/--type/--summary/--ref/--data/--id/--last] [--json]` - -## Usage - -```bash -/team:coordinate "实现用户认证模块" -/team:coordinate --team-name=auth-team "实现JWT刷新令牌" -``` - -## Pipeline - -``` -需求 → [PLAN: planner] → coordinator 审批 → [IMPL: executor] → [TEST + REVIEW: tester] → 汇报 → 等待新需求/关闭 -``` - -## Execution - -### Phase 1: 需求澄清 - -解析 `$ARGUMENTS` 获取 `--team-name` 和任务描述。使用 AskUserQuestion 收集: -- MVP 范围(最小可行 / 功能完整 / 全面实现) -- 关键约束(向后兼容 / 遵循模式 / 测试覆盖 / 性能敏感) - -简单任务可跳过澄清。 - -### Phase 2: 创建 Team + Spawn 3 Teammates - -```javascript -TeamCreate({ team_name: teamName }) -``` - -**Spawn 时只传角色和需求,工作细节由 skill 定义**: - -```javascript -// Planner -Task({ - subagent_type: "general-purpose", - team_name: teamName, - name: "planner", - mode: "plan", - prompt: `你是 team "${teamName}" 的 PLANNER。 - -当你收到 PLAN 任务时,调用 Skill(skill="team:plan") 执行规划工作。 - -当前需求: ${taskDescription} -约束: ${constraints} -复杂度: ${complexity} - -## 消息总线(必须) -每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录: -mcp__ccw-tools__team_msg({ operation: "log", team: "${teamName}", from: "planner", to: "coordinator", type: "", summary: "<摘要>", ref: "<文件路径>" }) - -工作流程: -1. TaskList → 找到分配给你的 PLAN-* 任务 -2. Skill(skill="team:plan") 执行探索和规划 -3. team_msg log + SendMessage 将 plan 摘要发给 coordinator -4. 等待 coordinator 审批或修改反馈 -5. 审批通过 → TaskUpdate completed → 检查下一个 PLAN 任务` -}) - -// Executor -Task({ - subagent_type: "general-purpose", - team_name: teamName, - name: "executor", - prompt: `你是 team "${teamName}" 的 EXECUTOR。 - -当你收到 IMPL 任务时,调用 Skill(skill="team:execute") 执行代码实现。 - -当前需求: ${taskDescription} -约束: ${constraints} - -## 消息总线(必须) -每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录: -mcp__ccw-tools__team_msg({ operation: "log", team: "${teamName}", from: "executor", to: "coordinator", type: "", summary: "<摘要>" }) - -工作流程: -1. TaskList → 找到未阻塞的 IMPL-* 任务 -2. Skill(skill="team:execute") 执行实现 -3. team_msg log + SendMessage 报告完成状态和变更文件 -4. TaskUpdate completed → 检查下一个 IMPL 任务` -}) - -// Tester (同时处理 TEST 和 REVIEW) -Task({ - subagent_type: "general-purpose", - team_name: teamName, - name: "tester", - prompt: `你是 team "${teamName}" 的 TESTER,同时负责测试和审查。 - -- 收到 TEST-* 任务 → 调用 Skill(skill="team:test") 执行测试修复循环 -- 收到 REVIEW-* 任务 → 调用 Skill(skill="team:review") 执行代码审查 - -当前需求: ${taskDescription} -约束: ${constraints} - -## 消息总线(必须) -每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录: -mcp__ccw-tools__team_msg({ operation: "log", team: "${teamName}", from: "tester", to: "coordinator", type: "", summary: "<摘要>" }) - -工作流程: -1. TaskList → 找到未阻塞的 TEST-* 或 REVIEW-* 任务 -2. 根据任务类型调用对应 Skill -3. team_msg log + SendMessage 报告结果给 coordinator -4. TaskUpdate completed → 检查下一个任务` -}) -``` - -### Phase 3: 创建任务链 - -```javascript -// PLAN-001 → IMPL-001 → TEST-001 + REVIEW-001 -TaskCreate({ subject: "PLAN-001: 探索和规划实现", description: `${taskDescription}\n\n写入: .workflow/.team-plan/${teamName}/`, activeForm: "规划中" }) -TaskUpdate({ taskId: planId, owner: "planner" }) - -TaskCreate({ subject: "IMPL-001: 实现已批准的计划", description: `${taskDescription}\n\nPlan: .workflow/.team-plan/${teamName}/plan.json`, activeForm: "实现中" }) -TaskUpdate({ taskId: implId, owner: "executor", addBlockedBy: [planId] }) - -TaskCreate({ subject: "TEST-001: 测试修复循环", description: `${taskDescription}`, activeForm: "测试中" }) -TaskUpdate({ taskId: testId, owner: "tester", addBlockedBy: [implId] }) - -TaskCreate({ subject: "REVIEW-001: 代码审查与需求验证", description: `${taskDescription}\n\nPlan: .workflow/.team-plan/${teamName}/plan.json`, activeForm: "审查中" }) -TaskUpdate({ taskId: reviewId, owner: "tester", addBlockedBy: [implId] }) -``` - -### Phase 4: 协调主循环 - -接收 teammate 消息,根据内容做调度决策。**每次做出决策前先 `team_msg list` 查看最近消息,每次做出决策后 `team_msg log` 记录**: - -| 收到消息 | 操作 | -|----------|------| -| Planner: plan 就绪 | 读取 plan → 审批/修改 → team_msg log(plan_approved/plan_revision) → TaskUpdate + SendMessage | -| Executor: 实现完成 | team_msg log(task_unblocked) → TaskUpdate IMPL completed → SendMessage 通知 tester | -| Tester: 测试结果 >= 95% | team_msg log(test_result) → TaskUpdate TEST completed | -| Tester: 测试结果 < 95% 且迭代 > 5 | team_msg log(error) → 上报用户 | -| Tester: 审查无 critical | team_msg log(review_result) → TaskUpdate REVIEW completed | -| Tester: 审查发现 critical | team_msg log(fix_required) → TaskCreate IMPL-fix → 分配 executor | -| 所有任务 completed | → Phase 5 | - -**用户可随时查看团队状态**: -```bash -# 用户在任意时刻调用查看 -mcp__ccw-tools__team_msg({ operation: "status", team: teamName }) -mcp__ccw-tools__team_msg({ operation: "list", team: teamName, last: 10 }) -``` - -### Phase 5: 汇报 + 持久循环 - -汇总变更文件、测试通过率、审查结果报告用户。 - -```javascript -AskUserQuestion({ - questions: [{ - question: "当前需求已完成。下一步:", - header: "Next", - multiSelect: false, - options: [ - { label: "新需求", description: "提交新需求给当前团队" }, - { label: "关闭团队", description: "关闭所有 teammate 并清理" } - ] - }] -}) -// 新需求 → 回到 Phase 1(复用 team,新建 PLAN/IMPL/TEST/REVIEW 任务) -// 关闭 → shutdown_request 给每个 teammate → TeamDelete() -``` - -## 错误处理 - -| 场景 | 处理 | -|------|------| -| Teammate 无响应 | 发追踪消息,2次无响应 → 重新 spawn | -| Plan 被拒 3+ 次 | Coordinator 自行规划 | -| 测试卡在 <80% 超 5 次迭代 | 上报用户 | -| Review 发现 critical | 创建 IMPL-fix 任务给 executor | diff --git a/.claude/commands/team/execute.md b/.claude/commands/team/execute.md deleted file mode 100644 index 01582a55..00000000 --- a/.claude/commands/team/execute.md +++ /dev/null @@ -1,373 +0,0 @@ ---- -name: execute -description: Team executor - 实现已批准的计划、编写代码、报告进度 -argument-hint: "" -allowed-tools: SendMessage(*), TaskUpdate(*), TaskList(*), TaskGet(*), TodoWrite(*), Read(*), Write(*), Edit(*), Bash(*), Glob(*), Grep(*), Task(*) -group: team ---- - -# Team Execute Command (/team:execute) - -## Overview - -Team executor role command. Operates as a teammate within an Agent Team, responsible for implementing approved plans by writing code, self-validating, and reporting progress to the coordinator. - -**Core capabilities:** -- Task discovery from shared team task list (IMPL-* tasks) -- Plan loading and task decomposition -- Code implementation following plan files list -- Self-validation: syntax checks, acceptance criteria verification -- Progress reporting to coordinator -- Sub-agent delegation for complex tasks - -## Role Definition - -**Name**: `executor` -**Responsibility**: Load plan → Implement code → Self-validate → Report completion -**Communication**: SendMessage to coordinator only - -## 消息总线 - -每次 SendMessage **前**,必须调用 `mcp__ccw-tools__team_msg` 记录消息: - -```javascript -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "executor", to: "coordinator", type: "", summary: "<摘要>" }) -``` - -### 支持的 Message Types - -| Type | 方向 | 触发时机 | 说明 | -|------|------|----------|------| -| `impl_progress` | executor → coordinator | 完成一个 batch/子任务 | 报告当前进度百分比和完成的子任务 | -| `impl_complete` | executor → coordinator | 全部实现完成 | 附带变更文件列表和 acceptance 状态 | -| `error` | executor → coordinator | 遇到阻塞问题 | Plan 文件缺失、文件冲突、子代理失败等 | - -### 调用示例 - -```javascript -// 进度更新 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "executor", to: "coordinator", type: "impl_progress", summary: "Batch 1/3 完成: auth middleware 已实现", data: { batch: 1, total: 3, files: ["src/middleware/auth.ts"] } }) - -// 实现完成 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "executor", to: "coordinator", type: "impl_complete", summary: "IMPL-001完成: 5个文件变更, acceptance全部满足", data: { changedFiles: 5, syntaxClean: true } }) - -// 错误上报 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "executor", to: "coordinator", type: "error", summary: "plan.json路径无效, 无法加载实现计划" }) -``` - -### CLI 回退 - -当 `mcp__ccw-tools__team_msg` MCP 不可用时,使用 `ccw team` CLI 作为等效回退: - -```javascript -// 回退: 将 MCP 调用替换为 Bash CLI(参数一一对应) -Bash(`ccw team log --team "${teamName}" --from "executor" --to "coordinator" --type "impl_complete" --summary "IMPL-001完成: 5个文件变更" --json`) - -// 带 data 参数 -Bash(`ccw team log --team "${teamName}" --from "executor" --to "coordinator" --type "impl_progress" --summary "Batch 1/3 完成" --data '{"batch":1,"total":3}' --json`) -``` - -**参数映射**: `team_msg(params)` → `ccw team log --team --from executor --to coordinator --type --summary "" [--ref ] [--data ''] [--json]` - -## Execution Process - -``` -Phase 1: Task & Plan Loading - ├─ TaskList to find unblocked IMPL-* tasks assigned to me - ├─ TaskGet to read full task details - ├─ TaskUpdate to mark in_progress - └─ Load plan.json from plan path in task description - -Phase 2: Task Grouping - ├─ Extract depends_on from plan tasks - ├─ Independent tasks → parallel batch - └─ Dependent tasks → sequential batches - -Phase 3: Code Implementation - ├─ For each task in plan: - │ ├─ Read files list - │ ├─ Read reference patterns - │ ├─ Implement changes (Edit/Write) - │ ├─ Complex tasks → code-developer sub-agent - │ └─ Simple tasks → direct file editing - └─ SendMessage progress updates for complex tasks - -Phase 4: Self-Validation - ├─ Syntax check (tsc --noEmit for TypeScript) - ├─ Verify acceptance criteria from plan - ├─ Run affected unit tests (if identifiable) - └─ Fix any immediate issues - -Phase 5: Completion Report - ├─ Compile changed files list - ├─ Summarize acceptance criteria status - ├─ SendMessage report to coordinator - ├─ Mark IMPL task completed - └─ Check TaskList for next IMPL task -``` - -## Implementation - -### Phase 1: Task & Plan Loading - -```javascript -// Find my assigned IMPL tasks -const tasks = TaskList() -const myImplTasks = tasks.filter(t => - t.subject.startsWith('IMPL-') && - t.owner === 'executor' && - t.status === 'pending' && - t.blockedBy.length === 0 // Not blocked -) - -if (myImplTasks.length === 0) { - // No tasks available, idle - return -} - -// Pick first available task (lowest ID) -const task = TaskGet({ taskId: myImplTasks[0].id }) -TaskUpdate({ taskId: task.id, status: 'in_progress' }) - -// Extract plan path from task description -const planPathMatch = task.description.match(/\.workflow\/\.team-plan\/[^\s]+\/plan\.json/) -const planPath = planPathMatch ? planPathMatch[0] : null - -if (!planPath) { - SendMessage({ - type: "message", - recipient: "coordinator", - content: `Cannot find plan.json path in task description for ${task.subject}. Please provide plan location.`, - summary: "Plan path not found" - }) - return -} - -const plan = JSON.parse(Read(planPath)) -``` - -### Phase 2: Task Grouping - -```javascript -// Extract dependencies and group tasks -function extractDependencies(planTasks) { - const taskIdToIndex = {} - planTasks.forEach((t, i) => { taskIdToIndex[t.id] = i }) - - return planTasks.map((task, i) => { - const deps = (task.depends_on || []) - .map(depId => taskIdToIndex[depId]) - .filter(idx => idx !== undefined && idx < i) - return { ...task, taskIndex: i, dependencies: deps } - }) -} - -function createBatches(planTasks) { - const tasksWithDeps = extractDependencies(planTasks) - const processed = new Set() - const batches = [] - - // Phase 1: Independent tasks → single parallel batch - const independent = tasksWithDeps.filter(t => t.dependencies.length === 0) - if (independent.length > 0) { - independent.forEach(t => processed.add(t.taskIndex)) - batches.push({ type: 'parallel', tasks: independent }) - } - - // Phase 2+: Dependent tasks in order - let remaining = tasksWithDeps.filter(t => !processed.has(t.taskIndex)) - while (remaining.length > 0) { - const ready = remaining.filter(t => t.dependencies.every(d => processed.has(d))) - if (ready.length === 0) break // circular dependency guard - ready.forEach(t => processed.add(t.taskIndex)) - batches.push({ type: ready.length > 1 ? 'parallel' : 'sequential', tasks: ready }) - remaining = remaining.filter(t => !processed.has(t.taskIndex)) - } - - return batches -} - -const batches = createBatches(plan.tasks) -``` - -### Phase 3: Code Implementation - -```javascript -// Unified Task Prompt Builder (from lite-execute) -function buildExecutionPrompt(planTask) { - return ` -## ${planTask.title} - -**Scope**: \`${planTask.scope}\` | **Action**: ${planTask.action || 'implement'} - -### Files -${(planTask.files || []).map(f => `- **${f.path}** → \`${f.target}\`: ${f.change}`).join('\n')} - -### How to do it -${planTask.description} - -${(planTask.implementation || []).map(step => `- ${step}`).join('\n')} - -### Reference -- Pattern: ${planTask.reference?.pattern || 'N/A'} -- Files: ${planTask.reference?.files?.join(', ') || 'N/A'} - -### Done when -${(planTask.convergence?.criteria || []).map(c => `- [ ] ${c}`).join('\n')} -` -} - -// Execute each batch -const changedFiles = [] -const previousResults = [] - -for (const batch of batches) { - if (batch.tasks.length === 1 && isSimpleTask(batch.tasks[0])) { - // Simple task: direct implementation - const t = batch.tasks[0] - // Read target files, apply modifications using Edit/Write - for (const f of (t.files || [])) { - const content = Read(f.path) - // Apply change based on file entry description - Edit({ file_path: f.path, old_string: "...", new_string: "..." }) - changedFiles.push(f.path) - } - } else { - // Complex task(s): delegate to code-developer sub-agent - const prompt = batch.tasks.map(buildExecutionPrompt).join('\n\n---\n') - - Task({ - subagent_type: "code-developer", - run_in_background: false, - description: batch.tasks.map(t => t.title).join(' | '), - prompt: `## Goal -${plan.summary} - -## Tasks -${prompt} - -## Context -### Project Guidelines -@.workflow/project-guidelines.json - -Complete each task according to its "Done when" checklist.` - }) - - // Collect changed files from sub-agent results - batch.tasks.forEach(t => { - (t.files || []).forEach(f => changedFiles.push(f.path)) - }) - } - - previousResults.push({ - batchType: batch.type, - tasks: batch.tasks.map(t => t.title), - status: 'completed' - }) -} -``` - -### Phase 4: Self-Validation - -```javascript -// Step 1: Syntax check -const syntaxResult = Bash(`tsc --noEmit 2>&1 || true`) -const hasSyntaxErrors = syntaxResult.includes('error TS') - -if (hasSyntaxErrors) { - // Attempt to fix syntax errors - // Parse error locations, apply fixes - console.log('Syntax errors detected, attempting fix...') -} - -// Step 2: Verify acceptance criteria -const acceptanceStatus = plan.tasks.map(t => ({ - title: t.title, - criteria: (t.convergence?.criteria || []).map(c => ({ - criterion: c, - met: true // Evaluate based on implementation - })) -})) - -// Step 3: Run affected tests (if identifiable) -const testFiles = changedFiles - .map(f => f.replace(/\/src\//, '/tests/').replace(/\.(ts|js)$/, '.test.$1')) - .filter(f => Bash(`test -f ${f} && echo exists || true`).includes('exists')) - -if (testFiles.length > 0) { - const testResult = Bash(`npx jest ${testFiles.join(' ')} --passWithNoTests 2>&1 || true`) - // Parse test results -} -``` - -### Phase 5: Completion Report - -```javascript -// Compile report -const report = { - task: task.subject, - changedFiles: [...new Set(changedFiles)], - newFiles: changedFiles.filter(f => /* detect new files */), - acceptanceStatus: acceptanceStatus, - syntaxClean: !hasSyntaxErrors, - testsPassed: testFiles.length > 0 ? testResult.includes('passed') : 'N/A' -} - -// Send to coordinator -SendMessage({ - type: "message", - recipient: "coordinator", - content: `## Implementation Complete - -**Task**: ${task.subject} - -### Changed Files -${report.changedFiles.map(f => `- ${f}`).join('\n')} - -### Acceptance Criteria -${acceptanceStatus.map(t => `**${t.title}**: ${t.criteria.every(c => c.met) ? 'All met' : 'Partial'}`).join('\n')} - -### Validation -- Syntax: ${report.syntaxClean ? 'Clean' : 'Has errors (attempted fix)'} -- Tests: ${report.testsPassed} - -Implementation is ready for testing and review.`, - summary: `IMPL complete: ${report.changedFiles.length} files changed` -}) - -// Mark task completed -TaskUpdate({ taskId: task.id, status: 'completed' }) - -// Check for next IMPL task -const nextTasks = TaskList().filter(t => - t.subject.startsWith('IMPL-') && - t.owner === 'executor' && - t.status === 'pending' && - t.blockedBy.length === 0 -) - -if (nextTasks.length > 0) { - // Continue with next task → back to Phase 1 -} -``` - -## Helper Functions - -```javascript -function isSimpleTask(task) { - return (task.files || []).length <= 2 && - !task.code_skeleton && - (task.risks || []).length === 0 -} -``` - -## Error Handling - -| Scenario | Resolution | -|----------|------------| -| Plan file not found | Notify coordinator, request plan location | -| Syntax errors after implementation | Attempt auto-fix, report remaining errors | -| Sub-agent failure | Retry once, then attempt direct implementation | -| File conflict / merge issue | Notify coordinator, request guidance | -| Test failures in self-validation | Report in completion message, let tester handle | -| Circular dependencies in plan | Execute in plan order, ignore dependency chain | diff --git a/.claude/commands/team/plan.md b/.claude/commands/team/plan.md deleted file mode 100644 index 10c437cc..00000000 --- a/.claude/commands/team/plan.md +++ /dev/null @@ -1,422 +0,0 @@ ---- -name: plan -description: Team planner - 多角度代码探索、结构化实现规划、提交coordinator审批 -argument-hint: "" -allowed-tools: SendMessage(*), TaskUpdate(*), TaskList(*), TaskGet(*), TodoWrite(*), Read(*), Write(*), Bash(*), Glob(*), Grep(*), Task(*) -group: team ---- - -# Team Plan Command (/team:plan) - -## Overview - -Team planner role command. Operates as a teammate within an Agent Team, responsible for multi-angle code exploration and structured implementation planning. Submits plans to the coordinator for approval. - -**Core capabilities:** -- Task discovery from shared team task list -- Multi-angle codebase exploration (architecture/security/performance/bugfix/feature) -- Complexity-adaptive planning (Low → direct, Medium/High → agent-assisted) -- Structured plan.json generation following schema -- Plan submission and revision cycle with coordinator - -## Role Definition - -**Name**: `planner` -**Responsibility**: Code exploration → Implementation planning → Coordinator approval -**Communication**: SendMessage to coordinator only - -## 消息总线 - -每次 SendMessage **前**,必须调用 `mcp__ccw-tools__team_msg` 记录消息: - -```javascript -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "planner", to: "coordinator", type: "", summary: "<摘要>", ref: "<文件路径>" }) -``` - -### 支持的 Message Types - -| Type | 方向 | 触发时机 | 说明 | -|------|------|----------|------| -| `plan_ready` | planner → coordinator | Plan 生成完成 | 附带 plan.json 路径和任务数摘要 | -| `plan_revision` | planner → coordinator | Plan 修订后重新提交 | 说明修改内容 | -| `impl_progress` | planner → coordinator | 探索阶段进展更新 | 可选,长时间探索时使用 | -| `error` | planner → coordinator | 遇到不可恢复错误 | 探索失败、schema缺失等 | - -### 调用示例 - -```javascript -// Plan 就绪 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "planner", to: "coordinator", type: "plan_ready", summary: "Plan就绪: 3个task, Medium复杂度", ref: ".workflow/.team-plan/auth-impl-2026-02-09/plan.json" }) - -// Plan 修订 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "planner", to: "coordinator", type: "plan_revision", summary: "已按反馈拆分task-2为两个子任务" }) - -// 错误上报 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "planner", to: "coordinator", type: "error", summary: "plan-overview-base-schema.json 未找到, 使用默认结构" }) -``` - -### CLI 回退 - -当 `mcp__ccw-tools__team_msg` MCP 不可用时,使用 `ccw team` CLI 作为等效回退: - -```javascript -// 回退: 将 MCP 调用替换为 Bash CLI(参数一一对应) -Bash(`ccw team log --team "${teamName}" --from "planner" --to "coordinator" --type "plan_ready" --summary "Plan就绪: 3个task" --ref "${sessionFolder}/plan.json" --json`) -``` - -**参数映射**: `team_msg(params)` → `ccw team log --team --from planner --to coordinator --type --summary "" [--ref ] [--data ''] [--json]` - -## Execution Process - -``` -Phase 1: Task Discovery - ├─ Read team config to identify coordinator - ├─ TaskList to find PLAN-* tasks assigned to me - ├─ TaskGet to read full task details - └─ TaskUpdate to mark in_progress - -Phase 2: Multi-Angle Exploration - ├─ Complexity assessment (Low/Medium/High) - ├─ Angle selection based on task type - ├─ Semantic search via mcp__ace-tool__search_context - ├─ Pattern search via Grep/Glob - ├─ Complex tasks: cli-explore-agent sub-agents - └─ Write exploration results to session folder - -Phase 3: Plan Generation - ├─ Read plan-overview-base-schema.json + task-schema.json for structure reference - ├─ Low complexity → Direct Claude planning - ├─ Medium/High → cli-lite-planning-agent - └─ Output: plan.json (overview with task_ids[]) + .task/TASK-*.json (independent task files) - -Phase 4: Submit for Approval - ├─ SendMessage plan summary to coordinator - ├─ Wait for approve/revision feedback - └─ If revision → update plan → resubmit - -Phase 5: Idle & Next Task - ├─ Mark current task completed - ├─ TaskList to check for new PLAN tasks - └─ No tasks → idle (wait for coordinator assignment) -``` - -## Implementation - -### Phase 1: Task Discovery - -```javascript -// Read team config -const teamConfig = JSON.parse(Read(`~/.claude/teams/${teamName}/config.json`)) - -// Find my assigned PLAN tasks -const tasks = TaskList() -const myPlanTasks = tasks.filter(t => - t.subject.startsWith('PLAN-') && - t.owner === 'planner' && - t.status === 'pending' && - t.blockedBy.length === 0 -) - -if (myPlanTasks.length === 0) { - // No tasks available, idle - return -} - -// Pick first available task (lowest ID) -const task = TaskGet({ taskId: myPlanTasks[0].id }) -TaskUpdate({ taskId: task.id, status: 'in_progress' }) -``` - -### Phase 2: Multi-Angle Exploration - -```javascript -// Session setup -const taskSlug = task.subject.toLowerCase().replace(/[^a-z0-9]+/g, '-').substring(0, 40) -const dateStr = new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString().substring(0, 10) -const sessionFolder = `.workflow/.team-plan/${taskSlug}-${dateStr}` -Bash(`mkdir -p ${sessionFolder}`) - -// Complexity assessment -function assessComplexity(desc) { - let score = 0 - if (/refactor|architect|restructure|模块|系统/.test(desc)) score += 2 - if (/multiple|多个|across|跨/.test(desc)) score += 2 - if (/integrate|集成|api|database/.test(desc)) score += 1 - if (/security|安全|performance|性能/.test(desc)) score += 1 - return score >= 4 ? 'High' : score >= 2 ? 'Medium' : 'Low' -} - -const complexity = assessComplexity(task.description) - -// Angle selection -const ANGLE_PRESETS = { - architecture: ['architecture', 'dependencies', 'modularity', 'integration-points'], - security: ['security', 'auth-patterns', 'dataflow', 'validation'], - performance: ['performance', 'bottlenecks', 'caching', 'data-access'], - bugfix: ['error-handling', 'dataflow', 'state-management', 'edge-cases'], - feature: ['patterns', 'integration-points', 'testing', 'dependencies'] -} - -function selectAngles(desc, count) { - const text = desc.toLowerCase() - let preset = 'feature' - if (/refactor|architect|restructure|modular/.test(text)) preset = 'architecture' - else if (/security|auth|permission|access/.test(text)) preset = 'security' - else if (/performance|slow|optimi|cache/.test(text)) preset = 'performance' - else if (/fix|bug|error|issue|broken/.test(text)) preset = 'bugfix' - return ANGLE_PRESETS[preset].slice(0, count) -} - -const angleCount = complexity === 'High' ? 4 : (complexity === 'Medium' ? 3 : 1) -const selectedAngles = selectAngles(task.description, angleCount) - -// Execute exploration -// Low complexity: direct search with mcp__ace-tool__search_context + Grep/Glob -// Medium/High: launch cli-explore-agent sub-agents in parallel - -if (complexity === 'Low') { - // Direct exploration - const results = mcp__ace-tool__search_context({ - project_root_path: projectRoot, - query: task.description - }) - // Write single exploration file - Write(`${sessionFolder}/exploration-${selectedAngles[0]}.json`, JSON.stringify({ - project_structure: "...", - relevant_files: [], - patterns: [], - dependencies: [], - integration_points: [], - constraints: [], - clarification_needs: [], - _metadata: { exploration_angle: selectedAngles[0] } - }, null, 2)) -} else { - // Launch parallel cli-explore-agent for each angle - selectedAngles.forEach((angle, index) => { - Task({ - subagent_type: "cli-explore-agent", - run_in_background: false, - description: `Explore: ${angle}`, - prompt: ` -## Task Objective -Execute **${angle}** exploration for task planning context. - -## Output Location -**Session Folder**: ${sessionFolder} -**Output File**: ${sessionFolder}/exploration-${angle}.json - -## Assigned Context -- **Exploration Angle**: ${angle} -- **Task Description**: ${task.description} -- **Exploration Index**: ${index + 1} of ${selectedAngles.length} - -## MANDATORY FIRST STEPS -1. Run: rg -l "{relevant_keyword}" --type ts (locate relevant files) -2. Execute: cat ~/.ccw/workflows/cli-templates/schemas/explore-json-schema.json (get output schema) -3. Read: .workflow/project-tech.json (if exists - technology stack) - -## Expected Output -Write JSON to: ${sessionFolder}/exploration-${angle}.json -Follow explore-json-schema.json structure with ${angle}-focused findings. - -**MANDATORY**: Every file in relevant_files MUST have: -- **rationale** (required): Specific selection basis tied to ${angle} topic (>10 chars, not generic) -- **role** (required): modify_target|dependency|pattern_reference|test_target|type_definition|integration_point|config|context_only -- **discovery_source** (recommended): bash-scan|cli-analysis|ace-search|dependency-trace|manual -- **key_symbols** (recommended): Key functions/classes/types relevant to task -` - }) - }) -} - -// Build explorations manifest -const explorationManifest = { - session_id: `${taskSlug}-${dateStr}`, - task_description: task.description, - complexity: complexity, - exploration_count: selectedAngles.length, - explorations: selectedAngles.map(angle => ({ - angle: angle, - file: `exploration-${angle}.json`, - path: `${sessionFolder}/exploration-${angle}.json` - })) -} -Write(`${sessionFolder}/explorations-manifest.json`, JSON.stringify(explorationManifest, null, 2)) -``` - -### Phase 3: Plan Generation - -```javascript -// Read schema reference -const schema = Bash(`cat ~/.ccw/workflows/cli-templates/schemas/plan-overview-base-schema.json`) - -if (complexity === 'Low') { - // Direct Claude planning - // Read all exploration files - explorationManifest.explorations.forEach(exp => { - const data = Read(exp.path) - // Incorporate findings into plan - }) - - // Generate task files in .task/ directory - Bash(`mkdir -p ${sessionFolder}/.task`) - - const tasks = [/* structured tasks with dependencies, files[].change, convergence.criteria */] - const taskIds = tasks.map(t => t.id) - - // Write individual task files following task-schema.json - tasks.forEach(task => { - Write(`${sessionFolder}/.task/${task.id}.json`, JSON.stringify(task, null, 2)) - }) - - // Generate plan overview following plan-overview-base-schema.json - const plan = { - summary: "...", - approach: "...", - task_ids: taskIds, - task_count: taskIds.length, - estimated_time: "...", - recommended_execution: "Agent", - complexity: "Low", - _metadata: { - timestamp: new Date().toISOString(), - source: "team-planner", - planning_mode: "direct", - plan_type: "feature" - } - } - Write(`${sessionFolder}/plan.json`, JSON.stringify(plan, null, 2)) -} else { - // Use cli-lite-planning-agent for Medium/High - Task({ - subagent_type: "cli-lite-planning-agent", - run_in_background: false, - description: "Generate detailed implementation plan", - prompt: ` -Generate implementation plan with two-layer output. - -## Output Location -**Session Folder**: ${sessionFolder} -**Output Files**: -- ${sessionFolder}/planning-context.md -- ${sessionFolder}/plan.json (overview with task_ids[]) -- ${sessionFolder}/.task/TASK-*.json (independent task files) - -## Output Schema Reference -Execute: cat ~/.ccw/workflows/cli-templates/schemas/plan-overview-base-schema.json -Execute: cat ~/.ccw/workflows/cli-templates/schemas/task-schema.json - -## Output Format: Two-Layer Structure -- plan.json: Overview with task_ids[] referencing .task/ files (NO tasks[] array) -- .task/TASK-*.json: Independent task files following task-schema.json - -plan.json required: summary, approach, task_ids, task_count, _metadata (with plan_type) -Task files required: id, title, description, depends_on, convergence (with criteria[]) -Task fields: files[].change (not modification_points), convergence.criteria (not acceptance), test (not verification) - -## Task Description -${task.description} - -## Multi-Angle Exploration Context -${explorationManifest.explorations.map(exp => `### Exploration: ${exp.angle} -Path: ${exp.path}`).join('\n\n')} - -## Complexity Level -${complexity} - -## Requirements -Generate plan.json + .task/*.json following schemas. Key constraints: -- 2-7 structured tasks (group by feature/module, NOT by file) -- Each task file: id, title, description, files[].change, convergence.criteria, depends_on -- Prefer parallel tasks (minimize depends_on) -` - }) -} -``` - -### Phase 4: Submit for Approval - -```javascript -// Read generated plan -const plan = JSON.parse(Read(`${sessionFolder}/plan.json`)) - -// Load tasks from .task/ directory (two-layer format) -const tasks = plan.task_ids.map(id => JSON.parse(Read(`${sessionFolder}/.task/${id}.json`))) -const taskCount = plan.task_count || plan.task_ids.length - -// Send plan summary to coordinator -SendMessage({ - type: "message", - recipient: "coordinator", // team lead - content: `## Plan Ready for Review - -**Task**: ${task.subject} -**Complexity**: ${complexity} -**Tasks**: ${taskCount} - -### Task Summary -${tasks.map((t, i) => `${i+1}. ${t.title} (${t.scope || 'N/A'})`).join('\n')} - -### Approach -${plan.approach} - -### Plan Location -${sessionFolder}/plan.json -${plan.task_ids ? `Task Files: ${sessionFolder}/.task/` : ''} - -Please review and approve or request revisions.`, - summary: `Plan ready: ${taskCount} tasks` -}) - -// Wait for coordinator response -// If approved → mark task completed -// If revision requested → update plan based on feedback → resubmit -``` - -### Phase 5: After Approval - -```javascript -// Mark PLAN task as completed -TaskUpdate({ taskId: task.id, status: 'completed' }) - -// Check for more PLAN tasks -const nextTasks = TaskList().filter(t => - t.subject.startsWith('PLAN-') && - t.owner === 'planner' && - t.status === 'pending' && - t.blockedBy.length === 0 -) - -if (nextTasks.length > 0) { - // Continue with next PLAN task → back to Phase 1 -} else { - // No more tasks, idle - // Will be woken by coordinator message for new assignments -} -``` - -## Session Files - -``` -.workflow/.team-plan/{task-slug}-{YYYY-MM-DD}/ -├── exploration-{angle1}.json # Per-angle exploration results -├── exploration-{angle2}.json -├── explorations-manifest.json # Exploration index -├── planning-context.md # Evidence + understanding (Medium/High) -├── plan.json # Plan overview with task_ids[] (NO embedded tasks[]) -└── .task/ # Independent task files - ├── TASK-001.json # Task file following task-schema.json - ├── TASK-002.json - └── ... -``` - -## Error Handling - -| Scenario | Resolution | -|----------|------------| -| Exploration agent failure | Skip exploration, plan from task description only | -| Planning agent failure | Fallback to direct Claude planning | -| Plan rejected 3+ times | Notify coordinator, suggest alternative approach | -| No PLAN tasks available | Idle, wait for coordinator assignment | -| Schema file not found | Use basic plan structure without schema validation | diff --git a/.claude/commands/team/review.md b/.claude/commands/team/review.md deleted file mode 100644 index cef38a0b..00000000 --- a/.claude/commands/team/review.md +++ /dev/null @@ -1,394 +0,0 @@ ---- -name: review -description: Team reviewer - 代码质量/安全/架构审查、需求验证、发现报告给coordinator -argument-hint: "" -allowed-tools: SendMessage(*), TaskUpdate(*), TaskList(*), TaskGet(*), TodoWrite(*), Read(*), Bash(*), Glob(*), Grep(*), Task(*) -group: team ---- - -# Team Review Command (/team:review) - -## Overview - -Team reviewer role command. Operates as a teammate within an Agent Team (typically handled by the tester), responsible for multi-dimensional code review and requirement verification. Reports findings to the coordinator with severity classification. - -**Core capabilities:** -- Task discovery from shared team task list (REVIEW-* tasks) -- Multi-dimensional review: quality, security, architecture, requirement verification -- Pattern-based security scanning with Grep -- Acceptance criteria verification against plan -- Severity-classified findings (critical/high/medium/low) -- Optional CLI-assisted deep analysis (Gemini/Qwen) - -## Role Definition - -**Name**: `tester` (same teammate handles both TEST and REVIEW tasks) -**Responsibility**: Review code changes → Verify requirements → Report findings -**Communication**: SendMessage to coordinator only - -## 消息总线 - -每次 SendMessage **前**,必须调用 `mcp__ccw-tools__team_msg` 记录消息: - -```javascript -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "", summary: "<摘要>" }) -``` - -### 支持的 Message Types - -| Type | 方向 | 触发时机 | 说明 | -|------|------|----------|------| -| `review_result` | tester → coordinator | 审查完成 | 附带 verdict(APPROVE/CONDITIONAL/BLOCK)和发现统计 | -| `fix_required` | tester → coordinator | 发现 critical issues | 需要创建 IMPL-fix 任务给 executor | -| `error` | tester → coordinator | 审查无法完成 | Plan 缺失、变更文件无法读取等 | - -### 调用示例 - -```javascript -// 审查通过 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "review_result", summary: "REVIEW-001: APPROVE, 2 medium + 1 low findings", data: { verdict: "APPROVE", critical: 0, high: 0, medium: 2, low: 1 } }) - -// 审查有条件通过 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "review_result", summary: "REVIEW-001: CONDITIONAL, 4 high severity findings需关注", data: { verdict: "CONDITIONAL", critical: 0, high: 4, medium: 3, low: 2 } }) - -// 发现 critical 问题 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "fix_required", summary: "发现eval()使用和硬编码密码, 需立即修复", data: { critical: 2, details: ["eval() in auth.ts:42", "hardcoded password in config.ts:15"] } }) - -// 错误上报 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "error", summary: "plan.json未找到, 无法进行需求验证" }) -``` - -### CLI 回退 - -当 `mcp__ccw-tools__team_msg` MCP 不可用时,使用 `ccw team` CLI 作为等效回退: - -```javascript -// 回退: 将 MCP 调用替换为 Bash CLI(参数一一对应) -Bash(`ccw team log --team "${teamName}" --from "tester" --to "coordinator" --type "review_result" --summary "REVIEW-001: APPROVE, 2 medium findings" --data '{"verdict":"APPROVE","critical":0}' --json`) -``` - -**参数映射**: `team_msg(params)` → `ccw team log --team --from tester --to coordinator --type --summary "" [--data ''] [--json]` - -## Execution Process - -``` -Phase 1: Task Discovery - ├─ TaskList to find unblocked REVIEW-* tasks assigned to me - ├─ TaskGet to read full task details - └─ TaskUpdate to mark in_progress - -Phase 2: Review Context Loading - ├─ Read plan.json (requirements + acceptance criteria) - ├─ git diff to get changed files - ├─ Read test results (if available) - └─ Read changed file contents - -Phase 3: Multi-Dimensional Review - ├─ Quality: code style, maintainability, @ts-ignore/any usage - ├─ Security: eval/exec/innerHTML/hardcoded secrets (Grep patterns) - ├─ Architecture: layering compliance, modularity, tech debt - ├─ Requirement Verification: plan acceptance criteria vs implementation - └─ Optional: CLI deep analysis (Gemini for security, Qwen for architecture) - -Phase 4: Finding Summary - ├─ Classify by severity: critical/high/medium/low - ├─ Generate actionable recommendations - └─ Determine overall verdict - -Phase 5: Report to Coordinator - ├─ SendMessage with review findings - ├─ No critical issues → mark REVIEW task completed - └─ Critical issues → flag for immediate attention -``` - -## Implementation - -### Phase 1: Task Discovery - -```javascript -// Find my assigned REVIEW tasks -const tasks = TaskList() -const myReviewTasks = tasks.filter(t => - t.subject.startsWith('REVIEW-') && - t.owner === 'tester' && - t.status === 'pending' && - t.blockedBy.length === 0 -) - -if (myReviewTasks.length === 0) return // idle - -const task = TaskGet({ taskId: myReviewTasks[0].id }) -TaskUpdate({ taskId: task.id, status: 'in_progress' }) -``` - -### Phase 2: Review Context Loading - -```javascript -// Load plan for acceptance criteria -const planPathMatch = task.description.match(/\.workflow\/\.team-plan\/[^\s]+\/plan\.json/) -let plan = null -if (planPathMatch) { - try { plan = JSON.parse(Read(planPathMatch[0])) } catch {} -} - -// Get changed files via git -const changedFiles = Bash(`git diff --name-only HEAD~1 2>/dev/null || git diff --name-only --cached`) - .split('\n') - .filter(f => f.trim() && !f.startsWith('.')) - -// Read changed file contents for review -const fileContents = {} -for (const file of changedFiles.slice(0, 20)) { // limit to 20 files - try { fileContents[file] = Read(file) } catch {} -} - -// Load test results if available -let testResults = null -const testSummary = tasks.find(t => t.subject.startsWith('TEST-') && t.status === 'completed') -``` - -### Phase 3: Multi-Dimensional Review - -```javascript -const findings = { - critical: [], - high: [], - medium: [], - low: [] -} - -// --- Quality Review --- -function reviewQuality(files) { - const issues = [] - - // Check for @ts-ignore, @ts-expect-error, any type - const tsIgnore = Grep({ pattern: '@ts-ignore|@ts-expect-error', glob: '*.{ts,tsx}', output_mode: 'content' }) - if (tsIgnore) issues.push({ type: 'quality', detail: '@ts-ignore/@ts-expect-error usage detected', severity: 'medium' }) - - const anyType = Grep({ pattern: ': any[^A-Z]|as any', glob: '*.{ts,tsx}', output_mode: 'content' }) - if (anyType) issues.push({ type: 'quality', detail: 'Untyped `any` usage detected', severity: 'medium' }) - - // Check for console.log left in production code - const consoleLogs = Grep({ pattern: 'console\\.log', glob: '*.{ts,tsx,js,jsx}', path: 'src/', output_mode: 'content' }) - if (consoleLogs) issues.push({ type: 'quality', detail: 'console.log found in source code', severity: 'low' }) - - // Check for empty catch blocks - const emptyCatch = Grep({ pattern: 'catch\\s*\\([^)]*\\)\\s*\\{\\s*\\}', glob: '*.{ts,tsx,js,jsx}', output_mode: 'content', multiline: true }) - if (emptyCatch) issues.push({ type: 'quality', detail: 'Empty catch blocks detected', severity: 'high' }) - - return issues -} - -// --- Security Review --- -function reviewSecurity(files) { - const issues = [] - - // Dangerous functions - const dangerousFns = Grep({ pattern: '\\beval\\b|\\bexec\\b|innerHTML|dangerouslySetInnerHTML', glob: '*.{ts,tsx,js,jsx}', output_mode: 'content' }) - if (dangerousFns) issues.push({ type: 'security', detail: 'Dangerous function usage: eval/exec/innerHTML', severity: 'critical' }) - - // Hardcoded secrets - const secrets = Grep({ pattern: 'password\\s*=\\s*["\']|secret\\s*=\\s*["\']|api_key\\s*=\\s*["\']', glob: '*.{ts,tsx,js,jsx,py}', output_mode: 'content', '-i': true }) - if (secrets) issues.push({ type: 'security', detail: 'Hardcoded secrets/passwords detected', severity: 'critical' }) - - // SQL injection risk - const sqlInjection = Grep({ pattern: 'query\\s*\\(\\s*`|execute\\s*\\(\\s*`|\\$\\{.*\\}.*(?:SELECT|INSERT|UPDATE|DELETE)', glob: '*.{ts,js,py}', output_mode: 'content', '-i': true }) - if (sqlInjection) issues.push({ type: 'security', detail: 'Potential SQL injection via template literals', severity: 'critical' }) - - // XSS via user input - const xssRisk = Grep({ pattern: 'document\\.write|window\\.location\\s*=', glob: '*.{ts,tsx,js,jsx}', output_mode: 'content' }) - if (xssRisk) issues.push({ type: 'security', detail: 'Potential XSS vectors detected', severity: 'high' }) - - return issues -} - -// --- Architecture Review --- -function reviewArchitecture(files) { - const issues = [] - - // Circular dependency indicators - // Check for imports that may create cycles - for (const [file, content] of Object.entries(fileContents)) { - const imports = content.match(/from\s+['"]([^'"]+)['"]/g) || [] - // Basic heuristic: component importing from parent directory - const parentImports = imports.filter(i => i.includes('../..')) - if (parentImports.length > 2) { - issues.push({ type: 'architecture', detail: `${file}: excessive parent directory imports (possible layering violation)`, severity: 'medium' }) - } - } - - // Large file detection - for (const [file, content] of Object.entries(fileContents)) { - const lines = content.split('\n').length - if (lines > 500) { - issues.push({ type: 'architecture', detail: `${file}: ${lines} lines - consider splitting`, severity: 'low' }) - } - } - - return issues -} - -// --- Requirement Verification --- -function verifyRequirements(plan) { - const issues = [] - if (!plan) { - issues.push({ type: 'requirement', detail: 'No plan found for requirement verification', severity: 'medium' }) - return issues - } - - for (const planTask of plan.tasks) { - for (const criterion of (planTask.acceptance || [])) { - // Check if criterion appears to be met - // This is a heuristic check - look for relevant code in changed files - const keywords = criterion.toLowerCase().split(/\s+/).filter(w => w.length > 4) - const hasEvidence = keywords.some(kw => - Object.values(fileContents).some(content => content.toLowerCase().includes(kw)) - ) - - if (!hasEvidence) { - issues.push({ - type: 'requirement', - detail: `Acceptance criterion may not be met: "${criterion}" (task: ${planTask.title})`, - severity: 'high' - }) - } - } - } - - return issues -} - -// Execute all review dimensions -const qualityIssues = reviewQuality(changedFiles) -const securityIssues = reviewSecurity(changedFiles) -const architectureIssues = reviewArchitecture(changedFiles) -const requirementIssues = plan ? verifyRequirements(plan) : [] - -// Classify into severity buckets -const allIssues = [...qualityIssues, ...securityIssues, ...architectureIssues, ...requirementIssues] -allIssues.forEach(issue => { - findings[issue.severity].push(issue) -}) -``` - -### Phase 4: Finding Summary - -```javascript -const totalIssues = Object.values(findings).flat().length -const hasCritical = findings.critical.length > 0 - -const verdict = hasCritical - ? 'BLOCK - Critical issues must be resolved' - : findings.high.length > 3 - ? 'CONDITIONAL - High severity issues should be addressed' - : 'APPROVE - No blocking issues found' - -const recommendations = [] -if (hasCritical) { - recommendations.push('Fix all critical security issues before merging') -} -if (findings.high.length > 0) { - recommendations.push('Address high severity issues in a follow-up') -} -if (findings.medium.length > 3) { - recommendations.push('Consider refactoring to reduce medium severity issues') -} -``` - -### Phase 5: Report to Coordinator - -```javascript -SendMessage({ - type: "message", - recipient: "coordinator", - content: `## Code Review Report - -**Task**: ${task.subject} -**Verdict**: ${verdict} -**Files Reviewed**: ${changedFiles.length} -**Total Findings**: ${totalIssues} - -### Finding Summary -- Critical: ${findings.critical.length} -- High: ${findings.high.length} -- Medium: ${findings.medium.length} -- Low: ${findings.low.length} - -${findings.critical.length > 0 ? `### Critical Issues -${findings.critical.map(f => `- [${f.type.toUpperCase()}] ${f.detail}`).join('\n')} -` : ''} -${findings.high.length > 0 ? `### High Severity -${findings.high.map(f => `- [${f.type.toUpperCase()}] ${f.detail}`).join('\n')} -` : ''} -${findings.medium.length > 0 ? `### Medium Severity -${findings.medium.map(f => `- [${f.type.toUpperCase()}] ${f.detail}`).join('\n')} -` : ''} -### Recommendations -${recommendations.map(r => `- ${r}`).join('\n')} - -${plan ? `### Requirement Verification -${plan.tasks.map(t => `- **${t.title}**: ${requirementIssues.filter(i => i.detail.includes(t.title)).length === 0 ? 'Criteria met' : 'Needs verification'}`).join('\n')} -` : ''}`, - summary: `Review: ${verdict.split(' - ')[0]} (${totalIssues} findings)` -}) - -// Mark task based on verdict -if (!hasCritical) { - TaskUpdate({ taskId: task.id, status: 'completed' }) -} else { - // Keep in_progress, coordinator needs to create fix tasks - SendMessage({ - type: "message", - recipient: "coordinator", - content: `Critical issues found in review. Recommend creating IMPL-fix tasks for executor to address: ${findings.critical.map(f => f.detail).join('; ')}`, - summary: "Critical issues need fix tasks" - }) -} - -// Check for next REVIEW task -const nextTasks = TaskList().filter(t => - t.subject.startsWith('REVIEW-') && - t.owner === 'tester' && - t.status === 'pending' && - t.blockedBy.length === 0 -) - -if (nextTasks.length > 0) { - // Continue with next task -} -``` - -## Optional: CLI Deep Analysis - -For complex reviews, the tester can invoke CLI tools for deeper analysis: - -```bash -# Security deep analysis (Gemini) -ccw cli -p " -PURPOSE: Deep security audit of implementation changes -TASK: Scan for OWASP Top 10 vulnerabilities, injection flaws, auth bypass vectors -CONTEXT: @src/**/*.{ts,tsx,js,jsx} -EXPECTED: Security findings with severity, file:line references, remediation -CONSTRAINTS: Focus on changed files only -" --tool gemini --mode analysis - -# Architecture deep analysis (Qwen) -ccw cli -p " -PURPOSE: Architecture compliance review -TASK: Evaluate layering, modularity, separation of concerns -CONTEXT: @src/**/* -EXPECTED: Architecture assessment with recommendations -CONSTRAINTS: Focus on changed modules -" --tool qwen --mode analysis -``` - -## Error Handling - -| Scenario | Resolution | -|----------|------------| -| Plan file not found | Review without requirement verification, note in report | -| No changed files detected | Report to coordinator, may need manual file list | -| Grep pattern errors | Skip specific check, continue with remaining | -| CLI analysis timeout | Report partial results, note incomplete analysis | -| Too many files to review (> 50) | Focus on source files, skip generated/vendor files | -| Cannot determine file content | Skip file, note in report | diff --git a/.claude/commands/team/spec-analyst.md b/.claude/commands/team/spec-analyst.md deleted file mode 100644 index b2c46367..00000000 --- a/.claude/commands/team/spec-analyst.md +++ /dev/null @@ -1,321 +0,0 @@ ---- -name: spec-analyst -description: Team spec analyst - 种子分析、代码库探索、上下文收集、多维度研究 -argument-hint: "" -allowed-tools: SendMessage(*), TaskUpdate(*), TaskList(*), TaskGet(*), TodoWrite(*), Read(*), Bash(*), Glob(*), Grep(*), Task(*) -group: team ---- - -# Team Spec Analyst Command (/team:spec-analyst) - -## Overview - -Team spec-analyst role command. Operates as a teammate within a Spec Team, responsible for discovery, codebase exploration, and multi-dimensional context gathering. Maps to spec-generator Phase 1 (Discovery). - -**Core capabilities:** -- Task discovery from shared team task list (RESEARCH-* tasks) -- Seed analysis: problem statement, users, domain, constraints extraction -- Codebase exploration: existing patterns, architecture, tech stack detection -- Multi-dimensional research: 3-5 exploration dimensions with complexity assessment -- Structured context output for downstream discussion and drafting - -## Role Definition - -**Name**: `spec-analyst` -**Responsibility**: Seed Analysis → Codebase Exploration → Context Packaging → Report -**Communication**: SendMessage to coordinator only - -## 消息总线 - -每次 SendMessage **前**,必须调用 `mcp__ccw-tools__team_msg` 记录消息: - -```javascript -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-analyst", to: "coordinator", type: "", summary: "<摘要>", ref: "<文件路径>" }) -``` - -### 支持的 Message Types - -| Type | 方向 | 触发时机 | 说明 | -|------|------|----------|------| -| `research_ready` | spec-analyst → coordinator | 研究完成 | 附带 discovery-context.json 路径和维度摘要 | -| `research_progress` | spec-analyst → coordinator | 长时间研究进展 | 阶段性进展更新 | -| `error` | spec-analyst → coordinator | 遇到不可恢复错误 | 代码库访问失败、CLI 超时等 | - -### 调用示例 - -```javascript -// 研究就绪 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-analyst", to: "coordinator", type: "research_ready", summary: "研究完成: 5个探索维度, 检测到React+Node技术栈", ref: ".workflow/.spec-team/session/discovery-context.json" }) - -// 进展更新 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-analyst", to: "coordinator", type: "research_progress", summary: "种子分析完成, 开始代码库探索 (2/3)" }) - -// 错误上报 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-analyst", to: "coordinator", type: "error", summary: "代码库探索失败: 项目根目录无法识别" }) -``` - -### CLI 回退 - -当 `mcp__ccw-tools__team_msg` MCP 不可用时,使用 `ccw team` CLI 作为等效回退: - -```javascript -// 回退: 将 MCP 调用替换为 Bash CLI(参数一一对应) -Bash(`ccw team log --team "${teamName}" --from "spec-analyst" --to "coordinator" --type "research_ready" --summary "研究完成: 5个探索维度" --ref "${sessionFolder}/discovery-context.json" --json`) -``` - -**参数映射**: `team_msg(params)` → `ccw team log --team --from spec-analyst --to coordinator --type --summary "" [--ref ] [--data ''] [--json]` - -## Execution Process - -``` -Phase 1: Task Discovery - ├─ TaskList to find unblocked RESEARCH-* tasks - ├─ TaskGet to read full task details - └─ TaskUpdate to mark in_progress - -Phase 2: Seed Analysis - ├─ Parse topic/idea from task description - ├─ Extract: problem statement, target users, domain, constraints - ├─ Identify 3-5 exploration dimensions - └─ Assess complexity (simple/moderate/complex) - -Phase 3: Codebase Exploration (conditional) - ├─ Detect project presence (package.json, Cargo.toml, etc.) - ├─ Explore architecture patterns and conventions - ├─ Map technology stack and dependencies - └─ Identify integration constraints - -Phase 4: Context Packaging - ├─ Generate spec-config.json (session state) - ├─ Generate discovery-context.json (research results) - └─ Validate output completeness - -Phase 5: Report to Coordinator - ├─ team_msg log + SendMessage research summary - ├─ TaskUpdate completed - └─ Check for next RESEARCH-* task -``` - -## Implementation - -### Phase 1: Task Discovery - -```javascript -// Find assigned RESEARCH-* tasks -const tasks = TaskList() -const myTasks = tasks.filter(t => - t.subject.startsWith('RESEARCH-') && - t.owner === 'spec-analyst' && - t.status === 'pending' && - t.blockedBy.length === 0 -) - -if (myTasks.length === 0) return // idle - -const task = TaskGet({ taskId: myTasks[0].id }) -TaskUpdate({ taskId: task.id, status: 'in_progress' }) -``` - -### Phase 2: Seed Analysis - -```javascript -// Extract session folder from task description -const sessionMatch = task.description.match(/Session:\s*(.+)/) -const sessionFolder = sessionMatch ? sessionMatch[1].trim() : '.workflow/.spec-team/default' - -// Parse topic from task description -const topicLines = task.description.split('\n').filter(l => !l.startsWith('Session:') && !l.startsWith('输出:') && l.trim()) -const topic = topicLines[0] || task.subject.replace('RESEARCH-001: ', '') - -// Use Gemini CLI for seed analysis -Bash({ - command: `ccw cli -p "PURPOSE: Analyze the following topic/idea and extract structured seed information for specification generation. -TASK: -• Extract problem statement (what problem does this solve) -• Identify target users and their pain points -• Determine domain and industry context -• List constraints and assumptions -• Identify 3-5 exploration dimensions for deeper research -• Assess complexity (simple/moderate/complex) - -TOPIC: ${topic} - -MODE: analysis -CONTEXT: @**/* -EXPECTED: JSON output with fields: problem_statement, target_users[], domain, constraints[], exploration_dimensions[], complexity_assessment -CONSTRAINTS: Output as valid JSON" --tool gemini --mode analysis --rule analysis-analyze-technical-document`, - run_in_background: true -}) -// Wait for CLI result - -// Parse Gemini analysis result -const seedAnalysis = parseCLIResult(geminiOutput) -``` - -### Phase 3: Codebase Exploration (conditional) - -```javascript -// Check if there's an existing codebase to explore -const hasProject = Bash(`test -f package.json || test -f Cargo.toml || test -f pyproject.toml || test -f go.mod; echo $?`) - -if (hasProject === '0') { - // Progress update - mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-analyst", to: "coordinator", type: "research_progress", summary: "种子分析完成, 开始代码库探索" }) - - // Explore codebase using ACE search - const archSearch = mcp__ace-tool__search_context({ - project_root_path: projectRoot, - query: `Architecture patterns, main modules, entry points for: ${topic}` - }) - - // Detect tech stack - const techStack = { - languages: [], - frameworks: [], - databases: [], - infrastructure: [] - } - - // Scan package files for dependencies - const pkgJson = Read('package.json') // if exists - // Parse and categorize dependencies - - // Explore existing patterns - const patterns = mcp__ace-tool__search_context({ - project_root_path: projectRoot, - query: `Similar features, existing conventions, coding patterns related to: ${topic}` - }) - - // Integration constraints - const integrationPoints = mcp__ace-tool__search_context({ - project_root_path: projectRoot, - query: `API endpoints, service boundaries, module interfaces that ${topic} would integrate with` - }) - - var codebaseContext = { - tech_stack: techStack, - architecture_patterns: archSearch, - existing_conventions: patterns, - integration_points: integrationPoints, - constraints_from_codebase: [] - } -} else { - var codebaseContext = null -} -``` - -### Phase 4: Context Packaging - -```javascript -// Generate spec-config.json -const specConfig = { - session_id: `SPEC-${topicSlug}-${dateStr}`, - topic: topic, - status: "research_complete", - complexity: seedAnalysis.complexity_assessment || "moderate", - phases_completed: ["discovery"], - created_at: new Date().toISOString(), - session_folder: sessionFolder, - discussion_depth: task.description.match(/讨论深度:\s*(.+)/)?.[1] || "standard" -} -Write(`${sessionFolder}/spec-config.json`, JSON.stringify(specConfig, null, 2)) - -// Generate discovery-context.json -const discoveryContext = { - session_id: specConfig.session_id, - phase: 1, - document_type: "discovery-context", - status: "complete", - generated_at: new Date().toISOString(), - seed_analysis: { - problem_statement: seedAnalysis.problem_statement, - target_users: seedAnalysis.target_users, - domain: seedAnalysis.domain, - constraints: seedAnalysis.constraints, - exploration_dimensions: seedAnalysis.exploration_dimensions, - complexity: seedAnalysis.complexity_assessment - }, - codebase_context: codebaseContext, - recommendations: { - focus_areas: seedAnalysis.exploration_dimensions?.slice(0, 3) || [], - risks: [], - open_questions: [] - } -} -Write(`${sessionFolder}/discovery-context.json`, JSON.stringify(discoveryContext, null, 2)) -``` - -### Phase 5: Report to Coordinator - -```javascript -const dimensionCount = discoveryContext.seed_analysis.exploration_dimensions?.length || 0 -const hasCodebase = codebaseContext !== null - -// Log before SendMessage -mcp__ccw-tools__team_msg({ - operation: "log", team: teamName, - from: "spec-analyst", to: "coordinator", - type: "research_ready", - summary: `研究完成: ${dimensionCount}个探索维度, ${hasCodebase ? '有' : '无'}代码库上下文, 复杂度=${specConfig.complexity}`, - ref: `${sessionFolder}/discovery-context.json` -}) - -SendMessage({ - type: "message", - recipient: "coordinator", - content: `## 研究分析结果 - -**Task**: ${task.subject} -**复杂度**: ${specConfig.complexity} -**代码库**: ${hasCodebase ? '已检测到现有项目' : '全新项目(无现有代码)'} - -### 问题陈述 -${discoveryContext.seed_analysis.problem_statement} - -### 目标用户 -${(discoveryContext.seed_analysis.target_users || []).map(u => `- ${u}`).join('\n')} - -### 探索维度 -${(discoveryContext.seed_analysis.exploration_dimensions || []).map((d, i) => `${i+1}. ${d}`).join('\n')} - -### 约束条件 -${(discoveryContext.seed_analysis.constraints || []).map(c => `- ${c}`).join('\n')} - -${hasCodebase ? `### 代码库上下文 -- 技术栈: ${JSON.stringify(codebaseContext.tech_stack)} -- 集成点: ${codebaseContext.integration_points?.length || 0}个` : ''} - -### 输出位置 -- Config: ${sessionFolder}/spec-config.json -- Context: ${sessionFolder}/discovery-context.json - -研究已就绪,可进入讨论轮次 DISCUSS-001。`, - summary: `研究就绪: ${dimensionCount}维度, ${specConfig.complexity}` -}) - -// Mark task completed -TaskUpdate({ taskId: task.id, status: 'completed' }) - -// Check for next RESEARCH task -const nextTasks = TaskList().filter(t => - t.subject.startsWith('RESEARCH-') && - t.owner === 'spec-analyst' && - t.status === 'pending' && - t.blockedBy.length === 0 -) - -if (nextTasks.length > 0) { - // Continue with next task -> back to Phase 1 -} -``` - -## Error Handling - -| Scenario | Resolution | -|----------|------------| -| No RESEARCH-* tasks available | Idle, wait for coordinator assignment | -| Gemini CLI analysis failure | Fallback to direct Claude analysis without CLI | -| Codebase detection failed | Continue as new project (no codebase context) | -| Session folder cannot be created | Notify coordinator, request alternative path | -| Topic too vague for analysis | Report to coordinator with clarification questions | -| Unexpected error | Log error via team_msg, report to coordinator | diff --git a/.claude/commands/team/spec-coordinate.md b/.claude/commands/team/spec-coordinate.md deleted file mode 100644 index ed790835..00000000 --- a/.claude/commands/team/spec-coordinate.md +++ /dev/null @@ -1,352 +0,0 @@ ---- -name: spec-coordinate -description: Team spec coordinator - 规格文档工作流编排、讨论轮次管理、跨阶段共识推进 -argument-hint: "[--team-name=NAME] \"spec topic description\"" -allowed-tools: TeamCreate(*), TeamDelete(*), SendMessage(*), TaskCreate(*), TaskUpdate(*), TaskList(*), TaskGet(*), Task(*), AskUserQuestion(*), TodoWrite(*), Read(*), Bash(*), Glob(*), Grep(*) -group: team ---- - -# Team Spec Coordinate Command (/team:spec-coordinate) - -规格文档团队协调器。需求发现 → 研究分析 → 讨论共识 → 文档撰写 → 质量审查 → 最终交付。每个阶段之间穿插结构化讨论轮次,确保多角度审视和团队共识。 - -## 消息总线 - -所有 teammate 在 SendMessage 的**同时**必须调用 `mcp__ccw-tools__team_msg` 记录消息: - -```javascript -// 记录消息(每个 teammate 发 SendMessage 前调用) -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-analyst", to: "coordinator", type: "research_ready", summary: "研究完成: 5个探索维度", ref: ".workflow/.spec-team/session/discovery-context.json" }) - -// 查看全部消息 -mcp__ccw-tools__team_msg({ operation: "list", team: teamName }) - -// 按角色过滤 -mcp__ccw-tools__team_msg({ operation: "list", team: teamName, from: "spec-discuss", last: 5 }) - -// 查看团队状态 -mcp__ccw-tools__team_msg({ operation: "status", team: teamName }) -``` - -**日志位置**: `.workflow/.team-msg/{team-name}/messages.jsonl` -**消息类型**: `research_ready | research_progress | draft_ready | draft_revision | quality_result | discussion_ready | discussion_blocked | fix_required | error | shutdown` - -### CLI 回退 - -当 `mcp__ccw-tools__team_msg` MCP 不可用时,使用 `ccw team` CLI 作为等效回退: - -```javascript -// 回退: 将 MCP 调用替换为 Bash CLI(参数一一对应) -// log -Bash(`ccw team log --team "${teamName}" --from "coordinator" --to "spec-analyst" --type "plan_approved" --summary "研究结果已确认" --json`) -// list -Bash(`ccw team list --team "${teamName}" --last 10 --json`) -// list (带过滤) -Bash(`ccw team list --team "${teamName}" --from "spec-discuss" --last 5 --json`) -// status -Bash(`ccw team status --team "${teamName}" --json`) -// read -Bash(`ccw team read --team "${teamName}" --id "MSG-003" --json`) -``` - -**参数映射**: `team_msg(params)` → `ccw team --team [--from/--to/--type/--summary/--ref/--data/--id/--last] [--json]` - -## Pipeline - -``` -Topic → [RESEARCH: spec-analyst] → [DISCUSS-001: 范围讨论] - → [DRAFT-001: Product Brief] → [DISCUSS-002: 多视角评审] - → [DRAFT-002: Requirements/PRD] → [DISCUSS-003: 需求完整性讨论] - → [DRAFT-003: Architecture] → [DISCUSS-004: 技术可行性讨论] - → [DRAFT-004: Epics & Stories] → [DISCUSS-005: 执行就绪讨论] - → [QUALITY-001: Readiness Check] → [DISCUSS-006: 最终签收] - → 交付 → 等待新需求/关闭 -``` - -## 讨论轮次设计 - -每个讨论轮次由 spec-discuss 角色执行,包含以下维度: - -| 讨论轮次 | 发生时机 | 讨论焦点 | 输入制品 | -|----------|----------|----------|----------| -| DISCUSS-001 | 研究完成后 | 范围确认、方向调整、风险预判 | discovery-context.json | -| DISCUSS-002 | Product Brief 后 | 产品定位、用户画像、竞品分析 | product-brief.md | -| DISCUSS-003 | PRD 后 | 需求完整性、优先级、可测试性 | requirements/_index.md | -| DISCUSS-004 | Architecture 后 | 技术选型、可扩展性、安全性 | architecture/_index.md | -| DISCUSS-005 | Epics 后 | 执行顺序、MVP范围、估算合理性 | epics/_index.md | -| DISCUSS-006 | Quality Check 后 | 最终交付确认、遗留问题、下一步 | readiness-report.md | - -## Execution - -### Phase 1: 需求解析 - -解析 `$ARGUMENTS` 获取 `--team-name` 和规格主题。使用 AskUserQuestion 收集: -- 规格范围(MVP / 完整 / 企业级) -- 重点领域(产品定义 / 技术架构 / 全面规格) -- 讨论深度(快速共识 / 深度讨论 / 全面辩论) - -简单主题可跳过澄清。 - -### Phase 2: 创建 Team + Spawn 4 Teammates - -```javascript -TeamCreate({ team_name: teamName }) - -// Session setup -const topicSlug = topic.toLowerCase().replace(/[^a-z0-9]+/g, '-').substring(0, 40) -const dateStr = new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString().substring(0, 10) -const sessionFolder = `.workflow/.spec-team/${topicSlug}-${dateStr}` -Bash(`mkdir -p ${sessionFolder}`) -``` - -**Spawn 时只传角色和需求,工作细节由 skill 定义**: - -```javascript -// Spec Analyst (研究分析) -Task({ - subagent_type: "general-purpose", - team_name: teamName, - name: "spec-analyst", - prompt: `你是 team "${teamName}" 的 SPEC ANALYST。 - -当你收到 RESEARCH-* 任务时,调用 Skill(skill="team:spec-analyst") 执行研究分析。 - -当前主题: ${topicDescription} -约束: ${constraints} -Session: ${sessionFolder} - -## 消息总线(必须) -每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录: -mcp__ccw-tools__team_msg({ operation: "log", team: "${teamName}", from: "spec-analyst", to: "coordinator", type: "", summary: "<摘要>", ref: "<文件路径>" }) - -工作流程: -1. TaskList → 找到分配给你的 RESEARCH-* 任务 -2. Skill(skill="team:spec-analyst") 执行发现和研究 -3. team_msg log + SendMessage 将研究结果发给 coordinator -4. TaskUpdate completed → 检查下一个 RESEARCH 任务` -}) - -// Spec Writer (文档撰写) -Task({ - subagent_type: "general-purpose", - team_name: teamName, - name: "spec-writer", - prompt: `你是 team "${teamName}" 的 SPEC WRITER。 - -当你收到 DRAFT-* 任务时,调用 Skill(skill="team:spec-writer") 执行文档撰写。 - -当前主题: ${topicDescription} -约束: ${constraints} -Session: ${sessionFolder} - -## 消息总线(必须) -每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录: -mcp__ccw-tools__team_msg({ operation: "log", team: "${teamName}", from: "spec-writer", to: "coordinator", type: "", summary: "<摘要>", ref: "<文件路径>" }) - -工作流程: -1. TaskList → 找到未阻塞的 DRAFT-* 任务 -2. Skill(skill="team:spec-writer") 执行文档生成 -3. team_msg log + SendMessage 报告文档就绪 -4. TaskUpdate completed → 检查下一个 DRAFT 任务` -}) - -// Spec Reviewer (质量审查) -Task({ - subagent_type: "general-purpose", - team_name: teamName, - name: "spec-reviewer", - prompt: `你是 team "${teamName}" 的 SPEC REVIEWER。 - -当你收到 QUALITY-* 任务时,调用 Skill(skill="team:spec-reviewer") 执行质量审查。 - -当前主题: ${topicDescription} -约束: ${constraints} -Session: ${sessionFolder} - -## 消息总线(必须) -每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录: -mcp__ccw-tools__team_msg({ operation: "log", team: "${teamName}", from: "spec-reviewer", to: "coordinator", type: "", summary: "<摘要>" }) - -工作流程: -1. TaskList → 找到未阻塞的 QUALITY-* 任务 -2. Skill(skill="team:spec-reviewer") 执行质量验证 -3. team_msg log + SendMessage 报告审查结果 -4. TaskUpdate completed → 检查下一个 QUALITY 任务` -}) - -// Spec Discuss (讨论促进者) -Task({ - subagent_type: "general-purpose", - team_name: teamName, - name: "spec-discuss", - prompt: `你是 team "${teamName}" 的 SPEC DISCUSS FACILITATOR。 - -当你收到 DISCUSS-* 任务时,调用 Skill(skill="team:spec-discuss") 执行结构化团队讨论。 - -当前主题: ${topicDescription} -约束: ${constraints} -Session: ${sessionFolder} -讨论深度: ${discussionDepth} - -## 消息总线(必须) -每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录: -mcp__ccw-tools__team_msg({ operation: "log", team: "${teamName}", from: "spec-discuss", to: "coordinator", type: "", summary: "<摘要>", ref: "<文件路径>" }) - -工作流程: -1. TaskList → 找到未阻塞的 DISCUSS-* 任务 -2. Skill(skill="team:spec-discuss") 执行结构化讨论 -3. team_msg log + SendMessage 报告讨论共识 -4. TaskUpdate completed → 检查下一个 DISCUSS 任务` -}) -``` - -### Phase 3: 创建完整任务链 - -```javascript -// ===== RESEARCH Phase ===== -TaskCreate({ subject: "RESEARCH-001: 主题发现与上下文研究", description: `${topicDescription}\n\nSession: ${sessionFolder}\n输出: ${sessionFolder}/spec-config.json + discovery-context.json`, activeForm: "研究中" }) -TaskUpdate({ taskId: researchId, owner: "spec-analyst" }) - -// ===== DISCUSS Round 1: 范围讨论 ===== -TaskCreate({ subject: "DISCUSS-001: 研究结果讨论 - 范围确认与方向调整", description: `讨论 RESEARCH-001 的发现结果\n\nSession: ${sessionFolder}\n输入: ${sessionFolder}/discovery-context.json\n输出: ${sessionFolder}/discussions/discuss-001-scope.md\n\n讨论维度: 范围确认、方向调整、风险预判、探索缺口`, activeForm: "讨论范围中" }) -TaskUpdate({ taskId: discuss1Id, owner: "spec-discuss", addBlockedBy: [researchId] }) - -// ===== DRAFT Phase 1: Product Brief ===== -TaskCreate({ subject: "DRAFT-001: 撰写 Product Brief", description: `基于研究和讨论共识撰写产品简报\n\nSession: ${sessionFolder}\n输入: discovery-context.json + discuss-001-scope.md\n输出: ${sessionFolder}/product-brief.md\n\n使用多视角分析: 产品/技术/用户`, activeForm: "撰写 Brief 中" }) -TaskUpdate({ taskId: draft1Id, owner: "spec-writer", addBlockedBy: [discuss1Id] }) - -// ===== DISCUSS Round 2: Brief 评审 ===== -TaskCreate({ subject: "DISCUSS-002: Product Brief 多视角评审", description: `评审 Product Brief 文档\n\nSession: ${sessionFolder}\n输入: ${sessionFolder}/product-brief.md\n输出: ${sessionFolder}/discussions/discuss-002-brief.md\n\n讨论维度: 产品定位、目标用户、成功指标、竞品差异`, activeForm: "评审 Brief 中" }) -TaskUpdate({ taskId: discuss2Id, owner: "spec-discuss", addBlockedBy: [draft1Id] }) - -// ===== DRAFT Phase 2: Requirements/PRD ===== -TaskCreate({ subject: "DRAFT-002: 撰写 Requirements/PRD", description: `基于 Brief 和讨论反馈撰写需求文档\n\nSession: ${sessionFolder}\n输入: product-brief.md + discuss-002-brief.md\n输出: ${sessionFolder}/requirements/\n\n包含: 功能需求(REQ-*) + 非功能需求(NFR-*) + MoSCoW 优先级`, activeForm: "撰写 PRD 中" }) -TaskUpdate({ taskId: draft2Id, owner: "spec-writer", addBlockedBy: [discuss2Id] }) - -// ===== DISCUSS Round 3: 需求完整性 ===== -TaskCreate({ subject: "DISCUSS-003: 需求完整性与优先级讨论", description: `讨论 PRD 需求完整性\n\nSession: ${sessionFolder}\n输入: ${sessionFolder}/requirements/_index.md\n输出: ${sessionFolder}/discussions/discuss-003-requirements.md\n\n讨论维度: 需求遗漏、MoSCoW合理性、验收标准可测性、非功能需求充分性`, activeForm: "讨论需求中" }) -TaskUpdate({ taskId: discuss3Id, owner: "spec-discuss", addBlockedBy: [draft2Id] }) - -// ===== DRAFT Phase 3: Architecture ===== -TaskCreate({ subject: "DRAFT-003: 撰写 Architecture Document", description: `基于需求和讨论反馈撰写架构文档\n\nSession: ${sessionFolder}\n输入: requirements/ + discuss-003-requirements.md\n输出: ${sessionFolder}/architecture/\n\n包含: 架构风格 + 组件图 + 技术选型 + ADR-* + 数据模型`, activeForm: "撰写架构中" }) -TaskUpdate({ taskId: draft3Id, owner: "spec-writer", addBlockedBy: [discuss3Id] }) - -// ===== DISCUSS Round 4: 技术可行性 ===== -TaskCreate({ subject: "DISCUSS-004: 架构决策与技术可行性讨论", description: `讨论架构设计合理性\n\nSession: ${sessionFolder}\n输入: ${sessionFolder}/architecture/_index.md\n输出: ${sessionFolder}/discussions/discuss-004-architecture.md\n\n讨论维度: 技术选型风险、可扩展性、安全架构、ADR替代方案`, activeForm: "讨论架构中" }) -TaskUpdate({ taskId: discuss4Id, owner: "spec-discuss", addBlockedBy: [draft3Id] }) - -// ===== DRAFT Phase 4: Epics & Stories ===== -TaskCreate({ subject: "DRAFT-004: 撰写 Epics & Stories", description: `基于架构和讨论反馈撰写史诗和用户故事\n\nSession: ${sessionFolder}\n输入: architecture/ + discuss-004-architecture.md\n输出: ${sessionFolder}/epics/\n\n包含: EPIC-* + STORY-* + 依赖图 + MVP定义 + 执行顺序`, activeForm: "撰写 Epics 中" }) -TaskUpdate({ taskId: draft4Id, owner: "spec-writer", addBlockedBy: [discuss4Id] }) - -// ===== DISCUSS Round 5: 执行就绪 ===== -TaskCreate({ subject: "DISCUSS-005: 执行计划与MVP范围讨论", description: `讨论执行计划就绪性\n\nSession: ${sessionFolder}\n输入: ${sessionFolder}/epics/_index.md\n输出: ${sessionFolder}/discussions/discuss-005-epics.md\n\n讨论维度: Epic粒度、故事估算、MVP范围、执行顺序、依赖风险`, activeForm: "讨论执行计划中" }) -TaskUpdate({ taskId: discuss5Id, owner: "spec-discuss", addBlockedBy: [draft4Id] }) - -// ===== QUALITY: Readiness Check ===== -TaskCreate({ subject: "QUALITY-001: 规格就绪度检查", description: `全文档交叉验证和质量评分\n\nSession: ${sessionFolder}\n输入: 全部文档\n输出: ${sessionFolder}/readiness-report.md + spec-summary.md\n\n评分维度: 完整性(25%) + 一致性(25%) + 可追溯性(25%) + 深度(25%)`, activeForm: "质量检查中" }) -TaskUpdate({ taskId: qualityId, owner: "spec-reviewer", addBlockedBy: [discuss5Id] }) - -// ===== DISCUSS Round 6: 最终签收 ===== -TaskCreate({ subject: "DISCUSS-006: 最终签收与交付确认", description: `最终讨论和签收\n\nSession: ${sessionFolder}\n输入: ${sessionFolder}/readiness-report.md\n输出: ${sessionFolder}/discussions/discuss-006-final.md\n\n讨论维度: 质量报告审查、遗留问题处理、交付确认、下一步建议`, activeForm: "最终签收讨论中" }) -TaskUpdate({ taskId: discuss6Id, owner: "spec-discuss", addBlockedBy: [qualityId] }) -``` - -### Phase 4: 协调主循环 - -接收 teammate 消息,根据内容做调度决策。**每次做出决策前先 `team_msg list` 查看最近消息,每次做出决策后 `team_msg log` 记录**: - -| 收到消息 | 操作 | -|----------|------| -| Analyst: 研究就绪 | 读取 discovery-context.json → team_msg log → 解锁 DISCUSS-001 | -| Discuss: 讨论共识达成 | 读取 discussion.md → 判断是否需要修订 → 解锁下一个 DRAFT 任务 | -| Discuss: 讨论阻塞 | 介入讨论 → AskUserQuestion 获取用户决策 → 手动推进 | -| Writer: 文档就绪 | 读取文档摘要 → team_msg log → 解锁对应 DISCUSS 任务 | -| Writer: 文档修订 | 更新依赖 → 解锁相关讨论任务 | -| Reviewer: 质量通过 (≥80%) | team_msg log → 解锁 DISCUSS-006 | -| Reviewer: 质量需审查 (60-79%) | team_msg log + 通知 writer 改进建议 | -| Reviewer: 质量失败 (<60%) | 创建 DRAFT-fix 任务 → 分配 writer | -| 所有任务 completed | → Phase 5 | - -**讨论阻塞处理**: -```javascript -// 当 DISCUSS 任务报告阻塞(无法达成共识) -// Coordinator 介入 -if (msgType === 'discussion_blocked') { - const blockReason = msg.data.reason - const options = msg.data.options - - // 上报用户做出决策 - AskUserQuestion({ - questions: [{ - question: `讨论 ${msg.ref} 遇到分歧: ${blockReason}\n请选择方向:`, - header: "Decision", - multiSelect: false, - options: options.map(opt => ({ label: opt.label, description: opt.description })) - }] - }) - - // 将用户决策写入讨论记录 - // 解锁后续任务 -} -``` - -### Phase 5: 汇报 + 持久循环 - -汇总所有文档、讨论轮次结果、质量评分报告用户。 - -```javascript -AskUserQuestion({ - questions: [{ - question: "规格文档已完成。下一步:", - header: "Next", - multiSelect: false, - options: [ - { label: "交付执行", description: "将规格交给 lite-plan/req-plan/plan 执行" }, - { label: "新主题", description: "为新主题生成规格(复用团队)" }, - { label: "关闭团队", description: "关闭所有 teammate 并清理" } - ] - }] -}) -// 交付执行 → 提示可用的执行 workflow -// 新主题 → 回到 Phase 1(复用 team,新建全套任务链) -// 关闭 → shutdown 给每个 teammate → TeamDelete() -``` - -## Session 文件结构 - -``` -.workflow/.spec-team/{topic-slug}-{YYYY-MM-DD}/ -├── spec-config.json # Session state -├── discovery-context.json # Research context (Phase 1) -├── product-brief.md # Product Brief (Phase 2) -├── requirements/ # PRD (Phase 3) -│ ├── _index.md -│ ├── REQ-001-*.md -│ └── NFR-*-*.md -├── architecture/ # Architecture (Phase 4) -│ ├── _index.md -│ └── ADR-001-*.md -├── epics/ # Epics & Stories (Phase 5) -│ ├── _index.md -│ └── EPIC-001-*.md -├── readiness-report.md # Quality validation (Phase 6) -├── spec-summary.md # Executive summary -└── discussions/ # 讨论记录 - ├── discuss-001-scope.md - ├── discuss-002-brief.md - ├── discuss-003-requirements.md - ├── discuss-004-architecture.md - ├── discuss-005-epics.md - └── discuss-006-final.md -``` - -## 错误处理 - -| 场景 | 处理 | -|------|------| -| Teammate 无响应 | 发追踪消息,2次无响应 → 重新 spawn | -| 讨论无法共识 | Coordinator 介入 → AskUserQuestion | -| 文档质量 <60% | 创建 DRAFT-fix 任务给 writer | -| Writer 修订 3+ 次 | 上报用户,建议调整范围 | -| Research 无法完成 | 降级为简化模式,跳过深度分析 | diff --git a/.claude/commands/team/spec-discuss.md b/.claude/commands/team/spec-discuss.md deleted file mode 100644 index 97689128..00000000 --- a/.claude/commands/team/spec-discuss.md +++ /dev/null @@ -1,498 +0,0 @@ ---- -name: spec-discuss -description: Team spec discuss - 结构化团队讨论、多视角批判、共识构建、分歧调解 -argument-hint: "" -allowed-tools: SendMessage(*), TaskUpdate(*), TaskList(*), TaskGet(*), TodoWrite(*), Read(*), Write(*), Bash(*), Glob(*), Grep(*), Task(*) -group: team ---- - -# Team Spec Discuss Command (/team:spec-discuss) - -## Overview - -Team spec-discuss role command. Operates as a teammate within a Spec Team, responsible for facilitating structured team discussions between specification phases. This is the **key differentiator** of the spec team workflow — ensuring multi-perspective critique, consensus building, and quality feedback before each phase transition. - -**Core capabilities:** -- Task discovery from shared team task list (DISCUSS-* tasks) -- Multi-perspective analysis: Product, Technical, Quality, Risk viewpoints -- Structured discussion facilitation with critique + suggestion format -- Consensus synthesis with action items and decision records -- Conflict identification and escalation when consensus cannot be reached -- CLI-assisted deep critique (parallel multi-model analysis) - -## Role Definition - -**Name**: `spec-discuss` -**Responsibility**: Load Artifact → Multi-Perspective Critique → Synthesize Consensus → Report -**Communication**: SendMessage to coordinator only - -## 消息总线 - -每次 SendMessage **前**,必须调用 `mcp__ccw-tools__team_msg` 记录消息: - -```javascript -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-discuss", to: "coordinator", type: "", summary: "<摘要>", ref: "<文件路径>" }) -``` - -### 支持的 Message Types - -| Type | 方向 | 触发时机 | 说明 | -|------|------|----------|------| -| `discussion_ready` | spec-discuss → coordinator | 讨论完成,共识达成 | 附带讨论记录路径和决策摘要 | -| `discussion_blocked` | spec-discuss → coordinator | 讨论无法达成共识 | 附带分歧点和可选方案,需 coordinator 介入 | -| `impl_progress` | spec-discuss → coordinator | 长讨论进展更新 | 多视角分析进度 | -| `error` | spec-discuss → coordinator | 讨论无法进行 | 输入制品缺失等 | - -### 调用示例 - -```javascript -// 讨论共识达成 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-discuss", to: "coordinator", type: "discussion_ready", summary: "DISCUSS-002: 共识达成, 3个改进建议 + 2个开放问题", ref: ".workflow/.spec-team/session/discussions/discuss-002-brief.md" }) - -// 讨论阻塞 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-discuss", to: "coordinator", type: "discussion_blocked", summary: "DISCUSS-004: 技术选型分歧 - 微服务 vs 单体, 需用户决策", data: { reason: "技术架构风格无法达成共识", options: [{ label: "微服务", description: "更好扩展性但增加复杂度" }, { label: "单体", description: "简单但限制扩展" }] } }) - -// 错误上报 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-discuss", to: "coordinator", type: "error", summary: "DISCUSS-001: 找不到 discovery-context.json" }) -``` - -### CLI 回退 - -当 `mcp__ccw-tools__team_msg` MCP 不可用时,使用 `ccw team` CLI 作为等效回退: - -```javascript -// 回退: 将 MCP 调用替换为 Bash CLI(参数一一对应) -Bash(`ccw team log --team "${teamName}" --from "spec-discuss" --to "coordinator" --type "discussion_ready" --summary "DISCUSS-002: 共识达成, 3个改进建议" --ref "${sessionFolder}/discussions/discuss-002-brief.md" --json`) - -// 带 data 参数(讨论阻塞时) -Bash(`ccw team log --team "${teamName}" --from "spec-discuss" --to "coordinator" --type "discussion_blocked" --summary "技术选型分歧" --data '{"reason":"微服务 vs 单体","options":[{"label":"微服务"},{"label":"单体"}]}' --json`) -``` - -**参数映射**: `team_msg(params)` → `ccw team log --team --from spec-discuss --to coordinator --type --summary "" [--ref ] [--data ''] [--json]` - -## 讨论维度模型 - -每个讨论轮次从4个视角进行结构化分析: - -| 视角 | 关注点 | 代表角色 | -|------|--------|----------| -| **产品视角** | 市场适配、用户价值、商业可行性、竞品差异 | Product Manager | -| **技术视角** | 可行性、技术债务、性能、安全、可维护性 | Tech Lead | -| **质量视角** | 完整性、可测试性、一致性、标准合规 | QA Lead | -| **风险视角** | 风险识别、依赖分析、假设验证、失败模式 | Risk Analyst | - -## 讨论轮次配置 - -| 轮次 | 制品 | 重点维度 | 讨论深度 | -|------|------|----------|----------| -| DISCUSS-001 | discovery-context | 产品+风险 | 范围确认、方向调整 | -| DISCUSS-002 | product-brief | 产品+技术+质量 | 定位审视、可行性 | -| DISCUSS-003 | requirements | 质量+产品 | 完整性、优先级 | -| DISCUSS-004 | architecture | 技术+风险 | 技术选型、安全性 | -| DISCUSS-005 | epics | 产品+技术+质量 | MVP范围、估算 | -| DISCUSS-006 | readiness-report | 全维度 | 最终签收 | - -## Execution Process - -``` -Phase 1: Task Discovery - ├─ TaskList to find unblocked DISCUSS-* tasks - ├─ TaskGet to read full task details - └─ TaskUpdate to mark in_progress - -Phase 2: Artifact Loading - ├─ Identify discussion round from task subject (001-006) - ├─ Load target artifact for discussion - ├─ Load prior discussion records for continuity - └─ Determine discussion dimensions from round config - -Phase 3: Multi-Perspective Critique - ├─ Product perspective analysis - ├─ Technical perspective analysis - ├─ Quality perspective analysis - ├─ Risk perspective analysis - └─ (Parallel CLI execution for depth) - -Phase 4: Consensus Synthesis - ├─ Identify convergent themes (areas of agreement) - ├─ Identify divergent views (conflicts) - ├─ Generate action items and recommendations - ├─ Formulate consensus or escalate divergence - └─ Write discussion record - -Phase 5: Report to Coordinator - ├─ team_msg log + SendMessage discussion results - ├─ TaskUpdate completed (if consensus) - └─ Flag discussion_blocked (if unresolvable conflict) -``` - -## Implementation - -### Phase 1: Task Discovery - -```javascript -// Find assigned DISCUSS-* tasks -const tasks = TaskList() -const myTasks = tasks.filter(t => - t.subject.startsWith('DISCUSS-') && - t.owner === 'spec-discuss' && - t.status === 'pending' && - t.blockedBy.length === 0 -) - -if (myTasks.length === 0) return // idle - -const task = TaskGet({ taskId: myTasks[0].id }) -TaskUpdate({ taskId: task.id, status: 'in_progress' }) -``` - -### Phase 2: Artifact Loading - -```javascript -// Extract session folder and discussion round -const sessionMatch = task.description.match(/Session:\s*(.+)/) -const sessionFolder = sessionMatch ? sessionMatch[1].trim() : '' -const roundMatch = task.subject.match(/DISCUSS-(\d+)/) -const roundNumber = roundMatch ? parseInt(roundMatch[1]) : 0 - -// Discussion round configuration -const roundConfig = { - 1: { artifact: 'discovery-context.json', type: 'json', outputFile: 'discuss-001-scope.md', perspectives: ['product', 'risk'], label: '范围讨论' }, - 2: { artifact: 'product-brief.md', type: 'md', outputFile: 'discuss-002-brief.md', perspectives: ['product', 'technical', 'quality'], label: 'Brief评审' }, - 3: { artifact: 'requirements/_index.md', type: 'md', outputFile: 'discuss-003-requirements.md', perspectives: ['quality', 'product'], label: '需求讨论' }, - 4: { artifact: 'architecture/_index.md', type: 'md', outputFile: 'discuss-004-architecture.md', perspectives: ['technical', 'risk'], label: '架构讨论' }, - 5: { artifact: 'epics/_index.md', type: 'md', outputFile: 'discuss-005-epics.md', perspectives: ['product', 'technical', 'quality'], label: 'Epics讨论' }, - 6: { artifact: 'readiness-report.md', type: 'md', outputFile: 'discuss-006-final.md', perspectives: ['product', 'technical', 'quality', 'risk'], label: '最终签收' } -} - -const config = roundConfig[roundNumber] -if (!config) { - mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-discuss", to: "coordinator", type: "error", summary: `未知讨论轮次: DISCUSS-${roundNumber}` }) - return -} - -// Load target artifact -let artifact = null -try { - const raw = Read(`${sessionFolder}/${config.artifact}`) - artifact = config.type === 'json' ? JSON.parse(raw) : raw -} catch (e) { - mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-discuss", to: "coordinator", type: "error", summary: `无法加载制品: ${config.artifact}` }) - return -} - -// Load prior discussion records for context continuity -const priorDiscussions = [] -for (let i = 1; i < roundNumber; i++) { - const priorConfig = roundConfig[i] - try { priorDiscussions.push(Read(`${sessionFolder}/discussions/${priorConfig.outputFile}`)) } catch {} -} - -// Ensure discussions directory exists -Bash(`mkdir -p ${sessionFolder}/discussions`) -``` - -### Phase 3: Multi-Perspective Critique - -```javascript -const perspectives = {} -const artifactContent = typeof artifact === 'string' ? artifact : JSON.stringify(artifact, null, 2) - -// Progress notification -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-discuss", to: "coordinator", type: "impl_progress", summary: `开始 ${config.label}: ${config.perspectives.length} 个视角分析` }) - -// --- Product Perspective --- -if (config.perspectives.includes('product')) { - Bash({ - command: `ccw cli -p "PURPOSE: Critique the following specification artifact from a PRODUCT MANAGER perspective. -TASK: -• Evaluate market fit and user value proposition -• Assess target user alignment and persona coverage -• Check business viability and competitive differentiation -• Identify gaps in user journey coverage -• Rate on scale 1-5 with specific improvement suggestions - -ARTIFACT TYPE: ${config.label} -CONTENT: ${artifactContent.substring(0, 8000)} - -${priorDiscussions.length > 0 ? `PRIOR DISCUSSION CONTEXT: ${priorDiscussions[priorDiscussions.length - 1]?.substring(0, 2000)}` : ''} - -EXPECTED: Structured critique with: strengths[], weaknesses[], suggestions[], open_questions[], rating (1-5) -CONSTRAINTS: Be constructive but rigorous" --tool gemini --mode analysis`, - run_in_background: true - }) - // perspectives.product = parseCLIResult(...) -} - -// --- Technical Perspective --- -if (config.perspectives.includes('technical')) { - Bash({ - command: `ccw cli -p "PURPOSE: Critique the following specification artifact from a TECH LEAD perspective. -TASK: -• Evaluate technical feasibility and implementation complexity -• Assess architecture decisions and technology choices -• Check for technical debt risks and scalability concerns -• Identify missing technical requirements or constraints -• Rate on scale 1-5 with specific improvement suggestions - -ARTIFACT TYPE: ${config.label} -CONTENT: ${artifactContent.substring(0, 8000)} - -EXPECTED: Structured critique with: strengths[], weaknesses[], suggestions[], risks[], rating (1-5) -CONSTRAINTS: Focus on engineering concerns" --tool codex --mode analysis`, - run_in_background: true - }) - // perspectives.technical = parseCLIResult(...) -} - -// --- Quality Perspective --- -if (config.perspectives.includes('quality')) { - Bash({ - command: `ccw cli -p "PURPOSE: Critique the following specification artifact from a QA LEAD perspective. -TASK: -• Evaluate completeness of acceptance criteria and testability -• Check consistency of terminology and formatting -• Assess traceability between documents -• Identify ambiguous or untestable requirements -• Rate on scale 1-5 with specific improvement suggestions - -ARTIFACT TYPE: ${config.label} -CONTENT: ${artifactContent.substring(0, 8000)} - -EXPECTED: Structured critique with: strengths[], weaknesses[], suggestions[], testability_issues[], rating (1-5) -CONSTRAINTS: Focus on quality and verifiability" --tool claude --mode analysis`, - run_in_background: true - }) - // perspectives.quality = parseCLIResult(...) -} - -// --- Risk Perspective --- -if (config.perspectives.includes('risk')) { - Bash({ - command: `ccw cli -p "PURPOSE: Critique the following specification artifact from a RISK ANALYST perspective. -TASK: -• Identify project risks and failure modes -• Assess dependency risks and external factors -• Validate assumptions made in the specification -• Check for missing contingency plans -• Rate risk level (Low/Medium/High/Critical) with mitigation suggestions - -ARTIFACT TYPE: ${config.label} -CONTENT: ${artifactContent.substring(0, 8000)} - -EXPECTED: Structured critique with: risks[{description, likelihood, impact, mitigation}], assumptions_to_validate[], dependencies[], overall_risk_level -CONSTRAINTS: Focus on risk identification" --tool gemini --mode analysis`, - run_in_background: true - }) - // perspectives.risk = parseCLIResult(...) -} - -// Wait for all parallel CLI analyses to complete -``` - -### Phase 4: Consensus Synthesis - -```javascript -// Analyze all perspectives for convergence and divergence -const synthesis = { - convergent_themes: [], // Areas where all perspectives agree - divergent_views: [], // Areas of conflict - action_items: [], // Concrete improvements to make - open_questions: [], // Unresolved questions for user/team - decisions: [], // Decisions made during discussion - risk_flags: [], // Risks identified - overall_sentiment: '', // positive/neutral/concerns/critical - consensus_reached: true // false if major unresolvable conflicts -} - -// Extract convergent themes (items mentioned positively by 2+ perspectives) -// Extract divergent views (items where perspectives conflict) -// Generate action items from suggestions - -// Check for unresolvable conflicts -const criticalDivergences = synthesis.divergent_views.filter(d => d.severity === 'high') -if (criticalDivergences.length > 0) { - synthesis.consensus_reached = false -} - -// Determine overall sentiment -const avgRating = Object.values(perspectives) - .map(p => p?.rating || 3) - .reduce((a, b) => a + b, 0) / config.perspectives.length - -synthesis.overall_sentiment = avgRating >= 4 ? 'positive' - : avgRating >= 3 ? 'neutral' - : avgRating >= 2 ? 'concerns' - : 'critical' - -// Generate discussion record -const discussionRecord = `# 讨论记录: ${config.label} (DISCUSS-${String(roundNumber).padStart(3, '0')}) - -**讨论对象**: ${config.artifact} -**参与视角**: ${config.perspectives.join(', ')} -**讨论时间**: ${new Date().toISOString()} -**共识状态**: ${synthesis.consensus_reached ? '已达成共识' : '存在分歧,需coordinator介入'} -**总体评价**: ${synthesis.overall_sentiment} - -## 多视角评审结果 - -${config.perspectives.map(p => { - const pData = perspectives[p] - return `### ${p === 'product' ? '产品视角 (Product Manager)' : - p === 'technical' ? '技术视角 (Tech Lead)' : - p === 'quality' ? '质量视角 (QA Lead)' : - '风险视角 (Risk Analyst)'} - -**评分**: ${pData?.rating || 'N/A'}/5 - -**优点**: -${(pData?.strengths || []).map(s => '- ' + s).join('\n') || '- (待分析)'} - -**不足**: -${(pData?.weaknesses || []).map(w => '- ' + w).join('\n') || '- (待分析)'} - -**建议**: -${(pData?.suggestions || []).map(s => '- ' + s).join('\n') || '- (待分析)'} -`}).join('\n')} - -## 共识分析 - -### 一致认同的优点 -${synthesis.convergent_themes.map(t => '- ' + t).join('\n') || '- (待合成)'} - -### 存在的分歧 -${synthesis.divergent_views.map(d => `- **${d.topic}**: ${d.description}`).join('\n') || '- 无重大分歧'} - -### 风险标记 -${synthesis.risk_flags.map(r => `- [${r.level}] ${r.description}`).join('\n') || '- 无重大风险'} - -## 行动项 - -${synthesis.action_items.map((item, i) => `${i+1}. ${item}`).join('\n') || '无需修改'} - -## 开放问题 - -${synthesis.open_questions.map((q, i) => `${i+1}. ${q}`).join('\n') || '无开放问题'} - -## 决策记录 - -${synthesis.decisions.map((d, i) => `${i+1}. **${d.topic}**: ${d.decision} (理由: ${d.rationale})`).join('\n') || '无新决策'} - -## 对下一阶段的建议 - -${roundNumber < 6 ? `下一阶段应关注: ${synthesis.action_items.slice(0, 3).join('; ') || '按原计划推进'}` : '所有阶段已完成,建议进入执行。'} -` - -Write(`${sessionFolder}/discussions/${config.outputFile}`, discussionRecord) -``` - -### Phase 5: Report to Coordinator - -```javascript -if (synthesis.consensus_reached) { - // Consensus reached - mcp__ccw-tools__team_msg({ - operation: "log", team: teamName, - from: "spec-discuss", to: "coordinator", - type: "discussion_ready", - summary: `${config.label}讨论完成: ${synthesis.action_items.length}个行动项, ${synthesis.open_questions.length}个开放问题, 总体${synthesis.overall_sentiment}`, - ref: `${sessionFolder}/discussions/${config.outputFile}` - }) - - SendMessage({ - type: "message", - recipient: "coordinator", - content: `## 讨论结果: ${config.label} - -**Task**: ${task.subject} -**共识**: 已达成 -**总体评价**: ${synthesis.overall_sentiment} -**参与视角**: ${config.perspectives.join(', ')} - -### 关键发现 -**一致优点**: ${synthesis.convergent_themes.length}项 -**分歧点**: ${synthesis.divergent_views.length}项 -**风险标记**: ${synthesis.risk_flags.length}项 - -### 行动项 (${synthesis.action_items.length}) -${synthesis.action_items.map((item, i) => `${i+1}. ${item}`).join('\n') || '无'} - -### 开放问题 (${synthesis.open_questions.length}) -${synthesis.open_questions.map((q, i) => `${i+1}. ${q}`).join('\n') || '无'} - -### 讨论记录 -${sessionFolder}/discussions/${config.outputFile} - -共识已达成,可推进至下一阶段。`, - summary: `${config.label}共识达成: ${synthesis.action_items.length}行动项` - }) - - TaskUpdate({ taskId: task.id, status: 'completed' }) -} else { - // Consensus blocked - escalate to coordinator - mcp__ccw-tools__team_msg({ - operation: "log", team: teamName, - from: "spec-discuss", to: "coordinator", - type: "discussion_blocked", - summary: `${config.label}讨论阻塞: ${criticalDivergences.length}个关键分歧需决策`, - data: { - reason: criticalDivergences.map(d => d.description).join('; '), - options: criticalDivergences.map(d => ({ - label: d.topic, - description: d.options?.join(' vs ') || d.description - })) - } - }) - - SendMessage({ - type: "message", - recipient: "coordinator", - content: `## 讨论阻塞: ${config.label} - -**Task**: ${task.subject} -**状态**: 无法达成共识,需要 coordinator 介入 - -### 关键分歧 -${criticalDivergences.map((d, i) => `${i+1}. **${d.topic}**: ${d.description} - - 选项A: ${d.optionA || ''} - - 选项B: ${d.optionB || ''}`).join('\n\n')} - -### 已达成共识的部分 -${synthesis.convergent_themes.map(t => `- ${t}`).join('\n') || '- 无'} - -### 建议 -请通过 AskUserQuestion 收集用户对分歧点的决策,然后将决策写入讨论记录以继续推进。 - -### 讨论记录(部分) -${sessionFolder}/discussions/${config.outputFile}`, - summary: `${config.label}阻塞: ${criticalDivergences.length}分歧` - }) - - // Keep task in_progress, wait for coordinator resolution -} - -// Check for next DISCUSS task -const nextTasks = TaskList().filter(t => - t.subject.startsWith('DISCUSS-') && - t.owner === 'spec-discuss' && - t.status === 'pending' && - t.blockedBy.length === 0 -) - -if (nextTasks.length > 0) { - // Continue with next task -> back to Phase 1 -} -``` - -## Error Handling - -| Scenario | Resolution | -|----------|------------| -| No DISCUSS-* tasks available | Idle, wait for coordinator assignment | -| Target artifact not found | Notify coordinator, request prerequisite completion | -| CLI perspective analysis failure | Fallback to direct Claude analysis for that perspective | -| All CLI analyses fail | Generate basic discussion from direct reading | -| Consensus timeout (all perspectives diverge) | Escalate as discussion_blocked | -| Prior discussion records missing | Continue without continuity context | -| Session folder not found | Notify coordinator, request session path | -| Unexpected error | Log error via team_msg, report to coordinator | diff --git a/.claude/commands/team/spec-reviewer.md b/.claude/commands/team/spec-reviewer.md deleted file mode 100644 index 7d551343..00000000 --- a/.claude/commands/team/spec-reviewer.md +++ /dev/null @@ -1,492 +0,0 @@ ---- -name: spec-reviewer -description: Team spec reviewer - 跨文档质量验证、完整性/一致性/可追溯性/深度评分、就绪度检查 -argument-hint: "" -allowed-tools: SendMessage(*), TaskUpdate(*), TaskList(*), TaskGet(*), TodoWrite(*), Read(*), Bash(*), Glob(*), Grep(*), Task(*) -group: team ---- - -# Team Spec Reviewer Command (/team:spec-reviewer) - -## Overview - -Team spec-reviewer role command. Operates as a teammate within a Spec Team, responsible for cross-document quality validation and readiness checks. Maps to spec-generator Phase 6 (Readiness Check). - -**Core capabilities:** -- Task discovery from shared team task list (QUALITY-* tasks) -- 4-dimension quality scoring: Completeness, Consistency, Traceability, Depth -- Cross-document validation (Brief → PRD → Architecture → Epics chain) -- Quality gate enforcement (Pass ≥80%, Review 60-79%, Fail <60%) -- Readiness report and executive summary generation -- CLI-assisted deep validation (optional) - -## Role Definition - -**Name**: `spec-reviewer` -**Responsibility**: Load All Documents → Cross-Validate → Score → Report -**Communication**: SendMessage to coordinator only - -## 消息总线 - -每次 SendMessage **前**,必须调用 `mcp__ccw-tools__team_msg` 记录消息: - -```javascript -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-reviewer", to: "coordinator", type: "", summary: "<摘要>" }) -``` - -### 支持的 Message Types - -| Type | 方向 | 触发时机 | 说明 | -|------|------|----------|------| -| `quality_result` | spec-reviewer → coordinator | 质量检查完成 | 附带评分和 gate 决策 (PASS/REVIEW/FAIL) | -| `fix_required` | spec-reviewer → coordinator | 发现关键质量问题 | 需创建 DRAFT-fix 任务 | -| `error` | spec-reviewer → coordinator | 审查无法完成 | 文档缺失、无法解析等 | - -### 调用示例 - -```javascript -// 质量通过 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-reviewer", to: "coordinator", type: "quality_result", summary: "质量检查 PASS: 85分 (完整性90/一致性85/可追溯性80/深度85)", data: { gate: "PASS", score: 85 } }) - -// 需要审查 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-reviewer", to: "coordinator", type: "quality_result", summary: "质量检查 REVIEW: 72分, 可追溯性不足", data: { gate: "REVIEW", score: 72 } }) - -// 质量失败 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-reviewer", to: "coordinator", type: "fix_required", summary: "质量 FAIL: 55分, 缺少架构 ADR + PRD 验收标准不可测", data: { gate: "FAIL", score: 55, issues: ["missing ADRs", "untestable AC"] } }) -``` - -### CLI 回退 - -当 `mcp__ccw-tools__team_msg` MCP 不可用时,使用 `ccw team` CLI 作为等效回退: - -```javascript -// 回退: 将 MCP 调用替换为 Bash CLI(参数一一对应) -Bash(`ccw team log --team "${teamName}" --from "spec-reviewer" --to "coordinator" --type "quality_result" --summary "质量检查 PASS: 85分" --data '{"gate":"PASS","score":85}' --json`) -``` - -**参数映射**: `team_msg(params)` → `ccw team log --team --from spec-reviewer --to coordinator --type --summary "" [--data ''] [--json]` - -## Execution Process - -``` -Phase 1: Task Discovery - ├─ TaskList to find unblocked QUALITY-* tasks - ├─ TaskGet to read full task details - └─ TaskUpdate to mark in_progress - -Phase 2: Document Collection - ├─ Load all generated documents from session folder - ├─ Verify document chain completeness - ├─ Load discussion records for context - └─ Build document inventory - -Phase 3: 4-Dimension Quality Scoring - ├─ Completeness (25%): All sections present with content - ├─ Consistency (25%): Terminology, format, references - ├─ Traceability (25%): Goals → Reqs → Arch → Stories chain - └─ Depth (25%): AC testable, ADRs justified, stories estimable - -Phase 4: Report Generation - ├─ Generate readiness-report.md (quality scores, issues, traceability) - ├─ Generate spec-summary.md (one-page executive summary) - └─ Determine quality gate decision - -Phase 5: Report to Coordinator - ├─ team_msg log + SendMessage quality results - ├─ TaskUpdate completed (if PASS/REVIEW) - └─ Flag fix_required (if FAIL) -``` - -## Implementation - -### Phase 1: Task Discovery - -```javascript -// Find assigned QUALITY-* tasks -const tasks = TaskList() -const myTasks = tasks.filter(t => - t.subject.startsWith('QUALITY-') && - t.owner === 'spec-reviewer' && - t.status === 'pending' && - t.blockedBy.length === 0 -) - -if (myTasks.length === 0) return // idle - -const task = TaskGet({ taskId: myTasks[0].id }) -TaskUpdate({ taskId: task.id, status: 'in_progress' }) -``` - -### Phase 2: Document Collection - -```javascript -// Extract session folder -const sessionMatch = task.description.match(/Session:\s*(.+)/) -const sessionFolder = sessionMatch ? sessionMatch[1].trim() : '' - -// Load all documents -const documents = { - config: null, - discoveryContext: null, - productBrief: null, - requirementsIndex: null, - requirements: [], - architectureIndex: null, - adrs: [], - epicsIndex: null, - epics: [], - discussions: [] -} - -try { documents.config = JSON.parse(Read(`${sessionFolder}/spec-config.json`)) } catch {} -try { documents.discoveryContext = JSON.parse(Read(`${sessionFolder}/discovery-context.json`)) } catch {} -try { documents.productBrief = Read(`${sessionFolder}/product-brief.md`) } catch {} -try { documents.requirementsIndex = Read(`${sessionFolder}/requirements/_index.md`) } catch {} -try { documents.architectureIndex = Read(`${sessionFolder}/architecture/_index.md`) } catch {} -try { documents.epicsIndex = Read(`${sessionFolder}/epics/_index.md`) } catch {} - -// Load individual requirements -const reqFiles = Glob({ pattern: `${sessionFolder}/requirements/REQ-*.md` }) -reqFiles.forEach(f => { try { documents.requirements.push(Read(f)) } catch {} }) -const nfrFiles = Glob({ pattern: `${sessionFolder}/requirements/NFR-*.md` }) -nfrFiles.forEach(f => { try { documents.requirements.push(Read(f)) } catch {} }) - -// Load individual ADRs -const adrFiles = Glob({ pattern: `${sessionFolder}/architecture/ADR-*.md` }) -adrFiles.forEach(f => { try { documents.adrs.push(Read(f)) } catch {} }) - -// Load individual Epics -const epicFiles = Glob({ pattern: `${sessionFolder}/epics/EPIC-*.md` }) -epicFiles.forEach(f => { try { documents.epics.push(Read(f)) } catch {} }) - -// Load discussions -const discussFiles = Glob({ pattern: `${sessionFolder}/discussions/discuss-*.md` }) -discussFiles.forEach(f => { try { documents.discussions.push(Read(f)) } catch {} }) - -// Verify completeness -const docInventory = { - config: !!documents.config, - discoveryContext: !!documents.discoveryContext, - productBrief: !!documents.productBrief, - requirements: documents.requirements.length > 0, - architecture: documents.adrs.length > 0, - epics: documents.epics.length > 0, - discussions: documents.discussions.length -} -``` - -### Phase 3: 4-Dimension Quality Scoring - -```javascript -const scores = { - completeness: 0, - consistency: 0, - traceability: 0, - depth: 0 -} - -// ===== Completeness (25%) ===== -function scoreCompleteness(docs) { - let score = 0 - const checks = [ - { name: 'spec-config.json', present: !!docs.config, weight: 5 }, - { name: 'discovery-context.json', present: !!docs.discoveryContext, weight: 10 }, - { name: 'product-brief.md', present: !!docs.productBrief, weight: 20 }, - { name: 'requirements/_index.md', present: !!docs.requirementsIndex, weight: 15 }, - { name: 'REQ-* files', present: docs.requirements.length > 0, weight: 10 }, - { name: 'architecture/_index.md', present: !!docs.architectureIndex, weight: 15 }, - { name: 'ADR-* files', present: docs.adrs.length > 0, weight: 10 }, - { name: 'epics/_index.md', present: !!docs.epicsIndex, weight: 10 }, - { name: 'EPIC-* files', present: docs.epics.length > 0, weight: 5 } - ] - - checks.forEach(check => { - if (check.present) score += check.weight - }) - - return { score, checks, issues: checks.filter(c => !c.present).map(c => `Missing: ${c.name}`) } -} - -// ===== Consistency (25%) ===== -function scoreConsistency(docs) { - let score = 100 - const issues = [] - - // Check session_id consistency across documents - const sessionId = docs.config?.session_id - if (sessionId) { - if (docs.productBrief && !docs.productBrief.includes(sessionId)) { - score -= 15; issues.push('Product Brief missing session_id reference') - } - } - - // Check terminology consistency - // Extract key terms from product brief, verify usage in other docs - if (docs.productBrief && docs.requirementsIndex) { - // Basic term consistency check - const briefTerms = docs.productBrief.match(/##\s+(.+)/g)?.map(h => h.replace('## ', '')) || [] - // Verify heading style consistency - } - - // Check YAML frontmatter format consistency - const docsWithFrontmatter = [docs.productBrief, docs.requirementsIndex, docs.architectureIndex, docs.epicsIndex].filter(Boolean) - const hasFrontmatter = docsWithFrontmatter.map(d => /^---\n[\s\S]+?\n---/.test(d)) - const frontmatterConsistent = hasFrontmatter.every(v => v === hasFrontmatter[0]) - if (!frontmatterConsistent) { - score -= 20; issues.push('Inconsistent YAML frontmatter across documents') - } - - return { score: Math.max(0, score), issues } -} - -// ===== Traceability (25%) ===== -function scoreTraceability(docs) { - let score = 0 - const issues = [] - - // Goals → Requirements tracing - if (docs.productBrief && docs.requirementsIndex) { - // Check if requirements reference product brief goals - const hasGoalRefs = docs.requirements.some(r => /goal|brief|vision/i.test(r)) - if (hasGoalRefs) score += 25 - else issues.push('Requirements lack references to Product Brief goals') - } - - // Requirements → Architecture tracing - if (docs.requirementsIndex && docs.architectureIndex) { - const hasReqRefs = docs.adrs.some(a => /REQ-|requirement/i.test(a)) - if (hasReqRefs) score += 25 - else issues.push('Architecture ADRs lack requirement references') - } - - // Requirements → Stories tracing - if (docs.requirementsIndex && docs.epicsIndex) { - const hasStoryRefs = docs.epics.some(e => /REQ-|requirement/i.test(e)) - if (hasStoryRefs) score += 25 - else issues.push('Epics/Stories lack requirement tracing') - } - - // Full chain check - if (score >= 50) score += 25 // bonus for good overall traceability - - return { score: Math.min(100, score), issues } -} - -// ===== Depth (25%) ===== -function scoreDepth(docs) { - let score = 100 - const issues = [] - - // Check acceptance criteria specificity - const acPattern = /acceptance|criteria|验收/i - const hasSpecificAC = docs.requirements.some(r => acPattern.test(r) && r.length > 200) - if (!hasSpecificAC) { - score -= 25; issues.push('Acceptance criteria may lack specificity') - } - - // Check ADR justification depth - const adrHasAlternatives = docs.adrs.some(a => /alternative|替代|pros|cons/i.test(a)) - if (!adrHasAlternatives && docs.adrs.length > 0) { - score -= 25; issues.push('ADRs lack alternatives analysis') - } - - // Check story estimability - const storySized = docs.epics.some(e => /\b[SMLX]{1,2}\b|Small|Medium|Large/.test(e)) - if (!storySized && docs.epics.length > 0) { - score -= 25; issues.push('Stories lack size estimates') - } - - // Check Mermaid diagrams presence - const hasDiagrams = [docs.architectureIndex, docs.epicsIndex].some(d => d && /```mermaid/.test(d)) - if (!hasDiagrams) { - score -= 10; issues.push('Missing Mermaid diagrams') - } - - return { score: Math.max(0, score), issues } -} - -// Execute all scoring -const completenessResult = scoreCompleteness(documents) -const consistencyResult = scoreConsistency(documents) -const traceabilityResult = scoreTraceability(documents) -const depthResult = scoreDepth(documents) - -scores.completeness = completenessResult.score -scores.consistency = consistencyResult.score -scores.traceability = traceabilityResult.score -scores.depth = depthResult.score - -const overallScore = (scores.completeness + scores.consistency + scores.traceability + scores.depth) / 4 -const qualityGate = overallScore >= 80 ? 'PASS' : overallScore >= 60 ? 'REVIEW' : 'FAIL' -``` - -### Phase 4: Report Generation - -```javascript -// Generate readiness-report.md -const readinessReport = `--- -session_id: ${documents.config?.session_id || 'unknown'} -phase: 6 -document_type: readiness-report -status: complete -generated_at: ${new Date().toISOString()} -version: 1 ---- - -# Readiness Report - -## Quality Scores - -| Dimension | Score | Weight | -|-----------|-------|--------| -| Completeness | ${scores.completeness}% | 25% | -| Consistency | ${scores.consistency}% | 25% | -| Traceability | ${scores.traceability}% | 25% | -| Depth | ${scores.depth}% | 25% | -| **Overall** | **${overallScore.toFixed(1)}%** | **100%** | - -## Quality Gate: ${qualityGate} - -${qualityGate === 'PASS' ? 'All quality criteria met. Specification is ready for execution.' : - qualityGate === 'REVIEW' ? 'Quality is acceptable with some areas needing attention.' : - 'Critical quality issues must be addressed before proceeding.'} - -## Issues Found - -### Completeness Issues -${completenessResult.issues.map(i => `- ${i}`).join('\n') || 'None'} - -### Consistency Issues -${consistencyResult.issues.map(i => `- ${i}`).join('\n') || 'None'} - -### Traceability Issues -${traceabilityResult.issues.map(i => `- ${i}`).join('\n') || 'None'} - -### Depth Issues -${depthResult.issues.map(i => `- ${i}`).join('\n') || 'None'} - -## Document Inventory -${Object.entries(docInventory).map(([k, v]) => `- ${k}: ${v === true ? '✓' : v === false ? '✗' : v}`).join('\n')} - -## Discussion Rounds Completed: ${documents.discussions.length} - -## Recommendations -${allIssues.map(i => `- ${i}`).join('\n') || 'No outstanding issues.'} -` -Write(`${sessionFolder}/readiness-report.md`, readinessReport) - -// Generate spec-summary.md (one-page executive summary) -const specSummary = `--- -session_id: ${documents.config?.session_id || 'unknown'} -phase: 6 -document_type: spec-summary -status: complete -generated_at: ${new Date().toISOString()} -version: 1 ---- - -# Specification Summary - -**Topic**: ${documents.config?.topic || 'N/A'} -**Complexity**: ${documents.config?.complexity || 'N/A'} -**Quality Score**: ${overallScore.toFixed(1)}% (${qualityGate}) -**Discussion Rounds**: ${documents.discussions.length} - -## Key Deliverables -- Product Brief: ${docInventory.productBrief ? '✓' : '✗'} -- Requirements (PRD): ${docInventory.requirements ? `✓ (${documents.requirements.length} items)` : '✗'} -- Architecture: ${docInventory.architecture ? `✓ (${documents.adrs.length} ADRs)` : '✗'} -- Epics & Stories: ${docInventory.epics ? `✓ (${documents.epics.length} epics)` : '✗'} - -## Next Steps -${qualityGate === 'PASS' ? '- Ready for handoff to execution workflows (lite-plan, req-plan, plan, issue:new)' : - qualityGate === 'REVIEW' ? '- Address review items, then proceed to execution' : - '- Fix critical issues before proceeding'} -` -Write(`${sessionFolder}/spec-summary.md`, specSummary) -``` - -### Phase 5: Report to Coordinator - -```javascript -const allIssues = [ - ...completenessResult.issues, - ...consistencyResult.issues, - ...traceabilityResult.issues, - ...depthResult.issues -] - -// Log before SendMessage -mcp__ccw-tools__team_msg({ - operation: "log", team: teamName, - from: "spec-reviewer", to: "coordinator", - type: qualityGate === 'FAIL' ? "fix_required" : "quality_result", - summary: `质量检查 ${qualityGate}: ${overallScore.toFixed(1)}分 (完整性${scores.completeness}/一致性${scores.consistency}/追溯${scores.traceability}/深度${scores.depth})`, - data: { gate: qualityGate, score: overallScore, issues: allIssues } -}) - -SendMessage({ - type: "message", - recipient: "coordinator", - content: `## 质量审查报告 - -**Task**: ${task.subject} -**总分**: ${overallScore.toFixed(1)}% -**Gate**: ${qualityGate} - -### 评分详情 -| 维度 | 分数 | -|------|------| -| 完整性 | ${scores.completeness}% | -| 一致性 | ${scores.consistency}% | -| 可追溯性 | ${scores.traceability}% | -| 深度 | ${scores.depth}% | - -### 问题列表 (${allIssues.length}) -${allIssues.map(i => `- ${i}`).join('\n') || '无问题'} - -### 文档清单 -${Object.entries(docInventory).map(([k, v]) => `- ${k}: ${typeof v === 'boolean' ? (v ? '✓' : '✗') : v}`).join('\n')} - -### 讨论轮次: ${documents.discussions.length} - -### 输出位置 -- 就绪报告: ${sessionFolder}/readiness-report.md -- 执行摘要: ${sessionFolder}/spec-summary.md - -${qualityGate === 'PASS' ? '质量达标,可进入最终讨论轮次 DISCUSS-006。' : - qualityGate === 'REVIEW' ? '质量基本达标但有改进空间,建议在讨论中审查。' : - '质量未达标,建议创建 DRAFT-fix 任务修复关键问题。'}`, - summary: `质量 ${qualityGate}: ${overallScore.toFixed(1)}分` -}) - -// Mark task -if (qualityGate !== 'FAIL') { - TaskUpdate({ taskId: task.id, status: 'completed' }) -} else { - // Keep in_progress, coordinator needs to create fix tasks -} - -// Check for next QUALITY task -const nextTasks = TaskList().filter(t => - t.subject.startsWith('QUALITY-') && - t.owner === 'spec-reviewer' && - t.status === 'pending' && - t.blockedBy.length === 0 -) - -if (nextTasks.length > 0) { - // Continue with next task -> back to Phase 1 -} -``` - -## Error Handling - -| Scenario | Resolution | -|----------|------------| -| No QUALITY-* tasks available | Idle, wait for coordinator assignment | -| Documents missing from session | Score as 0 for completeness, report to coordinator | -| Cannot parse YAML frontmatter | Skip consistency check for that document | -| Session folder not found | Notify coordinator, request session path | -| Scoring produces NaN | Default to 0 for that dimension, log warning | -| Unexpected error | Log error via team_msg, report to coordinator | diff --git a/.claude/commands/team/spec-writer.md b/.claude/commands/team/spec-writer.md deleted file mode 100644 index 8cf0b91e..00000000 --- a/.claude/commands/team/spec-writer.md +++ /dev/null @@ -1,492 +0,0 @@ ---- -name: spec-writer -description: Team spec writer - 产品简报/需求文档/架构文档/史诗故事撰写、模板驱动文档生成 -argument-hint: "" -allowed-tools: SendMessage(*), TaskUpdate(*), TaskList(*), TaskGet(*), TodoWrite(*), Read(*), Write(*), Edit(*), Bash(*), Glob(*), Grep(*), Task(*) -group: team ---- - -# Team Spec Writer Command (/team:spec-writer) - -## Overview - -Team spec-writer role command. Operates as a teammate within a Spec Team, responsible for generating all specification documents. Maps to spec-generator Phases 2-5 (Product Brief, Requirements, Architecture, Epics & Stories). - -**Core capabilities:** -- Task discovery from shared team task list (DRAFT-* tasks) -- Complexity-adaptive writing (Low → direct, Medium/High → multi-CLI analysis) -- Multi-perspective document generation (产品/技术/用户 parallel analysis) -- Template-driven output following spec-generator document standards -- Discussion feedback incorporation for iterative refinement - -## Role Definition - -**Name**: `spec-writer` -**Responsibility**: Load Context → Generate Document → Incorporate Feedback → Report -**Communication**: SendMessage to coordinator only - -## 消息总线 - -每次 SendMessage **前**,必须调用 `mcp__ccw-tools__team_msg` 记录消息: - -```javascript -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-writer", to: "coordinator", type: "", summary: "<摘要>", ref: "<文件路径>" }) -``` - -### 支持的 Message Types - -| Type | 方向 | 触发时机 | 说明 | -|------|------|----------|------| -| `draft_ready` | spec-writer → coordinator | 文档撰写完成 | 附带文档路径和类型 | -| `draft_revision` | spec-writer → coordinator | 文档修订后重新提交 | 说明修改内容 | -| `impl_progress` | spec-writer → coordinator | 长时间撰写进展 | 多文档阶段进度 | -| `error` | spec-writer → coordinator | 遇到不可恢复错误 | 模板缺失、上下文不足等 | - -### 调用示例 - -```javascript -// 文档就绪 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-writer", to: "coordinator", type: "draft_ready", summary: "Product Brief 完成: 8个章节, 3视角合成", ref: ".workflow/.spec-team/session/product-brief.md" }) - -// 文档修订 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-writer", to: "coordinator", type: "draft_revision", summary: "PRD 已按讨论反馈修订: 新增2个NFR, 调整3个优先级" }) - -// 错误上报 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-writer", to: "coordinator", type: "error", summary: "缺少 discovery-context.json, 无法生成 Product Brief" }) -``` - -### CLI 回退 - -当 `mcp__ccw-tools__team_msg` MCP 不可用时,使用 `ccw team` CLI 作为等效回退: - -```javascript -// 回退: 将 MCP 调用替换为 Bash CLI(参数一一对应) -Bash(`ccw team log --team "${teamName}" --from "spec-writer" --to "coordinator" --type "draft_ready" --summary "Product Brief 完成: 8个章节" --ref "${sessionFolder}/product-brief.md" --json`) -``` - -**参数映射**: `team_msg(params)` → `ccw team log --team --from spec-writer --to coordinator --type --summary "" [--ref ] [--data ''] [--json]` - -## Execution Process - -``` -Phase 1: Task Discovery - ├─ TaskList to find unblocked DRAFT-* tasks - ├─ TaskGet to read full task details - └─ TaskUpdate to mark in_progress - -Phase 2: Context & Discussion Loading - ├─ Read session config (spec-config.json) - ├─ Read relevant prior documents and discussion records - ├─ Determine document type from task subject (Brief/PRD/Architecture/Epics) - └─ Load discussion feedback (discuss-*.md) - -Phase 3: Document Generation (type-specific) - ├─ DRAFT-001: Product Brief (multi-CLI parallel analysis) - ├─ DRAFT-002: Requirements/PRD (functional + non-functional + MoSCoW) - ├─ DRAFT-003: Architecture (ADRs + tech stack + diagrams) - └─ DRAFT-004: Epics & Stories (decomposition + dependencies + MVP) - -Phase 4: Self-Validation - ├─ Check all template sections populated - ├─ Verify cross-references to prior documents - └─ Validate YAML frontmatter completeness - -Phase 5: Report to Coordinator - ├─ team_msg log + SendMessage document summary - ├─ TaskUpdate completed - └─ Check for next DRAFT-* task -``` - -## Implementation - -### Phase 1: Task Discovery - -```javascript -// Find assigned DRAFT-* tasks -const tasks = TaskList() -const myTasks = tasks.filter(t => - t.subject.startsWith('DRAFT-') && - t.owner === 'spec-writer' && - t.status === 'pending' && - t.blockedBy.length === 0 -) - -if (myTasks.length === 0) return // idle - -const task = TaskGet({ taskId: myTasks[0].id }) -TaskUpdate({ taskId: task.id, status: 'in_progress' }) -``` - -### Phase 2: Context & Discussion Loading - -```javascript -// Extract session folder from task description -const sessionMatch = task.description.match(/Session:\s*(.+)/) -const sessionFolder = sessionMatch ? sessionMatch[1].trim() : '' - -// Load session config -let specConfig = null -try { specConfig = JSON.parse(Read(`${sessionFolder}/spec-config.json`)) } catch {} - -// Determine document type from task subject -const docType = task.subject.includes('Product Brief') ? 'product-brief' - : task.subject.includes('Requirements') || task.subject.includes('PRD') ? 'requirements' - : task.subject.includes('Architecture') ? 'architecture' - : task.subject.includes('Epics') ? 'epics' - : 'unknown' - -// Load discussion feedback (from preceding DISCUSS task) -const discussionFiles = { - 'product-brief': 'discussions/discuss-001-scope.md', - 'requirements': 'discussions/discuss-002-brief.md', - 'architecture': 'discussions/discuss-003-requirements.md', - 'epics': 'discussions/discuss-004-architecture.md' -} -let discussionFeedback = null -try { - discussionFeedback = Read(`${sessionFolder}/${discussionFiles[docType]}`) -} catch {} - -// Load prior documents -const priorDocs = {} -if (docType !== 'product-brief') { - try { priorDocs.discoveryContext = Read(`${sessionFolder}/discovery-context.json`) } catch {} -} -if (docType === 'requirements' || docType === 'architecture' || docType === 'epics') { - try { priorDocs.productBrief = Read(`${sessionFolder}/product-brief.md`) } catch {} -} -if (docType === 'architecture' || docType === 'epics') { - try { priorDocs.requirementsIndex = Read(`${sessionFolder}/requirements/_index.md`) } catch {} -} -if (docType === 'epics') { - try { priorDocs.architectureIndex = Read(`${sessionFolder}/architecture/_index.md`) } catch {} -} -``` - -### Phase 3: Document Generation - -```javascript -// Route to specific generation logic based on document type -switch (docType) { - case 'product-brief': - await generateProductBrief(sessionFolder, specConfig, discussionFeedback) - break - case 'requirements': - await generateRequirements(sessionFolder, specConfig, priorDocs, discussionFeedback) - break - case 'architecture': - await generateArchitecture(sessionFolder, specConfig, priorDocs, discussionFeedback) - break - case 'epics': - await generateEpics(sessionFolder, specConfig, priorDocs, discussionFeedback) - break -} -``` - -#### DRAFT-001: Product Brief (Multi-Perspective Analysis) - -```javascript -async function generateProductBrief(sessionFolder, config, discussionFeedback) { - const discoveryContext = JSON.parse(Read(`${sessionFolder}/discovery-context.json`)) - const topic = config?.topic || discoveryContext.seed_analysis.problem_statement - - // 进展通知 - mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-writer", to: "coordinator", type: "impl_progress", summary: "开始 Product Brief 多视角分析 (1/3)" }) - - // Launch 3 parallel CLI analyses for multi-perspective synthesis - // 1. Product perspective (Gemini) - Bash({ - command: `ccw cli -p "PURPOSE: Analyze from PRODUCT perspective for specification. -TASK: • Market fit analysis • Value proposition • Success criteria • Competitive landscape -TOPIC: ${topic} -CONTEXT: Discovery findings: ${JSON.stringify(discoveryContext.seed_analysis)} -${discussionFeedback ? `DISCUSSION FEEDBACK: ${discussionFeedback}` : ''} -EXPECTED: Structured product analysis (vision, problem, goals, scope, constraints) -CONSTRAINTS: Focus on product strategy" --tool gemini --mode analysis`, - run_in_background: true - }) - - // 2. Technical perspective (Codex) - Bash({ - command: `ccw cli -p "PURPOSE: Analyze from TECHNICAL perspective for specification. -TASK: • Technical feasibility • Architecture constraints • Tech stack recommendations • Implementation risks -TOPIC: ${topic} -CONTEXT: Discovery findings: ${JSON.stringify(discoveryContext.seed_analysis)} -${discoveryContext.codebase_context ? `CODEBASE: ${JSON.stringify(discoveryContext.codebase_context)}` : ''} -EXPECTED: Technical feasibility assessment -CONSTRAINTS: Focus on engineering perspective" --tool codex --mode analysis`, - run_in_background: true - }) - - // 3. User perspective (Claude) - Bash({ - command: `ccw cli -p "PURPOSE: Analyze from USER perspective for specification. -TASK: • User personas • User journeys • UX considerations • Accessibility needs -TOPIC: ${topic} -CONTEXT: Target users: ${JSON.stringify(discoveryContext.seed_analysis.target_users)} -EXPECTED: User experience analysis (personas, journeys, pain points) -CONSTRAINTS: Focus on end-user perspective" --tool claude --mode analysis`, - run_in_background: true - }) - - // Wait for all 3 analyses to complete, then synthesize - - // Generate product-brief.md with YAML frontmatter - const brief = `--- -session_id: ${config.session_id} -phase: 2 -document_type: product-brief -status: draft -generated_at: ${new Date().toISOString()} -version: 1 -dependencies: - - discovery-context.json - - discuss-001-scope.md ---- - -# Product Brief: ${topic} - -## Vision -${productPerspective.vision} - -## Problem Statement -${discoveryContext.seed_analysis.problem_statement} - -## Target Users -${personas.map(p => `### ${p.name}\n- **Role**: ${p.role}\n- **Pain Points**: ${p.painPoints}\n- **Goals**: ${p.goals}`).join('\n\n')} - -## Goals & Success Metrics -${productPerspective.goals} - -## Scope -### In Scope -${productPerspective.inScope} - -### Out of Scope -${productPerspective.outOfScope} - -## Technical Feasibility -${technicalPerspective.summary} - -## User Experience Considerations -${userPerspective.summary} - -## Multi-Perspective Synthesis -### Convergent Themes -${synthesis.convergent} - -### Divergent Views -${synthesis.divergent} - -### Discussion Feedback Integration -${discussionFeedback ? discussionFeedback.summary : 'N/A (first draft)'} - -## Constraints -${discoveryContext.seed_analysis.constraints.map(c => `- ${c}`).join('\n')} - -## Open Questions -${openQuestions.map(q => `- ${q}`).join('\n')} -` - Write(`${sessionFolder}/product-brief.md`, brief) -} -``` - -#### DRAFT-002: Requirements/PRD - -```javascript -async function generateRequirements(sessionFolder, config, priorDocs, discussionFeedback) { - // Use Gemini CLI to expand requirements from product brief - Bash({ - command: `ccw cli -p "PURPOSE: Generate functional and non-functional requirements from Product Brief. -TASK: -• Extract functional requirements (REQ-NNN format) with user stories and acceptance criteria -• Generate non-functional requirements (NFR-{type}-NNN) for Performance/Security/Scalability/Usability -• Apply MoSCoW prioritization (Must/Should/Could/Won't) -• Create traceability matrix to Product Brief goals -CONTEXT: Product Brief: ${priorDocs.productBrief} -${discussionFeedback ? `DISCUSSION FEEDBACK: ${discussionFeedback}` : ''} -EXPECTED: Structured requirements list in JSON -CONSTRAINTS: Each requirement needs ID, title, user story, 2-4 acceptance criteria" --tool gemini --mode analysis`, - run_in_background: true - }) - - // Generate requirements/ directory structure - Bash(`mkdir -p ${sessionFolder}/requirements`) - - // Write _index.md + individual REQ-*.md + NFR-*.md files - // Following spec-generator templates/requirements-prd.md format -} -``` - -#### DRAFT-003: Architecture - -```javascript -async function generateArchitecture(sessionFolder, config, priorDocs, discussionFeedback) { - // Generate architecture via Gemini - Bash({ - command: `ccw cli -p "PURPOSE: Design system architecture based on requirements. -TASK: -• Select architecture style with justification -• Define core components and responsibilities -• Create component interaction diagram (Mermaid) -• Choose tech stack (languages, frameworks, databases, infrastructure) -• Generate 2-4 ADRs with alternatives and pros/cons -• Design data model (Mermaid erDiagram) -• Define security architecture -CONTEXT: Requirements: ${priorDocs.requirementsIndex} -Product Brief: ${priorDocs.productBrief} -${discussionFeedback ? `DISCUSSION FEEDBACK: ${discussionFeedback}` : ''} -EXPECTED: Complete architecture document -CONSTRAINTS: Include ADRs with alternatives" --tool gemini --mode analysis`, - run_in_background: true - }) - - // Challenge architecture via Codex - Bash({ - command: `ccw cli -p "PURPOSE: Challenge and review proposed architecture. -TASK: • Review ADR alternatives • Identify bottlenecks • Assess security gaps • Rate quality (1-5) -CONTEXT: [architecture output from above] -EXPECTED: Architecture review with ratings" --tool codex --mode analysis`, - run_in_background: true - }) - - // Generate architecture/ directory - Bash(`mkdir -p ${sessionFolder}/architecture`) - - // Write _index.md + ADR-*.md files -} -``` - -#### DRAFT-004: Epics & Stories - -```javascript -async function generateEpics(sessionFolder, config, priorDocs, discussionFeedback) { - // Decompose via Gemini - Bash({ - command: `ccw cli -p "PURPOSE: Decompose requirements into Epics and Stories. -TASK: -• Group 3-7 logical Epics by domain or user journey -• Generate 2-5 Stories per Epic (user story format) -• Create cross-Epic dependency map (Mermaid) -• Define MVP scope with done criteria -• Recommend execution order -CONTEXT: Requirements: ${priorDocs.requirementsIndex} -Architecture: ${priorDocs.architectureIndex} -Product Brief: ${priorDocs.productBrief} -${discussionFeedback ? `DISCUSSION FEEDBACK: ${discussionFeedback}` : ''} -EXPECTED: Epic/Story decomposition with dependencies -CONSTRAINTS: Each story needs AC, size estimate (S/M/L/XL), requirement tracing" --tool gemini --mode analysis`, - run_in_background: true - }) - - // Generate epics/ directory - Bash(`mkdir -p ${sessionFolder}/epics`) - - // Write _index.md + EPIC-*.md files -} -``` - -### Phase 4: Self-Validation - -```javascript -// Validate generated document -const validationChecks = { - has_frontmatter: false, - sections_complete: false, - cross_references: false, - discussion_integrated: false -} - -// Check YAML frontmatter -const docContent = Read(`${sessionFolder}/${outputPath}`) -validationChecks.has_frontmatter = /^---\n[\s\S]+?\n---/.test(docContent) - -// Check required sections based on doc type -const requiredSections = { - 'product-brief': ['Vision', 'Problem Statement', 'Target Users', 'Goals', 'Scope'], - 'requirements': ['_index.md', 'REQ-'], - 'architecture': ['_index.md', 'ADR-'], - 'epics': ['_index.md', 'EPIC-'] -} -// Verify all sections present - -// Check cross-references to prior documents -validationChecks.cross_references = docContent.includes('session_id') - -// Check discussion feedback integration -validationChecks.discussion_integrated = !discussionFeedback || docContent.includes('Discussion') - -const allValid = Object.values(validationChecks).every(v => v) -``` - -### Phase 5: Report to Coordinator - -```javascript -const docTypeLabel = { - 'product-brief': 'Product Brief', - 'requirements': 'Requirements/PRD', - 'architecture': 'Architecture Document', - 'epics': 'Epics & Stories' -} - -// Log before SendMessage -mcp__ccw-tools__team_msg({ - operation: "log", team: teamName, - from: "spec-writer", to: "coordinator", - type: "draft_ready", - summary: `${docTypeLabel[docType]} 完成: ${allValid ? '验证通过' : '部分验证失败'}`, - ref: `${sessionFolder}/${outputPath}` -}) - -SendMessage({ - type: "message", - recipient: "coordinator", - content: `## 文档撰写结果 - -**Task**: ${task.subject} -**文档类型**: ${docTypeLabel[docType]} -**验证状态**: ${allValid ? 'PASS' : 'PARTIAL'} - -### 文档摘要 -${documentSummary} - -### 讨论反馈整合 -${discussionFeedback ? '已整合前序讨论反馈' : '首次撰写(无前序讨论反馈)'} - -### 自验证结果 -${Object.entries(validationChecks).map(([k, v]) => `- ${k}: ${v ? '✓' : '✗'}`).join('\n')} - -### 输出位置 -${sessionFolder}/${outputPath} - -文档已就绪,可进入讨论轮次。`, - summary: `${docTypeLabel[docType]} 就绪` -}) - -// Mark task completed -TaskUpdate({ taskId: task.id, status: 'completed' }) - -// Check for next DRAFT task -const nextTasks = TaskList().filter(t => - t.subject.startsWith('DRAFT-') && - t.owner === 'spec-writer' && - t.status === 'pending' && - t.blockedBy.length === 0 -) - -if (nextTasks.length > 0) { - // Continue with next task -> back to Phase 1 -} -``` - -## Error Handling - -| Scenario | Resolution | -|----------|------------| -| No DRAFT-* tasks available | Idle, wait for coordinator assignment | -| Prior document not found | Notify coordinator, request prerequisite | -| CLI analysis failure | Retry with fallback tool, then direct generation | -| Template sections incomplete | Generate best-effort, note gaps in report | -| Discussion feedback contradicts prior docs | Note conflict in document, flag for next discussion | -| Session folder missing | Notify coordinator, request session path | -| Unexpected error | Log error via team_msg, report to coordinator | diff --git a/.claude/skills/team-lifecycle/SKILL.md b/.claude/skills/team-lifecycle/SKILL.md new file mode 100644 index 00000000..828b28a4 --- /dev/null +++ b/.claude/skills/team-lifecycle/SKILL.md @@ -0,0 +1,320 @@ +--- +name: team-lifecycle +description: Unified team skill for full lifecycle - spec/impl/test. All roles invoke this skill with --role arg for role-specific execution. +allowed-tools: TeamCreate(*), TeamDelete(*), SendMessage(*), TaskCreate(*), TaskUpdate(*), TaskList(*), TaskGet(*), Task(*), AskUserQuestion(*), TodoWrite(*), Read(*), Write(*), Edit(*), Bash(*), Glob(*), Grep(*) +--- + +# Team Lifecycle + +Unified team skill covering specification, implementation, testing, and review. All team members invoke this skill with `--role=xxx` to route to role-specific execution. + +## Architecture Overview + +``` +┌─────────────────────────────────────────────────┐ +│ Skill(skill="team-lifecycle", args="--role=xxx") │ +└───────────────────┬─────────────────────────────┘ + │ Role Router + ┌───────┬───────┼───────┬───────┬───────┬───────┬───────┐ + ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ +┌─────────┐┌───────┐┌──────┐┌──────────┐┌───────┐┌────────┐┌──────┐┌────────┐ +│coordinator││analyst││writer││discussant││planner││executor││tester││reviewer│ +│ roles/ ││roles/ ││roles/││ roles/ ││roles/ ││ roles/ ││roles/││ roles/ │ +└─────────┘└───────┘└──────┘└──────────┘└───────┘└────────┘└──────┘└────────┘ +``` + +## Role Router + +### Input Parsing + +Parse `$ARGUMENTS` to extract `--role`: + +```javascript +const args = "$ARGUMENTS" +const roleMatch = args.match(/--role[=\s]+(\w+)/) + +if (!roleMatch) { + throw new Error("Missing --role argument. Available roles: coordinator, analyst, writer, discussant, planner, executor, tester, reviewer") +} + +const role = roleMatch[1] +const teamName = args.match(/--team[=\s]+([\w-]+)/)?.[1] || "lifecycle" +``` + +### Role Dispatch + +```javascript +const VALID_ROLES = { + "coordinator": { file: "roles/coordinator.md", prefix: null }, + "analyst": { file: "roles/analyst.md", prefix: "RESEARCH" }, + "writer": { file: "roles/writer.md", prefix: "DRAFT" }, + "discussant": { file: "roles/discussant.md", prefix: "DISCUSS" }, + "planner": { file: "roles/planner.md", prefix: "PLAN" }, + "executor": { file: "roles/executor.md", prefix: "IMPL" }, + "tester": { file: "roles/tester.md", prefix: "TEST" }, + "reviewer": { file: "roles/reviewer.md", prefix: ["REVIEW", "QUALITY"] } +} + +if (!VALID_ROLES[role]) { + throw new Error(`Unknown role: ${role}. Available: ${Object.keys(VALID_ROLES).join(', ')}`) +} + +// Read and execute role-specific logic +Read(VALID_ROLES[role].file) +// → Execute the 5-phase process defined in that file +``` + +### Available Roles + +| Role | Task Prefix | Responsibility | Role File | +|------|-------------|----------------|-----------| +| `coordinator` | N/A | Pipeline orchestration, requirement clarification, task dispatch | [roles/coordinator.md](roles/coordinator.md) | +| `analyst` | RESEARCH-* | Seed analysis, codebase exploration, context gathering | [roles/analyst.md](roles/analyst.md) | +| `writer` | DRAFT-* | Product Brief / PRD / Architecture / Epics generation | [roles/writer.md](roles/writer.md) | +| `discussant` | DISCUSS-* | Multi-perspective critique, consensus building | [roles/discussant.md](roles/discussant.md) | +| `planner` | PLAN-* | Multi-angle exploration, structured planning | [roles/planner.md](roles/planner.md) | +| `executor` | IMPL-* | Code implementation following plans | [roles/executor.md](roles/executor.md) | +| `tester` | TEST-* | Adaptive test-fix cycles, quality gates | [roles/tester.md](roles/tester.md) | +| `reviewer` | `REVIEW-*` + `QUALITY-*` | Code review + Spec quality validation (auto-switch by prefix) | [roles/reviewer.md](roles/reviewer.md) | + +## Shared Infrastructure + +### Message Bus (All Roles) + +Every SendMessage **before**, must call `mcp__ccw-tools__team_msg` to log: + +```javascript +mcp__ccw-tools__team_msg({ + operation: "log", + team: teamName, + from: role, + to: "coordinator", + type: "", + summary: "", + ref: "" +}) +``` + +**Message types by role**: + +| Role | Types | +|------|-------| +| coordinator | `plan_approved`, `plan_revision`, `task_unblocked`, `fix_required`, `error`, `shutdown` | +| analyst | `research_ready`, `research_progress`, `error` | +| writer | `draft_ready`, `draft_revision`, `impl_progress`, `error` | +| discussant | `discussion_ready`, `discussion_blocked`, `impl_progress`, `error` | +| planner | `plan_ready`, `plan_revision`, `impl_progress`, `error` | +| executor | `impl_complete`, `impl_progress`, `error` | +| tester | `test_result`, `impl_progress`, `fix_required`, `error` | +| reviewer | `review_result`, `quality_result`, `fix_required`, `error` | + +### CLI Fallback + +When `mcp__ccw-tools__team_msg` MCP is unavailable: + +```javascript +Bash(`ccw team log --team "${teamName}" --from "${role}" --to "coordinator" --type "" --summary "" --json`) +Bash(`ccw team list --team "${teamName}" --last 10 --json`) +Bash(`ccw team status --team "${teamName}" --json`) +``` + +### Task Lifecycle (All Worker Roles) + +```javascript +// Standard task lifecycle every worker role follows +// Phase 1: Discovery +const tasks = TaskList() +const prefixes = Array.isArray(VALID_ROLES[role].prefix) ? VALID_ROLES[role].prefix : [VALID_ROLES[role].prefix] +const myTasks = tasks.filter(t => + prefixes.some(p => t.subject.startsWith(`${p}-`)) && + t.owner === role && + t.status === 'pending' && + t.blockedBy.length === 0 +) +if (myTasks.length === 0) return // idle +const task = TaskGet({ taskId: myTasks[0].id }) +TaskUpdate({ taskId: task.id, status: 'in_progress' }) + +// Phase 2-4: Role-specific (see roles/{role}.md) + +// Phase 5: Report + Loop +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: role, to: "coordinator", type: "...", summary: "..." }) +SendMessage({ type: "message", recipient: "coordinator", content: "...", summary: "..." }) +TaskUpdate({ taskId: task.id, status: 'completed' }) +// Check for next task → back to Phase 1 +``` + +## Three-Mode Pipeline + +``` +Spec-only: + RESEARCH-001 → DISCUSS-001 → DRAFT-001 → DISCUSS-002 + → DRAFT-002 → DISCUSS-003 → DRAFT-003 → DISCUSS-004 + → DRAFT-004 → DISCUSS-005 → QUALITY-001 → DISCUSS-006 + +Impl-only: + PLAN-001 → IMPL-001 → TEST-001 + REVIEW-001 + +Full-lifecycle: + [Spec pipeline] → PLAN-001(blockedBy: DISCUSS-006) → IMPL-001 → TEST-001 + REVIEW-001 +``` + +## Coordinator Spawn Template + +When coordinator creates teammates: + +```javascript +TeamCreate({ team_name: teamName }) + +// Analyst (spec-only / full) +Task({ + subagent_type: "general-purpose", + team_name: teamName, + name: "analyst", + prompt: `你是 team "${teamName}" 的 ANALYST。 +当你收到 RESEARCH-* 任务时,调用 Skill(skill="team-lifecycle", args="--role=analyst") 执行。 +当前需求: ${taskDescription} +约束: ${constraints} +Session: ${sessionFolder} + +## 消息总线(必须) +每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。 + +工作流程: +1. TaskList → 找到 RESEARCH-* 任务 +2. Skill(skill="team-lifecycle", args="--role=analyst") 执行 +3. team_msg log + SendMessage 结果给 coordinator +4. TaskUpdate completed → 检查下一个任务` +}) + +// Writer (spec-only / full) +Task({ + subagent_type: "general-purpose", + team_name: teamName, + name: "writer", + prompt: `你是 team "${teamName}" 的 WRITER。 +当你收到 DRAFT-* 任务时,调用 Skill(skill="team-lifecycle", args="--role=writer") 执行。 +当前需求: ${taskDescription} +Session: ${sessionFolder} + +## 消息总线(必须) +每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。 + +工作流程: +1. TaskList → 找到 DRAFT-* 任务 +2. Skill(skill="team-lifecycle", args="--role=writer") 执行 +3. team_msg log + SendMessage 结果给 coordinator +4. TaskUpdate completed → 检查下一个任务` +}) + +// Discussant (spec-only / full) +Task({ + subagent_type: "general-purpose", + team_name: teamName, + name: "discussant", + prompt: `你是 team "${teamName}" 的 DISCUSSANT。 +当你收到 DISCUSS-* 任务时,调用 Skill(skill="team-lifecycle", args="--role=discussant") 执行。 +当前需求: ${taskDescription} +Session: ${sessionFolder} +讨论深度: ${discussionDepth} + +## 消息总线(必须) +每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。 + +工作流程: +1. TaskList → 找到 DISCUSS-* 任务 +2. Skill(skill="team-lifecycle", args="--role=discussant") 执行 +3. team_msg log + SendMessage 结果给 coordinator +4. TaskUpdate completed → 检查下一个任务` +}) + +// Planner (impl-only / full) +Task({ + subagent_type: "general-purpose", + team_name: teamName, + name: "planner", + prompt: `你是 team "${teamName}" 的 PLANNER。 +当你收到 PLAN-* 任务时,调用 Skill(skill="team-lifecycle", args="--role=planner") 执行。 +当前需求: ${taskDescription} +约束: ${constraints} + +## 消息总线(必须) +每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。 + +工作流程: +1. TaskList → 找到 PLAN-* 任务 +2. Skill(skill="team-lifecycle", args="--role=planner") 执行 +3. team_msg log + SendMessage 结果给 coordinator +4. TaskUpdate completed → 检查下一个任务` +}) + +// Executor (impl-only / full) +Task({ + subagent_type: "general-purpose", + team_name: teamName, + name: "executor", + prompt: `你是 team "${teamName}" 的 EXECUTOR。 +当你收到 IMPL-* 任务时,调用 Skill(skill="team-lifecycle", args="--role=executor") 执行。 +当前需求: ${taskDescription} +约束: ${constraints} + +## 消息总线(必须) +每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。 + +工作流程: +1. TaskList → 找到 IMPL-* 任务 +2. Skill(skill="team-lifecycle", args="--role=executor") 执行 +3. team_msg log + SendMessage 结果给 coordinator +4. TaskUpdate completed → 检查下一个任务` +}) + +// Tester (impl-only / full) +Task({ + subagent_type: "general-purpose", + team_name: teamName, + name: "tester", + prompt: `你是 team "${teamName}" 的 TESTER。 +当你收到 TEST-* 任务时,调用 Skill(skill="team-lifecycle", args="--role=tester") 执行。 +当前需求: ${taskDescription} +约束: ${constraints} + +## 消息总线(必须) +每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。 + +工作流程: +1. TaskList → 找到 TEST-* 任务 +2. Skill(skill="team-lifecycle", args="--role=tester") 执行 +3. team_msg log + SendMessage 结果给 coordinator +4. TaskUpdate completed → 检查下一个任务` +}) + +// Reviewer (all modes) +Task({ + subagent_type: "general-purpose", + team_name: teamName, + name: "reviewer", + prompt: `你是 team "${teamName}" 的 REVIEWER。 +当你收到 REVIEW-* 或 QUALITY-* 任务时,调用 Skill(skill="team-lifecycle", args="--role=reviewer") 执行。 +- REVIEW-* → 代码审查逻辑 +- QUALITY-* → 规格质量检查逻辑 +当前需求: ${taskDescription} + +## 消息总线(必须) +每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。 + +工作流程: +1. TaskList → 找到 REVIEW-* 或 QUALITY-* 任务 +2. Skill(skill="team-lifecycle", args="--role=reviewer") 执行 +3. team_msg log + SendMessage 结果给 coordinator +4. TaskUpdate completed → 检查下一个任务` +}) +``` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| Unknown --role value | Error with available role list | +| Missing --role arg | Error with usage hint | +| Role file not found | Error with expected path | +| Task prefix conflict | Log warning, proceed | diff --git a/.claude/skills/team-lifecycle/roles/analyst.md b/.claude/skills/team-lifecycle/roles/analyst.md new file mode 100644 index 00000000..4e034911 --- /dev/null +++ b/.claude/skills/team-lifecycle/roles/analyst.md @@ -0,0 +1,207 @@ +# Role: analyst + +Seed analysis, codebase exploration, and multi-dimensional context gathering. Maps to spec-generator Phase 1 (Discovery). + +## Role Identity + +- **Name**: `analyst` +- **Task Prefix**: `RESEARCH-*` +- **Responsibility**: Seed Analysis → Codebase Exploration → Context Packaging → Report +- **Communication**: SendMessage to coordinator only + +## Message Types + +| Type | Direction | Trigger | Description | +|------|-----------|---------|-------------| +| `research_ready` | analyst → coordinator | Research complete | With discovery-context.json path and dimension summary | +| `research_progress` | analyst → coordinator | Long research progress | Intermediate progress update | +| `error` | analyst → coordinator | Unrecoverable error | Codebase access failure, CLI timeout, etc. | + +## Message Bus + +Before every `SendMessage`, MUST call `mcp__ccw-tools__team_msg` to log: + +```javascript +// Research complete +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "analyst", to: "coordinator", type: "research_ready", summary: "Research done: 5 exploration dimensions", ref: `${sessionFolder}/discovery-context.json` }) + +// Error report +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "analyst", to: "coordinator", type: "error", summary: "Codebase access failed" }) +``` + +### CLI Fallback + +When `mcp__ccw-tools__team_msg` MCP is unavailable: + +```javascript +Bash(`ccw team log --team "${teamName}" --from "analyst" --to "coordinator" --type "research_ready" --summary "Research done" --ref "${sessionFolder}/discovery-context.json" --json`) +``` + +## Execution (5-Phase) + +### Phase 1: Task Discovery + +```javascript +const tasks = TaskList() +const myTasks = tasks.filter(t => + t.subject.startsWith('RESEARCH-') && + t.owner === 'analyst' && + t.status === 'pending' && + t.blockedBy.length === 0 +) + +if (myTasks.length === 0) return // idle + +const task = TaskGet({ taskId: myTasks[0].id }) +TaskUpdate({ taskId: task.id, status: 'in_progress' }) +``` + +### Phase 2: Seed Analysis + +```javascript +// Extract session folder from task description +const sessionMatch = task.description.match(/Session:\s*(.+)/) +const sessionFolder = sessionMatch ? sessionMatch[1].trim() : '.workflow/.spec-team/default' + +// Parse topic from task description +const topicLines = task.description.split('\n').filter(l => !l.startsWith('Session:') && !l.startsWith('输出:') && l.trim()) +const topic = topicLines[0] || task.subject.replace('RESEARCH-001: ', '') + +// Use Gemini CLI for seed analysis +Bash({ + command: `ccw cli -p "PURPOSE: Analyze the following topic/idea and extract structured seed information for specification generation. +TASK: +• Extract problem statement (what problem does this solve) +• Identify target users and their pain points +• Determine domain and industry context +• List constraints and assumptions +• Identify 3-5 exploration dimensions for deeper research +• Assess complexity (simple/moderate/complex) + +TOPIC: ${topic} + +MODE: analysis +CONTEXT: @**/* +EXPECTED: JSON output with fields: problem_statement, target_users[], domain, constraints[], exploration_dimensions[], complexity_assessment +CONSTRAINTS: Output as valid JSON" --tool gemini --mode analysis --rule analysis-analyze-technical-document`, + run_in_background: true +}) +// Wait for CLI result, then parse +``` + +### Phase 3: Codebase Exploration (conditional) + +```javascript +// Check if there's an existing codebase to explore +const hasProject = Bash(`test -f package.json || test -f Cargo.toml || test -f pyproject.toml || test -f go.mod; echo $?`) + +if (hasProject === '0') { + mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "analyst", to: "coordinator", type: "research_progress", summary: "种子分析完成, 开始代码库探索" }) + + // Explore codebase using ACE search + const archSearch = mcp__ace-tool__search_context({ + project_root_path: projectRoot, + query: `Architecture patterns, main modules, entry points for: ${topic}` + }) + + // Detect tech stack from package files + // Explore existing patterns and integration points + + var codebaseContext = { tech_stack, architecture_patterns, existing_conventions, integration_points, constraints_from_codebase: [] } +} else { + var codebaseContext = null +} +``` + +### Phase 4: Context Packaging + +```javascript +// Generate spec-config.json +const specConfig = { + session_id: `SPEC-${topicSlug}-${dateStr}`, + topic: topic, + status: "research_complete", + complexity: seedAnalysis.complexity_assessment || "moderate", + phases_completed: ["discovery"], + created_at: new Date().toISOString(), + session_folder: sessionFolder, + discussion_depth: task.description.match(/讨论深度:\s*(.+)/)?.[1] || "standard" +} +Write(`${sessionFolder}/spec-config.json`, JSON.stringify(specConfig, null, 2)) + +// Generate discovery-context.json +const discoveryContext = { + session_id: specConfig.session_id, + phase: 1, + document_type: "discovery-context", + status: "complete", + generated_at: new Date().toISOString(), + seed_analysis: { + problem_statement: seedAnalysis.problem_statement, + target_users: seedAnalysis.target_users, + domain: seedAnalysis.domain, + constraints: seedAnalysis.constraints, + exploration_dimensions: seedAnalysis.exploration_dimensions, + complexity: seedAnalysis.complexity_assessment + }, + codebase_context: codebaseContext, + recommendations: { focus_areas: [], risks: [], open_questions: [] } +} +Write(`${sessionFolder}/discovery-context.json`, JSON.stringify(discoveryContext, null, 2)) +``` + +### Phase 5: Report to Coordinator + +```javascript +const dimensionCount = discoveryContext.seed_analysis.exploration_dimensions?.length || 0 +const hasCodebase = codebaseContext !== null + +mcp__ccw-tools__team_msg({ + operation: "log", team: teamName, + from: "analyst", to: "coordinator", + type: "research_ready", + summary: `研究完成: ${dimensionCount}个探索维度, ${hasCodebase ? '有' : '无'}代码库上下文, 复杂度=${specConfig.complexity}`, + ref: `${sessionFolder}/discovery-context.json` +}) + +SendMessage({ + type: "message", + recipient: "coordinator", + content: `## 研究分析结果 + +**Task**: ${task.subject} +**复杂度**: ${specConfig.complexity} +**代码库**: ${hasCodebase ? '已检测到现有项目' : '全新项目'} + +### 问题陈述 +${discoveryContext.seed_analysis.problem_statement} + +### 目标用户 +${(discoveryContext.seed_analysis.target_users || []).map(u => '- ' + u).join('\n')} + +### 探索维度 +${(discoveryContext.seed_analysis.exploration_dimensions || []).map((d, i) => (i+1) + '. ' + d).join('\n')} + +### 输出位置 +- Config: ${sessionFolder}/spec-config.json +- Context: ${sessionFolder}/discovery-context.json + +研究已就绪,可进入讨论轮次 DISCUSS-001。`, + summary: `研究就绪: ${dimensionCount}维度, ${specConfig.complexity}` +}) + +TaskUpdate({ taskId: task.id, status: 'completed' }) + +// Check for next RESEARCH task → back to Phase 1 +``` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| No RESEARCH-* tasks available | Idle, wait for coordinator assignment | +| Gemini CLI analysis failure | Fallback to direct Claude analysis without CLI | +| Codebase detection failed | Continue as new project (no codebase context) | +| Session folder cannot be created | Notify coordinator, request alternative path | +| Topic too vague for analysis | Report to coordinator with clarification questions | +| Unexpected error | Log error via team_msg, report to coordinator | diff --git a/.claude/skills/team-lifecycle/roles/coordinator.md b/.claude/skills/team-lifecycle/roles/coordinator.md new file mode 100644 index 00000000..77532870 --- /dev/null +++ b/.claude/skills/team-lifecycle/roles/coordinator.md @@ -0,0 +1,326 @@ +# Role: coordinator + +Team lifecycle coordinator. Orchestrates the full pipeline across three modes: spec-only, impl-only, and full-lifecycle. Handles requirement clarification, team creation, task chain management, cross-phase coordination, and result reporting. + +## Role Identity + +- **Name**: `coordinator` +- **Task Prefix**: N/A (coordinator creates tasks, doesn't receive them) +- **Responsibility**: Orchestration +- **Communication**: SendMessage to all teammates + +## Message Types + +| Type | Direction | Trigger | Description | +|------|-----------|---------|-------------| +| `plan_approved` | coordinator → planner | Plan reviewed and accepted | Planner can mark task completed | +| `plan_revision` | coordinator → planner | Plan needs changes | Feedback with required changes | +| `task_unblocked` | coordinator → any | Dependency resolved | Notify worker of available task | +| `fix_required` | coordinator → executor/writer | Review/Quality found issues | Create fix task | +| `error` | coordinator → all | Critical system error | Escalation to user | +| `shutdown` | coordinator → all | Team being dissolved | Clean shutdown signal | + +## Execution + +### Phase 1: Requirement Clarification + +Parse `$ARGUMENTS` to extract `--team-name` and task description. + +```javascript +const args = "$ARGUMENTS" +const teamNameMatch = args.match(/--team-name[=\s]+([\w-]+)/) +const teamName = teamNameMatch ? teamNameMatch[1] : `lifecycle-${Date.now().toString(36)}` +const taskDescription = args.replace(/--team-name[=\s]+[\w-]+/, '').replace(/--role[=\s]+\w+/, '').trim() +``` + +Use AskUserQuestion to collect mode and constraints: + +```javascript +AskUserQuestion({ + questions: [ + { + question: "选择工作模式:", + header: "Mode", + multiSelect: false, + options: [ + { label: "spec-only", description: "仅生成规格文档(研究→讨论→撰写→质量检查)" }, + { label: "impl-only", description: "仅实现代码(规划→实现→测试+审查)" }, + { label: "full-lifecycle", description: "完整生命周期(规格→实现→测试+审查)" } + ] + }, + { + question: "MVP 范围:", + header: "Scope", + multiSelect: false, + options: [ + { label: "最小可行", description: "核心功能优先" }, + { label: "功能完整", description: "覆盖主要用例" }, + { label: "全面实现", description: "包含边缘场景和优化" } + ] + } + ] +}) + +// Spec/Full 模式追加收集 +if (mode === 'spec-only' || mode === 'full-lifecycle') { + AskUserQuestion({ + questions: [ + { + question: "重点领域:", + header: "Focus", + multiSelect: false, + options: [ + { label: "产品定义", description: "聚焦用户需求和产品定位" }, + { label: "技术架构", description: "聚焦技术选型和系统设计" }, + { label: "全面规格", description: "均衡覆盖产品+技术" } + ] + }, + { + question: "讨论深度:", + header: "Depth", + multiSelect: false, + options: [ + { label: "快速共识", description: "每轮讨论简短聚焦,快速推进" }, + { label: "深度讨论", description: "每轮多视角深入分析" }, + { label: "全面辩论", description: "4个维度全覆盖,严格共识门控" } + ] + } + ] + }) +} +``` + +Simple tasks can skip clarification. + +### Phase 2: Create Team + Spawn Workers + +```javascript +TeamCreate({ team_name: teamName }) + +// Session setup +const topicSlug = taskDescription.toLowerCase().replace(/[^a-z0-9]+/g, '-').substring(0, 40) +const dateStr = new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString().substring(0, 10) +const specSessionFolder = `.workflow/.spec-team/${topicSlug}-${dateStr}` +const implSessionFolder = `.workflow/.team-plan/${topicSlug}-${dateStr}` + +if (mode === 'spec-only' || mode === 'full-lifecycle') { + Bash(`mkdir -p ${specSessionFolder}/discussions`) +} +if (mode === 'impl-only' || mode === 'full-lifecycle') { + Bash(`mkdir -p ${implSessionFolder}`) +} +``` + +**Conditional spawn based on mode** (see SKILL.md Coordinator Spawn Template for full prompts): + +| Mode | Spawned Workers | +|------|-----------------| +| spec-only | analyst, writer, discussant, reviewer (4) | +| impl-only | planner, executor, tester, reviewer (4) | +| full-lifecycle | analyst, writer, discussant, planner, executor, tester, reviewer (7) | + +Each worker receives a prompt that tells it to invoke `Skill(skill="team-lifecycle", args="--role=")` when receiving tasks. + +### Phase 3: Create Task Chain + +Task chain creation depends on the selected mode. + +#### Spec-only Task Chain + +```javascript +// RESEARCH Phase +TaskCreate({ subject: "RESEARCH-001: 主题发现与上下文研究", description: `${taskDescription}\n\nSession: ${specSessionFolder}\n输出: ${specSessionFolder}/spec-config.json + discovery-context.json`, activeForm: "研究中" }) +TaskUpdate({ taskId: researchId, owner: "analyst" }) + +// DISCUSS-001: 范围讨论 (blockedBy RESEARCH-001) +TaskCreate({ subject: "DISCUSS-001: 研究结果讨论 - 范围确认与方向调整", description: `讨论 RESEARCH-001 的发现结果\n\nSession: ${specSessionFolder}\n输入: ${specSessionFolder}/discovery-context.json\n输出: ${specSessionFolder}/discussions/discuss-001-scope.md\n\n讨论维度: 范围确认、方向调整、风险预判、探索缺口`, activeForm: "讨论范围中" }) +TaskUpdate({ taskId: discuss1Id, owner: "discussant", addBlockedBy: [researchId] }) + +// DRAFT-001: Product Brief (blockedBy DISCUSS-001) +TaskCreate({ subject: "DRAFT-001: 撰写 Product Brief", description: `基于研究和讨论共识撰写产品简报\n\nSession: ${specSessionFolder}\n输入: discovery-context.json + discuss-001-scope.md\n输出: ${specSessionFolder}/product-brief.md\n\n使用多视角分析: 产品/技术/用户`, activeForm: "撰写 Brief 中" }) +TaskUpdate({ taskId: draft1Id, owner: "writer", addBlockedBy: [discuss1Id] }) + +// DISCUSS-002: Brief 评审 (blockedBy DRAFT-001) +TaskCreate({ subject: "DISCUSS-002: Product Brief 多视角评审", description: `评审 Product Brief 文档\n\nSession: ${specSessionFolder}\n输入: ${specSessionFolder}/product-brief.md\n输出: ${specSessionFolder}/discussions/discuss-002-brief.md\n\n讨论维度: 产品定位、目标用户、成功指标、竞品差异`, activeForm: "评审 Brief 中" }) +TaskUpdate({ taskId: discuss2Id, owner: "discussant", addBlockedBy: [draft1Id] }) + +// DRAFT-002: Requirements/PRD (blockedBy DISCUSS-002) +TaskCreate({ subject: "DRAFT-002: 撰写 Requirements/PRD", description: `基于 Brief 和讨论反馈撰写需求文档\n\nSession: ${specSessionFolder}\n输入: product-brief.md + discuss-002-brief.md\n输出: ${specSessionFolder}/requirements/\n\n包含: 功能需求(REQ-*) + 非功能需求(NFR-*) + MoSCoW 优先级`, activeForm: "撰写 PRD 中" }) +TaskUpdate({ taskId: draft2Id, owner: "writer", addBlockedBy: [discuss2Id] }) + +// DISCUSS-003: 需求完整性 (blockedBy DRAFT-002) +TaskCreate({ subject: "DISCUSS-003: 需求完整性与优先级讨论", description: `讨论 PRD 需求完整性\n\nSession: ${specSessionFolder}\n输入: ${specSessionFolder}/requirements/_index.md\n输出: ${specSessionFolder}/discussions/discuss-003-requirements.md\n\n讨论维度: 需求遗漏、MoSCoW合理性、验收标准可测性、非功能需求充分性`, activeForm: "讨论需求中" }) +TaskUpdate({ taskId: discuss3Id, owner: "discussant", addBlockedBy: [draft2Id] }) + +// DRAFT-003: Architecture (blockedBy DISCUSS-003) +TaskCreate({ subject: "DRAFT-003: 撰写 Architecture Document", description: `基于需求和讨论反馈撰写架构文档\n\nSession: ${specSessionFolder}\n输入: requirements/ + discuss-003-requirements.md\n输出: ${specSessionFolder}/architecture/\n\n包含: 架构风格 + 组件图 + 技术选型 + ADR-* + 数据模型`, activeForm: "撰写架构中" }) +TaskUpdate({ taskId: draft3Id, owner: "writer", addBlockedBy: [discuss3Id] }) + +// DISCUSS-004: 技术可行性 (blockedBy DRAFT-003) +TaskCreate({ subject: "DISCUSS-004: 架构决策与技术可行性讨论", description: `讨论架构设计合理性\n\nSession: ${specSessionFolder}\n输入: ${specSessionFolder}/architecture/_index.md\n输出: ${specSessionFolder}/discussions/discuss-004-architecture.md\n\n讨论维度: 技术选型风险、可扩展性、安全架构、ADR替代方案`, activeForm: "讨论架构中" }) +TaskUpdate({ taskId: discuss4Id, owner: "discussant", addBlockedBy: [draft3Id] }) + +// DRAFT-004: Epics & Stories (blockedBy DISCUSS-004) +TaskCreate({ subject: "DRAFT-004: 撰写 Epics & Stories", description: `基于架构和讨论反馈撰写史诗和用户故事\n\nSession: ${specSessionFolder}\n输入: architecture/ + discuss-004-architecture.md\n输出: ${specSessionFolder}/epics/\n\n包含: EPIC-* + STORY-* + 依赖图 + MVP定义 + 执行顺序`, activeForm: "撰写 Epics 中" }) +TaskUpdate({ taskId: draft4Id, owner: "writer", addBlockedBy: [discuss4Id] }) + +// DISCUSS-005: 执行就绪 (blockedBy DRAFT-004) +TaskCreate({ subject: "DISCUSS-005: 执行计划与MVP范围讨论", description: `讨论执行计划就绪性\n\nSession: ${specSessionFolder}\n输入: ${specSessionFolder}/epics/_index.md\n输出: ${specSessionFolder}/discussions/discuss-005-epics.md\n\n讨论维度: Epic粒度、故事估算、MVP范围、执行顺序、依赖风险`, activeForm: "讨论执行计划中" }) +TaskUpdate({ taskId: discuss5Id, owner: "discussant", addBlockedBy: [draft4Id] }) + +// QUALITY-001: Readiness Check (blockedBy DISCUSS-005) +TaskCreate({ subject: "QUALITY-001: 规格就绪度检查", description: `全文档交叉验证和质量评分\n\nSession: ${specSessionFolder}\n输入: 全部文档\n输出: ${specSessionFolder}/readiness-report.md + spec-summary.md\n\n评分维度: 完整性(25%) + 一致性(25%) + 可追溯性(25%) + 深度(25%)`, activeForm: "质量检查中" }) +TaskUpdate({ taskId: qualityId, owner: "reviewer", addBlockedBy: [discuss5Id] }) + +// DISCUSS-006: 最终签收 (blockedBy QUALITY-001) +TaskCreate({ subject: "DISCUSS-006: 最终签收与交付确认", description: `最终讨论和签收\n\nSession: ${specSessionFolder}\n输入: ${specSessionFolder}/readiness-report.md\n输出: ${specSessionFolder}/discussions/discuss-006-final.md\n\n讨论维度: 质量报告审查、遗留问题处理、交付确认、下一步建议`, activeForm: "最终签收讨论中" }) +TaskUpdate({ taskId: discuss6Id, owner: "discussant", addBlockedBy: [qualityId] }) +``` + +#### Impl-only Task Chain + +```javascript +// PLAN-001 +TaskCreate({ subject: "PLAN-001: 探索和规划实现", description: `${taskDescription}\n\n写入: ${implSessionFolder}/`, activeForm: "规划中" }) +TaskUpdate({ taskId: planId, owner: "planner" }) + +// IMPL-001 (blockedBy PLAN-001) +TaskCreate({ subject: "IMPL-001: 实现已批准的计划", description: `${taskDescription}\n\nPlan: ${implSessionFolder}/plan.json`, activeForm: "实现中" }) +TaskUpdate({ taskId: implId, owner: "executor", addBlockedBy: [planId] }) + +// TEST-001 (blockedBy IMPL-001) +TaskCreate({ subject: "TEST-001: 测试修复循环", description: `${taskDescription}`, activeForm: "测试中" }) +TaskUpdate({ taskId: testId, owner: "tester", addBlockedBy: [implId] }) + +// REVIEW-001 (blockedBy IMPL-001, parallel with TEST-001) +TaskCreate({ subject: "REVIEW-001: 代码审查与需求验证", description: `${taskDescription}\n\nPlan: ${implSessionFolder}/plan.json`, activeForm: "审查中" }) +TaskUpdate({ taskId: reviewId, owner: "reviewer", addBlockedBy: [implId] }) +``` + +#### Full-lifecycle Task Chain + +Create both spec and impl chains, with PLAN-001 blockedBy DISCUSS-006: + +```javascript +// [All spec-only tasks as above] +// Then: +TaskCreate({ subject: "PLAN-001: 探索和规划实现", description: `${taskDescription}\n\nSpec: ${specSessionFolder}\n写入: ${implSessionFolder}/`, activeForm: "规划中" }) +TaskUpdate({ taskId: planId, owner: "planner", addBlockedBy: [discuss6Id] }) +// [Rest of impl-only tasks as above] +``` + +### Phase 4: Coordination Loop + +Receive teammate messages and make dispatch decisions. **Before each decision: `team_msg list` to review recent messages. After each decision: `team_msg log` to record.** + +#### Spec Messages + +| Received Message | Action | +|-----------------|--------| +| Analyst: research_ready | Read discovery-context.json → team_msg log → TaskUpdate RESEARCH completed (auto-unblocks DISCUSS-001) | +| Discussant: discussion_ready | Read discussion.md → judge if revision needed → unblock next DRAFT task | +| Discussant: discussion_blocked | Intervene → AskUserQuestion for user decision → write decision to discussion record → manually unblock | +| Writer: draft_ready | Read document summary → team_msg log → TaskUpdate DRAFT completed (auto-unblocks next DISCUSS) | +| Writer: draft_revision | Update dependencies → unblock related discussion tasks | +| Reviewer: quality_result (PASS ≥80%) | team_msg log → TaskUpdate QUALITY completed (auto-unblocks DISCUSS-006) | +| Reviewer: quality_result (REVIEW 60-79%) | team_msg log → notify writer of improvement suggestions | +| Reviewer: fix_required (FAIL <60%) | Create DRAFT-fix task → assign writer | + +#### Impl Messages + +| Received Message | Action | +|-----------------|--------| +| Planner: plan_ready | Read plan → approve/request revision → team_msg log(plan_approved/plan_revision) → TaskUpdate + SendMessage | +| Executor: impl_complete | team_msg log(task_unblocked) → TaskUpdate IMPL completed (auto-unblocks TEST + REVIEW) | +| Tester: test_result ≥ 95% | team_msg log → TaskUpdate TEST completed | +| Tester: test_result < 95% + iterations > 5 | team_msg log(error) → escalate to user | +| Reviewer: review_result (no critical) | team_msg log → TaskUpdate REVIEW completed | +| Reviewer: review_result (has critical) | team_msg log(fix_required) → TaskCreate IMPL-fix → assign executor | +| All tasks completed | → Phase 5 | + +#### Full-lifecycle Handoff + +When DISCUSS-006 completes in full-lifecycle mode, PLAN-001 is auto-unblocked via the dependency chain. + +#### Discussion Blocked Handling + +```javascript +if (msgType === 'discussion_blocked') { + const blockReason = msg.data.reason + const options = msg.data.options + + AskUserQuestion({ + questions: [{ + question: `讨论 ${msg.ref} 遇到分歧: ${blockReason}\n请选择方向:`, + header: "Decision", + multiSelect: false, + options: options.map(opt => ({ label: opt.label, description: opt.description })) + }] + }) + // Write user decision to discussion record, then unblock next task +} +``` + +### Phase 5: Report + Persistent Loop + +Summarize results based on mode: +- **spec-only**: Document inventory, quality scores, discussion rounds +- **impl-only**: Changed files, test pass rate, review verdict +- **full-lifecycle**: Both spec summary + impl summary + +```javascript +AskUserQuestion({ + questions: [{ + question: "当前需求已完成。下一步:", + header: "Next", + multiSelect: false, + options: [ + { label: "新需求", description: "提交新需求给当前团队" }, + { label: "交付执行", description: "将规格交给执行 workflow(仅 spec 模式)" }, + { label: "关闭团队", description: "关闭所有 teammate 并清理" } + ] + }] +}) +// 新需求 → 回到 Phase 1(复用 team,新建任务链) +// 交付执行 → 提示可用的执行 workflow +// 关闭 → shutdown 给每个 teammate → TeamDelete() +``` + +## Session File Structure + +``` +# Spec session +.workflow/.spec-team/{topic-slug}-{YYYY-MM-DD}/ +├── spec-config.json +├── discovery-context.json +├── product-brief.md +├── requirements/ +├── architecture/ +├── epics/ +├── readiness-report.md +├── spec-summary.md +└── discussions/ + └── discuss-001..006.md + +# Impl session +.workflow/.team-plan/{task-slug}-{YYYY-MM-DD}/ +├── exploration-*.json +├── explorations-manifest.json +├── planning-context.md +├── plan.json +└── .task/ + └── TASK-*.json +``` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| Teammate 无响应 | 发追踪消息,2次无响应 → 重新 spawn | +| Plan 被拒 3+ 次 | Coordinator 自行规划 | +| 测试卡在 <80% 超 5 次迭代 | 上报用户 | +| Review 发现 critical | 创建 IMPL-fix 任务给 executor | +| 讨论无法共识 | Coordinator 介入 → AskUserQuestion | +| 文档质量 <60% | 创建 DRAFT-fix 任务给 writer | +| Writer 修订 3+ 次 | 上报用户,建议调整范围 | +| Research 无法完成 | 降级为简化模式 | diff --git a/.claude/skills/team-lifecycle/roles/discussant.md b/.claude/skills/team-lifecycle/roles/discussant.md new file mode 100644 index 00000000..0dc1e945 --- /dev/null +++ b/.claude/skills/team-lifecycle/roles/discussant.md @@ -0,0 +1,223 @@ +# Role: discussant + +Multi-perspective critique, consensus building, and conflict escalation. The key differentiator of the spec team workflow — ensuring quality feedback between each phase transition. + +## Role Identity + +- **Name**: `discussant` +- **Task Prefix**: `DISCUSS-*` +- **Responsibility**: Load Artifact → Multi-Perspective Critique → Synthesize Consensus → Report +- **Communication**: SendMessage to coordinator only + +## Message Types + +| Type | Direction | Trigger | Description | +|------|-----------|---------|-------------| +| `discussion_ready` | discussant → coordinator | Discussion complete, consensus reached | With discussion record path and decision summary | +| `discussion_blocked` | discussant → coordinator | Cannot reach consensus | With divergence points and options, needs coordinator | +| `impl_progress` | discussant → coordinator | Long discussion progress | Multi-perspective analysis progress | +| `error` | discussant → coordinator | Discussion cannot proceed | Input artifact missing, etc. | + +## Message Bus + +Before every `SendMessage`, MUST call `mcp__ccw-tools__team_msg` to log: + +```javascript +// Discussion complete +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "discussant", to: "coordinator", type: "discussion_ready", summary: "Scope discussion consensus reached: 3 decisions", ref: `${sessionFolder}/discussions/discuss-001-scope.md` }) + +// Discussion blocked +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "discussant", to: "coordinator", type: "discussion_blocked", summary: "Cannot reach consensus on tech stack", data: { reason: "...", options: [...] } }) + +// Error report +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "discussant", to: "coordinator", type: "error", summary: "Input artifact missing" }) +``` + +### CLI Fallback + +When `mcp__ccw-tools__team_msg` MCP is unavailable: + +```javascript +Bash(`ccw team log --team "${teamName}" --from "discussant" --to "coordinator" --type "discussion_ready" --summary "Discussion complete" --ref "${sessionFolder}/discussions/discuss-001-scope.md" --json`) +``` + +## Discussion Dimension Model + +Each discussion round analyzes from 4 perspectives: + +| Perspective | Focus | Representative | +|-------------|-------|----------------| +| **Product** | Market fit, user value, business viability, competitive differentiation | Product Manager | +| **Technical** | Feasibility, tech debt, performance, security, maintainability | Tech Lead | +| **Quality** | Completeness, testability, consistency, standards compliance | QA Lead | +| **Risk** | Risk identification, dependency analysis, assumption validation, failure modes | Risk Analyst | + +## Discussion Round Configuration + +| Round | Artifact | Key Perspectives | Focus | +|-------|----------|-----------------|-------| +| DISCUSS-001 | discovery-context | product + risk | Scope confirmation, direction | +| DISCUSS-002 | product-brief | product + technical + quality | Positioning, feasibility | +| DISCUSS-003 | requirements | quality + product | Completeness, priority | +| DISCUSS-004 | architecture | technical + risk | Tech choices, security | +| DISCUSS-005 | epics | product + technical + quality | MVP scope, estimation | +| DISCUSS-006 | readiness-report | all 4 perspectives | Final sign-off | + +## Execution (5-Phase) + +### Phase 1: Task Discovery + +```javascript +const tasks = TaskList() +const myTasks = tasks.filter(t => + t.subject.startsWith('DISCUSS-') && + t.owner === 'discussant' && + t.status === 'pending' && + t.blockedBy.length === 0 +) + +if (myTasks.length === 0) return // idle + +const task = TaskGet({ taskId: myTasks[0].id }) +TaskUpdate({ taskId: task.id, status: 'in_progress' }) +``` + +### Phase 2: Artifact Loading + +```javascript +const sessionMatch = task.description.match(/Session:\s*(.+)/) +const sessionFolder = sessionMatch ? sessionMatch[1].trim() : '' +const roundMatch = task.subject.match(/DISCUSS-(\d+)/) +const roundNumber = roundMatch ? parseInt(roundMatch[1]) : 0 + +const roundConfig = { + 1: { artifact: 'discovery-context.json', type: 'json', outputFile: 'discuss-001-scope.md', perspectives: ['product', 'risk'], label: '范围讨论' }, + 2: { artifact: 'product-brief.md', type: 'md', outputFile: 'discuss-002-brief.md', perspectives: ['product', 'technical', 'quality'], label: 'Brief评审' }, + 3: { artifact: 'requirements/_index.md', type: 'md', outputFile: 'discuss-003-requirements.md', perspectives: ['quality', 'product'], label: '需求讨论' }, + 4: { artifact: 'architecture/_index.md', type: 'md', outputFile: 'discuss-004-architecture.md', perspectives: ['technical', 'risk'], label: '架构讨论' }, + 5: { artifact: 'epics/_index.md', type: 'md', outputFile: 'discuss-005-epics.md', perspectives: ['product', 'technical', 'quality'], label: 'Epics讨论' }, + 6: { artifact: 'readiness-report.md', type: 'md', outputFile: 'discuss-006-final.md', perspectives: ['product', 'technical', 'quality', 'risk'], label: '最终签收' } +} + +const config = roundConfig[roundNumber] +// Load target artifact and prior discussion records for continuity +Bash(`mkdir -p ${sessionFolder}/discussions`) +``` + +### Phase 3: Multi-Perspective Critique + +Launch parallel CLI analyses for each required perspective: + +- **Product Perspective** (gemini): Market fit, user value, business viability, competitive differentiation. Rate 1-5 with improvement suggestions. +- **Technical Perspective** (codex): Feasibility, complexity, architecture decisions, tech debt risks. Rate 1-5. +- **Quality Perspective** (claude): Completeness, testability, consistency, ambiguity detection. Rate 1-5. +- **Risk Perspective** (gemini): Risk identification, dependency analysis, assumption validation, failure modes. Rate risk level. + +Each CLI call produces structured critique with: strengths[], weaknesses[], suggestions[], rating. + +### Phase 4: Consensus Synthesis + +```javascript +const synthesis = { + convergent_themes: [], + divergent_views: [], + action_items: [], + open_questions: [], + decisions: [], + risk_flags: [], + overall_sentiment: '', // positive/neutral/concerns/critical + consensus_reached: true // false if major unresolvable conflicts +} + +// Extract convergent themes (items mentioned positively by 2+ perspectives) +// Extract divergent views (items where perspectives conflict) +// Check for unresolvable conflicts +const criticalDivergences = synthesis.divergent_views.filter(d => d.severity === 'high') +if (criticalDivergences.length > 0) synthesis.consensus_reached = false + +// Determine overall sentiment from average rating +// Generate discussion record markdown with all perspectives, convergence, divergence, action items + +Write(`${sessionFolder}/discussions/${config.outputFile}`, discussionRecord) +``` + +### Phase 5: Report to Coordinator + +```javascript +if (synthesis.consensus_reached) { + mcp__ccw-tools__team_msg({ + operation: "log", team: teamName, + from: "discussant", to: "coordinator", + type: "discussion_ready", + summary: `${config.label}讨论完成: ${synthesis.action_items.length}个行动项, ${synthesis.open_questions.length}个开放问题, 总体${synthesis.overall_sentiment}`, + ref: `${sessionFolder}/discussions/${config.outputFile}` + }) + + SendMessage({ + type: "message", + recipient: "coordinator", + content: `## 讨论结果: ${config.label} + +**Task**: ${task.subject} +**共识**: 已达成 +**总体评价**: ${synthesis.overall_sentiment} + +### 行动项 (${synthesis.action_items.length}) +${synthesis.action_items.map((item, i) => (i+1) + '. ' + item).join('\n') || '无'} + +### 开放问题 (${synthesis.open_questions.length}) +${synthesis.open_questions.map((q, i) => (i+1) + '. ' + q).join('\n') || '无'} + +### 讨论记录 +${sessionFolder}/discussions/${config.outputFile} + +共识已达成,可推进至下一阶段。`, + summary: `${config.label}共识达成: ${synthesis.action_items.length}行动项` + }) + + TaskUpdate({ taskId: task.id, status: 'completed' }) +} else { + // Consensus blocked - escalate to coordinator + mcp__ccw-tools__team_msg({ + operation: "log", team: teamName, + from: "discussant", to: "coordinator", + type: "discussion_blocked", + summary: `${config.label}讨论阻塞: ${criticalDivergences.length}个关键分歧需决策`, + data: { + reason: criticalDivergences.map(d => d.description).join('; '), + options: criticalDivergences.map(d => ({ label: d.topic, description: d.options?.join(' vs ') || d.description })) + } + }) + + SendMessage({ + type: "message", + recipient: "coordinator", + content: `## 讨论阻塞: ${config.label} + +**Task**: ${task.subject} +**状态**: 无法达成共识,需要 coordinator 介入 + +### 关键分歧 +${criticalDivergences.map((d, i) => (i+1) + '. **' + d.topic + '**: ' + d.description).join('\n\n')} + +请通过 AskUserQuestion 收集用户对分歧点的决策。`, + summary: `${config.label}阻塞: ${criticalDivergences.length}分歧` + }) + // Keep task in_progress, wait for coordinator resolution +} + +// Check for next DISCUSS task → back to Phase 1 +``` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| No DISCUSS-* tasks available | Idle, wait for coordinator assignment | +| Target artifact not found | Notify coordinator, request prerequisite completion | +| CLI perspective analysis failure | Fallback to direct Claude analysis for that perspective | +| All CLI analyses fail | Generate basic discussion from direct reading | +| Consensus timeout (all perspectives diverge) | Escalate as discussion_blocked | +| Prior discussion records missing | Continue without continuity context | +| Session folder not found | Notify coordinator, request session path | +| Unexpected error | Log error via team_msg, report to coordinator | diff --git a/.claude/skills/team-lifecycle/roles/executor.md b/.claude/skills/team-lifecycle/roles/executor.md new file mode 100644 index 00000000..ccfea816 --- /dev/null +++ b/.claude/skills/team-lifecycle/roles/executor.md @@ -0,0 +1,235 @@ +# Role: executor + +Code implementation following approved plans. Reads plan files, implements changes, self-validates, and reports completion. + +## Role Identity + +- **Name**: `executor` +- **Task Prefix**: `IMPL-*` +- **Responsibility**: Load plan → Implement code → Self-validate → Report completion +- **Communication**: SendMessage to coordinator only + +## Message Types + +| Type | Direction | Trigger | Description | +|------|-----------|---------|-------------| +| `impl_complete` | executor → coordinator | All implementation complete | With changed files list and acceptance status | +| `impl_progress` | executor → coordinator | Batch/subtask completed | Progress percentage and completed subtask | +| `error` | executor → coordinator | Blocking problem | Plan file missing, file conflict, sub-agent failure | + +## Message Bus + +Before every `SendMessage`, MUST call `mcp__ccw-tools__team_msg` to log: + +```javascript +// Progress update +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "executor", to: "coordinator", type: "impl_progress", summary: "Batch 1/3 done: auth middleware implemented", data: { batch: 1, total: 3, files: ["src/middleware/auth.ts"] } }) + +// Implementation complete +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "executor", to: "coordinator", type: "impl_complete", summary: "IMPL-001 complete: 5 files changed, all acceptance met", data: { changedFiles: 5, syntaxClean: true } }) + +// Error report +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "executor", to: "coordinator", type: "error", summary: "Invalid plan.json path, cannot load implementation plan" }) +``` + +### CLI Fallback + +When `mcp__ccw-tools__team_msg` MCP is unavailable: + +```javascript +Bash(`ccw team log --team "${teamName}" --from "executor" --to "coordinator" --type "impl_complete" --summary "IMPL-001 complete: 5 files changed" --json`) +``` + +## Execution (5-Phase) + +### Phase 1: Task & Plan Loading + +```javascript +const tasks = TaskList() +const myTasks = tasks.filter(t => + t.subject.startsWith('IMPL-') && + t.owner === 'executor' && + t.status === 'pending' && + t.blockedBy.length === 0 +) + +if (myTasks.length === 0) return // idle + +const task = TaskGet({ taskId: myTasks[0].id }) +TaskUpdate({ taskId: task.id, status: 'in_progress' }) + +// Extract plan path from task description +const planPathMatch = task.description.match(/\.workflow\/\.team-plan\/[^\s]+\/plan\.json/) +const planPath = planPathMatch ? planPathMatch[0] : null + +if (!planPath) { + mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "executor", to: "coordinator", type: "error", summary: "plan.json路径无效" }) + SendMessage({ type: "message", recipient: "coordinator", content: `Cannot find plan.json in ${task.subject}`, summary: "Plan path not found" }) + return +} + +const plan = JSON.parse(Read(planPath)) +``` + +### Phase 2: Task Grouping + +```javascript +// Extract dependencies and group into parallel/sequential batches +function createBatches(planTasks) { + const processed = new Set() + const batches = [] + + // Phase 1: Independent tasks → single parallel batch + const independent = planTasks.filter(t => (t.depends_on || []).length === 0) + if (independent.length > 0) { + independent.forEach(t => processed.add(t.id)) + batches.push({ type: 'parallel', tasks: independent }) + } + + // Phase 2+: Dependent tasks in topological order + let remaining = planTasks.filter(t => !processed.has(t.id)) + while (remaining.length > 0) { + const ready = remaining.filter(t => (t.depends_on || []).every(d => processed.has(d))) + if (ready.length === 0) break // circular dependency guard + ready.forEach(t => processed.add(t.id)) + batches.push({ type: ready.length > 1 ? 'parallel' : 'sequential', tasks: ready }) + remaining = remaining.filter(t => !processed.has(t.id)) + } + return batches +} + +// Load task files from .task/ directory +const planTasks = plan.task_ids.map(id => JSON.parse(Read(`${planPath.replace('plan.json', '')}.task/${id}.json`))) +const batches = createBatches(planTasks) +``` + +### Phase 3: Code Implementation + +```javascript +// Unified Task Prompt Builder +function buildExecutionPrompt(planTask) { + return ` +## ${planTask.title} + +**Scope**: \`${planTask.scope}\` | **Action**: ${planTask.action || 'implement'} + +### Files +${(planTask.files || []).map(f => `- **${f.path}** → \`${f.target}\`: ${f.change}`).join('\n')} + +### How to do it +${planTask.description} + +${(planTask.implementation || []).map(step => `- ${step}`).join('\n')} + +### Reference +- Pattern: ${planTask.reference?.pattern || 'N/A'} +- Files: ${planTask.reference?.files?.join(', ') || 'N/A'} + +### Done when +${(planTask.convergence?.criteria || []).map(c => `- [ ] ${c}`).join('\n')} +` +} + +const changedFiles = [] + +for (const batch of batches) { + if (batch.tasks.length === 1 && isSimpleTask(batch.tasks[0])) { + // Simple task: direct file editing + const t = batch.tasks[0] + for (const f of (t.files || [])) { + const content = Read(f.path) + Edit({ file_path: f.path, old_string: "...", new_string: "..." }) + changedFiles.push(f.path) + } + } else { + // Complex task(s): delegate to code-developer sub-agent + const prompt = batch.tasks.map(buildExecutionPrompt).join('\n\n---\n') + + Task({ + subagent_type: "code-developer", + run_in_background: false, + description: batch.tasks.map(t => t.title).join(' | '), + prompt: `## Goal\n${plan.summary}\n\n## Tasks\n${prompt}\n\n## Context\n### Project Guidelines\n@.workflow/project-guidelines.json\n\nComplete each task according to its "Done when" checklist.` + }) + + batch.tasks.forEach(t => (t.files || []).forEach(f => changedFiles.push(f.path))) + } + + // Progress update + mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "executor", to: "coordinator", type: "impl_progress", summary: `Batch完成: ${changedFiles.length}个文件已变更` }) +} + +function isSimpleTask(task) { + return (task.files || []).length <= 2 && (task.risks || []).length === 0 +} +``` + +### Phase 4: Self-Validation + +```javascript +// Syntax check +const syntaxResult = Bash(`tsc --noEmit 2>&1 || true`) +const hasSyntaxErrors = syntaxResult.includes('error TS') +if (hasSyntaxErrors) { /* attempt auto-fix */ } + +// Verify acceptance criteria +const acceptanceStatus = planTasks.map(t => ({ + title: t.title, + criteria: (t.convergence?.criteria || []).map(c => ({ criterion: c, met: true })) +})) + +// Run affected tests (if identifiable) +const testFiles = changedFiles + .map(f => f.replace(/\/src\//, '/tests/').replace(/\.(ts|js)$/, '.test.$1')) + .filter(f => Bash(`test -f ${f} && echo exists || true`).includes('exists')) +if (testFiles.length > 0) Bash(`npx jest ${testFiles.join(' ')} --passWithNoTests 2>&1 || true`) +``` + +### Phase 5: Report to Coordinator + +```javascript +mcp__ccw-tools__team_msg({ + operation: "log", team: teamName, + from: "executor", to: "coordinator", + type: "impl_complete", + summary: `IMPL完成: ${[...new Set(changedFiles)].length}个文件变更, syntax=${hasSyntaxErrors ? 'errors' : 'clean'}` +}) + +SendMessage({ + type: "message", + recipient: "coordinator", + content: `## Implementation Complete + +**Task**: ${task.subject} + +### Changed Files +${[...new Set(changedFiles)].map(f => '- ' + f).join('\n')} + +### Acceptance Criteria +${acceptanceStatus.map(t => '**' + t.title + '**: ' + (t.criteria.every(c => c.met) ? 'All met' : 'Partial')).join('\n')} + +### Validation +- Syntax: ${hasSyntaxErrors ? 'Has errors (attempted fix)' : 'Clean'} +- Tests: ${testFiles.length > 0 ? 'Ran' : 'N/A'} + +Implementation is ready for testing and review.`, + summary: `IMPL complete: ${[...new Set(changedFiles)].length} files changed` +}) + +TaskUpdate({ taskId: task.id, status: 'completed' }) + +// Check for next IMPL task → back to Phase 1 +``` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| No IMPL-* tasks available | Idle, wait for coordinator assignment | +| Plan file not found | Notify coordinator, request plan location | +| Syntax errors after implementation | Attempt auto-fix, report remaining errors | +| Sub-agent failure | Retry once, then attempt direct implementation | +| File conflict / merge issue | Notify coordinator, request guidance | +| Test failures in self-validation | Report in completion message, let tester handle | +| Circular dependencies in plan | Execute in plan order, ignore dependency chain | +| Unexpected error | Log error via team_msg, report to coordinator | diff --git a/.claude/skills/team-lifecycle/roles/planner.md b/.claude/skills/team-lifecycle/roles/planner.md new file mode 100644 index 00000000..e1d51a33 --- /dev/null +++ b/.claude/skills/team-lifecycle/roles/planner.md @@ -0,0 +1,274 @@ +# Role: planner + +Multi-angle code exploration and structured implementation planning. Submits plans to the coordinator for approval. + +## Role Identity + +- **Name**: `planner` +- **Task Prefix**: `PLAN-*` +- **Responsibility**: Code exploration → Implementation planning → Coordinator approval +- **Communication**: SendMessage to coordinator only + +## Message Types + +| Type | Direction | Trigger | Description | +|------|-----------|---------|-------------| +| `plan_ready` | planner → coordinator | Plan generation complete | With plan.json path and task count summary | +| `plan_revision` | planner → coordinator | Plan revised and resubmitted | Describes changes made | +| `impl_progress` | planner → coordinator | Exploration phase progress | Optional, for long explorations | +| `error` | planner → coordinator | Unrecoverable error | Exploration failure, schema missing, etc. | + +## Message Bus + +Before every `SendMessage`, MUST call `mcp__ccw-tools__team_msg` to log: + +```javascript +// Plan ready +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "planner", to: "coordinator", type: "plan_ready", summary: "Plan ready: 3 tasks, Medium complexity", ref: `${sessionFolder}/plan.json` }) + +// Plan revision +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "planner", to: "coordinator", type: "plan_revision", summary: "Split task-2 into two subtasks per feedback" }) + +// Error report +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "planner", to: "coordinator", type: "error", summary: "plan-overview-base-schema.json not found, using default structure" }) +``` + +### CLI Fallback + +When `mcp__ccw-tools__team_msg` MCP is unavailable: + +```javascript +Bash(`ccw team log --team "${teamName}" --from "planner" --to "coordinator" --type "plan_ready" --summary "Plan ready: 3 tasks" --ref "${sessionFolder}/plan.json" --json`) +``` + +## Execution (5-Phase) + +### Phase 1: Task Discovery + +```javascript +const tasks = TaskList() +const myTasks = tasks.filter(t => + t.subject.startsWith('PLAN-') && + t.owner === 'planner' && + t.status === 'pending' && + t.blockedBy.length === 0 +) + +if (myTasks.length === 0) return // idle + +const task = TaskGet({ taskId: myTasks[0].id }) +TaskUpdate({ taskId: task.id, status: 'in_progress' }) +``` + +### Phase 2: Multi-Angle Exploration + +```javascript +// Session setup +const taskSlug = task.subject.toLowerCase().replace(/[^a-z0-9]+/g, '-').substring(0, 40) +const dateStr = new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString().substring(0, 10) +const sessionFolder = `.workflow/.team-plan/${taskSlug}-${dateStr}` +Bash(`mkdir -p ${sessionFolder}`) + +// Complexity assessment +function assessComplexity(desc) { + let score = 0 + if (/refactor|architect|restructure|模块|系统/.test(desc)) score += 2 + if (/multiple|多个|across|跨/.test(desc)) score += 2 + if (/integrate|集成|api|database/.test(desc)) score += 1 + if (/security|安全|performance|性能/.test(desc)) score += 1 + return score >= 4 ? 'High' : score >= 2 ? 'Medium' : 'Low' +} + +const complexity = assessComplexity(task.description) + +// Angle selection based on task type +const ANGLE_PRESETS = { + architecture: ['architecture', 'dependencies', 'modularity', 'integration-points'], + security: ['security', 'auth-patterns', 'dataflow', 'validation'], + performance: ['performance', 'bottlenecks', 'caching', 'data-access'], + bugfix: ['error-handling', 'dataflow', 'state-management', 'edge-cases'], + feature: ['patterns', 'integration-points', 'testing', 'dependencies'] +} + +function selectAngles(desc, count) { + const text = desc.toLowerCase() + let preset = 'feature' + if (/refactor|architect|restructure|modular/.test(text)) preset = 'architecture' + else if (/security|auth|permission|access/.test(text)) preset = 'security' + else if (/performance|slow|optimi|cache/.test(text)) preset = 'performance' + else if (/fix|bug|error|issue|broken/.test(text)) preset = 'bugfix' + return ANGLE_PRESETS[preset].slice(0, count) +} + +const angleCount = complexity === 'High' ? 4 : (complexity === 'Medium' ? 3 : 1) +const selectedAngles = selectAngles(task.description, angleCount) + +// Execute exploration +if (complexity === 'Low') { + // Direct exploration via semantic search + const results = mcp__ace-tool__search_context({ + project_root_path: projectRoot, + query: task.description + }) + Write(`${sessionFolder}/exploration-${selectedAngles[0]}.json`, JSON.stringify({ + project_structure: "...", + relevant_files: [], + patterns: [], + dependencies: [], + integration_points: [], + constraints: [], + clarification_needs: [], + _metadata: { exploration_angle: selectedAngles[0] } + }, null, 2)) +} else { + // Launch parallel cli-explore-agent for each angle + selectedAngles.forEach((angle, index) => { + Task({ + subagent_type: "cli-explore-agent", + run_in_background: false, + description: `Explore: ${angle}`, + prompt: ` +## Task Objective +Execute **${angle}** exploration for task planning context. + +## Output Location +**Session Folder**: ${sessionFolder} +**Output File**: ${sessionFolder}/exploration-${angle}.json + +## Assigned Context +- **Exploration Angle**: ${angle} +- **Task Description**: ${task.description} +- **Exploration Index**: ${index + 1} of ${selectedAngles.length} + +## MANDATORY FIRST STEPS +1. Run: rg -l "{relevant_keyword}" --type ts (locate relevant files) +2. Execute: cat ~/.ccw/workflows/cli-templates/schemas/explore-json-schema.json (get output schema) +3. Read: .workflow/project-tech.json (if exists - technology stack) + +## Expected Output +Write JSON to: ${sessionFolder}/exploration-${angle}.json +Follow explore-json-schema.json structure with ${angle}-focused findings. + +**MANDATORY**: Every file in relevant_files MUST have: +- **rationale** (required): Specific selection basis tied to ${angle} topic (>10 chars, not generic) +- **role** (required): modify_target|dependency|pattern_reference|test_target|type_definition|integration_point|config|context_only +- **discovery_source** (recommended): bash-scan|cli-analysis|ace-search|dependency-trace|manual +- **key_symbols** (recommended): Key functions/classes/types relevant to task +` + }) + }) +} + +// Build explorations manifest +const explorationManifest = { + session_id: `${taskSlug}-${dateStr}`, + task_description: task.description, + complexity: complexity, + exploration_count: selectedAngles.length, + explorations: selectedAngles.map(angle => ({ + angle: angle, + file: `exploration-${angle}.json`, + path: `${sessionFolder}/exploration-${angle}.json` + })) +} +Write(`${sessionFolder}/explorations-manifest.json`, JSON.stringify(explorationManifest, null, 2)) +``` + +### Phase 3: Plan Generation + +```javascript +// Read schema reference +const schema = Bash(`cat ~/.ccw/workflows/cli-templates/schemas/plan-overview-base-schema.json`) + +if (complexity === 'Low') { + // Direct Claude planning + Bash(`mkdir -p ${sessionFolder}/.task`) + // Generate plan.json + .task/TASK-*.json following schemas +} else { + // Use cli-lite-planning-agent for Medium/High + Task({ + subagent_type: "cli-lite-planning-agent", + run_in_background: false, + description: "Generate detailed implementation plan", + prompt: `Generate implementation plan. +Output: ${sessionFolder}/plan.json + ${sessionFolder}/.task/TASK-*.json +Schema: cat ~/.ccw/workflows/cli-templates/schemas/plan-overview-base-schema.json +Task Description: ${task.description} +Explorations: ${explorationManifest} +Complexity: ${complexity} +Requirements: 2-7 tasks, each with id, title, files[].change, convergence.criteria, depends_on` + }) +} +``` + +### Phase 4: Submit for Approval + +```javascript +const plan = JSON.parse(Read(`${sessionFolder}/plan.json`)) +const planTasks = plan.task_ids.map(id => JSON.parse(Read(`${sessionFolder}/.task/${id}.json`))) +const taskCount = plan.task_count || plan.task_ids.length + +mcp__ccw-tools__team_msg({ + operation: "log", team: teamName, + from: "planner", to: "coordinator", + type: "plan_ready", + summary: `Plan就绪: ${taskCount}个task, ${complexity}复杂度`, + ref: `${sessionFolder}/plan.json` +}) + +SendMessage({ + type: "message", + recipient: "coordinator", + content: `## Plan Ready for Review + +**Task**: ${task.subject} +**Complexity**: ${complexity} +**Tasks**: ${taskCount} + +### Task Summary +${planTasks.map((t, i) => (i+1) + '. ' + t.title).join('\n')} + +### Approach +${plan.approach} + +### Plan Location +${sessionFolder}/plan.json +Task Files: ${sessionFolder}/.task/ + +Please review and approve or request revisions.`, + summary: `Plan ready: ${taskCount} tasks` +}) + +// Wait for coordinator response (approve → mark completed, revision → update and resubmit) +``` + +### Phase 5: After Approval + +```javascript +TaskUpdate({ taskId: task.id, status: 'completed' }) + +// Check for next PLAN task → back to Phase 1 +``` + +## Session Files + +``` +.workflow/.team-plan/{task-slug}-{YYYY-MM-DD}/ +├── exploration-{angle}.json +├── explorations-manifest.json +├── planning-context.md +├── plan.json +└── .task/ + └── TASK-*.json +``` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| No PLAN-* tasks available | Idle, wait for coordinator assignment | +| Exploration agent failure | Skip exploration, plan from task description only | +| Planning agent failure | Fallback to direct Claude planning | +| Plan rejected 3+ times | Notify coordinator, suggest alternative approach | +| Schema file not found | Use basic plan structure without schema validation | +| Unexpected error | Log error via team_msg, report to coordinator | diff --git a/.claude/skills/team-lifecycle/roles/reviewer.md b/.claude/skills/team-lifecycle/roles/reviewer.md new file mode 100644 index 00000000..e07dcbf5 --- /dev/null +++ b/.claude/skills/team-lifecycle/roles/reviewer.md @@ -0,0 +1,508 @@ +# Role: reviewer + +Unified review role handling both code review (REVIEW-*) and specification quality checks (QUALITY-*). Auto-switches behavior based on task prefix. + +## Role Identity + +- **Name**: `reviewer` +- **Task Prefix**: `REVIEW-*` + `QUALITY-*` +- **Responsibility**: Discover Task → Branch by Prefix → Review/Score → Report +- **Communication**: SendMessage to coordinator only + +## Message Types + +| Type | Direction | Trigger | Description | +|------|-----------|---------|-------------| +| `review_result` | reviewer → coordinator | Code review complete | With verdict (APPROVE/CONDITIONAL/BLOCK) and findings | +| `quality_result` | reviewer → coordinator | Spec quality check complete | With score and gate decision (PASS/REVIEW/FAIL) | +| `fix_required` | reviewer → coordinator | Critical issues found | Needs IMPL-fix or DRAFT-fix tasks | +| `error` | reviewer → coordinator | Review cannot proceed | Plan missing, documents missing, etc. | + +## Message Bus + +Before every `SendMessage`, MUST call `mcp__ccw-tools__team_msg` to log: + +```javascript +// Code review result +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "reviewer", to: "coordinator", type: "review_result", summary: "REVIEW APPROVE: 8 findings (critical=0, high=2)", data: { verdict: "APPROVE", critical: 0, high: 2 } }) + +// Spec quality result +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "reviewer", to: "coordinator", type: "quality_result", summary: "Quality check PASS: 85.0 score", data: { gate: "PASS", score: 85.0 } }) + +// Fix required +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "reviewer", to: "coordinator", type: "fix_required", summary: "Critical security issues found, IMPL-fix needed", data: { critical: 2 } }) + +// Error report +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "reviewer", to: "coordinator", type: "error", summary: "plan.json not found, cannot verify requirements" }) +``` + +### CLI Fallback + +When `mcp__ccw-tools__team_msg` MCP is unavailable: + +```javascript +Bash(`ccw team log --team "${teamName}" --from "reviewer" --to "coordinator" --type "review_result" --summary "REVIEW APPROVE: 8 findings" --data '{"verdict":"APPROVE","critical":0}' --json`) +``` + +## Execution (5-Phase) + +### Phase 1: Task Discovery (Dual-Prefix) + +```javascript +const tasks = TaskList() +const myTasks = tasks.filter(t => + (t.subject.startsWith('REVIEW-') || t.subject.startsWith('QUALITY-')) && + t.owner === 'reviewer' && + t.status === 'pending' && + t.blockedBy.length === 0 +) + +if (myTasks.length === 0) return // idle + +const task = TaskGet({ taskId: myTasks[0].id }) +TaskUpdate({ taskId: task.id, status: 'in_progress' }) + +// Determine review mode +const reviewMode = task.subject.startsWith('REVIEW-') ? 'code' : 'spec' +``` + +### Phase 2: Context Loading (Branch by Mode) + +**Code Review Mode (REVIEW-*):** + +```javascript +if (reviewMode === 'code') { + // Load plan for acceptance criteria + const planPathMatch = task.description.match(/\.workflow\/\.team-plan\/[^\s]+\/plan\.json/) + let plan = null + if (planPathMatch) { + try { plan = JSON.parse(Read(planPathMatch[0])) } catch {} + } + + // Get changed files via git + const changedFiles = Bash(`git diff --name-only HEAD~1 2>/dev/null || git diff --name-only --cached`) + .split('\n').filter(f => f.trim() && !f.startsWith('.')) + + // Read changed file contents (limit to 20 files) + const fileContents = {} + for (const file of changedFiles.slice(0, 20)) { + try { fileContents[file] = Read(file) } catch {} + } + + // Load test results if available + const testSummary = tasks.find(t => t.subject.startsWith('TEST-') && t.status === 'completed') +} +``` + +**Spec Quality Mode (QUALITY-*):** + +```javascript +if (reviewMode === 'spec') { + const sessionMatch = task.description.match(/Session:\s*(.+)/) + const sessionFolder = sessionMatch ? sessionMatch[1].trim() : '' + + // Load all spec documents + const documents = { + config: null, discoveryContext: null, productBrief: null, + requirementsIndex: null, requirements: [], architectureIndex: null, + adrs: [], epicsIndex: null, epics: [], discussions: [] + } + + try { documents.config = JSON.parse(Read(`${sessionFolder}/spec-config.json`)) } catch {} + try { documents.discoveryContext = JSON.parse(Read(`${sessionFolder}/discovery-context.json`)) } catch {} + try { documents.productBrief = Read(`${sessionFolder}/product-brief.md`) } catch {} + try { documents.requirementsIndex = Read(`${sessionFolder}/requirements/_index.md`) } catch {} + try { documents.architectureIndex = Read(`${sessionFolder}/architecture/_index.md`) } catch {} + try { documents.epicsIndex = Read(`${sessionFolder}/epics/_index.md`) } catch {} + + // Load individual documents + Glob({ pattern: `${sessionFolder}/requirements/REQ-*.md` }).forEach(f => { try { documents.requirements.push(Read(f)) } catch {} }) + Glob({ pattern: `${sessionFolder}/requirements/NFR-*.md` }).forEach(f => { try { documents.requirements.push(Read(f)) } catch {} }) + Glob({ pattern: `${sessionFolder}/architecture/ADR-*.md` }).forEach(f => { try { documents.adrs.push(Read(f)) } catch {} }) + Glob({ pattern: `${sessionFolder}/epics/EPIC-*.md` }).forEach(f => { try { documents.epics.push(Read(f)) } catch {} }) + Glob({ pattern: `${sessionFolder}/discussions/discuss-*.md` }).forEach(f => { try { documents.discussions.push(Read(f)) } catch {} }) + + const docInventory = { + config: !!documents.config, discoveryContext: !!documents.discoveryContext, + productBrief: !!documents.productBrief, requirements: documents.requirements.length > 0, + architecture: documents.adrs.length > 0, epics: documents.epics.length > 0, + discussions: documents.discussions.length + } +} +``` + +### Phase 3: Review Execution (Branch by Mode) + +**Code Review — 4-Dimension Analysis:** + +```javascript +if (reviewMode === 'code') { + const findings = { critical: [], high: [], medium: [], low: [] } + + // Quality: @ts-ignore, any, console.log, empty catch + const qualityIssues = reviewQuality(changedFiles) + + // Security: eval/exec/innerHTML, hardcoded secrets, SQL injection, XSS + const securityIssues = reviewSecurity(changedFiles) + + // Architecture: circular deps, large files, layering violations + const architectureIssues = reviewArchitecture(changedFiles, fileContents) + + // Requirement Verification: plan acceptance criteria vs implementation + const requirementIssues = plan ? verifyRequirements(plan, fileContents) : [] + + const allIssues = [...qualityIssues, ...securityIssues, ...architectureIssues, ...requirementIssues] + allIssues.forEach(issue => findings[issue.severity].push(issue)) + + // Verdict determination + const hasCritical = findings.critical.length > 0 + const verdict = hasCritical ? 'BLOCK' : findings.high.length > 3 ? 'CONDITIONAL' : 'APPROVE' +} +``` + +Review dimension functions: + +```javascript +function reviewQuality(files) { + const issues = [] + const tsIgnore = Grep({ pattern: '@ts-ignore|@ts-expect-error', glob: '*.{ts,tsx}', output_mode: 'content' }) + if (tsIgnore) issues.push({ type: 'quality', detail: '@ts-ignore/@ts-expect-error usage', severity: 'medium' }) + const anyType = Grep({ pattern: ': any[^A-Z]|as any', glob: '*.{ts,tsx}', output_mode: 'content' }) + if (anyType) issues.push({ type: 'quality', detail: 'Untyped `any` usage', severity: 'medium' }) + const consoleLogs = Grep({ pattern: 'console\\.log', glob: '*.{ts,tsx,js,jsx}', path: 'src/', output_mode: 'content' }) + if (consoleLogs) issues.push({ type: 'quality', detail: 'console.log in source code', severity: 'low' }) + const emptyCatch = Grep({ pattern: 'catch\\s*\\([^)]*\\)\\s*\\{\\s*\\}', glob: '*.{ts,tsx,js,jsx}', output_mode: 'content', multiline: true }) + if (emptyCatch) issues.push({ type: 'quality', detail: 'Empty catch blocks', severity: 'high' }) + return issues +} + +function reviewSecurity(files) { + const issues = [] + const dangerousFns = Grep({ pattern: '\\beval\\b|\\bexec\\b|innerHTML|dangerouslySetInnerHTML', glob: '*.{ts,tsx,js,jsx}', output_mode: 'content' }) + if (dangerousFns) issues.push({ type: 'security', detail: 'Dangerous function: eval/exec/innerHTML', severity: 'critical' }) + const secrets = Grep({ pattern: 'password\\s*=\\s*["\']|secret\\s*=\\s*["\']|api_key\\s*=\\s*["\']', glob: '*.{ts,tsx,js,jsx,py}', output_mode: 'content', '-i': true }) + if (secrets) issues.push({ type: 'security', detail: 'Hardcoded secrets/passwords', severity: 'critical' }) + const sqlInjection = Grep({ pattern: 'query\\s*\\(\\s*`|execute\\s*\\(\\s*`', glob: '*.{ts,js,py}', output_mode: 'content', '-i': true }) + if (sqlInjection) issues.push({ type: 'security', detail: 'Potential SQL injection via template literals', severity: 'critical' }) + const xssRisk = Grep({ pattern: 'document\\.write|window\\.location\\s*=', glob: '*.{ts,tsx,js,jsx}', output_mode: 'content' }) + if (xssRisk) issues.push({ type: 'security', detail: 'Potential XSS vectors', severity: 'high' }) + return issues +} + +function reviewArchitecture(files, fileContents) { + const issues = [] + for (const [file, content] of Object.entries(fileContents)) { + const imports = content.match(/from\s+['"]([^'"]+)['"]/g) || [] + if (imports.filter(i => i.includes('../..')).length > 2) { + issues.push({ type: 'architecture', detail: `${file}: excessive parent imports (layering violation)`, severity: 'medium' }) + } + if (content.split('\n').length > 500) { + issues.push({ type: 'architecture', detail: `${file}: ${content.split('\n').length} lines - consider splitting`, severity: 'low' }) + } + } + return issues +} + +function verifyRequirements(plan, fileContents) { + const issues = [] + for (const planTask of (plan.tasks || [])) { + for (const criterion of (planTask.acceptance || [])) { + const keywords = criterion.toLowerCase().split(/\s+/).filter(w => w.length > 4) + const hasEvidence = keywords.some(kw => Object.values(fileContents).some(c => c.toLowerCase().includes(kw))) + if (!hasEvidence) { + issues.push({ type: 'requirement', detail: `Acceptance criterion may not be met: "${criterion}" (${planTask.title})`, severity: 'high' }) + } + } + } + return issues +} +``` + +**Spec Quality — 4-Dimension Scoring:** + +```javascript +if (reviewMode === 'spec') { + const scores = { completeness: 0, consistency: 0, traceability: 0, depth: 0 } + + // Completeness (25%): all sections present with content + function scoreCompleteness(docs) { + let score = 0 + const checks = [ + { name: 'spec-config.json', present: !!docs.config, weight: 5 }, + { name: 'discovery-context.json', present: !!docs.discoveryContext, weight: 10 }, + { name: 'product-brief.md', present: !!docs.productBrief, weight: 20 }, + { name: 'requirements/_index.md', present: !!docs.requirementsIndex, weight: 15 }, + { name: 'REQ-* files', present: docs.requirements.length > 0, weight: 10 }, + { name: 'architecture/_index.md', present: !!docs.architectureIndex, weight: 15 }, + { name: 'ADR-* files', present: docs.adrs.length > 0, weight: 10 }, + { name: 'epics/_index.md', present: !!docs.epicsIndex, weight: 10 }, + { name: 'EPIC-* files', present: docs.epics.length > 0, weight: 5 } + ] + checks.forEach(c => { if (c.present) score += c.weight }) + return { score, issues: checks.filter(c => !c.present).map(c => `Missing: ${c.name}`) } + } + + // Consistency (25%): terminology, format, references + function scoreConsistency(docs) { + let score = 100 + const issues = [] + const sessionId = docs.config?.session_id + if (sessionId && docs.productBrief && !docs.productBrief.includes(sessionId)) { + score -= 15; issues.push('Product Brief missing session_id reference') + } + const docsWithFM = [docs.productBrief, docs.requirementsIndex, docs.architectureIndex, docs.epicsIndex].filter(Boolean) + const hasFM = docsWithFM.map(d => /^---\n[\s\S]+?\n---/.test(d)) + if (!hasFM.every(v => v === hasFM[0])) { + score -= 20; issues.push('Inconsistent YAML frontmatter across documents') + } + return { score: Math.max(0, score), issues } + } + + // Traceability (25%): goals → reqs → arch → stories chain + function scoreTraceability(docs) { + let score = 0 + const issues = [] + if (docs.productBrief && docs.requirementsIndex) { + if (docs.requirements.some(r => /goal|brief|vision/i.test(r))) score += 25 + else issues.push('Requirements lack references to Product Brief goals') + } + if (docs.requirementsIndex && docs.architectureIndex) { + if (docs.adrs.some(a => /REQ-|requirement/i.test(a))) score += 25 + else issues.push('Architecture ADRs lack requirement references') + } + if (docs.requirementsIndex && docs.epicsIndex) { + if (docs.epics.some(e => /REQ-|requirement/i.test(e))) score += 25 + else issues.push('Epics/Stories lack requirement tracing') + } + if (score >= 50) score += 25 + return { score: Math.min(100, score), issues } + } + + // Depth (25%): AC testable, ADRs justified, stories estimable + function scoreDepth(docs) { + let score = 100 + const issues = [] + if (!docs.requirements.some(r => /acceptance|criteria|验收/i.test(r) && r.length > 200)) { + score -= 25; issues.push('Acceptance criteria may lack specificity') + } + if (docs.adrs.length > 0 && !docs.adrs.some(a => /alternative|替代|pros|cons/i.test(a))) { + score -= 25; issues.push('ADRs lack alternatives analysis') + } + if (docs.epics.length > 0 && !docs.epics.some(e => /\b[SMLX]{1,2}\b|Small|Medium|Large/.test(e))) { + score -= 25; issues.push('Stories lack size estimates') + } + if (![docs.architectureIndex, docs.epicsIndex].some(d => d && /```mermaid/.test(d))) { + score -= 10; issues.push('Missing Mermaid diagrams') + } + return { score: Math.max(0, score), issues } + } + + const completenessResult = scoreCompleteness(documents) + const consistencyResult = scoreConsistency(documents) + const traceabilityResult = scoreTraceability(documents) + const depthResult = scoreDepth(documents) + + scores.completeness = completenessResult.score + scores.consistency = consistencyResult.score + scores.traceability = traceabilityResult.score + scores.depth = depthResult.score + + const overallScore = (scores.completeness + scores.consistency + scores.traceability + scores.depth) / 4 + const qualityGate = overallScore >= 80 ? 'PASS' : overallScore >= 60 ? 'REVIEW' : 'FAIL' + const allSpecIssues = [...completenessResult.issues, ...consistencyResult.issues, ...traceabilityResult.issues, ...depthResult.issues] +} +``` + +### Phase 4: Report Generation (Branch by Mode) + +**Code Review — Generate Recommendations:** + +```javascript +if (reviewMode === 'code') { + const totalIssues = Object.values(findings).flat().length + const recommendations = [] + if (hasCritical) recommendations.push('Fix all critical security issues before merging') + if (findings.high.length > 0) recommendations.push('Address high severity issues in a follow-up') + if (findings.medium.length > 3) recommendations.push('Consider refactoring to reduce medium severity issues') +} +``` + +**Spec Quality — Generate Reports:** + +```javascript +if (reviewMode === 'spec') { + // Generate readiness-report.md + const readinessReport = `--- +session_id: ${documents.config?.session_id || 'unknown'} +phase: 6 +document_type: readiness-report +status: complete +generated_at: ${new Date().toISOString()} +version: 1 +--- + +# Readiness Report + +## Quality Scores +| Dimension | Score | Weight | +|-----------|-------|--------| +| Completeness | ${scores.completeness}% | 25% | +| Consistency | ${scores.consistency}% | 25% | +| Traceability | ${scores.traceability}% | 25% | +| Depth | ${scores.depth}% | 25% | +| **Overall** | **${overallScore.toFixed(1)}%** | **100%** | + +## Quality Gate: ${qualityGate} + +## Issues Found +${allSpecIssues.map(i => '- ' + i).join('\n') || 'None'} + +## Document Inventory +${Object.entries(docInventory).map(([k, v]) => '- ' + k + ': ' + (v === true ? '✓' : v === false ? '✗' : v)).join('\n')} +` + Write(`${sessionFolder}/readiness-report.md`, readinessReport) + + // Generate spec-summary.md + const specSummary = `--- +session_id: ${documents.config?.session_id || 'unknown'} +phase: 6 +document_type: spec-summary +status: complete +generated_at: ${new Date().toISOString()} +version: 1 +--- + +# Specification Summary + +**Topic**: ${documents.config?.topic || 'N/A'} +**Complexity**: ${documents.config?.complexity || 'N/A'} +**Quality Score**: ${overallScore.toFixed(1)}% (${qualityGate}) +**Discussion Rounds**: ${documents.discussions.length} + +## Key Deliverables +- Product Brief: ${docInventory.productBrief ? '✓' : '✗'} +- Requirements (PRD): ${docInventory.requirements ? '✓ (' + documents.requirements.length + ' items)' : '✗'} +- Architecture: ${docInventory.architecture ? '✓ (' + documents.adrs.length + ' ADRs)' : '✗'} +- Epics & Stories: ${docInventory.epics ? '✓ (' + documents.epics.length + ' epics)' : '✗'} + +## Next Steps +${qualityGate === 'PASS' ? '- Ready for handoff to execution workflows' : + qualityGate === 'REVIEW' ? '- Address review items, then proceed to execution' : + '- Fix critical issues before proceeding'} +` + Write(`${sessionFolder}/spec-summary.md`, specSummary) +} +``` + +### Phase 5: Report to Coordinator (Branch by Mode) + +**Code Review Report:** + +```javascript +if (reviewMode === 'code') { + mcp__ccw-tools__team_msg({ + operation: "log", team: teamName, + from: "reviewer", to: "coordinator", + type: hasCritical ? "fix_required" : "review_result", + summary: `REVIEW ${verdict}: ${totalIssues}个发现 (critical=${findings.critical.length}, high=${findings.high.length})`, + data: { verdict, critical: findings.critical.length, high: findings.high.length, medium: findings.medium.length, low: findings.low.length } + }) + + SendMessage({ + type: "message", + recipient: "coordinator", + content: `## Code Review Report + +**Task**: ${task.subject} +**Verdict**: ${verdict} +**Files Reviewed**: ${changedFiles.length} +**Total Findings**: ${totalIssues} + +### Finding Summary +- Critical: ${findings.critical.length} +- High: ${findings.high.length} +- Medium: ${findings.medium.length} +- Low: ${findings.low.length} + +${findings.critical.length > 0 ? '### Critical Issues\n' + findings.critical.map(f => '- [' + f.type.toUpperCase() + '] ' + f.detail).join('\n') + '\n' : ''} +${findings.high.length > 0 ? '### High Severity\n' + findings.high.map(f => '- [' + f.type.toUpperCase() + '] ' + f.detail).join('\n') + '\n' : ''} +### Recommendations +${recommendations.map(r => '- ' + r).join('\n')} + +${plan ? '### Requirement Verification\n' + (plan.tasks || []).map(t => '- **' + t.title + '**: ' + (requirementIssues.filter(i => i.detail.includes(t.title)).length === 0 ? 'Met' : 'Needs verification')).join('\n') : ''}`, + summary: `Review: ${verdict} (${totalIssues} findings)` + }) + + if (!hasCritical) { + TaskUpdate({ taskId: task.id, status: 'completed' }) + } + // If critical, keep in_progress for coordinator to create fix tasks +} +``` + +**Spec Quality Report:** + +```javascript +if (reviewMode === 'spec') { + mcp__ccw-tools__team_msg({ + operation: "log", team: teamName, + from: "reviewer", to: "coordinator", + type: qualityGate === 'FAIL' ? "fix_required" : "quality_result", + summary: `质量检查 ${qualityGate}: ${overallScore.toFixed(1)}分 (完整性${scores.completeness}/一致性${scores.consistency}/追溯${scores.traceability}/深度${scores.depth})`, + data: { gate: qualityGate, score: overallScore, issues: allSpecIssues } + }) + + SendMessage({ + type: "message", + recipient: "coordinator", + content: `## 质量审查报告 + +**Task**: ${task.subject} +**总分**: ${overallScore.toFixed(1)}% +**Gate**: ${qualityGate} + +### 评分详情 +| 维度 | 分数 | +|------|------| +| 完整性 | ${scores.completeness}% | +| 一致性 | ${scores.consistency}% | +| 可追溯性 | ${scores.traceability}% | +| 深度 | ${scores.depth}% | + +### 问题列表 (${allSpecIssues.length}) +${allSpecIssues.map(i => '- ' + i).join('\n') || '无问题'} + +### 文档清单 +${Object.entries(docInventory).map(([k, v]) => '- ' + k + ': ' + (typeof v === 'boolean' ? (v ? '✓' : '✗') : v)).join('\n')} + +### 输出位置 +- 就绪报告: ${sessionFolder}/readiness-report.md +- 执行摘要: ${sessionFolder}/spec-summary.md + +${qualityGate === 'PASS' ? '质量达标,可进入最终讨论轮次 DISCUSS-006。' : + qualityGate === 'REVIEW' ? '质量基本达标但有改进空间,建议在讨论中审查。' : + '质量未达标,建议创建 DRAFT-fix 任务修复关键问题。'}`, + summary: `质量 ${qualityGate}: ${overallScore.toFixed(1)}分` + }) + + if (qualityGate !== 'FAIL') { + TaskUpdate({ taskId: task.id, status: 'completed' }) + } +} + +// Check for next REVIEW-* or QUALITY-* task → back to Phase 1 +``` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| No REVIEW-*/QUALITY-* tasks available | Idle, wait for coordinator assignment | +| Plan file not found (code review) | Review without requirement verification, note in report | +| No changed files detected | Report to coordinator, may need manual file list | +| Documents missing (spec quality) | Score as 0 for completeness, report to coordinator | +| Cannot parse YAML frontmatter | Skip consistency check for that document | +| Grep pattern errors | Skip specific check, continue with remaining | +| CLI analysis timeout | Report partial results, note incomplete analysis | +| Session folder not found | Notify coordinator, request session path | +| Unexpected error | Log error via team_msg, report to coordinator | diff --git a/.claude/commands/team/test.md b/.claude/skills/team-lifecycle/roles/tester.md similarity index 52% rename from .claude/commands/team/test.md rename to .claude/skills/team-lifecycle/roles/tester.md index 34fba44b..c00903c2 100644 --- a/.claude/commands/team/test.md +++ b/.claude/skills/team-lifecycle/roles/tester.md @@ -1,133 +1,68 @@ ---- -name: test -description: Team tester - 自适应测试修复循环、渐进式测试、报告结果给coordinator -argument-hint: "" -allowed-tools: SendMessage(*), TaskUpdate(*), TaskList(*), TaskGet(*), TodoWrite(*), Read(*), Write(*), Edit(*), Bash(*), Glob(*), Grep(*), Task(*) -group: team ---- +# Role: tester -# Team Test Command (/team:test) +Adaptive test-fix cycle with progressive testing strategy. Detects test framework, applies multi-strategy fixes, and reports results to coordinator. -## Overview +## Role Identity -Team tester role command. Operates as a teammate within an Agent Team, responsible for test execution with adaptive fix cycles and progressive testing. Reports results to the coordinator. +- **Name**: `tester` +- **Task Prefix**: `TEST-*` +- **Responsibility**: Detect Framework → Run Tests → Fix Cycle → Report Results +- **Communication**: SendMessage to coordinator only -**Core capabilities:** -- Task discovery from shared team task list (TEST-* tasks) -- Test framework auto-detection (jest/vitest/pytest/mocha) -- Adaptive strategy engine: conservative → aggressive → surgical -- Progressive testing: affected tests during iterations, full suite for final validation -- Fix cycle with max iterations and quality gate (>= 95% pass rate) -- Structured result reporting to coordinator +## Message Types -## Role Definition +| Type | Direction | Trigger | Description | +|------|-----------|---------|-------------| +| `test_result` | tester → coordinator | Test cycle ends (pass or max iterations) | With pass rate, iteration count, remaining failures | +| `impl_progress` | tester → coordinator | Fix cycle intermediate progress | Optional, for long fix cycles (iteration > 5) | +| `fix_required` | tester → coordinator | Found issues beyond tester scope | Architecture/design problems needing executor | +| `error` | tester → coordinator | Framework unavailable or crash | Command not found, timeout, environment issues | -**Name**: `tester` -**Responsibility**: Run tests → Fix cycle → Report results -**Communication**: SendMessage to coordinator only +## Message Bus -## 消息总线 - -每次 SendMessage **前**,必须调用 `mcp__ccw-tools__team_msg` 记录消息: +Before every `SendMessage`, MUST call `mcp__ccw-tools__team_msg` to log: ```javascript -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "", summary: "<摘要>" }) +// Test result +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "test_result", summary: "TEST passed: 98% pass rate, 3 iterations", data: { passRate: 98, iterations: 3, total: 50, passed: 49 } }) + +// Progress update (long fix cycles) +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "impl_progress", summary: "Fix iteration 6: 85% pass rate" }) + +// Error report +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "error", summary: "vitest command not found, falling back to npm test" }) ``` -### 支持的 Message Types +### CLI Fallback -| Type | 方向 | 触发时机 | 说明 | -|------|------|----------|------| -| `test_result` | tester → coordinator | 测试循环结束(通过或达到最大迭代) | 附带 pass rate、迭代次数、剩余失败 | -| `impl_progress` | tester → coordinator | 修复循环中间进度 | 可选,长时间修复时使用(如迭代>5) | -| `fix_required` | tester → coordinator | 测试发现需要 executor 修复的问题 | 超出 tester 修复能力的架构/设计问题 | -| `error` | tester → coordinator | 测试框架不可用或测试执行崩溃 | 命令未找到、超时、环境问题等 | - -### 调用示例 +When `mcp__ccw-tools__team_msg` MCP is unavailable: ```javascript -// 测试通过 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "test_result", summary: "TEST-001通过: 98% pass rate, 3次迭代", data: { passRate: 98, iterations: 3, total: 42, passed: 41, failed: 1 } }) - -// 测试未达标 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "test_result", summary: "TEST-001未达标: 82% pass rate, 10次迭代已用完", data: { passRate: 82, iterations: 10, criticalFailures: 2 } }) - -// 需要 executor 修复 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "fix_required", summary: "数据库连接池配置导致集成测试全部失败, 需executor修复" }) - -// 错误上报 -mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "error", summary: "jest命令未找到, 请确认测试框架已安装" }) +Bash(`ccw team log --team "${teamName}" --from "tester" --to "coordinator" --type "test_result" --summary "TEST passed: 98% pass rate" --data '{"passRate":98,"iterations":3}' --json`) ``` -### CLI 回退 - -当 `mcp__ccw-tools__team_msg` MCP 不可用时,使用 `ccw team` CLI 作为等效回退: - -```javascript -// 回退: 将 MCP 调用替换为 Bash CLI(参数一一对应) -Bash(`ccw team log --team "${teamName}" --from "tester" --to "coordinator" --type "test_result" --summary "TEST-001通过: 98% pass rate" --data '{"passRate":98,"iterations":3}' --json`) -``` - -**参数映射**: `team_msg(params)` → `ccw team log --team --from tester --to coordinator --type --summary "" [--data ''] [--json]` - -## Execution Process - -``` -Phase 1: Task Discovery - ├─ TaskList to find unblocked TEST-* tasks assigned to me - ├─ TaskGet to read full task details - └─ TaskUpdate to mark in_progress - -Phase 2: Test Framework Detection - ├─ Detect framework: jest/vitest/pytest/mocha - ├─ Identify test command from package.json/pyproject.toml - └─ Locate affected test files based on changed files - -Phase 3: Test Execution & Fix Cycle (max 10 iterations) - ├─ Strategy Engine: - │ ├─ Iteration 1-2: Conservative (single targeted fix) - │ ├─ Pass rate > 80% + similar failures: Aggressive (batch fix) - │ └─ Regression detected (drop > 10%): Surgical (minimal + rollback) - ├─ Progressive Testing: - │ ├─ Iterations: run affected tests only - │ └─ Final: full test suite validation - └─ Quality Gate: pass rate >= 95% - -Phase 4: Result Analysis - ├─ Calculate final pass rate - ├─ Classify failure severity (critical/high/medium/low) - └─ Generate test summary - -Phase 5: Report to Coordinator - ├─ SendMessage with test results - ├─ >= 95%: mark TEST task completed - └─ < 95% after max iterations: report needs intervention -``` - -## Implementation +## Execution (5-Phase) ### Phase 1: Task Discovery ```javascript -// Find my assigned TEST tasks const tasks = TaskList() -const myTestTasks = tasks.filter(t => +const myTasks = tasks.filter(t => t.subject.startsWith('TEST-') && t.owner === 'tester' && t.status === 'pending' && t.blockedBy.length === 0 ) -if (myTestTasks.length === 0) return // idle +if (myTasks.length === 0) return // idle -const task = TaskGet({ taskId: myTestTasks[0].id }) +const task = TaskGet({ taskId: myTasks[0].id }) TaskUpdate({ taskId: task.id, status: 'in_progress' }) ``` ### Phase 2: Test Framework Detection ```javascript -// Detect test framework function detectTestFramework() { // Check package.json try { @@ -144,17 +79,15 @@ function detectTestFramework() { if (pyproject.includes('pytest')) return { framework: 'pytest', command: 'pytest' } } catch {} - // Fallback return { framework: 'unknown', command: 'npm test' } } const testConfig = detectTestFramework() -// Locate affected test files +// Locate affected test files from changed files function findAffectedTests(changedFiles) { const testFiles = [] for (const file of changedFiles) { - // Convention: src/foo.ts → tests/foo.test.ts or __tests__/foo.test.ts const testVariants = [ file.replace(/\/src\//, '/tests/').replace(/\.(ts|js|tsx|jsx)$/, '.test.$1'), file.replace(/\/src\//, '/__tests__/').replace(/\.(ts|js|tsx|jsx)$/, '.test.$1'), @@ -169,7 +102,6 @@ function findAffectedTests(changedFiles) { return [...new Set(testFiles)] } -// Extract changed files from task description or git diff const changedFiles = Bash(`git diff --name-only HEAD~1 2>/dev/null || git diff --name-only --cached`).split('\n').filter(Boolean) const affectedTests = findAffectedTests(changedFiles) ``` @@ -202,20 +134,14 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) { previousPassRate = currentPassRate currentPassRate = results.passRate - // Record iteration iterationHistory.push({ - iteration, - pass_rate: currentPassRate, - strategy: strategy, - failed_tests: results.failedTests, - total: results.total, - passed: results.passed + iteration, pass_rate: currentPassRate, strategy, + failed_tests: results.failedTests, total: results.total, passed: results.passed }) // Quality gate check if (currentPassRate >= PASS_RATE_TARGET) { if (!isFullSuite) { - // Run full suite for final validation const fullOutput = Bash(`${testConfig.command} 2>&1 || true`, { timeout: 300000 }) const fullResults = parseTestResults(fullOutput, testConfig.framework) currentPassRate = fullResults.passRate @@ -229,24 +155,25 @@ for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) { // Apply fixes based on strategy applyFixes(results.failedTests, strategy, testOutput) + + // Progress update for long cycles + if (iteration > 5) { + mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "impl_progress", summary: `修复迭代${iteration}: ${currentPassRate}% pass rate` }) + } } // Strategy Engine function selectStrategy(iteration, passRate, prevPassRate, history) { // Regression detection if (prevPassRate > 0 && passRate < prevPassRate - 10) return 'surgical' - // Iteration-based default if (iteration <= 2) return 'conservative' - // Pattern-based upgrade if (passRate > 80) { - // Check if failures are similar (same test files, same error patterns) const recentFailures = history.slice(-2).flatMap(h => h.failed_tests) const uniqueFailures = [...new Set(recentFailures)] if (uniqueFailures.length <= recentFailures.length * 0.6) return 'aggressive' } - return 'conservative' } @@ -254,26 +181,13 @@ function selectStrategy(iteration, passRate, prevPassRate, history) { function applyFixes(failedTests, strategy, testOutput) { switch (strategy) { case 'conservative': - // Fix one failure at a time - // Read failing test, understand error, apply targeted fix - if (failedTests.length > 0) { - const target = failedTests[0] - // Analyze error message from testOutput - // Read source file and test file - // Apply minimal fix - } + // Fix one failure at a time - read failing test, understand error, apply targeted fix break - case 'aggressive': - // Batch fix similar failures - // Group by error pattern - // Apply fixes to all related failures + // Batch fix similar failures - group by error pattern, apply fixes to all related break - case 'surgical': - // Minimal changes, consider rollback - // Only fix the most critical failure - // Verify no regression + // Minimal changes, consider rollback - fix most critical failure only break } } @@ -281,40 +195,28 @@ function applyFixes(failedTests, strategy, testOutput) { // Test result parser function parseTestResults(output, framework) { let passed = 0, failed = 0, total = 0, failedTests = [] - if (framework === 'jest' || framework === 'vitest') { const passMatch = output.match(/(\d+) passed/) const failMatch = output.match(/(\d+) failed/) passed = passMatch ? parseInt(passMatch[1]) : 0 failed = failMatch ? parseInt(failMatch[1]) : 0 total = passed + failed - - // Extract failed test names const failPattern = /FAIL\s+(.+)/g let m while ((m = failPattern.exec(output)) !== null) failedTests.push(m[1].trim()) } else if (framework === 'pytest') { const summaryMatch = output.match(/(\d+) passed.*?(\d+) failed/) - if (summaryMatch) { - passed = parseInt(summaryMatch[1]) - failed = parseInt(summaryMatch[2]) - } + if (summaryMatch) { passed = parseInt(summaryMatch[1]); failed = parseInt(summaryMatch[2]) } total = passed + failed } - - return { - passed, failed, total, - passRate: total > 0 ? Math.round((passed / total) * 100) : 100, - failedTests - } + return { passed, failed, total, passRate: total > 0 ? Math.round((passed / total) * 100) : 100, failedTests } } ``` ### Phase 4: Result Analysis ```javascript -// Classify failure severity -function classifyFailures(failedTests, testOutput) { +function classifyFailures(failedTests) { return failedTests.map(test => { const testLower = test.toLowerCase() let severity = 'low' @@ -326,11 +228,7 @@ function classifyFailures(failedTests, testOutput) { }) } -const classifiedFailures = classifyFailures( - iterationHistory[iterationHistory.length - 1]?.failed_tests || [], - '' // last test output -) - +const classifiedFailures = classifyFailures(iterationHistory[iterationHistory.length - 1]?.failed_tests || []) const hasCriticalFailures = classifiedFailures.some(f => f.severity === 'critical') ``` @@ -340,6 +238,14 @@ const hasCriticalFailures = classifiedFailures.some(f => f.severity === 'critica const finalIteration = iterationHistory[iterationHistory.length - 1] const success = currentPassRate >= PASS_RATE_TARGET +mcp__ccw-tools__team_msg({ + operation: "log", team: teamName, + from: "tester", to: "coordinator", + type: "test_result", + summary: `TEST${success ? '通过' : '未达标'}: ${currentPassRate}% pass rate, ${iterationHistory.length}次迭代`, + data: { passRate: currentPassRate, iterations: iterationHistory.length, total: finalIteration?.total || 0, passed: finalIteration?.passed || 0 } +}) + SendMessage({ type: "message", recipient: "coordinator", @@ -369,34 +275,20 @@ if (success) { TaskUpdate({ taskId: task.id, status: 'completed' }) } else { // Keep in_progress, coordinator decides next steps - SendMessage({ - type: "message", - recipient: "coordinator", - content: `Test pass rate ${currentPassRate}% is below ${PASS_RATE_TARGET}% after ${MAX_ITERATIONS} iterations. Need coordinator decision on next steps.`, - summary: "Test target not met, need guidance" - }) } -// Check for next TEST task -const nextTasks = TaskList().filter(t => - t.subject.startsWith('TEST-') && - t.owner === 'tester' && - t.status === 'pending' && - t.blockedBy.length === 0 -) - -if (nextTasks.length > 0) { - // Continue with next task -} +// Check for next TEST task → back to Phase 1 ``` ## Error Handling | Scenario | Resolution | |----------|------------| +| No TEST-* tasks available | Idle, wait for coordinator assignment | | Test command not found | Detect framework, try alternatives (npm test, pytest, etc.) | | Test execution timeout | Reduce test scope, retry with affected tests only | | Regression detected (pass rate drops > 10%) | Switch to surgical strategy, consider rollback | | Stuck tests (same failure 3+ iterations) | Report to coordinator, suggest different approach | | Max iterations reached < 95% | Report failure details, let coordinator decide | | No test files found | Report to coordinator, suggest test generation needed | +| Unexpected error | Log error via team_msg, report to coordinator | diff --git a/.claude/skills/team-lifecycle/roles/writer.md b/.claude/skills/team-lifecycle/roles/writer.md new file mode 100644 index 00000000..f2631063 --- /dev/null +++ b/.claude/skills/team-lifecycle/roles/writer.md @@ -0,0 +1,192 @@ +# Role: writer + +Product Brief, Requirements/PRD, Architecture, and Epics & Stories document generation. Maps to spec-generator Phases 2-5. + +## Role Identity + +- **Name**: `writer` +- **Task Prefix**: `DRAFT-*` +- **Responsibility**: Load Context → Generate Document → Incorporate Feedback → Report +- **Communication**: SendMessage to coordinator only + +## Message Types + +| Type | Direction | Trigger | Description | +|------|-----------|---------|-------------| +| `draft_ready` | writer → coordinator | Document writing complete | With document path and type | +| `draft_revision` | writer → coordinator | Document revised and resubmitted | Describes changes made | +| `impl_progress` | writer → coordinator | Long writing progress | Multi-document stage progress | +| `error` | writer → coordinator | Unrecoverable error | Template missing, insufficient context, etc. | + +## Message Bus + +Before every `SendMessage`, MUST call `mcp__ccw-tools__team_msg` to log: + +```javascript +// Document ready +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "writer", to: "coordinator", type: "draft_ready", summary: "Product Brief complete", ref: `${sessionFolder}/product-brief.md` }) + +// Document revision +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "writer", to: "coordinator", type: "draft_revision", summary: "Requirements revised per discussion feedback" }) + +// Error report +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "writer", to: "coordinator", type: "error", summary: "Input artifact missing, cannot generate document" }) +``` + +### CLI Fallback + +When `mcp__ccw-tools__team_msg` MCP is unavailable: + +```javascript +Bash(`ccw team log --team "${teamName}" --from "writer" --to "coordinator" --type "draft_ready" --summary "Brief complete" --ref "${sessionFolder}/product-brief.md" --json`) +``` + +## Execution (5-Phase) + +### Phase 1: Task Discovery + +```javascript +const tasks = TaskList() +const myTasks = tasks.filter(t => + t.subject.startsWith('DRAFT-') && + t.owner === 'writer' && + t.status === 'pending' && + t.blockedBy.length === 0 +) + +if (myTasks.length === 0) return // idle + +const task = TaskGet({ taskId: myTasks[0].id }) +TaskUpdate({ taskId: task.id, status: 'in_progress' }) +``` + +### Phase 2: Context & Discussion Loading + +```javascript +// Extract session folder from task description +const sessionMatch = task.description.match(/Session:\s*(.+)/) +const sessionFolder = sessionMatch ? sessionMatch[1].trim() : '' + +// Load session config +let specConfig = null +try { specConfig = JSON.parse(Read(`${sessionFolder}/spec-config.json`)) } catch {} + +// Determine document type from task subject +const docType = task.subject.includes('Product Brief') ? 'product-brief' + : task.subject.includes('Requirements') || task.subject.includes('PRD') ? 'requirements' + : task.subject.includes('Architecture') ? 'architecture' + : task.subject.includes('Epics') ? 'epics' + : 'unknown' + +// Load discussion feedback (from preceding DISCUSS task) +const discussionFiles = { + 'product-brief': 'discussions/discuss-001-scope.md', + 'requirements': 'discussions/discuss-002-brief.md', + 'architecture': 'discussions/discuss-003-requirements.md', + 'epics': 'discussions/discuss-004-architecture.md' +} +let discussionFeedback = null +try { discussionFeedback = Read(`${sessionFolder}/${discussionFiles[docType]}`) } catch {} + +// Load prior documents progressively +const priorDocs = {} +if (docType !== 'product-brief') { + try { priorDocs.discoveryContext = Read(`${sessionFolder}/discovery-context.json`) } catch {} +} +if (['requirements', 'architecture', 'epics'].includes(docType)) { + try { priorDocs.productBrief = Read(`${sessionFolder}/product-brief.md`) } catch {} +} +if (['architecture', 'epics'].includes(docType)) { + try { priorDocs.requirementsIndex = Read(`${sessionFolder}/requirements/_index.md`) } catch {} +} +if (docType === 'epics') { + try { priorDocs.architectureIndex = Read(`${sessionFolder}/architecture/_index.md`) } catch {} +} +``` + +### Phase 3: Document Generation (type-specific) + +Route to specific generation logic based on document type: + +**DRAFT-001: Product Brief** — Multi-perspective analysis using 3 parallel CLI analyses (product/technical/user), then synthesize into product-brief.md with YAML frontmatter. + +**DRAFT-002: Requirements/PRD** — Expand requirements from Product Brief via CLI. Generate REQ-NNN functional requirements + NFR-{type}-NNN non-functional requirements with MoSCoW prioritization. Output to requirements/ directory. + +**DRAFT-003: Architecture** — Design system architecture from requirements via CLI. Generate architecture/_index.md + ADR-*.md files with tech stack, component diagrams (Mermaid), and data model. + +**DRAFT-004: Epics & Stories** — Decompose requirements into EPIC-* with STORY-* user stories, cross-Epic dependency map, MVP scope definition, and execution order. Output to epics/ directory. + +Each uses CLI tools (gemini/codex/claude) for multi-perspective analysis, with discussion feedback integration from the preceding DISCUSS round. + +### Phase 4: Self-Validation + +```javascript +const validationChecks = { + has_frontmatter: /^---\n[\s\S]+?\n---/.test(docContent), + sections_complete: /* verify all required sections present */, + cross_references: docContent.includes('session_id'), + discussion_integrated: !discussionFeedback || docContent.includes('Discussion') +} + +const allValid = Object.values(validationChecks).every(v => v) +``` + +### Phase 5: Report to Coordinator + +```javascript +const docTypeLabel = { + 'product-brief': 'Product Brief', + 'requirements': 'Requirements/PRD', + 'architecture': 'Architecture Document', + 'epics': 'Epics & Stories' +} + +mcp__ccw-tools__team_msg({ + operation: "log", team: teamName, + from: "writer", to: "coordinator", + type: "draft_ready", + summary: `${docTypeLabel[docType]} 完成: ${allValid ? '验证通过' : '部分验证失败'}`, + ref: `${sessionFolder}/${outputPath}` +}) + +SendMessage({ + type: "message", + recipient: "coordinator", + content: `## 文档撰写结果 + +**Task**: ${task.subject} +**文档类型**: ${docTypeLabel[docType]} +**验证状态**: ${allValid ? 'PASS' : 'PARTIAL'} + +### 文档摘要 +${documentSummary} + +### 讨论反馈整合 +${discussionFeedback ? '已整合前序讨论反馈' : '首次撰写'} + +### 自验证结果 +${Object.entries(validationChecks).map(([k, v]) => '- ' + k + ': ' + (v ? 'PASS' : 'FAIL')).join('\n')} + +### 输出位置 +${sessionFolder}/${outputPath} + +文档已就绪,可进入讨论轮次。`, + summary: `${docTypeLabel[docType]} 就绪` +}) + +TaskUpdate({ taskId: task.id, status: 'completed' }) + +// Check for next DRAFT task → back to Phase 1 +``` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| No DRAFT-* tasks available | Idle, wait for coordinator assignment | +| Prior document not found | Notify coordinator, request prerequisite | +| CLI analysis failure | Retry with fallback tool, then direct generation | +| Template sections incomplete | Generate best-effort, note gaps in report | +| Discussion feedback contradicts prior docs | Note conflict in document, flag for next discussion | +| Session folder missing | Notify coordinator, request session path | +| Unexpected error | Log error via team_msg, report to coordinator | diff --git a/.claude/skills/team-lifecycle/specs/team-config.json b/.claude/skills/team-lifecycle/specs/team-config.json new file mode 100644 index 00000000..478637ef --- /dev/null +++ b/.claude/skills/team-lifecycle/specs/team-config.json @@ -0,0 +1,78 @@ +{ + "team_name": "team-lifecycle", + "team_display_name": "Team Lifecycle", + "description": "Unified team skill covering spec-to-dev-to-test full lifecycle", + "version": "1.0.0", + + "roles": { + "coordinator": { + "task_prefix": null, + "responsibility": "Pipeline orchestration, requirement clarification, task chain creation, message dispatch", + "message_types": ["plan_approved", "plan_revision", "task_unblocked", "fix_required", "error", "shutdown"] + }, + "analyst": { + "task_prefix": "RESEARCH", + "responsibility": "Seed analysis, codebase exploration, multi-dimensional context gathering", + "message_types": ["research_ready", "research_progress", "error"] + }, + "writer": { + "task_prefix": "DRAFT", + "responsibility": "Product Brief / PRD / Architecture / Epics document generation", + "message_types": ["draft_ready", "draft_revision", "impl_progress", "error"] + }, + "discussant": { + "task_prefix": "DISCUSS", + "responsibility": "Multi-perspective critique, consensus building, conflict escalation", + "message_types": ["discussion_ready", "discussion_blocked", "impl_progress", "error"] + }, + "planner": { + "task_prefix": "PLAN", + "responsibility": "Multi-angle code exploration, structured implementation planning", + "message_types": ["plan_ready", "plan_revision", "impl_progress", "error"] + }, + "executor": { + "task_prefix": "IMPL", + "responsibility": "Code implementation following approved plans", + "message_types": ["impl_complete", "impl_progress", "error"] + }, + "tester": { + "task_prefix": "TEST", + "responsibility": "Adaptive test-fix cycles, progressive testing, quality gates", + "message_types": ["test_result", "impl_progress", "fix_required", "error"] + }, + "reviewer": { + "task_prefix": "REVIEW", + "additional_prefixes": ["QUALITY"], + "responsibility": "Code review (REVIEW-*) + Spec quality validation (QUALITY-*)", + "message_types": ["review_result", "quality_result", "fix_required", "error"] + } + }, + + "pipelines": { + "spec-only": { + "description": "Specification pipeline: research → discuss → draft → quality", + "task_chain": [ + "RESEARCH-001", + "DISCUSS-001", "DRAFT-001", "DISCUSS-002", + "DRAFT-002", "DISCUSS-003", "DRAFT-003", "DISCUSS-004", + "DRAFT-004", "DISCUSS-005", "QUALITY-001", "DISCUSS-006" + ] + }, + "impl-only": { + "description": "Implementation pipeline: plan → implement → test + review", + "task_chain": ["PLAN-001", "IMPL-001", "TEST-001", "REVIEW-001"] + }, + "full-lifecycle": { + "description": "Full lifecycle: spec pipeline → implementation pipeline", + "task_chain": "spec-only + impl-only (PLAN-001 blockedBy DISCUSS-006)" + } + }, + + "collaboration_patterns": ["CP-1", "CP-2", "CP-4", "CP-5", "CP-6", "CP-10"], + + "session_dirs": { + "spec": ".workflow/.spec-team/{topic-slug}-{YYYY-MM-DD}/", + "impl": ".workflow/.team-plan/{task-slug}-{YYYY-MM-DD}/", + "messages": ".workflow/.team-msg/{team-name}/" + } +}