feat: merge 10 team commands into unified team-lifecycle skill

Consolidate coordinate, plan, execute, test, review, spec-coordinate,
spec-analyst, spec-writer, spec-discuss, spec-reviewer into a single
team-lifecycle skill with role-based routing (Pattern B architecture).

- SKILL.md: role router with 8 roles, shared message bus, 3-mode pipeline
- coordinator: unified orchestrator for spec-only/impl-only/full-lifecycle
- 7 worker roles: analyst, writer, discussant, planner, executor, tester, reviewer
- reviewer: dual-prefix (REVIEW-*/QUALITY-*) auto-switching code/spec review
- Each role: 5-phase execution, message bus with CLI fallback, error handling
This commit is contained in:
catlog22
2026-02-13 22:58:47 +08:00
parent 25e27286b4
commit 696141ee66
19 changed files with 2420 additions and 3737 deletions

View File

@@ -1,228 +0,0 @@
---
name: coordinate
description: Team coordinator - 需求澄清、MVP路线图、创建持久化agent team、跨阶段协调plan/execute/test/review
argument-hint: "[--team-name=NAME] \"task description\""
allowed-tools: TeamCreate(*), TeamDelete(*), SendMessage(*), TaskCreate(*), TaskUpdate(*), TaskList(*), TaskGet(*), Task(*), AskUserQuestion(*), TodoWrite(*), Read(*), Bash(*), Glob(*), Grep(*)
group: team
---
# Team Coordinate Command (/team:coordinate)
纯调度协调器。需求澄清 → 创建 Team → 创建任务链 → 协调消息 → 持久循环。具体工作逻辑由各 teammate 调用自己的 skill 完成。
## 消息总线
所有 teammate 在 SendMessage 的**同时**必须调用 `mcp__ccw-tools__team_msg` 记录消息,实现持久化和用户可观测:
```javascript
// 记录消息(每个 teammate 发 SendMessage 前调用)
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "planner", to: "coordinator", type: "plan_ready", summary: "Plan就绪: 3个task", ref: ".workflow/.team-plan/auth-team/plan.json" })
// Coordinator 查看全部消息
mcp__ccw-tools__team_msg({ operation: "list", team: teamName })
// 按角色过滤
mcp__ccw-tools__team_msg({ operation: "list", team: teamName, from: "tester", last: 5 })
// 查看团队状态
mcp__ccw-tools__team_msg({ operation: "status", team: teamName })
// 读取特定消息
mcp__ccw-tools__team_msg({ operation: "read", team: teamName, id: "MSG-003" })
```
**日志位置**: `.workflow/.team-msg/{team-name}/messages.jsonl`
**消息类型**: `plan_ready | plan_approved | plan_revision | task_unblocked | impl_complete | impl_progress | test_result | review_result | fix_required | error | shutdown`
### CLI 回退
`mcp__ccw-tools__team_msg` MCP 不可用时,使用 `ccw team` CLI 作为等效回退:
```javascript
// 回退: 将 MCP 调用替换为 Bash CLI参数一一对应
// log
Bash(`ccw team log --team "${teamName}" --from "coordinator" --to "planner" --type "plan_approved" --summary "Plan已批准" --json`)
// list
Bash(`ccw team list --team "${teamName}" --last 10 --json`)
// list (带过滤)
Bash(`ccw team list --team "${teamName}" --from "tester" --last 5 --json`)
// status
Bash(`ccw team status --team "${teamName}" --json`)
// read
Bash(`ccw team read --team "${teamName}" --id "MSG-003" --json`)
```
**参数映射**: `team_msg(params)``ccw team <operation> --team <team> [--from/--to/--type/--summary/--ref/--data/--id/--last] [--json]`
## Usage
```bash
/team:coordinate "实现用户认证模块"
/team:coordinate --team-name=auth-team "实现JWT刷新令牌"
```
## Pipeline
```
需求 → [PLAN: planner] → coordinator 审批 → [IMPL: executor] → [TEST + REVIEW: tester] → 汇报 → 等待新需求/关闭
```
## Execution
### Phase 1: 需求澄清
解析 `$ARGUMENTS` 获取 `--team-name` 和任务描述。使用 AskUserQuestion 收集:
- MVP 范围(最小可行 / 功能完整 / 全面实现)
- 关键约束(向后兼容 / 遵循模式 / 测试覆盖 / 性能敏感)
简单任务可跳过澄清。
### Phase 2: 创建 Team + Spawn 3 Teammates
```javascript
TeamCreate({ team_name: teamName })
```
**Spawn 时只传角色和需求,工作细节由 skill 定义**
```javascript
// Planner
Task({
subagent_type: "general-purpose",
team_name: teamName,
name: "planner",
mode: "plan",
prompt: `你是 team "${teamName}" 的 PLANNER。
当你收到 PLAN 任务时,调用 Skill(skill="team:plan") 执行规划工作。
当前需求: ${taskDescription}
约束: ${constraints}
复杂度: ${complexity}
## 消息总线(必须)
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录:
mcp__ccw-tools__team_msg({ operation: "log", team: "${teamName}", from: "planner", to: "coordinator", type: "<type>", summary: "<摘要>", ref: "<文件路径>" })
工作流程:
1. TaskList → 找到分配给你的 PLAN-* 任务
2. Skill(skill="team:plan") 执行探索和规划
3. team_msg log + SendMessage 将 plan 摘要发给 coordinator
4. 等待 coordinator 审批或修改反馈
5. 审批通过 → TaskUpdate completed → 检查下一个 PLAN 任务`
})
// Executor
Task({
subagent_type: "general-purpose",
team_name: teamName,
name: "executor",
prompt: `你是 team "${teamName}" 的 EXECUTOR。
当你收到 IMPL 任务时,调用 Skill(skill="team:execute") 执行代码实现。
当前需求: ${taskDescription}
约束: ${constraints}
## 消息总线(必须)
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录:
mcp__ccw-tools__team_msg({ operation: "log", team: "${teamName}", from: "executor", to: "coordinator", type: "<type>", summary: "<摘要>" })
工作流程:
1. TaskList → 找到未阻塞的 IMPL-* 任务
2. Skill(skill="team:execute") 执行实现
3. team_msg log + SendMessage 报告完成状态和变更文件
4. TaskUpdate completed → 检查下一个 IMPL 任务`
})
// Tester (同时处理 TEST 和 REVIEW)
Task({
subagent_type: "general-purpose",
team_name: teamName,
name: "tester",
prompt: `你是 team "${teamName}" 的 TESTER同时负责测试和审查。
- 收到 TEST-* 任务 → 调用 Skill(skill="team:test") 执行测试修复循环
- 收到 REVIEW-* 任务 → 调用 Skill(skill="team:review") 执行代码审查
当前需求: ${taskDescription}
约束: ${constraints}
## 消息总线(必须)
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录:
mcp__ccw-tools__team_msg({ operation: "log", team: "${teamName}", from: "tester", to: "coordinator", type: "<type>", summary: "<摘要>" })
工作流程:
1. TaskList → 找到未阻塞的 TEST-* 或 REVIEW-* 任务
2. 根据任务类型调用对应 Skill
3. team_msg log + SendMessage 报告结果给 coordinator
4. TaskUpdate completed → 检查下一个任务`
})
```
### Phase 3: 创建任务链
```javascript
// PLAN-001 → IMPL-001 → TEST-001 + REVIEW-001
TaskCreate({ subject: "PLAN-001: 探索和规划实现", description: `${taskDescription}\n\n写入: .workflow/.team-plan/${teamName}/`, activeForm: "规划中" })
TaskUpdate({ taskId: planId, owner: "planner" })
TaskCreate({ subject: "IMPL-001: 实现已批准的计划", description: `${taskDescription}\n\nPlan: .workflow/.team-plan/${teamName}/plan.json`, activeForm: "实现中" })
TaskUpdate({ taskId: implId, owner: "executor", addBlockedBy: [planId] })
TaskCreate({ subject: "TEST-001: 测试修复循环", description: `${taskDescription}`, activeForm: "测试中" })
TaskUpdate({ taskId: testId, owner: "tester", addBlockedBy: [implId] })
TaskCreate({ subject: "REVIEW-001: 代码审查与需求验证", description: `${taskDescription}\n\nPlan: .workflow/.team-plan/${teamName}/plan.json`, activeForm: "审查中" })
TaskUpdate({ taskId: reviewId, owner: "tester", addBlockedBy: [implId] })
```
### Phase 4: 协调主循环
接收 teammate 消息,根据内容做调度决策。**每次做出决策前先 `team_msg list` 查看最近消息,每次做出决策后 `team_msg log` 记录**
| 收到消息 | 操作 |
|----------|------|
| Planner: plan 就绪 | 读取 plan → 审批/修改 → team_msg log(plan_approved/plan_revision) → TaskUpdate + SendMessage |
| Executor: 实现完成 | team_msg log(task_unblocked) → TaskUpdate IMPL completed → SendMessage 通知 tester |
| Tester: 测试结果 >= 95% | team_msg log(test_result) → TaskUpdate TEST completed |
| Tester: 测试结果 < 95% 且迭代 > 5 | team_msg log(error) → 上报用户 |
| Tester: 审查无 critical | team_msg log(review_result) → TaskUpdate REVIEW completed |
| Tester: 审查发现 critical | team_msg log(fix_required) → TaskCreate IMPL-fix → 分配 executor |
| 所有任务 completed | → Phase 5 |
**用户可随时查看团队状态**
```bash
# 用户在任意时刻调用查看
mcp__ccw-tools__team_msg({ operation: "status", team: teamName })
mcp__ccw-tools__team_msg({ operation: "list", team: teamName, last: 10 })
```
### Phase 5: 汇报 + 持久循环
汇总变更文件、测试通过率、审查结果报告用户。
```javascript
AskUserQuestion({
questions: [{
question: "当前需求已完成。下一步:",
header: "Next",
multiSelect: false,
options: [
{ label: "新需求", description: "提交新需求给当前团队" },
{ label: "关闭团队", description: "关闭所有 teammate 并清理" }
]
}]
})
// 新需求 → 回到 Phase 1复用 team新建 PLAN/IMPL/TEST/REVIEW 任务)
// 关闭 → shutdown_request 给每个 teammate → TeamDelete()
```
## 错误处理
| 场景 | 处理 |
|------|------|
| Teammate 无响应 | 发追踪消息2次无响应 → 重新 spawn |
| Plan 被拒 3+ 次 | Coordinator 自行规划 |
| 测试卡在 <80% 超 5 次迭代 | 上报用户 |
| Review 发现 critical | 创建 IMPL-fix 任务给 executor |

View File

