mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-14 02:42:04 +08:00
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-*)
This commit is contained in:
310
.claude/commands/team/spec-analyst.md
Normal file
310
.claude/commands/team/spec-analyst.md
Normal file
@@ -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: "<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 |
|
||||||
332
.claude/commands/team/spec-coordinate.md
Normal file
332
.claude/commands/team/spec-coordinate.md
Normal file
@@ -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: "<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: "<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: "<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: "<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 无法完成 | 降级为简化模式,跳过深度分析 |
|
||||||
484
.claude/commands/team/spec-discuss.md
Normal file
484
.claude/commands/team/spec-discuss.md
Normal file
@@ -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: "<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 |
|
||||||
481
.claude/commands/team/spec-reviewer.md
Normal file
481
.claude/commands/team/spec-reviewer.md
Normal file
@@ -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: "<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 |
|
||||||
481
.claude/commands/team/spec-writer.md
Normal file
481
.claude/commands/team/spec-writer.md
Normal file
@@ -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: "<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 |
|
||||||
Reference in New Issue
Block a user