From 0b67cd4bf416570d88a4bf72bb8312966a0e40ee Mon Sep 17 00:00:00 2001 From: catlog22 Date: Fri, 13 Feb 2026 10:52:19 +0800 Subject: [PATCH] feat: add spec team commands with structured discussion rounds Design 5 team commands based on spec-generator skill workflow, introducing multi-perspective discussion rounds between each phase: - spec-coordinate: workflow orchestration + discussion management - spec-analyst: seed analysis + codebase exploration (RESEARCH-*) - spec-writer: 4-type document generation (DRAFT-*) - spec-reviewer: 4-dimension quality scoring (QUALITY-*) - spec-discuss: multi-perspective critique + consensus building (DISCUSS-*) --- .claude/commands/team/spec-analyst.md | 310 +++++++++++++++ .claude/commands/team/spec-coordinate.md | 332 ++++++++++++++++ .claude/commands/team/spec-discuss.md | 484 +++++++++++++++++++++++ .claude/commands/team/spec-reviewer.md | 481 ++++++++++++++++++++++ .claude/commands/team/spec-writer.md | 481 ++++++++++++++++++++++ 5 files changed, 2088 insertions(+) create mode 100644 .claude/commands/team/spec-analyst.md create mode 100644 .claude/commands/team/spec-coordinate.md create mode 100644 .claude/commands/team/spec-discuss.md create mode 100644 .claude/commands/team/spec-reviewer.md create mode 100644 .claude/commands/team/spec-writer.md diff --git a/.claude/commands/team/spec-analyst.md b/.claude/commands/team/spec-analyst.md new file mode 100644 index 00000000..d6d530f4 --- /dev/null +++ b/.claude/commands/team/spec-analyst.md @@ -0,0 +1,310 @@ +--- +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: "代码库探索失败: 项目根目录无法识别" }) +``` + +## 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 new file mode 100644 index 00000000..1384c8ec --- /dev/null +++ b/.claude/commands/team/spec-coordinate.md @@ -0,0 +1,332 @@ +--- +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` + +## 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 new file mode 100644 index 00000000..1058c4fd --- /dev/null +++ b/.claude/commands/team/spec-discuss.md @@ -0,0 +1,484 @@ +--- +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" }) +``` + +## 讨论维度模型 + +每个讨论轮次从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 new file mode 100644 index 00000000..6c6a7a0f --- /dev/null +++ b/.claude/commands/team/spec-reviewer.md @@ -0,0 +1,481 @@ +--- +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"] } }) +``` + +## 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 new file mode 100644 index 00000000..3da77043 --- /dev/null +++ b/.claude/commands/team/spec-writer.md @@ -0,0 +1,481 @@ +--- +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" }) +``` + +## 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 |