@@ -1,373 +0,0 @@
---
name: execute
description: Team executor - 实现已批准的计划、编写代码、报告进度
argument-hint: ""
allowed-tools: SendMessage(*), TaskUpdate(*), TaskList(*), TaskGet(*), TodoWrite(*), Read(*), Write(*), Edit(*), Bash(*), Glob(*), Grep(*), Task(*)
group: team
---
# Team Execute Command (/team:execute)
## Overview
Team executor role command. Operates as a teammate within an Agent Team, responsible for implementing approved plans by writing code, self-validating, and reporting progress to the coordinator.
**Core capabilities:**
- Task discovery from shared team task list (IMPL-* tasks)
- Plan loading and task decomposition
- Code implementation following plan files list
- Self-validation: syntax checks, acceptance criteria verification
- Progress reporting to coordinator
- Sub-agent delegation for complex tasks
## Role Definition
**Name**: `executor`
**Responsibility**: Load plan → Implement code → Self-validate → Report completion
**Communication**: SendMessage to coordinator only
## 消息总线
每次 SendMessage **前**,必须调用 `mcp__ccw-tools__team_msg` 记录消息:
```javascript
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "executor", to: "coordinator", type: "<type>", summary: "<摘要>" })
```
### 支持的 Message Types
| Type | 方向 | 触发时机 | 说明 |
|------|------|----------|------|
| `impl_progress` | executor → coordinator | 完成一个 batch/子任务 | 报告当前进度百分比和完成的子任务 |
| `impl_complete` | executor → coordinator | 全部实现完成 | 附带变更文件列表和 acceptance 状态 |
| `error` | executor → coordinator | 遇到阻塞问题 | Plan 文件缺失、文件冲突、子代理失败等 |
### 调用示例
```javascript
// 进度更新
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "executor", to: "coordinator", type: "impl_progress", summary: "Batch 1/3 完成: auth middleware 已实现", data: { batch: 1, total: 3, files: ["src/middleware/auth.ts"] } })
// 实现完成
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "executor", to: "coordinator", type: "impl_complete", summary: "IMPL-001完成: 5个文件变更, acceptance全部满足", data: { changedFiles: 5, syntaxClean: true } })
// 错误上报
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "executor", to: "coordinator", type: "error", summary: "plan.json路径无效, 无法加载实现计划" })
```
### CLI 回退
`mcp__ccw-tools__team_msg` MCP 不可用时,使用 `ccw team` CLI 作为等效回退:
```javascript
// 回退: 将 MCP 调用替换为 Bash CLI参数一一对应
Bash(`ccw team log --team "${teamName}" --from "executor" --to "coordinator" --type "impl_complete" --summary "IMPL-001完成: 5个文件变更" --json`)
// 带 data 参数
Bash(`ccw team log --team "${teamName}" --from "executor" --to "coordinator" --type "impl_progress" --summary "Batch 1/3 完成" --data '{"batch":1,"total":3}' --json`)
```
**参数映射**: `team_msg(params)``ccw team log --team <team> --from executor --to coordinator --type <type> --summary "<text>" [--ref <path>] [--data '<json>'] [--json]`
## Execution Process
```
Phase 1: Task & Plan Loading
├─ TaskList to find unblocked IMPL-* tasks assigned to me
├─ TaskGet to read full task details
├─ TaskUpdate to mark in_progress
└─ Load plan.json from plan path in task description
Phase 2: Task Grouping
├─ Extract depends_on from plan tasks
├─ Independent tasks → parallel batch
└─ Dependent tasks → sequential batches
Phase 3: Code Implementation
├─ For each task in plan:
│ ├─ Read files list
│ ├─ Read reference patterns
│ ├─ Implement changes (Edit/Write)
│ ├─ Complex tasks → code-developer sub-agent
│ └─ Simple tasks → direct file editing
└─ SendMessage progress updates for complex tasks
Phase 4: Self-Validation
├─ Syntax check (tsc --noEmit for TypeScript)
├─ Verify acceptance criteria from plan
├─ Run affected unit tests (if identifiable)
└─ Fix any immediate issues
Phase 5: Completion Report
├─ Compile changed files list
├─ Summarize acceptance criteria status
├─ SendMessage report to coordinator
├─ Mark IMPL task completed
└─ Check TaskList for next IMPL task
```
## Implementation
### Phase 1: Task & Plan Loading
```javascript
// Find my assigned IMPL tasks
const tasks = TaskList()
const myImplTasks = tasks.filter(t =>
t.subject.startsWith('IMPL-') &&
t.owner === 'executor' &&
t.status === 'pending' &&
t.blockedBy.length === 0 // Not blocked
)
if (myImplTasks.length === 0) {
// No tasks available, idle
return
}
// Pick first available task (lowest ID)
const task = TaskGet({ taskId: myImplTasks[0].id })
TaskUpdate({ taskId: task.id, status: 'in_progress' })
// Extract plan path from task description
const planPathMatch = task.description.match(/\.workflow\/\.team-plan\/[^\s]+\/plan\.json/)
const planPath = planPathMatch ? planPathMatch[0] : null
if (!planPath) {
SendMessage({
type: "message",
recipient: "coordinator",
content: `Cannot find plan.json path in task description for ${task.subject}. Please provide plan location.`,
summary: "Plan path not found"
})
return
}
const plan = JSON.parse(Read(planPath))
```
### Phase 2: Task Grouping
```javascript
// Extract dependencies and group tasks
function extractDependencies(planTasks) {
const taskIdToIndex = {}
planTasks.forEach((t, i) => { taskIdToIndex[t.id] = i })
return planTasks.map((task, i) => {
const deps = (task.depends_on || [])
.map(depId => taskIdToIndex[depId])
.filter(idx => idx !== undefined && idx < i)
return { ...task, taskIndex: i, dependencies: deps }
})
}
function createBatches(planTasks) {
const tasksWithDeps = extractDependencies(planTasks)
const processed = new Set()
const batches = []
// Phase 1: Independent tasks → single parallel batch
const independent = tasksWithDeps.filter(t => t.dependencies.length === 0)
if (independent.length > 0) {
independent.forEach(t => processed.add(t.taskIndex))
batches.push({ type: 'parallel', tasks: independent })
}
// Phase 2+: Dependent tasks in order
let remaining = tasksWithDeps.filter(t => !processed.has(t.taskIndex))
while (remaining.length > 0) {
const ready = remaining.filter(t => t.dependencies.every(d => processed.has(d)))
if (ready.length === 0) break // circular dependency guard
ready.forEach(t => processed.add(t.taskIndex))
batches.push({ type: ready.length > 1 ? 'parallel' : 'sequential', tasks: ready })
remaining = remaining.filter(t => !processed.has(t.taskIndex))
}
return batches
}
const batches = createBatches(plan.tasks)
```
### Phase 3: Code Implementation
```javascript
// Unified Task Prompt Builder (from lite-execute)
function buildExecutionPrompt(planTask) {
return `
## ${planTask.title}
**Scope**: \`${planTask.scope}\` | **Action**: ${planTask.action || 'implement'}
### Files
${(planTask.files || []).map(f => `- **${f.path}** → \`${f.target}\`: ${f.change}`).join('\n')}
### How to do it
${planTask.description}
${(planTask.implementation || []).map(step => `- ${step}`).join('\n')}
### Reference
- Pattern: ${planTask.reference?.pattern || 'N/A'}
- Files: ${planTask.reference?.files?.join(', ') || 'N/A'}
### Done when
${(planTask.convergence?.criteria || []).map(c => `- [ ] ${c}`).join('\n')}
`
}
// Execute each batch
const changedFiles = []
const previousResults = []
for (const batch of batches) {
if (batch.tasks.length === 1 && isSimpleTask(batch.tasks[0])) {
// Simple task: direct implementation
const t = batch.tasks[0]
// Read target files, apply modifications using Edit/Write
for (const f of (t.files || [])) {
const content = Read(f.path)
// Apply change based on file entry description
Edit({ file_path: f.path, old_string: "...", new_string: "..." })
changedFiles.push(f.path)
}
} else {
// Complex task(s): delegate to code-developer sub-agent
const prompt = batch.tasks.map(buildExecutionPrompt).join('\n\n---\n')
Task({
subagent_type: "code-developer",
run_in_background: false,
description: batch.tasks.map(t => t.title).join(' | '),
prompt: `## Goal
${plan.summary}
## Tasks
${prompt}
## Context
### Project Guidelines
@.workflow/project-guidelines.json
Complete each task according to its "Done when" checklist.`
})
// Collect changed files from sub-agent results
batch.tasks.forEach(t => {
(t.files || []).forEach(f => changedFiles.push(f.path))
})
}
previousResults.push({
batchType: batch.type,
tasks: batch.tasks.map(t => t.title),
status: 'completed'
})
}
```
### Phase 4: Self-Validation
```javascript
// Step 1: Syntax check
const syntaxResult = Bash(`tsc --noEmit 2>&1 || true`)
const hasSyntaxErrors = syntaxResult.includes('error TS')
if (hasSyntaxErrors) {
// Attempt to fix syntax errors
// Parse error locations, apply fixes
console.log('Syntax errors detected, attempting fix...')
}
// Step 2: Verify acceptance criteria
const acceptanceStatus = plan.tasks.map(t => ({
title: t.title,
criteria: (t.convergence?.criteria || []).map(c => ({
criterion: c,
met: true // Evaluate based on implementation
}))
}))
// Step 3: Run affected tests (if identifiable)
const testFiles = changedFiles
.map(f => f.replace(/\/src\//, '/tests/').replace(/\.(ts|js)$/, '.test.$1'))
.filter(f => Bash(`test -f ${f} && echo exists || true`).includes('exists'))
if (testFiles.length > 0) {
const testResult = Bash(`npx jest ${testFiles.join(' ')} --passWithNoTests 2>&1 || true`)
// Parse test results
}
```
### Phase 5: Completion Report
```javascript
// Compile report
const report = {
task: task.subject,
changedFiles: [...new Set(changedFiles)],
newFiles: changedFiles.filter(f => /* detect new files */),
acceptanceStatus: acceptanceStatus,
syntaxClean: !hasSyntaxErrors,
testsPassed: testFiles.length > 0 ? testResult.includes('passed') : 'N/A'
}
// Send to coordinator
SendMessage({
type: "message",
recipient: "coordinator",
content: `## Implementation Complete
**Task**: ${task.subject}
### Changed Files
${report.changedFiles.map(f => `- ${f}`).join('\n')}
### Acceptance Criteria
${acceptanceStatus.map(t => `**${t.title}**: ${t.criteria.every(c => c.met) ? 'All met' : 'Partial'}`).join('\n')}
### Validation
- Syntax: ${report.syntaxClean ? 'Clean' : 'Has errors (attempted fix)'}
- Tests: ${report.testsPassed}
Implementation is ready for testing and review.`,
summary: `IMPL complete: ${report.changedFiles.length} files changed`
})
// Mark task completed
TaskUpdate({ taskId: task.id, status: 'completed' })
// Check for next IMPL task
const nextTasks = TaskList().filter(t =>
t.subject.startsWith('IMPL-') &&
t.owner === 'executor' &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (nextTasks.length > 0) {
// Continue with next task → back to Phase 1
}
```
## Helper Functions
```javascript
function isSimpleTask(task) {
return (task.files || []).length <= 2 &&
!task.code_skeleton &&
(task.risks || []).length === 0
}
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| Plan file not found | Notify coordinator, request plan location |
| Syntax errors after implementation | Attempt auto-fix, report remaining errors |
| Sub-agent failure | Retry once, then attempt direct implementation |
| File conflict / merge issue | Notify coordinator, request guidance |
| Test failures in self-validation | Report in completion message, let tester handle |
| Circular dependencies in plan | Execute in plan order, ignore dependency chain |

View File

@@ -1,422 +0,0 @@
---
name: plan
description: Team planner - 多角度代码探索、结构化实现规划、提交coordinator审批
argument-hint: ""
allowed-tools: SendMessage(*), TaskUpdate(*), TaskList(*), TaskGet(*), TodoWrite(*), Read(*), Write(*), Bash(*), Glob(*), Grep(*), Task(*)
group: team
---
# Team Plan Command (/team:plan)
## Overview
Team planner role command. Operates as a teammate within an Agent Team, responsible for multi-angle code exploration and structured implementation planning. Submits plans to the coordinator for approval.
**Core capabilities:**
- Task discovery from shared team task list
- Multi-angle codebase exploration (architecture/security/performance/bugfix/feature)
- Complexity-adaptive planning (Low → direct, Medium/High → agent-assisted)
- Structured plan.json generation following schema
- Plan submission and revision cycle with coordinator
## Role Definition
**Name**: `planner`
**Responsibility**: Code exploration → Implementation planning → Coordinator approval
**Communication**: SendMessage to coordinator only
## 消息总线
每次 SendMessage **前**,必须调用 `mcp__ccw-tools__team_msg` 记录消息:
```javascript
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "planner", to: "coordinator", type: "<type>", summary: "<摘要>", ref: "<文件路径>" })
```
### 支持的 Message Types
| Type | 方向 | 触发时机 | 说明 |
|------|------|----------|------|
| `plan_ready` | planner → coordinator | Plan 生成完成 | 附带 plan.json 路径和任务数摘要 |
| `plan_revision` | planner → coordinator | Plan 修订后重新提交 | 说明修改内容 |
| `impl_progress` | planner → coordinator | 探索阶段进展更新 | 可选,长时间探索时使用 |
| `error` | planner → coordinator | 遇到不可恢复错误 | 探索失败、schema缺失等 |
### 调用示例
```javascript
// Plan 就绪
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "planner", to: "coordinator", type: "plan_ready", summary: "Plan就绪: 3个task, Medium复杂度", ref: ".workflow/.team-plan/auth-impl-2026-02-09/plan.json" })
// Plan 修订
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "planner", to: "coordinator", type: "plan_revision", summary: "已按反馈拆分task-2为两个子任务" })
// 错误上报
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "planner", to: "coordinator", type: "error", summary: "plan-overview-base-schema.json 未找到, 使用默认结构" })
```
### CLI 回退
`mcp__ccw-tools__team_msg` MCP 不可用时,使用 `ccw team` CLI 作为等效回退:
```javascript
// 回退: 将 MCP 调用替换为 Bash CLI参数一一对应
Bash(`ccw team log --team "${teamName}" --from "planner" --to "coordinator" --type "plan_ready" --summary "Plan就绪: 3个task" --ref "${sessionFolder}/plan.json" --json`)
```
**参数映射**: `team_msg(params)``ccw team log --team <team> --from planner --to coordinator --type <type> --summary "<text>" [--ref <path>] [--data '<json>'] [--json]`
## Execution Process
```
Phase 1: Task Discovery
├─ Read team config to identify coordinator
├─ TaskList to find PLAN-* tasks assigned to me
├─ TaskGet to read full task details
└─ TaskUpdate to mark in_progress
Phase 2: Multi-Angle Exploration
├─ Complexity assessment (Low/Medium/High)
├─ Angle selection based on task type
├─ Semantic search via mcp__ace-tool__search_context
├─ Pattern search via Grep/Glob
├─ Complex tasks: cli-explore-agent sub-agents
└─ Write exploration results to session folder
Phase 3: Plan Generation
├─ Read plan-overview-base-schema.json + task-schema.json for structure reference
├─ Low complexity → Direct Claude planning
├─ Medium/High → cli-lite-planning-agent
└─ Output: plan.json (overview with task_ids[]) + .task/TASK-*.json (independent task files)
Phase 4: Submit for Approval
├─ SendMessage plan summary to coordinator
├─ Wait for approve/revision feedback
└─ If revision → update plan → resubmit
Phase 5: Idle & Next Task
├─ Mark current task completed
├─ TaskList to check for new PLAN tasks
└─ No tasks → idle (wait for coordinator assignment)
```
## Implementation
### Phase 1: Task Discovery
```javascript
// Read team config
const teamConfig = JSON.parse(Read(`~/.claude/teams/${teamName}/config.json`))
// Find my assigned PLAN tasks
const tasks = TaskList()
const myPlanTasks = tasks.filter(t =>
t.subject.startsWith('PLAN-') &&
t.owner === 'planner' &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (myPlanTasks.length === 0) {
// No tasks available, idle
return
}
// Pick first available task (lowest ID)
const task = TaskGet({ taskId: myPlanTasks[0].id })
TaskUpdate({ taskId: task.id, status: 'in_progress' })
```
### Phase 2: Multi-Angle Exploration
```javascript
// Session setup
const taskSlug = task.subject.toLowerCase().replace(/[^a-z0-9]+/g, '-').substring(0, 40)
const dateStr = new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString().substring(0, 10)
const sessionFolder = `.workflow/.team-plan/${taskSlug}-${dateStr}`
Bash(`mkdir -p ${sessionFolder}`)
// Complexity assessment
function assessComplexity(desc) {
let score = 0
if (/refactor|architect|restructure|模块|系统/.test(desc)) score += 2
if (/multiple|多个|across|跨/.test(desc)) score += 2
if (/integrate|集成|api|database/.test(desc)) score += 1
if (/security|安全|performance|性能/.test(desc)) score += 1
return score >= 4 ? 'High' : score >= 2 ? 'Medium' : 'Low'
}
const complexity = assessComplexity(task.description)
// Angle selection
const ANGLE_PRESETS = {
architecture: ['architecture', 'dependencies', 'modularity', 'integration-points'],
security: ['security', 'auth-patterns', 'dataflow', 'validation'],
performance: ['performance', 'bottlenecks', 'caching', 'data-access'],
bugfix: ['error-handling', 'dataflow', 'state-management', 'edge-cases'],
feature: ['patterns', 'integration-points', 'testing', 'dependencies']
}
function selectAngles(desc, count) {
const text = desc.toLowerCase()
let preset = 'feature'
if (/refactor|architect|restructure|modular/.test(text)) preset = 'architecture'
else if (/security|auth|permission|access/.test(text)) preset = 'security'
else if (/performance|slow|optimi|cache/.test(text)) preset = 'performance'
else if (/fix|bug|error|issue|broken/.test(text)) preset = 'bugfix'
return ANGLE_PRESETS[preset].slice(0, count)
}
const angleCount = complexity === 'High' ? 4 : (complexity === 'Medium' ? 3 : 1)
const selectedAngles = selectAngles(task.description, angleCount)
// Execute exploration
// Low complexity: direct search with mcp__ace-tool__search_context + Grep/Glob
// Medium/High: launch cli-explore-agent sub-agents in parallel
if (complexity === 'Low') {
// Direct exploration
const results = mcp__ace-tool__search_context({
project_root_path: projectRoot,
query: task.description
})
// Write single exploration file
Write(`${sessionFolder}/exploration-${selectedAngles[0]}.json`, JSON.stringify({
project_structure: "...",
relevant_files: [],
patterns: [],
dependencies: [],
integration_points: [],
constraints: [],
clarification_needs: [],
_metadata: { exploration_angle: selectedAngles[0] }
}, null, 2))
} else {
// Launch parallel cli-explore-agent for each angle
selectedAngles.forEach((angle, index) => {
Task({
subagent_type: "cli-explore-agent",
run_in_background: false,
description: `Explore: ${angle}`,
prompt: `
## Task Objective
Execute **${angle}** exploration for task planning context.
## Output Location
**Session Folder**: ${sessionFolder}
**Output File**: ${sessionFolder}/exploration-${angle}.json
## Assigned Context
- **Exploration Angle**: ${angle}
- **Task Description**: ${task.description}
- **Exploration Index**: ${index + 1} of ${selectedAngles.length}
## MANDATORY FIRST STEPS
1. Run: rg -l "{relevant_keyword}" --type ts (locate relevant files)
2. Execute: cat ~/.ccw/workflows/cli-templates/schemas/explore-json-schema.json (get output schema)
3. Read: .workflow/project-tech.json (if exists - technology stack)
## Expected Output
Write JSON to: ${sessionFolder}/exploration-${angle}.json
Follow explore-json-schema.json structure with ${angle}-focused findings.
**MANDATORY**: Every file in relevant_files MUST have:
- **rationale** (required): Specific selection basis tied to ${angle} topic (>10 chars, not generic)
- **role** (required): modify_target|dependency|pattern_reference|test_target|type_definition|integration_point|config|context_only
- **discovery_source** (recommended): bash-scan|cli-analysis|ace-search|dependency-trace|manual
- **key_symbols** (recommended): Key functions/classes/types relevant to task
`
})
})
}
// Build explorations manifest
const explorationManifest = {
session_id: `${taskSlug}-${dateStr}`,
task_description: task.description,
complexity: complexity,
exploration_count: selectedAngles.length,
explorations: selectedAngles.map(angle => ({
angle: angle,
file: `exploration-${angle}.json`,
path: `${sessionFolder}/exploration-${angle}.json`
}))
}
Write(`${sessionFolder}/explorations-manifest.json`, JSON.stringify(explorationManifest, null, 2))
```
### Phase 3: Plan Generation
```javascript
// Read schema reference
const schema = Bash(`cat ~/.ccw/workflows/cli-templates/schemas/plan-overview-base-schema.json`)
if (complexity === 'Low') {
// Direct Claude planning
// Read all exploration files
explorationManifest.explorations.forEach(exp => {
const data = Read(exp.path)
// Incorporate findings into plan
})
// Generate task files in .task/ directory
Bash(`mkdir -p ${sessionFolder}/.task`)
const tasks = [/* structured tasks with dependencies, files[].change, convergence.criteria */]
const taskIds = tasks.map(t => t.id)
// Write individual task files following task-schema.json
tasks.forEach(task => {
Write(`${sessionFolder}/.task/${task.id}.json`, JSON.stringify(task, null, 2))
})
// Generate plan overview following plan-overview-base-schema.json
const plan = {
summary: "...",
approach: "...",
task_ids: taskIds,
task_count: taskIds.length,
estimated_time: "...",
recommended_execution: "Agent",
complexity: "Low",
_metadata: {
timestamp: new Date().toISOString(),
source: "team-planner",
planning_mode: "direct",
plan_type: "feature"
}
}
Write(`${sessionFolder}/plan.json`, JSON.stringify(plan, null, 2))
} else {
// Use cli-lite-planning-agent for Medium/High
Task({
subagent_type: "cli-lite-planning-agent",
run_in_background: false,
description: "Generate detailed implementation plan",
prompt: `
Generate implementation plan with two-layer output.
## Output Location
**Session Folder**: ${sessionFolder}
**Output Files**:
- ${sessionFolder}/planning-context.md
- ${sessionFolder}/plan.json (overview with task_ids[])
- ${sessionFolder}/.task/TASK-*.json (independent task files)
## Output Schema Reference
Execute: cat ~/.ccw/workflows/cli-templates/schemas/plan-overview-base-schema.json
Execute: cat ~/.ccw/workflows/cli-templates/schemas/task-schema.json
## Output Format: Two-Layer Structure
- plan.json: Overview with task_ids[] referencing .task/ files (NO tasks[] array)
- .task/TASK-*.json: Independent task files following task-schema.json
plan.json required: summary, approach, task_ids, task_count, _metadata (with plan_type)
Task files required: id, title, description, depends_on, convergence (with criteria[])
Task fields: files[].change (not modification_points), convergence.criteria (not acceptance), test (not verification)
## Task Description
${task.description}
## Multi-Angle Exploration Context
${explorationManifest.explorations.map(exp => `### Exploration: ${exp.angle}
Path: ${exp.path}`).join('\n\n')}
## Complexity Level
${complexity}
## Requirements
Generate plan.json + .task/*.json following schemas. Key constraints:
- 2-7 structured tasks (group by feature/module, NOT by file)
- Each task file: id, title, description, files[].change, convergence.criteria, depends_on
- Prefer parallel tasks (minimize depends_on)
`
})
}
```
### Phase 4: Submit for Approval
```javascript
// Read generated plan
const plan = JSON.parse(Read(`${sessionFolder}/plan.json`))
// Load tasks from .task/ directory (two-layer format)
const tasks = plan.task_ids.map(id => JSON.parse(Read(`${sessionFolder}/.task/${id}.json`)))
const taskCount = plan.task_count || plan.task_ids.length
// Send plan summary to coordinator
SendMessage({
type: "message",
recipient: "coordinator", // team lead
content: `## Plan Ready for Review
**Task**: ${task.subject}
**Complexity**: ${complexity}
**Tasks**: ${taskCount}
### Task Summary
${tasks.map((t, i) => `${i+1}. ${t.title} (${t.scope || 'N/A'})`).join('\n')}
### Approach
${plan.approach}
### Plan Location
${sessionFolder}/plan.json
${plan.task_ids ? `Task Files: ${sessionFolder}/.task/` : ''}
Please review and approve or request revisions.`,
summary: `Plan ready: ${taskCount} tasks`
})
// Wait for coordinator response
// If approved → mark task completed
// If revision requested → update plan based on feedback → resubmit
```
### Phase 5: After Approval
```javascript
// Mark PLAN task as completed
TaskUpdate({ taskId: task.id, status: 'completed' })
// Check for more PLAN tasks
const nextTasks = TaskList().filter(t =>
t.subject.startsWith('PLAN-') &&
t.owner === 'planner' &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (nextTasks.length > 0) {
// Continue with next PLAN task → back to Phase 1
} else {
// No more tasks, idle
// Will be woken by coordinator message for new assignments
}
```
## Session Files
```
.workflow/.team-plan/{task-slug}-{YYYY-MM-DD}/
├── exploration-{angle1}.json # Per-angle exploration results
├── exploration-{angle2}.json
├── explorations-manifest.json # Exploration index
├── planning-context.md # Evidence + understanding (Medium/High)
├── plan.json # Plan overview with task_ids[] (NO embedded tasks[])
└── .task/ # Independent task files
├── TASK-001.json # Task file following task-schema.json
├── TASK-002.json
└── ...
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| Exploration agent failure | Skip exploration, plan from task description only |
| Planning agent failure | Fallback to direct Claude planning |
| Plan rejected 3+ times | Notify coordinator, suggest alternative approach |
| No PLAN tasks available | Idle, wait for coordinator assignment |
| Schema file not found | Use basic plan structure without schema validation |

View File

@@ -1,394 +0,0 @@
---
name: review
description: Team reviewer - 代码质量/安全/架构审查、需求验证、发现报告给coordinator
argument-hint: ""
allowed-tools: SendMessage(*), TaskUpdate(*), TaskList(*), TaskGet(*), TodoWrite(*), Read(*), Bash(*), Glob(*), Grep(*), Task(*)
group: team
---
# Team Review Command (/team:review)
## Overview
Team reviewer role command. Operates as a teammate within an Agent Team (typically handled by the tester), responsible for multi-dimensional code review and requirement verification. Reports findings to the coordinator with severity classification.
**Core capabilities:**
- Task discovery from shared team task list (REVIEW-* tasks)
- Multi-dimensional review: quality, security, architecture, requirement verification
- Pattern-based security scanning with Grep
- Acceptance criteria verification against plan
- Severity-classified findings (critical/high/medium/low)
- Optional CLI-assisted deep analysis (Gemini/Qwen)
## Role Definition
**Name**: `tester` (same teammate handles both TEST and REVIEW tasks)
**Responsibility**: Review code changes → Verify requirements → Report findings
**Communication**: SendMessage to coordinator only
## 消息总线
每次 SendMessage **前**,必须调用 `mcp__ccw-tools__team_msg` 记录消息:
```javascript
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "<type>", summary: "<摘要>" })
```
### 支持的 Message Types
| Type | 方向 | 触发时机 | 说明 |
|------|------|----------|------|
| `review_result` | tester → coordinator | 审查完成 | 附带 verdictAPPROVE/CONDITIONAL/BLOCK和发现统计 |
| `fix_required` | tester → coordinator | 发现 critical issues | 需要创建 IMPL-fix 任务给 executor |
| `error` | tester → coordinator | 审查无法完成 | Plan 缺失、变更文件无法读取等 |
### 调用示例
```javascript
// 审查通过
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "review_result", summary: "REVIEW-001: APPROVE, 2 medium + 1 low findings", data: { verdict: "APPROVE", critical: 0, high: 0, medium: 2, low: 1 } })
// 审查有条件通过
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "review_result", summary: "REVIEW-001: CONDITIONAL, 4 high severity findings需关注", data: { verdict: "CONDITIONAL", critical: 0, high: 4, medium: 3, low: 2 } })
// 发现 critical 问题
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "fix_required", summary: "发现eval()使用和硬编码密码, 需立即修复", data: { critical: 2, details: ["eval() in auth.ts:42", "hardcoded password in config.ts:15"] } })
// 错误上报
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "error", summary: "plan.json未找到, 无法进行需求验证" })
```
### CLI 回退
`mcp__ccw-tools__team_msg` MCP 不可用时,使用 `ccw team` CLI 作为等效回退:
```javascript
// 回退: 将 MCP 调用替换为 Bash CLI参数一一对应
Bash(`ccw team log --team "${teamName}" --from "tester" --to "coordinator" --type "review_result" --summary "REVIEW-001: APPROVE, 2 medium findings" --data '{"verdict":"APPROVE","critical":0}' --json`)
```
**参数映射**: `team_msg(params)``ccw team log --team <team> --from tester --to coordinator --type <type> --summary "<text>" [--data '<json>'] [--json]`
## Execution Process
```
Phase 1: Task Discovery
├─ TaskList to find unblocked REVIEW-* tasks assigned to me
├─ TaskGet to read full task details
└─ TaskUpdate to mark in_progress
Phase 2: Review Context Loading
├─ Read plan.json (requirements + acceptance criteria)
├─ git diff to get changed files
├─ Read test results (if available)
└─ Read changed file contents
Phase 3: Multi-Dimensional Review
├─ Quality: code style, maintainability, @ts-ignore/any usage
├─ Security: eval/exec/innerHTML/hardcoded secrets (Grep patterns)
├─ Architecture: layering compliance, modularity, tech debt
├─ Requirement Verification: plan acceptance criteria vs implementation
└─ Optional: CLI deep analysis (Gemini for security, Qwen for architecture)
Phase 4: Finding Summary
├─ Classify by severity: critical/high/medium/low
├─ Generate actionable recommendations
└─ Determine overall verdict
Phase 5: Report to Coordinator
├─ SendMessage with review findings
├─ No critical issues → mark REVIEW task completed
└─ Critical issues → flag for immediate attention
```
## Implementation
### Phase 1: Task Discovery
```javascript
// Find my assigned REVIEW tasks
const tasks = TaskList()
const myReviewTasks = tasks.filter(t =>
t.subject.startsWith('REVIEW-') &&
t.owner === 'tester' &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (myReviewTasks.length === 0) return // idle
const task = TaskGet({ taskId: myReviewTasks[0].id })
TaskUpdate({ taskId: task.id, status: 'in_progress' })
```
### Phase 2: Review Context Loading
```javascript
// Load plan for acceptance criteria
const planPathMatch = task.description.match(/\.workflow\/\.team-plan\/[^\s]+\/plan\.json/)
let plan = null
if (planPathMatch) {
try { plan = JSON.parse(Read(planPathMatch[0])) } catch {}
}
// Get changed files via git
const changedFiles = Bash(`git diff --name-only HEAD~1 2>/dev/null || git diff --name-only --cached`)
.split('\n')
.filter(f => f.trim() && !f.startsWith('.'))
// Read changed file contents for review
const fileContents = {}
for (const file of changedFiles.slice(0, 20)) { // limit to 20 files
try { fileContents[file] = Read(file) } catch {}
}
// Load test results if available
let testResults = null
const testSummary = tasks.find(t => t.subject.startsWith('TEST-') && t.status === 'completed')
```
### Phase 3: Multi-Dimensional Review
```javascript
const findings = {
critical: [],
high: [],
medium: [],
low: []
}
// --- Quality Review ---
function reviewQuality(files) {
const issues = []
// Check for @ts-ignore, @ts-expect-error, any type
const tsIgnore = Grep({ pattern: '@ts-ignore|@ts-expect-error', glob: '*.{ts,tsx}', output_mode: 'content' })
if (tsIgnore) issues.push({ type: 'quality', detail: '@ts-ignore/@ts-expect-error usage detected', severity: 'medium' })
const anyType = Grep({ pattern: ': any[^A-Z]|as any', glob: '*.{ts,tsx}', output_mode: 'content' })
if (anyType) issues.push({ type: 'quality', detail: 'Untyped `any` usage detected', severity: 'medium' })
// Check for console.log left in production code
const consoleLogs = Grep({ pattern: 'console\\.log', glob: '*.{ts,tsx,js,jsx}', path: 'src/', output_mode: 'content' })
if (consoleLogs) issues.push({ type: 'quality', detail: 'console.log found in source code', severity: 'low' })
// Check for empty catch blocks
const emptyCatch = Grep({ pattern: 'catch\\s*\\([^)]*\\)\\s*\\{\\s*\\}', glob: '*.{ts,tsx,js,jsx}', output_mode: 'content', multiline: true })
if (emptyCatch) issues.push({ type: 'quality', detail: 'Empty catch blocks detected', severity: 'high' })
return issues
}
// --- Security Review ---
function reviewSecurity(files) {
const issues = []
// Dangerous functions
const dangerousFns = Grep({ pattern: '\\beval\\b|\\bexec\\b|innerHTML|dangerouslySetInnerHTML', glob: '*.{ts,tsx,js,jsx}', output_mode: 'content' })
if (dangerousFns) issues.push({ type: 'security', detail: 'Dangerous function usage: eval/exec/innerHTML', severity: 'critical' })
// Hardcoded secrets
const secrets = Grep({ pattern: 'password\\s*=\\s*["\']|secret\\s*=\\s*["\']|api_key\\s*=\\s*["\']', glob: '*.{ts,tsx,js,jsx,py}', output_mode: 'content', '-i': true })
if (secrets) issues.push({ type: 'security', detail: 'Hardcoded secrets/passwords detected', severity: 'critical' })
// SQL injection risk
const sqlInjection = Grep({ pattern: 'query\\s*\\(\\s*`|execute\\s*\\(\\s*`|\\$\\{.*\\}.*(?:SELECT|INSERT|UPDATE|DELETE)', glob: '*.{ts,js,py}', output_mode: 'content', '-i': true })
if (sqlInjection) issues.push({ type: 'security', detail: 'Potential SQL injection via template literals', severity: 'critical' })
// XSS via user input
const xssRisk = Grep({ pattern: 'document\\.write|window\\.location\\s*=', glob: '*.{ts,tsx,js,jsx}', output_mode: 'content' })
if (xssRisk) issues.push({ type: 'security', detail: 'Potential XSS vectors detected', severity: 'high' })
return issues
}
// --- Architecture Review ---
function reviewArchitecture(files) {
const issues = []
// Circular dependency indicators
// Check for imports that may create cycles
for (const [file, content] of Object.entries(fileContents)) {
const imports = content.match(/from\s+['"]([^'"]+)['"]/g) || []
// Basic heuristic: component importing from parent directory
const parentImports = imports.filter(i => i.includes('../..'))
if (parentImports.length > 2) {
issues.push({ type: 'architecture', detail: `${file}: excessive parent directory imports (possible layering violation)`, severity: 'medium' })
}
}
// Large file detection
for (const [file, content] of Object.entries(fileContents)) {
const lines = content.split('\n').length
if (lines > 500) {
issues.push({ type: 'architecture', detail: `${file}: ${lines} lines - consider splitting`, severity: 'low' })
}
}
return issues
}
// --- Requirement Verification ---
function verifyRequirements(plan) {
const issues = []
if (!plan) {
issues.push({ type: 'requirement', detail: 'No plan found for requirement verification', severity: 'medium' })
return issues
}
for (const planTask of plan.tasks) {
for (const criterion of (planTask.acceptance || [])) {
// Check if criterion appears to be met
// This is a heuristic check - look for relevant code in changed files
const keywords = criterion.toLowerCase().split(/\s+/).filter(w => w.length > 4)
const hasEvidence = keywords.some(kw =>
Object.values(fileContents).some(content => content.toLowerCase().includes(kw))
)
if (!hasEvidence) {
issues.push({
type: 'requirement',
detail: `Acceptance criterion may not be met: "${criterion}" (task: ${planTask.title})`,
severity: 'high'
})
}
}
}
return issues
}
// Execute all review dimensions
const qualityIssues = reviewQuality(changedFiles)
const securityIssues = reviewSecurity(changedFiles)
const architectureIssues = reviewArchitecture(changedFiles)
const requirementIssues = plan ? verifyRequirements(plan) : []
// Classify into severity buckets
const allIssues = [...qualityIssues, ...securityIssues, ...architectureIssues, ...requirementIssues]
allIssues.forEach(issue => {
findings[issue.severity].push(issue)
})
```
### Phase 4: Finding Summary
```javascript
const totalIssues = Object.values(findings).flat().length
const hasCritical = findings.critical.length > 0
const verdict = hasCritical
? 'BLOCK - Critical issues must be resolved'
: findings.high.length > 3
? 'CONDITIONAL - High severity issues should be addressed'
: 'APPROVE - No blocking issues found'
const recommendations = []
if (hasCritical) {
recommendations.push('Fix all critical security issues before merging')
}
if (findings.high.length > 0) {
recommendations.push('Address high severity issues in a follow-up')
}
if (findings.medium.length > 3) {
recommendations.push('Consider refactoring to reduce medium severity issues')
}
```
### Phase 5: Report to Coordinator
```javascript
SendMessage({
type: "message",
recipient: "coordinator",
content: `## Code Review Report
**Task**: ${task.subject}
**Verdict**: ${verdict}
**Files Reviewed**: ${changedFiles.length}
**Total Findings**: ${totalIssues}
### Finding Summary
- Critical: ${findings.critical.length}
- High: ${findings.high.length}
- Medium: ${findings.medium.length}
- Low: ${findings.low.length}
${findings.critical.length > 0 ? `### Critical Issues
${findings.critical.map(f => `- [${f.type.toUpperCase()}] ${f.detail}`).join('\n')}
` : ''}
${findings.high.length > 0 ? `### High Severity
${findings.high.map(f => `- [${f.type.toUpperCase()}] ${f.detail}`).join('\n')}
` : ''}
${findings.medium.length > 0 ? `### Medium Severity
${findings.medium.map(f => `- [${f.type.toUpperCase()}] ${f.detail}`).join('\n')}
` : ''}
### Recommendations
${recommendations.map(r => `- ${r}`).join('\n')}
${plan ? `### Requirement Verification
${plan.tasks.map(t => `- **${t.title}**: ${requirementIssues.filter(i => i.detail.includes(t.title)).length === 0 ? 'Criteria met' : 'Needs verification'}`).join('\n')}
` : ''}`,
summary: `Review: ${verdict.split(' - ')[0]} (${totalIssues} findings)`
})
// Mark task based on verdict
if (!hasCritical) {
TaskUpdate({ taskId: task.id, status: 'completed' })
} else {
// Keep in_progress, coordinator needs to create fix tasks
SendMessage({
type: "message",
recipient: "coordinator",
content: `Critical issues found in review. Recommend creating IMPL-fix tasks for executor to address: ${findings.critical.map(f => f.detail).join('; ')}`,
summary: "Critical issues need fix tasks"
})
}
// Check for next REVIEW task
const nextTasks = TaskList().filter(t =>
t.subject.startsWith('REVIEW-') &&
t.owner === 'tester' &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (nextTasks.length > 0) {
// Continue with next task
}
```
## Optional: CLI Deep Analysis
For complex reviews, the tester can invoke CLI tools for deeper analysis:
```bash
# Security deep analysis (Gemini)
ccw cli -p "
PURPOSE: Deep security audit of implementation changes
TASK: Scan for OWASP Top 10 vulnerabilities, injection flaws, auth bypass vectors
CONTEXT: @src/**/*.{ts,tsx,js,jsx}
EXPECTED: Security findings with severity, file:line references, remediation
CONSTRAINTS: Focus on changed files only
" --tool gemini --mode analysis
# Architecture deep analysis (Qwen)
ccw cli -p "
PURPOSE: Architecture compliance review
TASK: Evaluate layering, modularity, separation of concerns
CONTEXT: @src/**/*
EXPECTED: Architecture assessment with recommendations
CONSTRAINTS: Focus on changed modules
" --tool qwen --mode analysis
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| Plan file not found | Review without requirement verification, note in report |
| No changed files detected | Report to coordinator, may need manual file list |
| Grep pattern errors | Skip specific check, continue with remaining |
| CLI analysis timeout | Report partial results, note incomplete analysis |
| Too many files to review (> 50) | Focus on source files, skip generated/vendor files |
| Cannot determine file content | Skip file, note in report |

View File

@@ -1,321 +0,0 @@
---
name: spec-analyst
description: Team spec analyst - 种子分析、代码库探索、上下文收集、多维度研究
argument-hint: ""
allowed-tools: SendMessage(*), TaskUpdate(*), TaskList(*), TaskGet(*), TodoWrite(*), Read(*), Bash(*), Glob(*), Grep(*), Task(*)
group: team
---
# Team Spec Analyst Command (/team:spec-analyst)
## Overview
Team spec-analyst role command. Operates as a teammate within a Spec Team, responsible for discovery, codebase exploration, and multi-dimensional context gathering. Maps to spec-generator Phase 1 (Discovery).
**Core capabilities:**
- Task discovery from shared team task list (RESEARCH-* tasks)
- Seed analysis: problem statement, users, domain, constraints extraction
- Codebase exploration: existing patterns, architecture, tech stack detection
- Multi-dimensional research: 3-5 exploration dimensions with complexity assessment
- Structured context output for downstream discussion and drafting
## Role Definition
**Name**: `spec-analyst`
**Responsibility**: Seed Analysis → Codebase Exploration → Context Packaging → Report
**Communication**: SendMessage to coordinator only
## 消息总线
每次 SendMessage **前**,必须调用 `mcp__ccw-tools__team_msg` 记录消息:
```javascript
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-analyst", to: "coordinator", type: "<type>", summary: "<摘要>", ref: "<文件路径>" })
```
### 支持的 Message Types
| Type | 方向 | 触发时机 | 说明 |
|------|------|----------|------|
| `research_ready` | spec-analyst → coordinator | 研究完成 | 附带 discovery-context.json 路径和维度摘要 |
| `research_progress` | spec-analyst → coordinator | 长时间研究进展 | 阶段性进展更新 |
| `error` | spec-analyst → coordinator | 遇到不可恢复错误 | 代码库访问失败、CLI 超时等 |
### 调用示例
```javascript
// 研究就绪
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-analyst", to: "coordinator", type: "research_ready", summary: "研究完成: 5个探索维度, 检测到React+Node技术栈", ref: ".workflow/.spec-team/session/discovery-context.json" })
// 进展更新
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-analyst", to: "coordinator", type: "research_progress", summary: "种子分析完成, 开始代码库探索 (2/3)" })
// 错误上报
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-analyst", to: "coordinator", type: "error", summary: "代码库探索失败: 项目根目录无法识别" })
```
### CLI 回退
`mcp__ccw-tools__team_msg` MCP 不可用时,使用 `ccw team` CLI 作为等效回退:
```javascript
// 回退: 将 MCP 调用替换为 Bash CLI参数一一对应
Bash(`ccw team log --team "${teamName}" --from "spec-analyst" --to "coordinator" --type "research_ready" --summary "研究完成: 5个探索维度" --ref "${sessionFolder}/discovery-context.json" --json`)
```
**参数映射**: `team_msg(params)``ccw team log --team <team> --from spec-analyst --to coordinator --type <type> --summary "<text>" [--ref <path>] [--data '<json>'] [--json]`
## Execution Process
```
Phase 1: Task Discovery
├─ TaskList to find unblocked RESEARCH-* tasks
├─ TaskGet to read full task details
└─ TaskUpdate to mark in_progress
Phase 2: Seed Analysis
├─ Parse topic/idea from task description
├─ Extract: problem statement, target users, domain, constraints
├─ Identify 3-5 exploration dimensions
└─ Assess complexity (simple/moderate/complex)
Phase 3: Codebase Exploration (conditional)
├─ Detect project presence (package.json, Cargo.toml, etc.)
├─ Explore architecture patterns and conventions
├─ Map technology stack and dependencies
└─ Identify integration constraints
Phase 4: Context Packaging
├─ Generate spec-config.json (session state)
├─ Generate discovery-context.json (research results)
└─ Validate output completeness
Phase 5: Report to Coordinator
├─ team_msg log + SendMessage research summary
├─ TaskUpdate completed
└─ Check for next RESEARCH-* task
```
## Implementation
### Phase 1: Task Discovery
```javascript
// Find assigned RESEARCH-* tasks
const tasks = TaskList()
const myTasks = tasks.filter(t =>
t.subject.startsWith('RESEARCH-') &&
t.owner === 'spec-analyst' &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (myTasks.length === 0) return // idle
const task = TaskGet({ taskId: myTasks[0].id })
TaskUpdate({ taskId: task.id, status: 'in_progress' })
```
### Phase 2: Seed Analysis
```javascript
// Extract session folder from task description
const sessionMatch = task.description.match(/Session:\s*(.+)/)
const sessionFolder = sessionMatch ? sessionMatch[1].trim() : '.workflow/.spec-team/default'
// Parse topic from task description
const topicLines = task.description.split('\n').filter(l => !l.startsWith('Session:') && !l.startsWith('输出:') && l.trim())
const topic = topicLines[0] || task.subject.replace('RESEARCH-001: ', '')
// Use Gemini CLI for seed analysis
Bash({
command: `ccw cli -p "PURPOSE: Analyze the following topic/idea and extract structured seed information for specification generation.
TASK:
• Extract problem statement (what problem does this solve)
• Identify target users and their pain points
• Determine domain and industry context
• List constraints and assumptions
• Identify 3-5 exploration dimensions for deeper research
• Assess complexity (simple/moderate/complex)
TOPIC: ${topic}
MODE: analysis
CONTEXT: @**/*
EXPECTED: JSON output with fields: problem_statement, target_users[], domain, constraints[], exploration_dimensions[], complexity_assessment
CONSTRAINTS: Output as valid JSON" --tool gemini --mode analysis --rule analysis-analyze-technical-document`,
run_in_background: true
})
// Wait for CLI result
// Parse Gemini analysis result
const seedAnalysis = parseCLIResult(geminiOutput)
```
### Phase 3: Codebase Exploration (conditional)
```javascript
// Check if there's an existing codebase to explore
const hasProject = Bash(`test -f package.json || test -f Cargo.toml || test -f pyproject.toml || test -f go.mod; echo $?`)
if (hasProject === '0') {
// Progress update
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-analyst", to: "coordinator", type: "research_progress", summary: "种子分析完成, 开始代码库探索" })
// Explore codebase using ACE search
const archSearch = mcp__ace-tool__search_context({
project_root_path: projectRoot,
query: `Architecture patterns, main modules, entry points for: ${topic}`
})
// Detect tech stack
const techStack = {
languages: [],
frameworks: [],
databases: [],
infrastructure: []
}
// Scan package files for dependencies
const pkgJson = Read('package.json') // if exists
// Parse and categorize dependencies
// Explore existing patterns
const patterns = mcp__ace-tool__search_context({
project_root_path: projectRoot,
query: `Similar features, existing conventions, coding patterns related to: ${topic}`
})
// Integration constraints
const integrationPoints = mcp__ace-tool__search_context({
project_root_path: projectRoot,
query: `API endpoints, service boundaries, module interfaces that ${topic} would integrate with`
})
var codebaseContext = {
tech_stack: techStack,
architecture_patterns: archSearch,
existing_conventions: patterns,
integration_points: integrationPoints,
constraints_from_codebase: []
}
} else {
var codebaseContext = null
}
```
### Phase 4: Context Packaging
```javascript
// Generate spec-config.json
const specConfig = {
session_id: `SPEC-${topicSlug}-${dateStr}`,
topic: topic,
status: "research_complete",
complexity: seedAnalysis.complexity_assessment || "moderate",
phases_completed: ["discovery"],
created_at: new Date().toISOString(),
session_folder: sessionFolder,
discussion_depth: task.description.match(/讨论深度:\s*(.+)/)?.[1] || "standard"
}
Write(`${sessionFolder}/spec-config.json`, JSON.stringify(specConfig, null, 2))
// Generate discovery-context.json
const discoveryContext = {
session_id: specConfig.session_id,
phase: 1,
document_type: "discovery-context",
status: "complete",
generated_at: new Date().toISOString(),
seed_analysis: {
problem_statement: seedAnalysis.problem_statement,
target_users: seedAnalysis.target_users,
domain: seedAnalysis.domain,
constraints: seedAnalysis.constraints,
exploration_dimensions: seedAnalysis.exploration_dimensions,
complexity: seedAnalysis.complexity_assessment
},
codebase_context: codebaseContext,
recommendations: {
focus_areas: seedAnalysis.exploration_dimensions?.slice(0, 3) || [],
risks: [],
open_questions: []
}
}
Write(`${sessionFolder}/discovery-context.json`, JSON.stringify(discoveryContext, null, 2))
```
### Phase 5: Report to Coordinator
```javascript
const dimensionCount = discoveryContext.seed_analysis.exploration_dimensions?.length || 0
const hasCodebase = codebaseContext !== null
// Log before SendMessage
mcp__ccw-tools__team_msg({
operation: "log", team: teamName,
from: "spec-analyst", to: "coordinator",
type: "research_ready",
summary: `研究完成: ${dimensionCount}个探索维度, ${hasCodebase ? '有' : '无'}代码库上下文, 复杂度=${specConfig.complexity}`,
ref: `${sessionFolder}/discovery-context.json`
})
SendMessage({
type: "message",
recipient: "coordinator",
content: `## 研究分析结果
**Task**: ${task.subject}
**复杂度**: ${specConfig.complexity}
**代码库**: ${hasCodebase ? '已检测到现有项目' : '全新项目(无现有代码)'}
### 问题陈述
${discoveryContext.seed_analysis.problem_statement}
### 目标用户
${(discoveryContext.seed_analysis.target_users || []).map(u => `- ${u}`).join('\n')}
### 探索维度
${(discoveryContext.seed_analysis.exploration_dimensions || []).map((d, i) => `${i+1}. ${d}`).join('\n')}
### 约束条件
${(discoveryContext.seed_analysis.constraints || []).map(c => `- ${c}`).join('\n')}
${hasCodebase ? `### 代码库上下文
- 技术栈: ${JSON.stringify(codebaseContext.tech_stack)}
- 集成点: ${codebaseContext.integration_points?.length || 0}` : ''}
### 输出位置
- Config: ${sessionFolder}/spec-config.json
- Context: ${sessionFolder}/discovery-context.json
研究已就绪,可进入讨论轮次 DISCUSS-001。`,
summary: `研究就绪: ${dimensionCount}维度, ${specConfig.complexity}`
})
// Mark task completed
TaskUpdate({ taskId: task.id, status: 'completed' })
// Check for next RESEARCH task
const nextTasks = TaskList().filter(t =>
t.subject.startsWith('RESEARCH-') &&
t.owner === 'spec-analyst' &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (nextTasks.length > 0) {
// Continue with next task -> back to Phase 1
}
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| No RESEARCH-* tasks available | Idle, wait for coordinator assignment |
| Gemini CLI analysis failure | Fallback to direct Claude analysis without CLI |
| Codebase detection failed | Continue as new project (no codebase context) |
| Session folder cannot be created | Notify coordinator, request alternative path |
| Topic too vague for analysis | Report to coordinator with clarification questions |
| Unexpected error | Log error via team_msg, report to coordinator |

View File

@@ -1,352 +0,0 @@
---
name: spec-coordinate
description: Team spec coordinator - 规格文档工作流编排、讨论轮次管理、跨阶段共识推进
argument-hint: "[--team-name=NAME] \"spec topic description\""
allowed-tools: TeamCreate(*), TeamDelete(*), SendMessage(*), TaskCreate(*), TaskUpdate(*), TaskList(*), TaskGet(*), Task(*), AskUserQuestion(*), TodoWrite(*), Read(*), Bash(*), Glob(*), Grep(*)
group: team
---
# Team Spec Coordinate Command (/team:spec-coordinate)
规格文档团队协调器。需求发现 → 研究分析 → 讨论共识 → 文档撰写 → 质量审查 → 最终交付。每个阶段之间穿插结构化讨论轮次,确保多角度审视和团队共识。
## 消息总线
所有 teammate 在 SendMessage 的**同时**必须调用 `mcp__ccw-tools__team_msg` 记录消息:
```javascript
// 记录消息(每个 teammate 发 SendMessage 前调用)
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-analyst", to: "coordinator", type: "research_ready", summary: "研究完成: 5个探索维度", ref: ".workflow/.spec-team/session/discovery-context.json" })
// 查看全部消息
mcp__ccw-tools__team_msg({ operation: "list", team: teamName })
// 按角色过滤
mcp__ccw-tools__team_msg({ operation: "list", team: teamName, from: "spec-discuss", last: 5 })
// 查看团队状态
mcp__ccw-tools__team_msg({ operation: "status", team: teamName })
```
**日志位置**: `.workflow/.team-msg/{team-name}/messages.jsonl`
**消息类型**: `research_ready | research_progress | draft_ready | draft_revision | quality_result | discussion_ready | discussion_blocked | fix_required | error | shutdown`
### CLI 回退
`mcp__ccw-tools__team_msg` MCP 不可用时,使用 `ccw team` CLI 作为等效回退:
```javascript
// 回退: 将 MCP 调用替换为 Bash CLI参数一一对应
// log
Bash(`ccw team log --team "${teamName}" --from "coordinator" --to "spec-analyst" --type "plan_approved" --summary "研究结果已确认" --json`)
// list
Bash(`ccw team list --team "${teamName}" --last 10 --json`)
// list (带过滤)
Bash(`ccw team list --team "${teamName}" --from "spec-discuss" --last 5 --json`)
// status
Bash(`ccw team status --team "${teamName}" --json`)
// read
Bash(`ccw team read --team "${teamName}" --id "MSG-003" --json`)
```
**参数映射**: `team_msg(params)``ccw team <operation> --team <team> [--from/--to/--type/--summary/--ref/--data/--id/--last] [--json]`
## Pipeline
```
Topic → [RESEARCH: spec-analyst] → [DISCUSS-001: 范围讨论]
→ [DRAFT-001: Product Brief] → [DISCUSS-002: 多视角评审]
→ [DRAFT-002: Requirements/PRD] → [DISCUSS-003: 需求完整性讨论]
→ [DRAFT-003: Architecture] → [DISCUSS-004: 技术可行性讨论]
→ [DRAFT-004: Epics & Stories] → [DISCUSS-005: 执行就绪讨论]
→ [QUALITY-001: Readiness Check] → [DISCUSS-006: 最终签收]
→ 交付 → 等待新需求/关闭
```
## 讨论轮次设计
每个讨论轮次由 spec-discuss 角色执行,包含以下维度:
| 讨论轮次 | 发生时机 | 讨论焦点 | 输入制品 |
|----------|----------|----------|----------|
| DISCUSS-001 | 研究完成后 | 范围确认、方向调整、风险预判 | discovery-context.json |
| DISCUSS-002 | Product Brief 后 | 产品定位、用户画像、竞品分析 | product-brief.md |
| DISCUSS-003 | PRD 后 | 需求完整性、优先级、可测试性 | requirements/_index.md |
| DISCUSS-004 | Architecture 后 | 技术选型、可扩展性、安全性 | architecture/_index.md |
| DISCUSS-005 | Epics 后 | 执行顺序、MVP范围、估算合理性 | epics/_index.md |
| DISCUSS-006 | Quality Check 后 | 最终交付确认、遗留问题、下一步 | readiness-report.md |
## Execution
### Phase 1: 需求解析
解析 `$ARGUMENTS` 获取 `--team-name` 和规格主题。使用 AskUserQuestion 收集:
- 规格范围MVP / 完整 / 企业级)
- 重点领域(产品定义 / 技术架构 / 全面规格)
- 讨论深度(快速共识 / 深度讨论 / 全面辩论)
简单主题可跳过澄清。
### Phase 2: 创建 Team + Spawn 4 Teammates
```javascript
TeamCreate({ team_name: teamName })
// Session setup
const topicSlug = topic.toLowerCase().replace(/[^a-z0-9]+/g, '-').substring(0, 40)
const dateStr = new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString().substring(0, 10)
const sessionFolder = `.workflow/.spec-team/${topicSlug}-${dateStr}`
Bash(`mkdir -p ${sessionFolder}`)
```
**Spawn 时只传角色和需求,工作细节由 skill 定义**
```javascript
// Spec Analyst (研究分析)
Task({
subagent_type: "general-purpose",
team_name: teamName,
name: "spec-analyst",
prompt: `你是 team "${teamName}" 的 SPEC ANALYST。
当你收到 RESEARCH-* 任务时,调用 Skill(skill="team:spec-analyst") 执行研究分析。
当前主题: ${topicDescription}
约束: ${constraints}
Session: ${sessionFolder}
## 消息总线(必须)
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录:
mcp__ccw-tools__team_msg({ operation: "log", team: "${teamName}", from: "spec-analyst", to: "coordinator", type: "<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 无法完成 | 降级为简化模式,跳过深度分析 |

View File

@@ -1,498 +0,0 @@
---
name: spec-discuss
description: Team spec discuss - 结构化团队讨论、多视角批判、共识构建、分歧调解
argument-hint: ""
allowed-tools: SendMessage(*), TaskUpdate(*), TaskList(*), TaskGet(*), TodoWrite(*), Read(*), Write(*), Bash(*), Glob(*), Grep(*), Task(*)
group: team
---
# Team Spec Discuss Command (/team:spec-discuss)
## Overview
Team spec-discuss role command. Operates as a teammate within a Spec Team, responsible for facilitating structured team discussions between specification phases. This is the **key differentiator** of the spec team workflow — ensuring multi-perspective critique, consensus building, and quality feedback before each phase transition.
**Core capabilities:**
- Task discovery from shared team task list (DISCUSS-* tasks)
- Multi-perspective analysis: Product, Technical, Quality, Risk viewpoints
- Structured discussion facilitation with critique + suggestion format
- Consensus synthesis with action items and decision records
- Conflict identification and escalation when consensus cannot be reached
- CLI-assisted deep critique (parallel multi-model analysis)
## Role Definition
**Name**: `spec-discuss`
**Responsibility**: Load Artifact → Multi-Perspective Critique → Synthesize Consensus → Report
**Communication**: SendMessage to coordinator only
## 消息总线
每次 SendMessage **前**,必须调用 `mcp__ccw-tools__team_msg` 记录消息:
```javascript
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-discuss", to: "coordinator", type: "<type>", summary: "<摘要>", ref: "<文件路径>" })
```
### 支持的 Message Types
| Type | 方向 | 触发时机 | 说明 |
|------|------|----------|------|
| `discussion_ready` | spec-discuss → coordinator | 讨论完成,共识达成 | 附带讨论记录路径和决策摘要 |
| `discussion_blocked` | spec-discuss → coordinator | 讨论无法达成共识 | 附带分歧点和可选方案,需 coordinator 介入 |
| `impl_progress` | spec-discuss → coordinator | 长讨论进展更新 | 多视角分析进度 |
| `error` | spec-discuss → coordinator | 讨论无法进行 | 输入制品缺失等 |
### 调用示例
```javascript
// 讨论共识达成
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-discuss", to: "coordinator", type: "discussion_ready", summary: "DISCUSS-002: 共识达成, 3个改进建议 + 2个开放问题", ref: ".workflow/.spec-team/session/discussions/discuss-002-brief.md" })
// 讨论阻塞
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-discuss", to: "coordinator", type: "discussion_blocked", summary: "DISCUSS-004: 技术选型分歧 - 微服务 vs 单体, 需用户决策", data: { reason: "技术架构风格无法达成共识", options: [{ label: "微服务", description: "更好扩展性但增加复杂度" }, { label: "单体", description: "简单但限制扩展" }] } })
// 错误上报
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-discuss", to: "coordinator", type: "error", summary: "DISCUSS-001: 找不到 discovery-context.json" })
```
### CLI 回退
`mcp__ccw-tools__team_msg` MCP 不可用时,使用 `ccw team` CLI 作为等效回退:
```javascript
// 回退: 将 MCP 调用替换为 Bash CLI参数一一对应
Bash(`ccw team log --team "${teamName}" --from "spec-discuss" --to "coordinator" --type "discussion_ready" --summary "DISCUSS-002: 共识达成, 3个改进建议" --ref "${sessionFolder}/discussions/discuss-002-brief.md" --json`)
// 带 data 参数(讨论阻塞时)
Bash(`ccw team log --team "${teamName}" --from "spec-discuss" --to "coordinator" --type "discussion_blocked" --summary "技术选型分歧" --data '{"reason":"微服务 vs 单体","options":[{"label":"微服务"},{"label":"单体"}]}' --json`)
```
**参数映射**: `team_msg(params)``ccw team log --team <team> --from spec-discuss --to coordinator --type <type> --summary "<text>" [--ref <path>] [--data '<json>'] [--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 |

View File

@@ -1,492 +0,0 @@
---
name: spec-reviewer
description: Team spec reviewer - 跨文档质量验证、完整性/一致性/可追溯性/深度评分、就绪度检查
argument-hint: ""
allowed-tools: SendMessage(*), TaskUpdate(*), TaskList(*), TaskGet(*), TodoWrite(*), Read(*), Bash(*), Glob(*), Grep(*), Task(*)
group: team
---
# Team Spec Reviewer Command (/team:spec-reviewer)
## Overview
Team spec-reviewer role command. Operates as a teammate within a Spec Team, responsible for cross-document quality validation and readiness checks. Maps to spec-generator Phase 6 (Readiness Check).
**Core capabilities:**
- Task discovery from shared team task list (QUALITY-* tasks)
- 4-dimension quality scoring: Completeness, Consistency, Traceability, Depth
- Cross-document validation (Brief → PRD → Architecture → Epics chain)
- Quality gate enforcement (Pass ≥80%, Review 60-79%, Fail <60%)
- Readiness report and executive summary generation
- CLI-assisted deep validation (optional)
## Role Definition
**Name**: `spec-reviewer`
**Responsibility**: Load All Documents → Cross-Validate → Score → Report
**Communication**: SendMessage to coordinator only
## 消息总线
每次 SendMessage **前**,必须调用 `mcp__ccw-tools__team_msg` 记录消息:
```javascript
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-reviewer", to: "coordinator", type: "<type>", summary: "<摘要>" })
```
### 支持的 Message Types
| Type | 方向 | 触发时机 | 说明 |
|------|------|----------|------|
| `quality_result` | spec-reviewer → coordinator | 质量检查完成 | 附带评分和 gate 决策 (PASS/REVIEW/FAIL) |
| `fix_required` | spec-reviewer → coordinator | 发现关键质量问题 | 需创建 DRAFT-fix 任务 |
| `error` | spec-reviewer → coordinator | 审查无法完成 | 文档缺失、无法解析等 |
### 调用示例
```javascript
// 质量通过
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-reviewer", to: "coordinator", type: "quality_result", summary: "质量检查 PASS: 85分 (完整性90/一致性85/可追溯性80/深度85)", data: { gate: "PASS", score: 85 } })
// 需要审查
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-reviewer", to: "coordinator", type: "quality_result", summary: "质量检查 REVIEW: 72分, 可追溯性不足", data: { gate: "REVIEW", score: 72 } })
// 质量失败
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-reviewer", to: "coordinator", type: "fix_required", summary: "质量 FAIL: 55分, 缺少架构 ADR + PRD 验收标准不可测", data: { gate: "FAIL", score: 55, issues: ["missing ADRs", "untestable AC"] } })
```
### CLI 回退
`mcp__ccw-tools__team_msg` MCP 不可用时,使用 `ccw team` CLI 作为等效回退:
```javascript
// 回退: 将 MCP 调用替换为 Bash CLI参数一一对应
Bash(`ccw team log --team "${teamName}" --from "spec-reviewer" --to "coordinator" --type "quality_result" --summary "质量检查 PASS: 85分" --data '{"gate":"PASS","score":85}' --json`)
```
**参数映射**: `team_msg(params)``ccw team log --team <team> --from spec-reviewer --to coordinator --type <type> --summary "<text>" [--data '<json>'] [--json]`
## Execution Process
```
Phase 1: Task Discovery
├─ TaskList to find unblocked QUALITY-* tasks
├─ TaskGet to read full task details
└─ TaskUpdate to mark in_progress
Phase 2: Document Collection
├─ Load all generated documents from session folder
├─ Verify document chain completeness
├─ Load discussion records for context
└─ Build document inventory
Phase 3: 4-Dimension Quality Scoring
├─ Completeness (25%): All sections present with content
├─ Consistency (25%): Terminology, format, references
├─ Traceability (25%): Goals → Reqs → Arch → Stories chain
└─ Depth (25%): AC testable, ADRs justified, stories estimable
Phase 4: Report Generation
├─ Generate readiness-report.md (quality scores, issues, traceability)
├─ Generate spec-summary.md (one-page executive summary)
└─ Determine quality gate decision
Phase 5: Report to Coordinator
├─ team_msg log + SendMessage quality results
├─ TaskUpdate completed (if PASS/REVIEW)
└─ Flag fix_required (if FAIL)
```
## Implementation
### Phase 1: Task Discovery
```javascript
// Find assigned QUALITY-* tasks
const tasks = TaskList()
const myTasks = tasks.filter(t =>
t.subject.startsWith('QUALITY-') &&
t.owner === 'spec-reviewer' &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (myTasks.length === 0) return // idle
const task = TaskGet({ taskId: myTasks[0].id })
TaskUpdate({ taskId: task.id, status: 'in_progress' })
```
### Phase 2: Document Collection
```javascript
// Extract session folder
const sessionMatch = task.description.match(/Session:\s*(.+)/)
const sessionFolder = sessionMatch ? sessionMatch[1].trim() : ''
// Load all documents
const documents = {
config: null,
discoveryContext: null,
productBrief: null,
requirementsIndex: null,
requirements: [],
architectureIndex: null,
adrs: [],
epicsIndex: null,
epics: [],
discussions: []
}
try { documents.config = JSON.parse(Read(`${sessionFolder}/spec-config.json`)) } catch {}
try { documents.discoveryContext = JSON.parse(Read(`${sessionFolder}/discovery-context.json`)) } catch {}
try { documents.productBrief = Read(`${sessionFolder}/product-brief.md`) } catch {}
try { documents.requirementsIndex = Read(`${sessionFolder}/requirements/_index.md`) } catch {}
try { documents.architectureIndex = Read(`${sessionFolder}/architecture/_index.md`) } catch {}
try { documents.epicsIndex = Read(`${sessionFolder}/epics/_index.md`) } catch {}
// Load individual requirements
const reqFiles = Glob({ pattern: `${sessionFolder}/requirements/REQ-*.md` })
reqFiles.forEach(f => { try { documents.requirements.push(Read(f)) } catch {} })
const nfrFiles = Glob({ pattern: `${sessionFolder}/requirements/NFR-*.md` })
nfrFiles.forEach(f => { try { documents.requirements.push(Read(f)) } catch {} })
// Load individual ADRs
const adrFiles = Glob({ pattern: `${sessionFolder}/architecture/ADR-*.md` })
adrFiles.forEach(f => { try { documents.adrs.push(Read(f)) } catch {} })
// Load individual Epics
const epicFiles = Glob({ pattern: `${sessionFolder}/epics/EPIC-*.md` })
epicFiles.forEach(f => { try { documents.epics.push(Read(f)) } catch {} })
// Load discussions
const discussFiles = Glob({ pattern: `${sessionFolder}/discussions/discuss-*.md` })
discussFiles.forEach(f => { try { documents.discussions.push(Read(f)) } catch {} })
// Verify completeness
const docInventory = {
config: !!documents.config,
discoveryContext: !!documents.discoveryContext,
productBrief: !!documents.productBrief,
requirements: documents.requirements.length > 0,
architecture: documents.adrs.length > 0,
epics: documents.epics.length > 0,
discussions: documents.discussions.length
}
```
### Phase 3: 4-Dimension Quality Scoring
```javascript
const scores = {
completeness: 0,
consistency: 0,
traceability: 0,
depth: 0
}
// ===== Completeness (25%) =====
function scoreCompleteness(docs) {
let score = 0
const checks = [
{ name: 'spec-config.json', present: !!docs.config, weight: 5 },
{ name: 'discovery-context.json', present: !!docs.discoveryContext, weight: 10 },
{ name: 'product-brief.md', present: !!docs.productBrief, weight: 20 },
{ name: 'requirements/_index.md', present: !!docs.requirementsIndex, weight: 15 },
{ name: 'REQ-* files', present: docs.requirements.length > 0, weight: 10 },
{ name: 'architecture/_index.md', present: !!docs.architectureIndex, weight: 15 },
{ name: 'ADR-* files', present: docs.adrs.length > 0, weight: 10 },
{ name: 'epics/_index.md', present: !!docs.epicsIndex, weight: 10 },
{ name: 'EPIC-* files', present: docs.epics.length > 0, weight: 5 }
]
checks.forEach(check => {
if (check.present) score += check.weight
})
return { score, checks, issues: checks.filter(c => !c.present).map(c => `Missing: ${c.name}`) }
}
// ===== Consistency (25%) =====
function scoreConsistency(docs) {
let score = 100
const issues = []
// Check session_id consistency across documents
const sessionId = docs.config?.session_id
if (sessionId) {
if (docs.productBrief && !docs.productBrief.includes(sessionId)) {
score -= 15; issues.push('Product Brief missing session_id reference')
}
}
// Check terminology consistency
// Extract key terms from product brief, verify usage in other docs
if (docs.productBrief && docs.requirementsIndex) {
// Basic term consistency check
const briefTerms = docs.productBrief.match(/##\s+(.+)/g)?.map(h => h.replace('## ', '')) || []
// Verify heading style consistency
}
// Check YAML frontmatter format consistency
const docsWithFrontmatter = [docs.productBrief, docs.requirementsIndex, docs.architectureIndex, docs.epicsIndex].filter(Boolean)
const hasFrontmatter = docsWithFrontmatter.map(d => /^---\n[\s\S]+?\n---/.test(d))
const frontmatterConsistent = hasFrontmatter.every(v => v === hasFrontmatter[0])
if (!frontmatterConsistent) {
score -= 20; issues.push('Inconsistent YAML frontmatter across documents')
}
return { score: Math.max(0, score), issues }
}
// ===== Traceability (25%) =====
function scoreTraceability(docs) {
let score = 0
const issues = []
// Goals → Requirements tracing
if (docs.productBrief && docs.requirementsIndex) {
// Check if requirements reference product brief goals
const hasGoalRefs = docs.requirements.some(r => /goal|brief|vision/i.test(r))
if (hasGoalRefs) score += 25
else issues.push('Requirements lack references to Product Brief goals')
}
// Requirements → Architecture tracing
if (docs.requirementsIndex && docs.architectureIndex) {
const hasReqRefs = docs.adrs.some(a => /REQ-|requirement/i.test(a))
if (hasReqRefs) score += 25
else issues.push('Architecture ADRs lack requirement references')
}
// Requirements → Stories tracing
if (docs.requirementsIndex && docs.epicsIndex) {
const hasStoryRefs = docs.epics.some(e => /REQ-|requirement/i.test(e))
if (hasStoryRefs) score += 25
else issues.push('Epics/Stories lack requirement tracing')
}
// Full chain check
if (score >= 50) score += 25 // bonus for good overall traceability
return { score: Math.min(100, score), issues }
}
// ===== Depth (25%) =====
function scoreDepth(docs) {
let score = 100
const issues = []
// Check acceptance criteria specificity
const acPattern = /acceptance|criteria|验收/i
const hasSpecificAC = docs.requirements.some(r => acPattern.test(r) && r.length > 200)
if (!hasSpecificAC) {
score -= 25; issues.push('Acceptance criteria may lack specificity')
}
// Check ADR justification depth
const adrHasAlternatives = docs.adrs.some(a => /alternative|替代|pros|cons/i.test(a))
if (!adrHasAlternatives && docs.adrs.length > 0) {
score -= 25; issues.push('ADRs lack alternatives analysis')
}
// Check story estimability
const storySized = docs.epics.some(e => /\b[SMLX]{1,2}\b|Small|Medium|Large/.test(e))
if (!storySized && docs.epics.length > 0) {
score -= 25; issues.push('Stories lack size estimates')
}
// Check Mermaid diagrams presence
const hasDiagrams = [docs.architectureIndex, docs.epicsIndex].some(d => d && /```mermaid/.test(d))
if (!hasDiagrams) {
score -= 10; issues.push('Missing Mermaid diagrams')
}
return { score: Math.max(0, score), issues }
}
// Execute all scoring
const completenessResult = scoreCompleteness(documents)
const consistencyResult = scoreConsistency(documents)
const traceabilityResult = scoreTraceability(documents)
const depthResult = scoreDepth(documents)
scores.completeness = completenessResult.score
scores.consistency = consistencyResult.score
scores.traceability = traceabilityResult.score
scores.depth = depthResult.score
const overallScore = (scores.completeness + scores.consistency + scores.traceability + scores.depth) / 4
const qualityGate = overallScore >= 80 ? 'PASS' : overallScore >= 60 ? 'REVIEW' : 'FAIL'
```
### Phase 4: Report Generation
```javascript
// Generate readiness-report.md
const readinessReport = `---
session_id: ${documents.config?.session_id || 'unknown'}
phase: 6
document_type: readiness-report
status: complete
generated_at: ${new Date().toISOString()}
version: 1
---
# Readiness Report
## Quality Scores
| Dimension | Score | Weight |
|-----------|-------|--------|
| Completeness | ${scores.completeness}% | 25% |
| Consistency | ${scores.consistency}% | 25% |
| Traceability | ${scores.traceability}% | 25% |
| Depth | ${scores.depth}% | 25% |
| **Overall** | **${overallScore.toFixed(1)}%** | **100%** |
## Quality Gate: ${qualityGate}
${qualityGate === 'PASS' ? 'All quality criteria met. Specification is ready for execution.' :
qualityGate === 'REVIEW' ? 'Quality is acceptable with some areas needing attention.' :
'Critical quality issues must be addressed before proceeding.'}
## Issues Found
### Completeness Issues
${completenessResult.issues.map(i => `- ${i}`).join('\n') || 'None'}
### Consistency Issues
${consistencyResult.issues.map(i => `- ${i}`).join('\n') || 'None'}
### Traceability Issues
${traceabilityResult.issues.map(i => `- ${i}`).join('\n') || 'None'}
### Depth Issues
${depthResult.issues.map(i => `- ${i}`).join('\n') || 'None'}
## Document Inventory
${Object.entries(docInventory).map(([k, v]) => `- ${k}: ${v === true ? '✓' : v === false ? '✗' : v}`).join('\n')}
## Discussion Rounds Completed: ${documents.discussions.length}
## Recommendations
${allIssues.map(i => `- ${i}`).join('\n') || 'No outstanding issues.'}
`
Write(`${sessionFolder}/readiness-report.md`, readinessReport)
// Generate spec-summary.md (one-page executive summary)
const specSummary = `---
session_id: ${documents.config?.session_id || 'unknown'}
phase: 6
document_type: spec-summary
status: complete
generated_at: ${new Date().toISOString()}
version: 1
---
# Specification Summary
**Topic**: ${documents.config?.topic || 'N/A'}
**Complexity**: ${documents.config?.complexity || 'N/A'}
**Quality Score**: ${overallScore.toFixed(1)}% (${qualityGate})
**Discussion Rounds**: ${documents.discussions.length}
## Key Deliverables
- Product Brief: ${docInventory.productBrief ? '✓' : '✗'}
- Requirements (PRD): ${docInventory.requirements ? `✓ (${documents.requirements.length} items)` : '✗'}
- Architecture: ${docInventory.architecture ? `✓ (${documents.adrs.length} ADRs)` : '✗'}
- Epics & Stories: ${docInventory.epics ? `✓ (${documents.epics.length} epics)` : '✗'}
## Next Steps
${qualityGate === 'PASS' ? '- Ready for handoff to execution workflows (lite-plan, req-plan, plan, issue:new)' :
qualityGate === 'REVIEW' ? '- Address review items, then proceed to execution' :
'- Fix critical issues before proceeding'}
`
Write(`${sessionFolder}/spec-summary.md`, specSummary)
```
### Phase 5: Report to Coordinator
```javascript
const allIssues = [
...completenessResult.issues,
...consistencyResult.issues,
...traceabilityResult.issues,
...depthResult.issues
]
// Log before SendMessage
mcp__ccw-tools__team_msg({
operation: "log", team: teamName,
from: "spec-reviewer", to: "coordinator",
type: qualityGate === 'FAIL' ? "fix_required" : "quality_result",
summary: `质量检查 ${qualityGate}: ${overallScore.toFixed(1)}分 (完整性${scores.completeness}/一致性${scores.consistency}/追溯${scores.traceability}/深度${scores.depth})`,
data: { gate: qualityGate, score: overallScore, issues: allIssues }
})
SendMessage({
type: "message",
recipient: "coordinator",
content: `## 质量审查报告
**Task**: ${task.subject}
**总分**: ${overallScore.toFixed(1)}%
**Gate**: ${qualityGate}
### 评分详情
| 维度 | 分数 |
|------|------|
| 完整性 | ${scores.completeness}% |
| 一致性 | ${scores.consistency}% |
| 可追溯性 | ${scores.traceability}% |
| 深度 | ${scores.depth}% |
### 问题列表 (${allIssues.length})
${allIssues.map(i => `- ${i}`).join('\n') || '无问题'}
### 文档清单
${Object.entries(docInventory).map(([k, v]) => `- ${k}: ${typeof v === 'boolean' ? (v ? '✓' : '✗') : v}`).join('\n')}
### 讨论轮次: ${documents.discussions.length}
### 输出位置
- 就绪报告: ${sessionFolder}/readiness-report.md
- 执行摘要: ${sessionFolder}/spec-summary.md
${qualityGate === 'PASS' ? '质量达标,可进入最终讨论轮次 DISCUSS-006。' :
qualityGate === 'REVIEW' ? '质量基本达标但有改进空间,建议在讨论中审查。' :
'质量未达标,建议创建 DRAFT-fix 任务修复关键问题。'}`,
summary: `质量 ${qualityGate}: ${overallScore.toFixed(1)}`
})
// Mark task
if (qualityGate !== 'FAIL') {
TaskUpdate({ taskId: task.id, status: 'completed' })
} else {
// Keep in_progress, coordinator needs to create fix tasks
}
// Check for next QUALITY task
const nextTasks = TaskList().filter(t =>
t.subject.startsWith('QUALITY-') &&
t.owner === 'spec-reviewer' &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (nextTasks.length > 0) {
// Continue with next task -> back to Phase 1
}
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| No QUALITY-* tasks available | Idle, wait for coordinator assignment |
| Documents missing from session | Score as 0 for completeness, report to coordinator |
| Cannot parse YAML frontmatter | Skip consistency check for that document |
| Session folder not found | Notify coordinator, request session path |
| Scoring produces NaN | Default to 0 for that dimension, log warning |
| Unexpected error | Log error via team_msg, report to coordinator |

View File

@@ -1,492 +0,0 @@
---
name: spec-writer
description: Team spec writer - 产品简报/需求文档/架构文档/史诗故事撰写、模板驱动文档生成
argument-hint: ""
allowed-tools: SendMessage(*), TaskUpdate(*), TaskList(*), TaskGet(*), TodoWrite(*), Read(*), Write(*), Edit(*), Bash(*), Glob(*), Grep(*), Task(*)
group: team
---
# Team Spec Writer Command (/team:spec-writer)
## Overview
Team spec-writer role command. Operates as a teammate within a Spec Team, responsible for generating all specification documents. Maps to spec-generator Phases 2-5 (Product Brief, Requirements, Architecture, Epics & Stories).
**Core capabilities:**
- Task discovery from shared team task list (DRAFT-* tasks)
- Complexity-adaptive writing (Low → direct, Medium/High → multi-CLI analysis)
- Multi-perspective document generation (产品/技术/用户 parallel analysis)
- Template-driven output following spec-generator document standards
- Discussion feedback incorporation for iterative refinement
## Role Definition
**Name**: `spec-writer`
**Responsibility**: Load Context → Generate Document → Incorporate Feedback → Report
**Communication**: SendMessage to coordinator only
## 消息总线
每次 SendMessage **前**,必须调用 `mcp__ccw-tools__team_msg` 记录消息:
```javascript
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-writer", to: "coordinator", type: "<type>", summary: "<摘要>", ref: "<文件路径>" })
```
### 支持的 Message Types
| Type | 方向 | 触发时机 | 说明 |
|------|------|----------|------|
| `draft_ready` | spec-writer → coordinator | 文档撰写完成 | 附带文档路径和类型 |
| `draft_revision` | spec-writer → coordinator | 文档修订后重新提交 | 说明修改内容 |
| `impl_progress` | spec-writer → coordinator | 长时间撰写进展 | 多文档阶段进度 |
| `error` | spec-writer → coordinator | 遇到不可恢复错误 | 模板缺失、上下文不足等 |
### 调用示例
```javascript
// 文档就绪
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-writer", to: "coordinator", type: "draft_ready", summary: "Product Brief 完成: 8个章节, 3视角合成", ref: ".workflow/.spec-team/session/product-brief.md" })
// 文档修订
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-writer", to: "coordinator", type: "draft_revision", summary: "PRD 已按讨论反馈修订: 新增2个NFR, 调整3个优先级" })
// 错误上报
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-writer", to: "coordinator", type: "error", summary: "缺少 discovery-context.json, 无法生成 Product Brief" })
```
### CLI 回退
`mcp__ccw-tools__team_msg` MCP 不可用时,使用 `ccw team` CLI 作为等效回退:
```javascript
// 回退: 将 MCP 调用替换为 Bash CLI参数一一对应
Bash(`ccw team log --team "${teamName}" --from "spec-writer" --to "coordinator" --type "draft_ready" --summary "Product Brief 完成: 8个章节" --ref "${sessionFolder}/product-brief.md" --json`)
```
**参数映射**: `team_msg(params)``ccw team log --team <team> --from spec-writer --to coordinator --type <type> --summary "<text>" [--ref <path>] [--data '<json>'] [--json]`
## Execution Process
```
Phase 1: Task Discovery
├─ TaskList to find unblocked DRAFT-* tasks
├─ TaskGet to read full task details
└─ TaskUpdate to mark in_progress
Phase 2: Context & Discussion Loading
├─ Read session config (spec-config.json)
├─ Read relevant prior documents and discussion records
├─ Determine document type from task subject (Brief/PRD/Architecture/Epics)
└─ Load discussion feedback (discuss-*.md)
Phase 3: Document Generation (type-specific)
├─ DRAFT-001: Product Brief (multi-CLI parallel analysis)
├─ DRAFT-002: Requirements/PRD (functional + non-functional + MoSCoW)
├─ DRAFT-003: Architecture (ADRs + tech stack + diagrams)
└─ DRAFT-004: Epics & Stories (decomposition + dependencies + MVP)
Phase 4: Self-Validation
├─ Check all template sections populated
├─ Verify cross-references to prior documents
└─ Validate YAML frontmatter completeness
Phase 5: Report to Coordinator
├─ team_msg log + SendMessage document summary
├─ TaskUpdate completed
└─ Check for next DRAFT-* task
```
## Implementation
### Phase 1: Task Discovery
```javascript
// Find assigned DRAFT-* tasks
const tasks = TaskList()
const myTasks = tasks.filter(t =>
t.subject.startsWith('DRAFT-') &&
t.owner === 'spec-writer' &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (myTasks.length === 0) return // idle
const task = TaskGet({ taskId: myTasks[0].id })
TaskUpdate({ taskId: task.id, status: 'in_progress' })
```
### Phase 2: Context & Discussion Loading
```javascript
// Extract session folder from task description
const sessionMatch = task.description.match(/Session:\s*(.+)/)
const sessionFolder = sessionMatch ? sessionMatch[1].trim() : ''
// Load session config
let specConfig = null
try { specConfig = JSON.parse(Read(`${sessionFolder}/spec-config.json`)) } catch {}
// Determine document type from task subject
const docType = task.subject.includes('Product Brief') ? 'product-brief'
: task.subject.includes('Requirements') || task.subject.includes('PRD') ? 'requirements'
: task.subject.includes('Architecture') ? 'architecture'
: task.subject.includes('Epics') ? 'epics'
: 'unknown'
// Load discussion feedback (from preceding DISCUSS task)
const discussionFiles = {
'product-brief': 'discussions/discuss-001-scope.md',
'requirements': 'discussions/discuss-002-brief.md',
'architecture': 'discussions/discuss-003-requirements.md',
'epics': 'discussions/discuss-004-architecture.md'
}
let discussionFeedback = null
try {
discussionFeedback = Read(`${sessionFolder}/${discussionFiles[docType]}`)
} catch {}
// Load prior documents
const priorDocs = {}
if (docType !== 'product-brief') {
try { priorDocs.discoveryContext = Read(`${sessionFolder}/discovery-context.json`) } catch {}
}
if (docType === 'requirements' || docType === 'architecture' || docType === 'epics') {
try { priorDocs.productBrief = Read(`${sessionFolder}/product-brief.md`) } catch {}
}
if (docType === 'architecture' || docType === 'epics') {
try { priorDocs.requirementsIndex = Read(`${sessionFolder}/requirements/_index.md`) } catch {}
}
if (docType === 'epics') {
try { priorDocs.architectureIndex = Read(`${sessionFolder}/architecture/_index.md`) } catch {}
}
```
### Phase 3: Document Generation
```javascript
// Route to specific generation logic based on document type
switch (docType) {
case 'product-brief':
await generateProductBrief(sessionFolder, specConfig, discussionFeedback)
break
case 'requirements':
await generateRequirements(sessionFolder, specConfig, priorDocs, discussionFeedback)
break
case 'architecture':
await generateArchitecture(sessionFolder, specConfig, priorDocs, discussionFeedback)
break
case 'epics':
await generateEpics(sessionFolder, specConfig, priorDocs, discussionFeedback)
break
}
```
#### DRAFT-001: Product Brief (Multi-Perspective Analysis)
```javascript
async function generateProductBrief(sessionFolder, config, discussionFeedback) {
const discoveryContext = JSON.parse(Read(`${sessionFolder}/discovery-context.json`))
const topic = config?.topic || discoveryContext.seed_analysis.problem_statement
// 进展通知
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-writer", to: "coordinator", type: "impl_progress", summary: "开始 Product Brief 多视角分析 (1/3)" })
// Launch 3 parallel CLI analyses for multi-perspective synthesis
// 1. Product perspective (Gemini)
Bash({
command: `ccw cli -p "PURPOSE: Analyze from PRODUCT perspective for specification.
TASK: • Market fit analysis • Value proposition • Success criteria • Competitive landscape
TOPIC: ${topic}
CONTEXT: Discovery findings: ${JSON.stringify(discoveryContext.seed_analysis)}
${discussionFeedback ? `DISCUSSION FEEDBACK: ${discussionFeedback}` : ''}
EXPECTED: Structured product analysis (vision, problem, goals, scope, constraints)
CONSTRAINTS: Focus on product strategy" --tool gemini --mode analysis`,
run_in_background: true
})
// 2. Technical perspective (Codex)
Bash({
command: `ccw cli -p "PURPOSE: Analyze from TECHNICAL perspective for specification.
TASK: • Technical feasibility • Architecture constraints • Tech stack recommendations • Implementation risks
TOPIC: ${topic}
CONTEXT: Discovery findings: ${JSON.stringify(discoveryContext.seed_analysis)}
${discoveryContext.codebase_context ? `CODEBASE: ${JSON.stringify(discoveryContext.codebase_context)}` : ''}
EXPECTED: Technical feasibility assessment
CONSTRAINTS: Focus on engineering perspective" --tool codex --mode analysis`,
run_in_background: true
})
// 3. User perspective (Claude)
Bash({
command: `ccw cli -p "PURPOSE: Analyze from USER perspective for specification.
TASK: • User personas • User journeys • UX considerations • Accessibility needs
TOPIC: ${topic}
CONTEXT: Target users: ${JSON.stringify(discoveryContext.seed_analysis.target_users)}
EXPECTED: User experience analysis (personas, journeys, pain points)
CONSTRAINTS: Focus on end-user perspective" --tool claude --mode analysis`,
run_in_background: true
})
// Wait for all 3 analyses to complete, then synthesize
// Generate product-brief.md with YAML frontmatter
const brief = `---
session_id: ${config.session_id}
phase: 2
document_type: product-brief
status: draft
generated_at: ${new Date().toISOString()}
version: 1
dependencies:
- discovery-context.json
- discuss-001-scope.md
---
# Product Brief: ${topic}
## Vision
${productPerspective.vision}
## Problem Statement
${discoveryContext.seed_analysis.problem_statement}
## Target Users
${personas.map(p => `### ${p.name}\n- **Role**: ${p.role}\n- **Pain Points**: ${p.painPoints}\n- **Goals**: ${p.goals}`).join('\n\n')}
## Goals & Success Metrics
${productPerspective.goals}
## Scope
### In Scope
${productPerspective.inScope}
### Out of Scope
${productPerspective.outOfScope}
## Technical Feasibility
${technicalPerspective.summary}
## User Experience Considerations
${userPerspective.summary}
## Multi-Perspective Synthesis
### Convergent Themes
${synthesis.convergent}
### Divergent Views
${synthesis.divergent}
### Discussion Feedback Integration
${discussionFeedback ? discussionFeedback.summary : 'N/A (first draft)'}
## Constraints
${discoveryContext.seed_analysis.constraints.map(c => `- ${c}`).join('\n')}
## Open Questions
${openQuestions.map(q => `- ${q}`).join('\n')}
`
Write(`${sessionFolder}/product-brief.md`, brief)
}
```
#### DRAFT-002: Requirements/PRD
```javascript
async function generateRequirements(sessionFolder, config, priorDocs, discussionFeedback) {
// Use Gemini CLI to expand requirements from product brief
Bash({
command: `ccw cli -p "PURPOSE: Generate functional and non-functional requirements from Product Brief.
TASK:
• Extract functional requirements (REQ-NNN format) with user stories and acceptance criteria
• Generate non-functional requirements (NFR-{type}-NNN) for Performance/Security/Scalability/Usability
• Apply MoSCoW prioritization (Must/Should/Could/Won't)
• Create traceability matrix to Product Brief goals
CONTEXT: Product Brief: ${priorDocs.productBrief}
${discussionFeedback ? `DISCUSSION FEEDBACK: ${discussionFeedback}` : ''}
EXPECTED: Structured requirements list in JSON
CONSTRAINTS: Each requirement needs ID, title, user story, 2-4 acceptance criteria" --tool gemini --mode analysis`,
run_in_background: true
})
// Generate requirements/ directory structure
Bash(`mkdir -p ${sessionFolder}/requirements`)
// Write _index.md + individual REQ-*.md + NFR-*.md files
// Following spec-generator templates/requirements-prd.md format
}
```
#### DRAFT-003: Architecture
```javascript
async function generateArchitecture(sessionFolder, config, priorDocs, discussionFeedback) {
// Generate architecture via Gemini
Bash({
command: `ccw cli -p "PURPOSE: Design system architecture based on requirements.
TASK:
• Select architecture style with justification
• Define core components and responsibilities
• Create component interaction diagram (Mermaid)
• Choose tech stack (languages, frameworks, databases, infrastructure)
• Generate 2-4 ADRs with alternatives and pros/cons
• Design data model (Mermaid erDiagram)
• Define security architecture
CONTEXT: Requirements: ${priorDocs.requirementsIndex}
Product Brief: ${priorDocs.productBrief}
${discussionFeedback ? `DISCUSSION FEEDBACK: ${discussionFeedback}` : ''}
EXPECTED: Complete architecture document
CONSTRAINTS: Include ADRs with alternatives" --tool gemini --mode analysis`,
run_in_background: true
})
// Challenge architecture via Codex
Bash({
command: `ccw cli -p "PURPOSE: Challenge and review proposed architecture.
TASK: • Review ADR alternatives • Identify bottlenecks • Assess security gaps • Rate quality (1-5)
CONTEXT: [architecture output from above]
EXPECTED: Architecture review with ratings" --tool codex --mode analysis`,
run_in_background: true
})
// Generate architecture/ directory
Bash(`mkdir -p ${sessionFolder}/architecture`)
// Write _index.md + ADR-*.md files
}
```
#### DRAFT-004: Epics & Stories
```javascript
async function generateEpics(sessionFolder, config, priorDocs, discussionFeedback) {
// Decompose via Gemini
Bash({
command: `ccw cli -p "PURPOSE: Decompose requirements into Epics and Stories.
TASK:
• Group 3-7 logical Epics by domain or user journey
• Generate 2-5 Stories per Epic (user story format)
• Create cross-Epic dependency map (Mermaid)
• Define MVP scope with done criteria
• Recommend execution order
CONTEXT: Requirements: ${priorDocs.requirementsIndex}
Architecture: ${priorDocs.architectureIndex}
Product Brief: ${priorDocs.productBrief}
${discussionFeedback ? `DISCUSSION FEEDBACK: ${discussionFeedback}` : ''}
EXPECTED: Epic/Story decomposition with dependencies
CONSTRAINTS: Each story needs AC, size estimate (S/M/L/XL), requirement tracing" --tool gemini --mode analysis`,
run_in_background: true
})
// Generate epics/ directory
Bash(`mkdir -p ${sessionFolder}/epics`)
// Write _index.md + EPIC-*.md files
}
```
### Phase 4: Self-Validation
```javascript
// Validate generated document
const validationChecks = {
has_frontmatter: false,
sections_complete: false,
cross_references: false,
discussion_integrated: false
}
// Check YAML frontmatter
const docContent = Read(`${sessionFolder}/${outputPath}`)
validationChecks.has_frontmatter = /^---\n[\s\S]+?\n---/.test(docContent)
// Check required sections based on doc type
const requiredSections = {
'product-brief': ['Vision', 'Problem Statement', 'Target Users', 'Goals', 'Scope'],
'requirements': ['_index.md', 'REQ-'],
'architecture': ['_index.md', 'ADR-'],
'epics': ['_index.md', 'EPIC-']
}
// Verify all sections present
// Check cross-references to prior documents
validationChecks.cross_references = docContent.includes('session_id')
// Check discussion feedback integration
validationChecks.discussion_integrated = !discussionFeedback || docContent.includes('Discussion')
const allValid = Object.values(validationChecks).every(v => v)
```
### Phase 5: Report to Coordinator
```javascript
const docTypeLabel = {
'product-brief': 'Product Brief',
'requirements': 'Requirements/PRD',
'architecture': 'Architecture Document',
'epics': 'Epics & Stories'
}
// Log before SendMessage
mcp__ccw-tools__team_msg({
operation: "log", team: teamName,
from: "spec-writer", to: "coordinator",
type: "draft_ready",
summary: `${docTypeLabel[docType]} 完成: ${allValid ? '验证通过' : '部分验证失败'}`,
ref: `${sessionFolder}/${outputPath}`
})
SendMessage({
type: "message",
recipient: "coordinator",
content: `## 文档撰写结果
**Task**: ${task.subject}
**文档类型**: ${docTypeLabel[docType]}
**验证状态**: ${allValid ? 'PASS' : 'PARTIAL'}
### 文档摘要
${documentSummary}
### 讨论反馈整合
${discussionFeedback ? '已整合前序讨论反馈' : '首次撰写(无前序讨论反馈)'}
### 自验证结果
${Object.entries(validationChecks).map(([k, v]) => `- ${k}: ${v ? '✓' : '✗'}`).join('\n')}
### 输出位置
${sessionFolder}/${outputPath}
文档已就绪,可进入讨论轮次。`,
summary: `${docTypeLabel[docType]} 就绪`
})
// Mark task completed
TaskUpdate({ taskId: task.id, status: 'completed' })
// Check for next DRAFT task
const nextTasks = TaskList().filter(t =>
t.subject.startsWith('DRAFT-') &&
t.owner === 'spec-writer' &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (nextTasks.length > 0) {
// Continue with next task -> back to Phase 1
}
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| No DRAFT-* tasks available | Idle, wait for coordinator assignment |
| Prior document not found | Notify coordinator, request prerequisite |
| CLI analysis failure | Retry with fallback tool, then direct generation |
| Template sections incomplete | Generate best-effort, note gaps in report |
| Discussion feedback contradicts prior docs | Note conflict in document, flag for next discussion |
| Session folder missing | Notify coordinator, request session path |
| Unexpected error | Log error via team_msg, report to coordinator |

View File

@@ -1,402 +0,0 @@
---
name: test
description: Team tester - 自适应测试修复循环、渐进式测试、报告结果给coordinator
argument-hint: ""
allowed-tools: SendMessage(*), TaskUpdate(*), TaskList(*), TaskGet(*), TodoWrite(*), Read(*), Write(*), Edit(*), Bash(*), Glob(*), Grep(*), Task(*)
group: team
---
# Team Test Command (/team:test)
## Overview
Team tester role command. Operates as a teammate within an Agent Team, responsible for test execution with adaptive fix cycles and progressive testing. Reports results to the coordinator.
**Core capabilities:**
- Task discovery from shared team task list (TEST-* tasks)
- Test framework auto-detection (jest/vitest/pytest/mocha)
- Adaptive strategy engine: conservative → aggressive → surgical
- Progressive testing: affected tests during iterations, full suite for final validation
- Fix cycle with max iterations and quality gate (>= 95% pass rate)
- Structured result reporting to coordinator
## Role Definition
**Name**: `tester`
**Responsibility**: Run tests → Fix cycle → Report results
**Communication**: SendMessage to coordinator only
## 消息总线
每次 SendMessage **前**,必须调用 `mcp__ccw-tools__team_msg` 记录消息:
```javascript
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "<type>", summary: "<摘要>" })
```
### 支持的 Message Types
| Type | 方向 | 触发时机 | 说明 |
|------|------|----------|------|
| `test_result` | tester → coordinator | 测试循环结束(通过或达到最大迭代) | 附带 pass rate、迭代次数、剩余失败 |
| `impl_progress` | tester → coordinator | 修复循环中间进度 | 可选,长时间修复时使用(如迭代>5 |
| `fix_required` | tester → coordinator | 测试发现需要 executor 修复的问题 | 超出 tester 修复能力的架构/设计问题 |
| `error` | tester → coordinator | 测试框架不可用或测试执行崩溃 | 命令未找到、超时、环境问题等 |
### 调用示例
```javascript
// 测试通过
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "test_result", summary: "TEST-001通过: 98% pass rate, 3次迭代", data: { passRate: 98, iterations: 3, total: 42, passed: 41, failed: 1 } })
// 测试未达标
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "test_result", summary: "TEST-001未达标: 82% pass rate, 10次迭代已用完", data: { passRate: 82, iterations: 10, criticalFailures: 2 } })
// 需要 executor 修复
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "fix_required", summary: "数据库连接池配置导致集成测试全部失败, 需executor修复" })
// 错误上报
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "error", summary: "jest命令未找到, 请确认测试框架已安装" })
```
### CLI 回退
`mcp__ccw-tools__team_msg` MCP 不可用时,使用 `ccw team` CLI 作为等效回退:
```javascript
// 回退: 将 MCP 调用替换为 Bash CLI参数一一对应
Bash(`ccw team log --team "${teamName}" --from "tester" --to "coordinator" --type "test_result" --summary "TEST-001通过: 98% pass rate" --data '{"passRate":98,"iterations":3}' --json`)
```
**参数映射**: `team_msg(params)``ccw team log --team <team> --from tester --to coordinator --type <type> --summary "<text>" [--data '<json>'] [--json]`
## Execution Process
```
Phase 1: Task Discovery
├─ TaskList to find unblocked TEST-* tasks assigned to me
├─ TaskGet to read full task details
└─ TaskUpdate to mark in_progress
Phase 2: Test Framework Detection
├─ Detect framework: jest/vitest/pytest/mocha
├─ Identify test command from package.json/pyproject.toml
└─ Locate affected test files based on changed files
Phase 3: Test Execution & Fix Cycle (max 10 iterations)
├─ Strategy Engine:
│ ├─ Iteration 1-2: Conservative (single targeted fix)
│ ├─ Pass rate > 80% + similar failures: Aggressive (batch fix)
│ └─ Regression detected (drop > 10%): Surgical (minimal + rollback)
├─ Progressive Testing:
│ ├─ Iterations: run affected tests only
│ └─ Final: full test suite validation
└─ Quality Gate: pass rate >= 95%
Phase 4: Result Analysis
├─ Calculate final pass rate
├─ Classify failure severity (critical/high/medium/low)
└─ Generate test summary
Phase 5: Report to Coordinator
├─ SendMessage with test results
├─ >= 95%: mark TEST task completed
└─ < 95% after max iterations: report needs intervention
```
## Implementation
### Phase 1: Task Discovery
```javascript
// Find my assigned TEST tasks
const tasks = TaskList()
const myTestTasks = tasks.filter(t =>
t.subject.startsWith('TEST-') &&
t.owner === 'tester' &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (myTestTasks.length === 0) return // idle
const task = TaskGet({ taskId: myTestTasks[0].id })
TaskUpdate({ taskId: task.id, status: 'in_progress' })
```
### Phase 2: Test Framework Detection
```javascript
// Detect test framework
function detectTestFramework() {
// Check package.json
try {
const pkg = JSON.parse(Read('package.json'))
const deps = { ...pkg.dependencies, ...pkg.devDependencies }
if (deps.vitest) return { framework: 'vitest', command: 'npx vitest run' }
if (deps.jest) return { framework: 'jest', command: 'npx jest' }
if (deps.mocha) return { framework: 'mocha', command: 'npx mocha' }
} catch {}
// Check pyproject.toml / pytest
try {
const pyproject = Read('pyproject.toml')
if (pyproject.includes('pytest')) return { framework: 'pytest', command: 'pytest' }
} catch {}
// Fallback
return { framework: 'unknown', command: 'npm test' }
}
const testConfig = detectTestFramework()
// Locate affected test files
function findAffectedTests(changedFiles) {
const testFiles = []
for (const file of changedFiles) {
// Convention: src/foo.ts → tests/foo.test.ts or __tests__/foo.test.ts
const testVariants = [
file.replace(/\/src\//, '/tests/').replace(/\.(ts|js|tsx|jsx)$/, '.test.$1'),
file.replace(/\/src\//, '/__tests__/').replace(/\.(ts|js|tsx|jsx)$/, '.test.$1'),
file.replace(/\.(ts|js|tsx|jsx)$/, '.test.$1'),
file.replace(/\.(ts|js|tsx|jsx)$/, '.spec.$1')
]
for (const variant of testVariants) {
const exists = Bash(`test -f "${variant}" && echo exists || true`)
if (exists.includes('exists')) testFiles.push(variant)
}
}
return [...new Set(testFiles)]
}
// Extract changed files from task description or git diff
const changedFiles = Bash(`git diff --name-only HEAD~1 2>/dev/null || git diff --name-only --cached`).split('\n').filter(Boolean)
const affectedTests = findAffectedTests(changedFiles)
```
### Phase 3: Test Execution & Fix Cycle
```javascript
const MAX_ITERATIONS = 10
const PASS_RATE_TARGET = 95
let currentPassRate = 0
let previousPassRate = 0
const iterationHistory = []
for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
// Strategy selection
const strategy = selectStrategy(iteration, currentPassRate, previousPassRate, iterationHistory)
// Determine test scope
const isFullSuite = iteration === MAX_ITERATIONS || currentPassRate >= PASS_RATE_TARGET
const testCommand = isFullSuite
? testConfig.command
: `${testConfig.command} ${affectedTests.join(' ')}`
// Run tests
const testOutput = Bash(`${testCommand} 2>&1 || true`, { timeout: 300000 })
// Parse results
const results = parseTestResults(testOutput, testConfig.framework)
previousPassRate = currentPassRate
currentPassRate = results.passRate
// Record iteration
iterationHistory.push({
iteration,
pass_rate: currentPassRate,
strategy: strategy,
failed_tests: results.failedTests,
total: results.total,
passed: results.passed
})
// Quality gate check
if (currentPassRate >= PASS_RATE_TARGET) {
if (!isFullSuite) {
// Run full suite for final validation
const fullOutput = Bash(`${testConfig.command} 2>&1 || true`, { timeout: 300000 })
const fullResults = parseTestResults(fullOutput, testConfig.framework)
currentPassRate = fullResults.passRate
if (currentPassRate >= PASS_RATE_TARGET) break
} else {
break
}
}
if (iteration >= MAX_ITERATIONS) break
// Apply fixes based on strategy
applyFixes(results.failedTests, strategy, testOutput)
}
// Strategy Engine
function selectStrategy(iteration, passRate, prevPassRate, history) {
// Regression detection
if (prevPassRate > 0 && passRate < prevPassRate - 10) return 'surgical'
// Iteration-based default
if (iteration <= 2) return 'conservative'
// Pattern-based upgrade
if (passRate > 80) {
// Check if failures are similar (same test files, same error patterns)
const recentFailures = history.slice(-2).flatMap(h => h.failed_tests)
const uniqueFailures = [...new Set(recentFailures)]
if (uniqueFailures.length <= recentFailures.length * 0.6) return 'aggressive'
}
return 'conservative'
}
// Fix application
function applyFixes(failedTests, strategy, testOutput) {
switch (strategy) {
case 'conservative':
// Fix one failure at a time
// Read failing test, understand error, apply targeted fix
if (failedTests.length > 0) {
const target = failedTests[0]
// Analyze error message from testOutput
// Read source file and test file
// Apply minimal fix
}
break
case 'aggressive':
// Batch fix similar failures
// Group by error pattern
// Apply fixes to all related failures
break
case 'surgical':
// Minimal changes, consider rollback
// Only fix the most critical failure
// Verify no regression
break
}
}
// Test result parser
function parseTestResults(output, framework) {
let passed = 0, failed = 0, total = 0, failedTests = []
if (framework === 'jest' || framework === 'vitest') {
const passMatch = output.match(/(\d+) passed/)
const failMatch = output.match(/(\d+) failed/)
passed = passMatch ? parseInt(passMatch[1]) : 0
failed = failMatch ? parseInt(failMatch[1]) : 0
total = passed + failed
// Extract failed test names
const failPattern = /FAIL\s+(.+)/g
let m
while ((m = failPattern.exec(output)) !== null) failedTests.push(m[1].trim())
} else if (framework === 'pytest') {
const summaryMatch = output.match(/(\d+) passed.*?(\d+) failed/)
if (summaryMatch) {
passed = parseInt(summaryMatch[1])
failed = parseInt(summaryMatch[2])
}
total = passed + failed
}
return {
passed, failed, total,
passRate: total > 0 ? Math.round((passed / total) * 100) : 100,
failedTests
}
}
```
### Phase 4: Result Analysis
```javascript
// Classify failure severity
function classifyFailures(failedTests, testOutput) {
return failedTests.map(test => {
const testLower = test.toLowerCase()
let severity = 'low'
if (/auth|security|permission|login|password/.test(testLower)) severity = 'critical'
else if (/core|main|primary|data|state/.test(testLower)) severity = 'high'
else if (/edge|flaky|timeout|env/.test(testLower)) severity = 'low'
else severity = 'medium'
return { test, severity }
})
}
const classifiedFailures = classifyFailures(
iterationHistory[iterationHistory.length - 1]?.failed_tests || [],
'' // last test output
)
const hasCriticalFailures = classifiedFailures.some(f => f.severity === 'critical')
```
### Phase 5: Report to Coordinator
```javascript
const finalIteration = iterationHistory[iterationHistory.length - 1]
const success = currentPassRate >= PASS_RATE_TARGET
SendMessage({
type: "message",
recipient: "coordinator",
content: `## Test Results
**Task**: ${task.subject}
**Status**: ${success ? 'PASSED' : 'NEEDS ATTENTION'}
### Summary
- **Pass Rate**: ${currentPassRate}% (target: ${PASS_RATE_TARGET}%)
- **Iterations**: ${iterationHistory.length}/${MAX_ITERATIONS}
- **Total Tests**: ${finalIteration?.total || 0}
- **Passed**: ${finalIteration?.passed || 0}
- **Failed**: ${finalIteration?.total - finalIteration?.passed || 0}
### Strategy History
${iterationHistory.map(h => `- Iteration ${h.iteration}: ${h.strategy}${h.pass_rate}%`).join('\n')}
${!success ? `### Remaining Failures
${classifiedFailures.map(f => `- [${f.severity.toUpperCase()}] ${f.test}`).join('\n')}
${hasCriticalFailures ? '**CRITICAL failures detected - immediate attention required**' : ''}` : '### All tests passing'}`,
summary: `Tests: ${currentPassRate}% pass rate (${iterationHistory.length} iterations)`
})
if (success) {
TaskUpdate({ taskId: task.id, status: 'completed' })
} else {
// Keep in_progress, coordinator decides next steps
SendMessage({
type: "message",
recipient: "coordinator",
content: `Test pass rate ${currentPassRate}% is below ${PASS_RATE_TARGET}% after ${MAX_ITERATIONS} iterations. Need coordinator decision on next steps.`,
summary: "Test target not met, need guidance"
})
}
// Check for next TEST task
const nextTasks = TaskList().filter(t =>
t.subject.startsWith('TEST-') &&
t.owner === 'tester' &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (nextTasks.length > 0) {
// Continue with next task
}
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| Test command not found | Detect framework, try alternatives (npm test, pytest, etc.) |
| Test execution timeout | Reduce test scope, retry with affected tests only |
| Regression detected (pass rate drops > 10%) | Switch to surgical strategy, consider rollback |
| Stuck tests (same failure 3+ iterations) | Report to coordinator, suggest different approach |
| Max iterations reached < 95% | Report failure details, let coordinator decide |
| No test files found | Report to coordinator, suggest test generation needed |