mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-28 09:23:08 +08:00
feat: Add 4 new team skills with Generator-Critic loops, shared memory, and dynamic pipelines
Create team-brainstorm (ideator↔challenger GC, quick/deep/full pipelines), team-testing (generator↔executor GC, L1/L2/L3 test layers), team-iterdev (developer↔reviewer GC, task-ledger sprint tracking), and team-uidesign (designer↔reviewer GC, CP-9 dual-track with sync points). Each team includes SKILL.md router, 5 roles, and team-config.json.
This commit is contained in:
356
.claude/skills/team-brainstorm/SKILL.md
Normal file
356
.claude/skills/team-brainstorm/SKILL.md
Normal file
@@ -0,0 +1,356 @@
|
|||||||
|
---
|
||||||
|
name: team-brainstorm
|
||||||
|
description: Unified team skill for brainstorming team. All roles invoke this skill with --role arg for role-specific execution. Triggers on "team brainstorm".
|
||||||
|
allowed-tools: TeamCreate(*), TeamDelete(*), SendMessage(*), TaskCreate(*), TaskUpdate(*), TaskList(*), TaskGet(*), Task(*), AskUserQuestion(*), Read(*), Write(*), Edit(*), Bash(*), Glob(*), Grep(*)
|
||||||
|
---
|
||||||
|
|
||||||
|
# Team Brainstorm
|
||||||
|
|
||||||
|
头脑风暴团队技能。通过 Generator-Critic 循环、共享记忆和动态管道选择,实现多角度创意发散、挑战验证和收敛筛选。所有团队成员通过 `--role=xxx` 路由到角色执行逻辑。
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
```
|
||||||
|
┌───────────────────────────────────────────────────┐
|
||||||
|
│ Skill(skill="team-brainstorm", args="--role=xxx") │
|
||||||
|
└───────────────────┬───────────────────────────────┘
|
||||||
|
│ Role Router
|
||||||
|
┌───────────┬───┼───────────┬───────────┐
|
||||||
|
↓ ↓ ↓ ↓ ↓
|
||||||
|
┌──────────┐┌───────┐┌──────────┐┌──────────┐┌─────────┐
|
||||||
|
│coordinator││ideator││challenger││synthesizer││evaluator│
|
||||||
|
│ roles/ ││roles/ ││ roles/ ││ roles/ ││ roles/ │
|
||||||
|
└──────────┘└───────┘└──────────┘└──────────┘└─────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Role Router
|
||||||
|
|
||||||
|
### Input Parsing
|
||||||
|
|
||||||
|
Parse `$ARGUMENTS` to extract `--role`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const args = "$ARGUMENTS"
|
||||||
|
const roleMatch = args.match(/--role[=\s]+(\w+)/)
|
||||||
|
|
||||||
|
if (!roleMatch) {
|
||||||
|
throw new Error("Missing --role argument. Available roles: coordinator, ideator, challenger, synthesizer, evaluator")
|
||||||
|
}
|
||||||
|
|
||||||
|
const role = roleMatch[1]
|
||||||
|
const teamName = args.match(/--team[=\s]+([\w-]+)/)?.[1] || "brainstorm"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Role Dispatch
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const VALID_ROLES = {
|
||||||
|
"coordinator": { file: "roles/coordinator.md", prefix: null },
|
||||||
|
"ideator": { file: "roles/ideator.md", prefix: "IDEA" },
|
||||||
|
"challenger": { file: "roles/challenger.md", prefix: "CHALLENGE" },
|
||||||
|
"synthesizer": { file: "roles/synthesizer.md", prefix: "SYNTH" },
|
||||||
|
"evaluator": { file: "roles/evaluator.md", prefix: "EVAL" }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!VALID_ROLES[role]) {
|
||||||
|
throw new Error(`Unknown role: ${role}. Available: ${Object.keys(VALID_ROLES).join(', ')}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read and execute role-specific logic
|
||||||
|
Read(VALID_ROLES[role].file)
|
||||||
|
// → Execute the 5-phase process defined in that file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Available Roles
|
||||||
|
|
||||||
|
| Role | Task Prefix | Responsibility | Role File |
|
||||||
|
|------|-------------|----------------|-----------|
|
||||||
|
| `coordinator` | N/A | 话题澄清、复杂度评估、管道选择、收敛监控 | [roles/coordinator.md](roles/coordinator.md) |
|
||||||
|
| `ideator` | IDEA-* | 多角度创意生成、概念探索、发散思维 | [roles/ideator.md](roles/ideator.md) |
|
||||||
|
| `challenger` | CHALLENGE-* | 魔鬼代言人、假设挑战、可行性质疑 | [roles/challenger.md](roles/challenger.md) |
|
||||||
|
| `synthesizer` | SYNTH-* | 跨想法整合、主题提取、冲突解决 | [roles/synthesizer.md](roles/synthesizer.md) |
|
||||||
|
| `evaluator` | EVAL-* | 评分排序、优先级推荐、最终筛选 | [roles/evaluator.md](roles/evaluator.md) |
|
||||||
|
|
||||||
|
## Shared Infrastructure
|
||||||
|
|
||||||
|
### Role Isolation Rules
|
||||||
|
|
||||||
|
**核心原则**: 每个角色仅能执行自己职责范围内的工作。
|
||||||
|
|
||||||
|
#### Output Tagging(强制)
|
||||||
|
|
||||||
|
所有角色的输出必须带 `[role_name]` 标识前缀:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// SendMessage — content 和 summary 都必须带标识
|
||||||
|
SendMessage({
|
||||||
|
content: `## [${role}] ...`,
|
||||||
|
summary: `[${role}] ...`
|
||||||
|
})
|
||||||
|
|
||||||
|
// team_msg — summary 必须带标识
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
summary: `[${role}] ...`
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Coordinator 隔离
|
||||||
|
|
||||||
|
| 允许 | 禁止 |
|
||||||
|
|------|------|
|
||||||
|
| 需求澄清 (AskUserQuestion) | ❌ 直接生成创意 |
|
||||||
|
| 创建任务链 (TaskCreate) | ❌ 直接评估/挑战想法 |
|
||||||
|
| 分发任务给 worker | ❌ 直接执行分析/综合 |
|
||||||
|
| 监控进度 (消息总线) | ❌ 绕过 worker 自行完成任务 |
|
||||||
|
| 汇报结果给用户 | ❌ 修改源代码或产物文件 |
|
||||||
|
|
||||||
|
#### Worker 隔离
|
||||||
|
|
||||||
|
| 允许 | 禁止 |
|
||||||
|
|------|------|
|
||||||
|
| 处理自己前缀的任务 | ❌ 处理其他角色前缀的任务 |
|
||||||
|
| SendMessage 给 coordinator | ❌ 直接与其他 worker 通信 |
|
||||||
|
| 读取 shared-memory.json | ❌ 为其他角色创建任务 (TaskCreate) |
|
||||||
|
| 写入 shared-memory.json (自己的字段) | ❌ 修改不属于本职责的资源 |
|
||||||
|
|
||||||
|
### Team Configuration
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const TEAM_CONFIG = {
|
||||||
|
name: "brainstorm",
|
||||||
|
sessionDir: ".workflow/.team/BRS-{slug}-{date}/",
|
||||||
|
msgDir: ".workflow/.team-msg/brainstorm/",
|
||||||
|
sharedMemory: "shared-memory.json"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Shared Memory (创新模式)
|
||||||
|
|
||||||
|
所有角色在 Phase 2 读取、Phase 5 写入 `shared-memory.json`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Phase 2: 读取共享记忆
|
||||||
|
const memoryPath = `${sessionFolder}/shared-memory.json`
|
||||||
|
let sharedMemory = {}
|
||||||
|
try { sharedMemory = JSON.parse(Read(memoryPath)) } catch {}
|
||||||
|
|
||||||
|
// Phase 5: 写入共享记忆(仅更新自己负责的字段)
|
||||||
|
// ideator → sharedMemory.generated_ideas
|
||||||
|
// challenger → sharedMemory.critique_insights
|
||||||
|
// synthesizer → sharedMemory.synthesis_themes
|
||||||
|
// evaluator → sharedMemory.evaluation_scores
|
||||||
|
Write(memoryPath, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Message Bus (All Roles)
|
||||||
|
|
||||||
|
Every SendMessage **before**, must call `mcp__ccw-tools__team_msg` to log:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log",
|
||||||
|
team: teamName,
|
||||||
|
from: role,
|
||||||
|
to: "coordinator",
|
||||||
|
type: "<type>",
|
||||||
|
summary: `[${role}] <summary>`,
|
||||||
|
ref: "<file_path>"
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
**Message types by role**:
|
||||||
|
|
||||||
|
| Role | Types |
|
||||||
|
|------|-------|
|
||||||
|
| coordinator | `pipeline_selected`, `gc_loop_trigger`, `task_unblocked`, `error`, `shutdown` |
|
||||||
|
| ideator | `ideas_ready`, `ideas_revised`, `error` |
|
||||||
|
| challenger | `critique_ready`, `error` |
|
||||||
|
| synthesizer | `synthesis_ready`, `error` |
|
||||||
|
| evaluator | `evaluation_ready`, `error` |
|
||||||
|
|
||||||
|
### CLI Fallback
|
||||||
|
|
||||||
|
When `mcp__ccw-tools__team_msg` MCP is unavailable:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Bash(`ccw team log --team "${teamName}" --from "${role}" --to "coordinator" --type "<type>" --summary "<summary>" --json`)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task Lifecycle (All Worker Roles)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const tasks = TaskList()
|
||||||
|
const myTasks = tasks.filter(t =>
|
||||||
|
t.subject.startsWith(`${VALID_ROLES[role].prefix}-`) &&
|
||||||
|
t.owner === role &&
|
||||||
|
t.status === 'pending' &&
|
||||||
|
t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
if (myTasks.length === 0) return // idle
|
||||||
|
const task = TaskGet({ taskId: myTasks[0].id })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||||
|
|
||||||
|
// Phase 2-4: Role-specific (see roles/{role}.md)
|
||||||
|
|
||||||
|
// Phase 5: Report + Loop — 所有输出必须带 [role] 标识
|
||||||
|
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: role, to: "coordinator", type: "...", summary: `[${role}] ...` })
|
||||||
|
SendMessage({ type: "message", recipient: "coordinator", content: `## [${role}] ...`, summary: `[${role}] ...` })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||||
|
// Check for next task → back to Phase 1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Three-Pipeline Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
Quick:
|
||||||
|
IDEA-001 → CHALLENGE-001 → SYNTH-001
|
||||||
|
|
||||||
|
Deep (Generator-Critic Loop):
|
||||||
|
IDEA-001 → CHALLENGE-001 → IDEA-002(fix) → CHALLENGE-002 → SYNTH-001 → EVAL-001
|
||||||
|
|
||||||
|
Full (Fan-out + Generator-Critic):
|
||||||
|
[IDEA-001 + IDEA-002 + IDEA-003](parallel) → CHALLENGE-001(batch) → IDEA-004(fix) → SYNTH-001 → EVAL-001
|
||||||
|
```
|
||||||
|
|
||||||
|
### Generator-Critic Loop
|
||||||
|
|
||||||
|
ideator ↔ challenger 循环,最多2轮:
|
||||||
|
|
||||||
|
```
|
||||||
|
IDEA → CHALLENGE → (if critique.severity >= HIGH) → IDEA-fix → CHALLENGE-2 → SYNTH
|
||||||
|
(if critique.severity < HIGH) → SYNTH
|
||||||
|
```
|
||||||
|
|
||||||
|
## Unified Session Directory
|
||||||
|
|
||||||
|
```
|
||||||
|
.workflow/.team/BRS-{slug}-{YYYY-MM-DD}/
|
||||||
|
├── team-session.json # Session state
|
||||||
|
├── shared-memory.json # 累积: generated_ideas / critique_insights / synthesis_themes / evaluation_scores
|
||||||
|
├── ideas/ # Ideator output
|
||||||
|
│ ├── idea-001.md
|
||||||
|
│ ├── idea-002.md
|
||||||
|
│ └── idea-003.md
|
||||||
|
├── critiques/ # Challenger output
|
||||||
|
│ ├── critique-001.md
|
||||||
|
│ └── critique-002.md
|
||||||
|
├── synthesis/ # Synthesizer output
|
||||||
|
│ └── synthesis-001.md
|
||||||
|
└── evaluation/ # Evaluator output
|
||||||
|
└── evaluation-001.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Coordinator Spawn Template
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
TeamCreate({ team_name: teamName })
|
||||||
|
|
||||||
|
// Ideator
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
team_name: teamName,
|
||||||
|
name: "ideator",
|
||||||
|
prompt: `你是 team "${teamName}" 的 IDEATOR。
|
||||||
|
当你收到 IDEA-* 任务时,调用 Skill(skill="team-brainstorm", args="--role=ideator") 执行。
|
||||||
|
当前话题: ${taskDescription}
|
||||||
|
约束: ${constraints}
|
||||||
|
|
||||||
|
## 角色准则(强制)
|
||||||
|
- 你只能处理 IDEA-* 前缀的任务,不得执行其他角色的工作
|
||||||
|
- 所有输出(SendMessage、team_msg)必须带 [ideator] 标识前缀
|
||||||
|
- 仅与 coordinator 通信,不得直接联系其他 worker
|
||||||
|
- 不得使用 TaskCreate 为其他角色创建任务
|
||||||
|
|
||||||
|
## 消息总线(必须)
|
||||||
|
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||||
|
|
||||||
|
工作流程:
|
||||||
|
1. TaskList → 找到 IDEA-* 任务
|
||||||
|
2. Skill(skill="team-brainstorm", args="--role=ideator") 执行
|
||||||
|
3. team_msg log + SendMessage 结果给 coordinator(带 [ideator] 标识)
|
||||||
|
4. TaskUpdate completed → 检查下一个任务`
|
||||||
|
})
|
||||||
|
|
||||||
|
// Challenger
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
team_name: teamName,
|
||||||
|
name: "challenger",
|
||||||
|
prompt: `你是 team "${teamName}" 的 CHALLENGER。
|
||||||
|
当你收到 CHALLENGE-* 任务时,调用 Skill(skill="team-brainstorm", args="--role=challenger") 执行。
|
||||||
|
当前话题: ${taskDescription}
|
||||||
|
|
||||||
|
## 角色准则(强制)
|
||||||
|
- 你只能处理 CHALLENGE-* 前缀的任务
|
||||||
|
- 所有输出必须带 [challenger] 标识前缀
|
||||||
|
- 仅与 coordinator 通信
|
||||||
|
|
||||||
|
## 消息总线(必须)
|
||||||
|
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||||
|
|
||||||
|
工作流程:
|
||||||
|
1. TaskList → 找到 CHALLENGE-* 任务
|
||||||
|
2. Skill(skill="team-brainstorm", args="--role=challenger") 执行
|
||||||
|
3. team_msg log + SendMessage 结果给 coordinator(带 [challenger] 标识)
|
||||||
|
4. TaskUpdate completed → 检查下一个任务`
|
||||||
|
})
|
||||||
|
|
||||||
|
// Synthesizer
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
team_name: teamName,
|
||||||
|
name: "synthesizer",
|
||||||
|
prompt: `你是 team "${teamName}" 的 SYNTHESIZER。
|
||||||
|
当你收到 SYNTH-* 任务时,调用 Skill(skill="team-brainstorm", args="--role=synthesizer") 执行。
|
||||||
|
当前话题: ${taskDescription}
|
||||||
|
|
||||||
|
## 角色准则(强制)
|
||||||
|
- 你只能处理 SYNTH-* 前缀的任务
|
||||||
|
- 所有输出必须带 [synthesizer] 标识前缀
|
||||||
|
- 仅与 coordinator 通信
|
||||||
|
|
||||||
|
## 消息总线(必须)
|
||||||
|
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||||
|
|
||||||
|
工作流程:
|
||||||
|
1. TaskList → 找到 SYNTH-* 任务
|
||||||
|
2. Skill(skill="team-brainstorm", args="--role=synthesizer") 执行
|
||||||
|
3. team_msg log + SendMessage 结果给 coordinator(带 [synthesizer] 标识)
|
||||||
|
4. TaskUpdate completed → 检查下一个任务`
|
||||||
|
})
|
||||||
|
|
||||||
|
// Evaluator
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
team_name: teamName,
|
||||||
|
name: "evaluator",
|
||||||
|
prompt: `你是 team "${teamName}" 的 EVALUATOR。
|
||||||
|
当你收到 EVAL-* 任务时,调用 Skill(skill="team-brainstorm", args="--role=evaluator") 执行。
|
||||||
|
当前话题: ${taskDescription}
|
||||||
|
|
||||||
|
## 角色准则(强制)
|
||||||
|
- 你只能处理 EVAL-* 前缀的任务
|
||||||
|
- 所有输出必须带 [evaluator] 标识前缀
|
||||||
|
- 仅与 coordinator 通信
|
||||||
|
|
||||||
|
## 消息总线(必须)
|
||||||
|
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||||
|
|
||||||
|
工作流程:
|
||||||
|
1. TaskList → 找到 EVAL-* 任务
|
||||||
|
2. Skill(skill="team-brainstorm", args="--role=evaluator") 执行
|
||||||
|
3. team_msg log + SendMessage 结果给 coordinator(带 [evaluator] 标识)
|
||||||
|
4. TaskUpdate completed → 检查下一个任务`
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| Unknown --role value | Error with available role list |
|
||||||
|
| Missing --role arg | Error with usage hint |
|
||||||
|
| Role file not found | Error with expected path (roles/{name}.md) |
|
||||||
|
| Task prefix conflict | Log warning, proceed |
|
||||||
|
| Generator-Critic loop exceeds 2 rounds | Force convergence → SYNTH |
|
||||||
|
| No ideas generated | Coordinator prompts with seed questions |
|
||||||
208
.claude/skills/team-brainstorm/roles/challenger.md
Normal file
208
.claude/skills/team-brainstorm/roles/challenger.md
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
# Role: challenger
|
||||||
|
|
||||||
|
魔鬼代言人角色。负责假设挑战、可行性质疑、风险识别。作为 Generator-Critic 循环中的 Critic 角色。
|
||||||
|
|
||||||
|
## Role Identity
|
||||||
|
|
||||||
|
- **Name**: `challenger`
|
||||||
|
- **Task Prefix**: `CHALLENGE-*`
|
||||||
|
- **Responsibility**: Read-only analysis (批判性分析)
|
||||||
|
- **Communication**: SendMessage to coordinator only
|
||||||
|
- **Output Tag**: `[challenger]`
|
||||||
|
|
||||||
|
## Role Boundaries
|
||||||
|
|
||||||
|
### MUST
|
||||||
|
|
||||||
|
- 仅处理 `CHALLENGE-*` 前缀的任务
|
||||||
|
- 所有输出必须带 `[challenger]` 标识
|
||||||
|
- 仅通过 SendMessage 与 coordinator 通信
|
||||||
|
- Phase 2 读取 shared-memory.json,Phase 5 写入 critique_insights
|
||||||
|
- 为每个创意标记挑战严重度 (LOW/MEDIUM/HIGH/CRITICAL)
|
||||||
|
|
||||||
|
### MUST NOT
|
||||||
|
|
||||||
|
- ❌ 生成创意、综合想法或评估排序
|
||||||
|
- ❌ 直接与其他 worker 角色通信
|
||||||
|
- ❌ 为其他角色创建任务
|
||||||
|
- ❌ 修改 shared-memory.json 中不属于自己的字段
|
||||||
|
|
||||||
|
## Message Types
|
||||||
|
|
||||||
|
| Type | Direction | Trigger | Description |
|
||||||
|
|------|-----------|---------|-------------|
|
||||||
|
| `critique_ready` | challenger → coordinator | Critique completed | 挑战分析完成 |
|
||||||
|
| `error` | challenger → coordinator | Processing failure | 错误上报 |
|
||||||
|
|
||||||
|
## Execution (5-Phase)
|
||||||
|
|
||||||
|
### Phase 1: Task Discovery
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const tasks = TaskList()
|
||||||
|
const myTasks = tasks.filter(t =>
|
||||||
|
t.subject.startsWith('CHALLENGE-') &&
|
||||||
|
t.owner === 'challenger' &&
|
||||||
|
t.status === 'pending' &&
|
||||||
|
t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
|
||||||
|
if (myTasks.length === 0) return
|
||||||
|
const task = TaskGet({ taskId: myTasks[0].id })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Context Loading + Shared Memory Read
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const sessionMatch = task.description.match(/Session:\s*([^\n]+)/)
|
||||||
|
const sessionFolder = sessionMatch?.[1]?.trim()
|
||||||
|
|
||||||
|
const memoryPath = `${sessionFolder}/shared-memory.json`
|
||||||
|
let sharedMemory = {}
|
||||||
|
try { sharedMemory = JSON.parse(Read(memoryPath)) } catch {}
|
||||||
|
|
||||||
|
// Read all idea files referenced in task
|
||||||
|
const ideaFiles = Glob({ pattern: `${sessionFolder}/ideas/*.md` })
|
||||||
|
const ideas = ideaFiles.map(f => Read(f))
|
||||||
|
|
||||||
|
// Read previous critiques for context (avoid repeating)
|
||||||
|
const prevCritiques = sharedMemory.critique_insights || []
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Critical Analysis
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// For each idea, apply 4 challenge dimensions:
|
||||||
|
// 1. Assumption Validity — 核心假设是否成立?有什么反例?
|
||||||
|
// 2. Feasibility — 技术/资源/时间上是否可行?
|
||||||
|
// 3. Risk Assessment — 最坏情况是什么?有什么隐藏风险?
|
||||||
|
// 4. Competitive Analysis — 已有更好的替代方案吗?
|
||||||
|
|
||||||
|
// Severity classification:
|
||||||
|
// LOW — Minor concern, does not invalidate the idea
|
||||||
|
// MEDIUM — Notable weakness, needs consideration
|
||||||
|
// HIGH — Significant flaw, requires revision
|
||||||
|
// CRITICAL — Fundamental issue, idea may need replacement
|
||||||
|
|
||||||
|
const challengeNum = task.subject.match(/CHALLENGE-(\d+)/)?.[1] || '001'
|
||||||
|
const outputPath = `${sessionFolder}/critiques/critique-${challengeNum}.md`
|
||||||
|
|
||||||
|
const critiqueContent = `# Critique — Round ${challengeNum}
|
||||||
|
|
||||||
|
**Ideas Reviewed**: ${ideas.length} files
|
||||||
|
**Challenge Dimensions**: Assumption Validity, Feasibility, Risk, Competition
|
||||||
|
|
||||||
|
## Challenges
|
||||||
|
|
||||||
|
${challenges.map((c, i) => `### Idea: ${c.ideaTitle}
|
||||||
|
|
||||||
|
**Severity**: ${c.severity}
|
||||||
|
|
||||||
|
| Dimension | Finding |
|
||||||
|
|-----------|---------|
|
||||||
|
| Assumption Validity | ${c.assumption} |
|
||||||
|
| Feasibility | ${c.feasibility} |
|
||||||
|
| Risk Assessment | ${c.risk} |
|
||||||
|
| Competitive Analysis | ${c.competition} |
|
||||||
|
|
||||||
|
**Key Challenge**: ${c.keyChallenge}
|
||||||
|
**Suggested Direction**: ${c.suggestion}
|
||||||
|
`).join('\n')}
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
| Severity | Count |
|
||||||
|
|----------|-------|
|
||||||
|
| CRITICAL | ${challenges.filter(c => c.severity === 'CRITICAL').length} |
|
||||||
|
| HIGH | ${challenges.filter(c => c.severity === 'HIGH').length} |
|
||||||
|
| MEDIUM | ${challenges.filter(c => c.severity === 'MEDIUM').length} |
|
||||||
|
| LOW | ${challenges.filter(c => c.severity === 'LOW').length} |
|
||||||
|
|
||||||
|
**Generator-Critic Signal**: ${
|
||||||
|
challenges.some(c => c.severity === 'CRITICAL' || c.severity === 'HIGH')
|
||||||
|
? 'REVISION_NEEDED — Critical/High issues require ideator revision'
|
||||||
|
: 'CONVERGED — No critical issues, ready for synthesis'
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
Write(outputPath, critiqueContent)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Severity Summary
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Aggregate severity counts for coordinator decision
|
||||||
|
const severitySummary = {
|
||||||
|
critical: challenges.filter(c => c.severity === 'CRITICAL').length,
|
||||||
|
high: challenges.filter(c => c.severity === 'HIGH').length,
|
||||||
|
medium: challenges.filter(c => c.severity === 'MEDIUM').length,
|
||||||
|
low: challenges.filter(c => c.severity === 'LOW').length,
|
||||||
|
signal: (challenges.some(c => c.severity === 'CRITICAL' || c.severity === 'HIGH'))
|
||||||
|
? 'REVISION_NEEDED' : 'CONVERGED'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Report to Coordinator + Shared Memory Write
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Update shared memory
|
||||||
|
sharedMemory.critique_insights = [
|
||||||
|
...sharedMemory.critique_insights,
|
||||||
|
...challenges.map(c => ({
|
||||||
|
idea: c.ideaTitle,
|
||||||
|
severity: c.severity,
|
||||||
|
key_challenge: c.keyChallenge,
|
||||||
|
round: parseInt(challengeNum)
|
||||||
|
}))
|
||||||
|
]
|
||||||
|
Write(memoryPath, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log",
|
||||||
|
team: teamName,
|
||||||
|
from: "challenger",
|
||||||
|
to: "coordinator",
|
||||||
|
type: "critique_ready",
|
||||||
|
summary: `[challenger] Critique complete: ${severitySummary.critical}C/${severitySummary.high}H/${severitySummary.medium}M/${severitySummary.low}L — Signal: ${severitySummary.signal}`,
|
||||||
|
ref: outputPath
|
||||||
|
})
|
||||||
|
|
||||||
|
SendMessage({
|
||||||
|
type: "message",
|
||||||
|
recipient: "coordinator",
|
||||||
|
content: `## [challenger] Critique Results
|
||||||
|
|
||||||
|
**Task**: ${task.subject}
|
||||||
|
**Signal**: ${severitySummary.signal}
|
||||||
|
**Severity**: ${severitySummary.critical} Critical, ${severitySummary.high} High, ${severitySummary.medium} Medium, ${severitySummary.low} Low
|
||||||
|
**Output**: ${outputPath}
|
||||||
|
|
||||||
|
${severitySummary.signal === 'REVISION_NEEDED'
|
||||||
|
? '### Requires Revision\n' + challenges.filter(c => ['CRITICAL', 'HIGH'].includes(c.severity)).map(c => `- **${c.ideaTitle}** (${c.severity}): ${c.keyChallenge}`).join('\n')
|
||||||
|
: '### All Clear — Ready for Synthesis'}`,
|
||||||
|
summary: `[challenger] Critique: ${severitySummary.signal}`
|
||||||
|
})
|
||||||
|
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||||
|
|
||||||
|
// Check for next task
|
||||||
|
const nextTasks = TaskList().filter(t =>
|
||||||
|
t.subject.startsWith('CHALLENGE-') &&
|
||||||
|
t.owner === 'challenger' &&
|
||||||
|
t.status === 'pending' &&
|
||||||
|
t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
if (nextTasks.length > 0) {
|
||||||
|
// back to Phase 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| No CHALLENGE-* tasks | Idle, wait for assignment |
|
||||||
|
| Ideas file not found | Notify coordinator |
|
||||||
|
| All ideas trivially good | Mark all LOW, signal CONVERGED |
|
||||||
|
| Cannot assess feasibility | Mark MEDIUM with note, suggest deeper analysis |
|
||||||
304
.claude/skills/team-brainstorm/roles/coordinator.md
Normal file
304
.claude/skills/team-brainstorm/roles/coordinator.md
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
# Role: coordinator
|
||||||
|
|
||||||
|
头脑风暴团队协调者。负责话题澄清、复杂度评估、管道选择、Generator-Critic 循环控制和收敛监控。
|
||||||
|
|
||||||
|
## Role Identity
|
||||||
|
|
||||||
|
- **Name**: `coordinator`
|
||||||
|
- **Task Prefix**: N/A (coordinator creates tasks, doesn't receive them)
|
||||||
|
- **Responsibility**: Orchestration
|
||||||
|
- **Communication**: SendMessage to all teammates
|
||||||
|
- **Output Tag**: `[coordinator]`
|
||||||
|
|
||||||
|
## Role Boundaries
|
||||||
|
|
||||||
|
### MUST
|
||||||
|
|
||||||
|
- 所有输出(SendMessage、team_msg、日志)必须带 `[coordinator]` 标识
|
||||||
|
- 仅负责话题澄清、任务创建/分发、进度监控、结果汇报
|
||||||
|
- 通过 TaskCreate 创建任务并分配给 worker 角色
|
||||||
|
- 通过消息总线监控 worker 进度并路由消息
|
||||||
|
- 管理 Generator-Critic 循环计数,决定是否继续迭代
|
||||||
|
|
||||||
|
### MUST NOT
|
||||||
|
|
||||||
|
- ❌ **直接生成创意、挑战假设、综合想法或评估排序**
|
||||||
|
- ❌ 直接调用实现类 subagent
|
||||||
|
- ❌ 直接修改产物文件(ideas/*.md, critiques/*.md 等)
|
||||||
|
- ❌ 绕过 worker 角色自行完成应委派的工作
|
||||||
|
- ❌ 在输出中省略 `[coordinator]` 标识
|
||||||
|
|
||||||
|
> **核心原则**: coordinator 是指挥者,不是执行者。所有实际工作必须通过 TaskCreate 委派给 worker 角色。
|
||||||
|
|
||||||
|
## Message Types
|
||||||
|
|
||||||
|
| Type | Direction | Trigger | Description |
|
||||||
|
|------|-----------|---------|-------------|
|
||||||
|
| `pipeline_selected` | coordinator → all | Pipeline decided | Notify selected pipeline mode |
|
||||||
|
| `gc_loop_trigger` | coordinator → ideator | Critique severity >= HIGH | Trigger ideator to revise |
|
||||||
|
| `task_unblocked` | coordinator → any | Dependency resolved | Notify worker of available task |
|
||||||
|
| `error` | coordinator → all | Critical system error | Escalation to user |
|
||||||
|
| `shutdown` | coordinator → all | Team being dissolved | Clean shutdown signal |
|
||||||
|
|
||||||
|
## Execution
|
||||||
|
|
||||||
|
### Phase 1: Topic Clarification + Complexity Assessment
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const args = "$ARGUMENTS"
|
||||||
|
const teamNameMatch = args.match(/--team-name[=\s]+([\w-]+)/)
|
||||||
|
const teamName = teamNameMatch ? teamNameMatch[1] : `brainstorm-${Date.now().toString(36)}`
|
||||||
|
const taskDescription = args.replace(/--team-name[=\s]+[\w-]+/, '').replace(/--role[=\s]+\w+/, '').trim()
|
||||||
|
```
|
||||||
|
|
||||||
|
Assess topic complexity and select pipeline:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function assessComplexity(topic) {
|
||||||
|
let score = 0
|
||||||
|
if (/strategy|architecture|system|framework|paradigm/.test(topic)) score += 3
|
||||||
|
if (/multiple|compare|tradeoff|versus|alternative/.test(topic)) score += 2
|
||||||
|
if (/innovative|creative|novel|breakthrough/.test(topic)) score += 2
|
||||||
|
if (/simple|quick|straightforward|basic/.test(topic)) score -= 2
|
||||||
|
return score >= 4 ? 'high' : score >= 2 ? 'medium' : 'low'
|
||||||
|
}
|
||||||
|
|
||||||
|
const complexity = assessComplexity(taskDescription)
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
AskUserQuestion({
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
question: "选择头脑风暴模式:",
|
||||||
|
header: "Mode",
|
||||||
|
multiSelect: false,
|
||||||
|
options: [
|
||||||
|
{ label: complexity === 'low' ? "quick (推荐)" : "quick", description: "快速模式:创意→挑战→综合(3步)" },
|
||||||
|
{ label: complexity === 'medium' ? "deep (推荐)" : "deep", description: "深度模式:含 Generator-Critic 循环(6步)" },
|
||||||
|
{ label: complexity === 'high' ? "full (推荐)" : "full", description: "完整模式:并行发散 + 循环 + 评估(7步)" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "创意发散角度:",
|
||||||
|
header: "Angles",
|
||||||
|
multiSelect: true,
|
||||||
|
options: [
|
||||||
|
{ label: "技术视角", description: "技术可行性、实现方案、架构设计" },
|
||||||
|
{ label: "产品视角", description: "用户需求、市场定位、商业模式" },
|
||||||
|
{ label: "创新视角", description: "颠覆性思路、跨领域借鉴、未来趋势" },
|
||||||
|
{ label: "风险视角", description: "潜在问题、约束条件、替代方案" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Create Team + Spawn Workers
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
TeamCreate({ team_name: teamName })
|
||||||
|
|
||||||
|
const topicSlug = taskDescription.toLowerCase().replace(/[^a-z0-9]+/g, '-').substring(0, 40)
|
||||||
|
const dateStr = new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString().substring(0, 10)
|
||||||
|
const sessionId = `BRS-${topicSlug}-${dateStr}`
|
||||||
|
const sessionFolder = `.workflow/.team/${sessionId}`
|
||||||
|
|
||||||
|
Bash(`mkdir -p "${sessionFolder}/ideas" "${sessionFolder}/critiques" "${sessionFolder}/synthesis" "${sessionFolder}/evaluation"`)
|
||||||
|
|
||||||
|
// Initialize shared memory
|
||||||
|
const sharedMemory = {
|
||||||
|
topic: taskDescription,
|
||||||
|
pipeline: selectedPipeline,
|
||||||
|
angles: selectedAngles,
|
||||||
|
gc_round: 0,
|
||||||
|
max_gc_rounds: 2,
|
||||||
|
generated_ideas: [],
|
||||||
|
critique_insights: [],
|
||||||
|
synthesis_themes: [],
|
||||||
|
evaluation_scores: []
|
||||||
|
}
|
||||||
|
Write(`${sessionFolder}/shared-memory.json`, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
// Create team-session.json
|
||||||
|
const teamSession = {
|
||||||
|
session_id: sessionId,
|
||||||
|
team_name: teamName,
|
||||||
|
topic: taskDescription,
|
||||||
|
pipeline: selectedPipeline,
|
||||||
|
status: "active",
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
|
gc_round: 0,
|
||||||
|
completed_tasks: []
|
||||||
|
}
|
||||||
|
Write(`${sessionFolder}/team-session.json`, JSON.stringify(teamSession, null, 2))
|
||||||
|
```
|
||||||
|
|
||||||
|
Spawn workers (see SKILL.md Coordinator Spawn Template).
|
||||||
|
|
||||||
|
### Phase 3: Create Task Chain
|
||||||
|
|
||||||
|
Task chain depends on the selected pipeline.
|
||||||
|
|
||||||
|
#### Quick Pipeline
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// IDEA-001: 创意生成
|
||||||
|
TaskCreate({ subject: "IDEA-001: 多角度创意生成", description: `话题: ${taskDescription}\n\nSession: ${sessionFolder}\n角度: ${selectedAngles.join(', ')}\n输出: ${sessionFolder}/ideas/idea-001.md\n\n要求: 每个角度至少产出3个创意,总计不少于6个`, activeForm: "生成创意中" })
|
||||||
|
TaskUpdate({ taskId: ideaId, owner: "ideator" })
|
||||||
|
|
||||||
|
// CHALLENGE-001: 挑战质疑 (blockedBy IDEA-001)
|
||||||
|
TaskCreate({ subject: "CHALLENGE-001: 假设挑战与可行性质疑", description: `对 IDEA-001 的创意进行批判性分析\n\nSession: ${sessionFolder}\n输入: ${sessionFolder}/ideas/idea-001.md\n输出: ${sessionFolder}/critiques/critique-001.md\n\n要求: 标记每个创意的挑战严重度 (LOW/MEDIUM/HIGH/CRITICAL)`, activeForm: "挑战创意中" })
|
||||||
|
TaskUpdate({ taskId: challengeId, owner: "challenger", addBlockedBy: [ideaId] })
|
||||||
|
|
||||||
|
// SYNTH-001: 综合整合 (blockedBy CHALLENGE-001)
|
||||||
|
TaskCreate({ subject: "SYNTH-001: 跨想法整合与主题提取", description: `整合所有创意和挑战反馈\n\nSession: ${sessionFolder}\n输入: ideas/ + critiques/\n输出: ${sessionFolder}/synthesis/synthesis-001.md\n\n要求: 提取核心主题、解决冲突、生成整合方案`, activeForm: "综合整合中" })
|
||||||
|
TaskUpdate({ taskId: synthId, owner: "synthesizer", addBlockedBy: [challengeId] })
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Deep Pipeline (with Generator-Critic Loop)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// IDEA-001 → CHALLENGE-001 → IDEA-002(fix) → CHALLENGE-002 → SYNTH-001 → EVAL-001
|
||||||
|
|
||||||
|
TaskCreate({ subject: "IDEA-001: 初始创意生成", description: `话题: ${taskDescription}\n\nSession: ${sessionFolder}\n输出: ${sessionFolder}/ideas/idea-001.md`, activeForm: "生成创意中" })
|
||||||
|
TaskUpdate({ taskId: idea1Id, owner: "ideator" })
|
||||||
|
|
||||||
|
TaskCreate({ subject: "CHALLENGE-001: 第一轮挑战", description: `挑战 IDEA-001 的创意\n\nSession: ${sessionFolder}\n输入: ideas/idea-001.md\n输出: critiques/critique-001.md\n标记严重度`, activeForm: "挑战中" })
|
||||||
|
TaskUpdate({ taskId: challenge1Id, owner: "challenger", addBlockedBy: [idea1Id] })
|
||||||
|
|
||||||
|
TaskCreate({ subject: "IDEA-002: 创意修订(Generator-Critic Round 1)", description: `基于 CHALLENGE-001 的反馈修订创意\n\nSession: ${sessionFolder}\n输入: ideas/idea-001.md + critiques/critique-001.md\n输出: ideas/idea-002.md\n\n要求: 针对 HIGH/CRITICAL 严重度的挑战进行修订或替换`, activeForm: "修订创意中" })
|
||||||
|
TaskUpdate({ taskId: idea2Id, owner: "ideator", addBlockedBy: [challenge1Id] })
|
||||||
|
|
||||||
|
TaskCreate({ subject: "CHALLENGE-002: 第二轮验证", description: `验证修订后的创意\n\nSession: ${sessionFolder}\n输入: ideas/idea-002.md + critiques/critique-001.md\n输出: critiques/critique-002.md`, activeForm: "验证中" })
|
||||||
|
TaskUpdate({ taskId: challenge2Id, owner: "challenger", addBlockedBy: [idea2Id] })
|
||||||
|
|
||||||
|
TaskCreate({ subject: "SYNTH-001: 综合整合", description: `整合全部创意和挑战反馈\n\nSession: ${sessionFolder}\n输入: ideas/ + critiques/\n输出: synthesis/synthesis-001.md`, activeForm: "综合中" })
|
||||||
|
TaskUpdate({ taskId: synthId, owner: "synthesizer", addBlockedBy: [challenge2Id] })
|
||||||
|
|
||||||
|
TaskCreate({ subject: "EVAL-001: 评分排序与最终筛选", description: `对综合方案评分排序\n\nSession: ${sessionFolder}\n输入: synthesis/synthesis-001.md + shared-memory.json\n输出: evaluation/evaluation-001.md\n\n评分维度: 可行性(30%) + 创新性(25%) + 影响力(25%) + 实施成本(20%)`, activeForm: "评估中" })
|
||||||
|
TaskUpdate({ taskId: evalId, owner: "evaluator", addBlockedBy: [synthId] })
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Full Pipeline (Fan-out + Generator-Critic)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 并行创意: IDEA-001 + IDEA-002 + IDEA-003 (no dependencies between them)
|
||||||
|
const ideaAngles = selectedAngles.slice(0, 3)
|
||||||
|
ideaAngles.forEach((angle, i) => {
|
||||||
|
TaskCreate({ subject: `IDEA-00${i+1}: ${angle}角度创意生成`, description: `话题: ${taskDescription}\n角度: ${angle}\n\nSession: ${sessionFolder}\n输出: ideas/idea-00${i+1}.md`, activeForm: `${angle}创意生成中` })
|
||||||
|
TaskUpdate({ taskId: ideaIds[i], owner: "ideator" })
|
||||||
|
})
|
||||||
|
|
||||||
|
// CHALLENGE-001: 批量挑战 (blockedBy all IDEA-001..003)
|
||||||
|
TaskCreate({ subject: "CHALLENGE-001: 批量创意挑战", description: `批量挑战所有角度的创意\n\nSession: ${sessionFolder}\n输入: ideas/idea-001..003.md\n输出: critiques/critique-001.md`, activeForm: "批量挑战中" })
|
||||||
|
TaskUpdate({ taskId: challenge1Id, owner: "challenger", addBlockedBy: ideaIds })
|
||||||
|
|
||||||
|
// IDEA-004: 修订 (blockedBy CHALLENGE-001)
|
||||||
|
TaskCreate({ subject: "IDEA-004: 创意修订", description: `基于批量挑战反馈修订\n\nSession: ${sessionFolder}\n输入: ideas/ + critiques/critique-001.md\n输出: ideas/idea-004.md`, activeForm: "修订中" })
|
||||||
|
TaskUpdate({ taskId: idea4Id, owner: "ideator", addBlockedBy: [challenge1Id] })
|
||||||
|
|
||||||
|
// SYNTH-001 (blockedBy IDEA-004)
|
||||||
|
TaskCreate({ subject: "SYNTH-001: 综合整合", description: `整合全部创意\n\nSession: ${sessionFolder}\n输入: ideas/ + critiques/\n输出: synthesis/synthesis-001.md`, activeForm: "综合中" })
|
||||||
|
TaskUpdate({ taskId: synthId, owner: "synthesizer", addBlockedBy: [idea4Id] })
|
||||||
|
|
||||||
|
// EVAL-001 (blockedBy SYNTH-001)
|
||||||
|
TaskCreate({ subject: "EVAL-001: 评分排序", description: `最终评估\n\nSession: ${sessionFolder}\n输入: synthesis/ + shared-memory.json\n输出: evaluation/evaluation-001.md`, activeForm: "评估中" })
|
||||||
|
TaskUpdate({ taskId: evalId, owner: "evaluator", addBlockedBy: [synthId] })
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Coordination Loop + Generator-Critic Control
|
||||||
|
|
||||||
|
| Received Message | Action |
|
||||||
|
|-----------------|--------|
|
||||||
|
| ideator: ideas_ready | Read ideas → team_msg log → TaskUpdate completed → unblock CHALLENGE |
|
||||||
|
| challenger: critique_ready | Read critique → **Generator-Critic 判断** → 决定是否触发 IDEA-fix |
|
||||||
|
| ideator: ideas_revised | Read revised ideas → team_msg log → TaskUpdate completed → unblock CHALLENGE-2 |
|
||||||
|
| synthesizer: synthesis_ready | Read synthesis → team_msg log → TaskUpdate completed → unblock EVAL (if exists) |
|
||||||
|
| evaluator: evaluation_ready | Read evaluation → team_msg log → TaskUpdate completed → Phase 5 |
|
||||||
|
| All tasks completed | → Phase 5 |
|
||||||
|
|
||||||
|
#### Generator-Critic Loop Control
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
if (msgType === 'critique_ready') {
|
||||||
|
const critique = Read(`${sessionFolder}/critiques/critique-${round}.md`)
|
||||||
|
const sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`))
|
||||||
|
|
||||||
|
// Count HIGH/CRITICAL severity challenges
|
||||||
|
const criticalCount = (critique.match(/severity:\s*(HIGH|CRITICAL)/gi) || []).length
|
||||||
|
const gcRound = sharedMemory.gc_round || 0
|
||||||
|
|
||||||
|
if (criticalCount > 0 && gcRound < sharedMemory.max_gc_rounds) {
|
||||||
|
// Trigger another ideator round
|
||||||
|
sharedMemory.gc_round = gcRound + 1
|
||||||
|
Write(`${sessionFolder}/shared-memory.json`, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log", team: teamName, from: "coordinator", to: "ideator",
|
||||||
|
type: "gc_loop_trigger",
|
||||||
|
summary: `[coordinator] Generator-Critic round ${gcRound + 1}: ${criticalCount} critical challenges need revision`
|
||||||
|
})
|
||||||
|
// Unblock IDEA-fix task
|
||||||
|
} else {
|
||||||
|
// Converged → unblock SYNTH
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log", team: teamName, from: "coordinator", to: "synthesizer",
|
||||||
|
type: "task_unblocked",
|
||||||
|
summary: `[coordinator] Critique converged (round ${gcRound}), proceeding to synthesis`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Report + Persist
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Read final results
|
||||||
|
const synthesis = Read(`${sessionFolder}/synthesis/synthesis-001.md`)
|
||||||
|
const evaluation = selectedPipeline !== 'quick' ? Read(`${sessionFolder}/evaluation/evaluation-001.md`) : null
|
||||||
|
const sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`))
|
||||||
|
|
||||||
|
// Report to user
|
||||||
|
SendMessage({
|
||||||
|
content: `## [coordinator] 头脑风暴完成
|
||||||
|
|
||||||
|
**话题**: ${taskDescription}
|
||||||
|
**管道**: ${selectedPipeline}
|
||||||
|
**Generator-Critic 轮次**: ${sharedMemory.gc_round}
|
||||||
|
**创意总数**: ${sharedMemory.generated_ideas.length}
|
||||||
|
|
||||||
|
### 综合结果
|
||||||
|
${synthesis}
|
||||||
|
|
||||||
|
${evaluation ? `### 评估排序\n${evaluation}` : ''}`,
|
||||||
|
summary: `[coordinator] Brainstorm complete: ${sharedMemory.generated_ideas.length} ideas, ${sharedMemory.gc_round} GC rounds`
|
||||||
|
})
|
||||||
|
|
||||||
|
// Update session
|
||||||
|
updateSession(sessionFolder, { status: 'completed', completed_at: new Date().toISOString() })
|
||||||
|
|
||||||
|
AskUserQuestion({
|
||||||
|
questions: [{
|
||||||
|
question: "头脑风暴已完成。下一步:",
|
||||||
|
header: "Next",
|
||||||
|
multiSelect: false,
|
||||||
|
options: [
|
||||||
|
{ label: "新话题", description: "继续头脑风暴新话题" },
|
||||||
|
{ label: "深化探索", description: "对排名最高的创意进行深入分析" },
|
||||||
|
{ label: "关闭团队", description: "关闭所有 teammate 并清理" }
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| Teammate 无响应 | 发追踪消息,2次无响应 → 重新 spawn |
|
||||||
|
| Generator-Critic 循环超限 | 强制收敛到 SYNTH 阶段 |
|
||||||
|
| Ideator 无法产出 | Coordinator 提供种子问题引导 |
|
||||||
|
| Challenger 全部标记 LOW | 直接进入 SYNTH,跳过修订 |
|
||||||
|
| 综合冲突无法解决 | 上报用户,AskUserQuestion 决定方向 |
|
||||||
195
.claude/skills/team-brainstorm/roles/evaluator.md
Normal file
195
.claude/skills/team-brainstorm/roles/evaluator.md
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
# Role: evaluator
|
||||||
|
|
||||||
|
评分排序与最终筛选。负责对综合方案进行多维度评分、优先级推荐、生成最终排名。
|
||||||
|
|
||||||
|
## Role Identity
|
||||||
|
|
||||||
|
- **Name**: `evaluator`
|
||||||
|
- **Task Prefix**: `EVAL-*`
|
||||||
|
- **Responsibility**: Validation (评估验证)
|
||||||
|
- **Communication**: SendMessage to coordinator only
|
||||||
|
- **Output Tag**: `[evaluator]`
|
||||||
|
|
||||||
|
## Role Boundaries
|
||||||
|
|
||||||
|
### MUST
|
||||||
|
|
||||||
|
- 仅处理 `EVAL-*` 前缀的任务
|
||||||
|
- 所有输出必须带 `[evaluator]` 标识
|
||||||
|
- 仅通过 SendMessage 与 coordinator 通信
|
||||||
|
- Phase 2 读取 shared-memory.json,Phase 5 写入 evaluation_scores
|
||||||
|
- 使用标准化评分维度,确保评分可追溯
|
||||||
|
|
||||||
|
### MUST NOT
|
||||||
|
|
||||||
|
- ❌ 生成新创意、挑战假设或综合整合
|
||||||
|
- ❌ 直接与其他 worker 角色通信
|
||||||
|
- ❌ 为其他角色创建任务
|
||||||
|
- ❌ 修改 shared-memory.json 中不属于自己的字段
|
||||||
|
|
||||||
|
## Message Types
|
||||||
|
|
||||||
|
| Type | Direction | Trigger | Description |
|
||||||
|
|------|-----------|---------|-------------|
|
||||||
|
| `evaluation_ready` | evaluator → coordinator | Evaluation completed | 评估排序完成 |
|
||||||
|
| `error` | evaluator → coordinator | Processing failure | 错误上报 |
|
||||||
|
|
||||||
|
## Execution (5-Phase)
|
||||||
|
|
||||||
|
### Phase 1: Task Discovery
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const tasks = TaskList()
|
||||||
|
const myTasks = tasks.filter(t =>
|
||||||
|
t.subject.startsWith('EVAL-') &&
|
||||||
|
t.owner === 'evaluator' &&
|
||||||
|
t.status === 'pending' &&
|
||||||
|
t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
|
||||||
|
if (myTasks.length === 0) return
|
||||||
|
const task = TaskGet({ taskId: myTasks[0].id })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Context Loading + Shared Memory Read
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const sessionMatch = task.description.match(/Session:\s*([^\n]+)/)
|
||||||
|
const sessionFolder = sessionMatch?.[1]?.trim()
|
||||||
|
|
||||||
|
const memoryPath = `${sessionFolder}/shared-memory.json`
|
||||||
|
let sharedMemory = {}
|
||||||
|
try { sharedMemory = JSON.parse(Read(memoryPath)) } catch {}
|
||||||
|
|
||||||
|
// Read synthesis results
|
||||||
|
const synthesisFiles = Glob({ pattern: `${sessionFolder}/synthesis/*.md` })
|
||||||
|
const synthesis = synthesisFiles.map(f => Read(f))
|
||||||
|
|
||||||
|
// Read all ideas and critiques for full context
|
||||||
|
const ideaFiles = Glob({ pattern: `${sessionFolder}/ideas/*.md` })
|
||||||
|
const critiqueFiles = Glob({ pattern: `${sessionFolder}/critiques/*.md` })
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Evaluation & Scoring
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Scoring dimensions:
|
||||||
|
// 1. Feasibility (30%) — 技术可行性、资源需求、时间框架
|
||||||
|
// 2. Innovation (25%) — 新颖性、差异化、突破性
|
||||||
|
// 3. Impact (25%) — 影响范围、价值创造、问题解决度
|
||||||
|
// 4. Cost (20%) — 实施成本、风险成本、机会成本
|
||||||
|
|
||||||
|
const evalNum = task.subject.match(/EVAL-(\d+)/)?.[1] || '001'
|
||||||
|
const outputPath = `${sessionFolder}/evaluation/evaluation-${evalNum}.md`
|
||||||
|
|
||||||
|
const evaluationContent = `# Evaluation — Round ${evalNum}
|
||||||
|
|
||||||
|
**Input**: ${synthesisFiles.length} synthesis files
|
||||||
|
**Scoring Dimensions**: Feasibility(30%), Innovation(25%), Impact(25%), Cost(20%)
|
||||||
|
|
||||||
|
## Scoring Matrix
|
||||||
|
|
||||||
|
| Rank | Proposal | Feasibility | Innovation | Impact | Cost | **Weighted Score** |
|
||||||
|
|------|----------|-------------|------------|--------|------|-------------------|
|
||||||
|
${scoredProposals.map((p, i) => `| ${i + 1} | ${p.title} | ${p.feasibility}/10 | ${p.innovation}/10 | ${p.impact}/10 | ${p.cost}/10 | **${p.weightedScore.toFixed(1)}** |`).join('\n')}
|
||||||
|
|
||||||
|
## Detailed Evaluation
|
||||||
|
|
||||||
|
${scoredProposals.map((p, i) => `### ${i + 1}. ${p.title} (Score: ${p.weightedScore.toFixed(1)}/10)
|
||||||
|
|
||||||
|
**Feasibility** (${p.feasibility}/10):
|
||||||
|
${p.feasibilityRationale}
|
||||||
|
|
||||||
|
**Innovation** (${p.innovation}/10):
|
||||||
|
${p.innovationRationale}
|
||||||
|
|
||||||
|
**Impact** (${p.impact}/10):
|
||||||
|
${p.impactRationale}
|
||||||
|
|
||||||
|
**Cost Efficiency** (${p.cost}/10):
|
||||||
|
${p.costRationale}
|
||||||
|
|
||||||
|
**Recommendation**: ${p.recommendation}
|
||||||
|
`).join('\n')}
|
||||||
|
|
||||||
|
## Final Recommendation
|
||||||
|
|
||||||
|
**Top Pick**: ${scoredProposals[0].title}
|
||||||
|
**Runner-up**: ${scoredProposals.length > 1 ? scoredProposals[1].title : 'N/A'}
|
||||||
|
|
||||||
|
### Action Items
|
||||||
|
${actionItems.map((item, i) => `${i + 1}. ${item}`).join('\n')}
|
||||||
|
|
||||||
|
### Risk Summary
|
||||||
|
${riskSummary.map(r => `- **${r.risk}**: ${r.mitigation}`).join('\n')}
|
||||||
|
`
|
||||||
|
|
||||||
|
Write(outputPath, evaluationContent)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Consistency Check
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Verify scoring consistency
|
||||||
|
// - No proposal should have all 10s
|
||||||
|
// - Scores should reflect critique findings
|
||||||
|
// - Rankings should be deterministic
|
||||||
|
const maxScore = Math.max(...scoredProposals.map(p => p.weightedScore))
|
||||||
|
const minScore = Math.min(...scoredProposals.map(p => p.weightedScore))
|
||||||
|
const spread = maxScore - minScore
|
||||||
|
|
||||||
|
if (spread < 0.5 && scoredProposals.length > 1) {
|
||||||
|
// Too close — re-evaluate differentiators
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Report to Coordinator + Shared Memory Write
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
sharedMemory.evaluation_scores = scoredProposals.map(p => ({
|
||||||
|
title: p.title,
|
||||||
|
weighted_score: p.weightedScore,
|
||||||
|
rank: p.rank,
|
||||||
|
recommendation: p.recommendation
|
||||||
|
}))
|
||||||
|
Write(memoryPath, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log",
|
||||||
|
team: teamName,
|
||||||
|
from: "evaluator",
|
||||||
|
to: "coordinator",
|
||||||
|
type: "evaluation_ready",
|
||||||
|
summary: `[evaluator] Evaluation complete: Top pick "${scoredProposals[0].title}" (${scoredProposals[0].weightedScore.toFixed(1)}/10)`,
|
||||||
|
ref: outputPath
|
||||||
|
})
|
||||||
|
|
||||||
|
SendMessage({
|
||||||
|
type: "message",
|
||||||
|
recipient: "coordinator",
|
||||||
|
content: `## [evaluator] Evaluation Results
|
||||||
|
|
||||||
|
**Task**: ${task.subject}
|
||||||
|
**Proposals Evaluated**: ${scoredProposals.length}
|
||||||
|
**Output**: ${outputPath}
|
||||||
|
|
||||||
|
### Rankings
|
||||||
|
${scoredProposals.map((p, i) => `${i + 1}. **${p.title}** — ${p.weightedScore.toFixed(1)}/10 (${p.recommendation})`).join('\n')}
|
||||||
|
|
||||||
|
### Top Pick: ${scoredProposals[0].title}
|
||||||
|
${scoredProposals[0].feasibilityRationale}`,
|
||||||
|
summary: `[evaluator] Top: ${scoredProposals[0].title} (${scoredProposals[0].weightedScore.toFixed(1)}/10)`
|
||||||
|
})
|
||||||
|
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| No EVAL-* tasks | Idle, wait for assignment |
|
||||||
|
| Synthesis files not found | Notify coordinator |
|
||||||
|
| Only one proposal | Evaluate against absolute criteria, recommend or reject |
|
||||||
|
| All proposals score below 5 | Flag all as weak, recommend re-brainstorming |
|
||||||
225
.claude/skills/team-brainstorm/roles/ideator.md
Normal file
225
.claude/skills/team-brainstorm/roles/ideator.md
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
# Role: ideator
|
||||||
|
|
||||||
|
多角度创意生成者。负责发散思维、概念探索、创意修订。作为 Generator-Critic 循环中的 Generator 角色。
|
||||||
|
|
||||||
|
## Role Identity
|
||||||
|
|
||||||
|
- **Name**: `ideator`
|
||||||
|
- **Task Prefix**: `IDEA-*`
|
||||||
|
- **Responsibility**: Read-only analysis (创意生成不修改代码)
|
||||||
|
- **Communication**: SendMessage to coordinator only
|
||||||
|
- **Output Tag**: `[ideator]`
|
||||||
|
|
||||||
|
## Role Boundaries
|
||||||
|
|
||||||
|
### MUST
|
||||||
|
|
||||||
|
- 仅处理 `IDEA-*` 前缀的任务
|
||||||
|
- 所有输出(SendMessage、team_msg、日志)必须带 `[ideator]` 标识
|
||||||
|
- 仅通过 SendMessage 与 coordinator 通信
|
||||||
|
- Phase 2 读取 shared-memory.json,Phase 5 写入 generated_ideas
|
||||||
|
|
||||||
|
### MUST NOT
|
||||||
|
|
||||||
|
- ❌ 执行挑战/评估/综合等其他角色工作
|
||||||
|
- ❌ 直接与其他 worker 角色通信
|
||||||
|
- ❌ 为其他角色创建任务(TaskCreate 是 coordinator 专属)
|
||||||
|
- ❌ 修改 shared-memory.json 中不属于自己的字段
|
||||||
|
|
||||||
|
## Message Types
|
||||||
|
|
||||||
|
| Type | Direction | Trigger | Description |
|
||||||
|
|------|-----------|---------|-------------|
|
||||||
|
| `ideas_ready` | ideator → coordinator | Initial ideas generated | 初始创意完成 |
|
||||||
|
| `ideas_revised` | ideator → coordinator | Ideas revised after critique | 修订创意完成 (GC 循环) |
|
||||||
|
| `error` | ideator → coordinator | Processing failure | 错误上报 |
|
||||||
|
|
||||||
|
## Execution (5-Phase)
|
||||||
|
|
||||||
|
### Phase 1: Task Discovery
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const tasks = TaskList()
|
||||||
|
const myTasks = tasks.filter(t =>
|
||||||
|
t.subject.startsWith('IDEA-') &&
|
||||||
|
t.owner === 'ideator' &&
|
||||||
|
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 Loading + Shared Memory Read
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Extract session folder from task description
|
||||||
|
const sessionMatch = task.description.match(/Session:\s*([^\n]+)/)
|
||||||
|
const sessionFolder = sessionMatch?.[1]?.trim()
|
||||||
|
|
||||||
|
// Read shared memory
|
||||||
|
const memoryPath = `${sessionFolder}/shared-memory.json`
|
||||||
|
let sharedMemory = {}
|
||||||
|
try { sharedMemory = JSON.parse(Read(memoryPath)) } catch {}
|
||||||
|
|
||||||
|
const topic = sharedMemory.topic || task.description
|
||||||
|
const angles = sharedMemory.angles || ['技术', '产品', '创新']
|
||||||
|
const gcRound = sharedMemory.gc_round || 0
|
||||||
|
|
||||||
|
// If this is a revision task (GC loop), read previous critique
|
||||||
|
let previousCritique = null
|
||||||
|
if (task.subject.includes('修订') || task.subject.includes('fix')) {
|
||||||
|
const critiqueFiles = Glob({ pattern: `${sessionFolder}/critiques/*.md` })
|
||||||
|
if (critiqueFiles.length > 0) {
|
||||||
|
previousCritique = Read(critiqueFiles[critiqueFiles.length - 1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read previous ideas for context
|
||||||
|
const previousIdeas = sharedMemory.generated_ideas || []
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Idea Generation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Determine generation mode
|
||||||
|
const isRevision = !!previousCritique
|
||||||
|
|
||||||
|
if (isRevision) {
|
||||||
|
// === Generator-Critic Revision Mode ===
|
||||||
|
// Focus on HIGH/CRITICAL severity challenges
|
||||||
|
// Revise or replace challenged ideas
|
||||||
|
// Keep unchallenged ideas intact
|
||||||
|
|
||||||
|
// Output structure:
|
||||||
|
// - Retained ideas (unchallenged)
|
||||||
|
// - Revised ideas (with revision rationale)
|
||||||
|
// - New replacement ideas (for unsalvageable ones)
|
||||||
|
} else {
|
||||||
|
// === Initial Generation Mode ===
|
||||||
|
// For each angle, generate 3+ ideas
|
||||||
|
// Each idea includes:
|
||||||
|
// - Title
|
||||||
|
// - Description (2-3 sentences)
|
||||||
|
// - Key assumption
|
||||||
|
// - Potential impact
|
||||||
|
// - Implementation hint
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write ideas to file
|
||||||
|
const ideaNum = task.subject.match(/IDEA-(\d+)/)?.[1] || '001'
|
||||||
|
const outputPath = `${sessionFolder}/ideas/idea-${ideaNum}.md`
|
||||||
|
|
||||||
|
const ideaContent = `# ${isRevision ? 'Revised' : 'Initial'} Ideas — Round ${ideaNum}
|
||||||
|
|
||||||
|
**Topic**: ${topic}
|
||||||
|
**Angles**: ${angles.join(', ')}
|
||||||
|
**Mode**: ${isRevision ? 'Generator-Critic Revision (Round ' + gcRound + ')' : 'Initial Generation'}
|
||||||
|
|
||||||
|
${isRevision ? `## Revision Context\n\nBased on critique feedback:\n${previousCritique}\n\n` : ''}
|
||||||
|
|
||||||
|
## Ideas
|
||||||
|
|
||||||
|
${generatedIdeas.map((idea, i) => `### Idea ${i + 1}: ${idea.title}
|
||||||
|
|
||||||
|
**Description**: ${idea.description}
|
||||||
|
**Key Assumption**: ${idea.assumption}
|
||||||
|
**Potential Impact**: ${idea.impact}
|
||||||
|
**Implementation Hint**: ${idea.implementation}
|
||||||
|
${isRevision ? `**Revision Note**: ${idea.revision_note || 'New idea'}` : ''}
|
||||||
|
`).join('\n')}
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
- Total ideas: ${generatedIdeas.length}
|
||||||
|
- ${isRevision ? `Retained: ${retainedCount}, Revised: ${revisedCount}, New: ${newCount}` : `Per angle: ${angles.map(a => `${a}: ${countByAngle[a]}`).join(', ')}`}
|
||||||
|
`
|
||||||
|
|
||||||
|
Write(outputPath, ideaContent)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Self-Review
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Verify minimum idea count
|
||||||
|
const ideaCount = generatedIdeas.length
|
||||||
|
const minimumRequired = isRevision ? 3 : 6
|
||||||
|
|
||||||
|
if (ideaCount < minimumRequired) {
|
||||||
|
// Generate additional ideas to meet minimum
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify no duplicate ideas
|
||||||
|
const titles = generatedIdeas.map(i => i.title.toLowerCase())
|
||||||
|
const duplicates = titles.filter((t, i) => titles.indexOf(t) !== i)
|
||||||
|
if (duplicates.length > 0) {
|
||||||
|
// Replace duplicates
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Report to Coordinator + Shared Memory Write
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Update shared memory
|
||||||
|
sharedMemory.generated_ideas = [
|
||||||
|
...sharedMemory.generated_ideas,
|
||||||
|
...generatedIdeas.map(i => ({
|
||||||
|
id: `idea-${ideaNum}-${i.index}`,
|
||||||
|
title: i.title,
|
||||||
|
round: parseInt(ideaNum),
|
||||||
|
revised: isRevision
|
||||||
|
}))
|
||||||
|
]
|
||||||
|
Write(memoryPath, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
// Log message
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log",
|
||||||
|
team: teamName,
|
||||||
|
from: "ideator",
|
||||||
|
to: "coordinator",
|
||||||
|
type: isRevision ? "ideas_revised" : "ideas_ready",
|
||||||
|
summary: `[ideator] ${isRevision ? 'Revised' : 'Generated'} ${ideaCount} ideas (round ${ideaNum})`,
|
||||||
|
ref: outputPath
|
||||||
|
})
|
||||||
|
|
||||||
|
SendMessage({
|
||||||
|
type: "message",
|
||||||
|
recipient: "coordinator",
|
||||||
|
content: `## [ideator] ${isRevision ? 'Ideas Revised' : 'Ideas Generated'}
|
||||||
|
|
||||||
|
**Task**: ${task.subject}
|
||||||
|
**Ideas**: ${ideaCount}
|
||||||
|
**Output**: ${outputPath}
|
||||||
|
|
||||||
|
### Highlights
|
||||||
|
${generatedIdeas.slice(0, 3).map(i => `- **${i.title}**: ${i.description.substring(0, 100)}...`).join('\n')}`,
|
||||||
|
summary: `[ideator] ${ideaCount} ideas ${isRevision ? 'revised' : 'generated'}`
|
||||||
|
})
|
||||||
|
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||||
|
|
||||||
|
// Check for next task
|
||||||
|
const nextTasks = TaskList().filter(t =>
|
||||||
|
t.subject.startsWith('IDEA-') &&
|
||||||
|
t.owner === 'ideator' &&
|
||||||
|
t.status === 'pending' &&
|
||||||
|
t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
if (nextTasks.length > 0) {
|
||||||
|
// Continue with next task → back to Phase 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| No IDEA-* tasks available | Idle, wait for coordinator assignment |
|
||||||
|
| Session folder not found | Notify coordinator, request path |
|
||||||
|
| Shared memory read fails | Initialize empty, proceed with generation |
|
||||||
|
| Topic too vague | Generate meta-questions as seed ideas |
|
||||||
|
| Previous critique not found (revision) | Generate new ideas instead of revising |
|
||||||
205
.claude/skills/team-brainstorm/roles/synthesizer.md
Normal file
205
.claude/skills/team-brainstorm/roles/synthesizer.md
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
# Role: synthesizer
|
||||||
|
|
||||||
|
跨想法整合者。负责从多个创意和挑战反馈中提取主题、解决冲突、生成整合方案。
|
||||||
|
|
||||||
|
## Role Identity
|
||||||
|
|
||||||
|
- **Name**: `synthesizer`
|
||||||
|
- **Task Prefix**: `SYNTH-*`
|
||||||
|
- **Responsibility**: Read-only analysis (综合整合)
|
||||||
|
- **Communication**: SendMessage to coordinator only
|
||||||
|
- **Output Tag**: `[synthesizer]`
|
||||||
|
|
||||||
|
## Role Boundaries
|
||||||
|
|
||||||
|
### MUST
|
||||||
|
|
||||||
|
- 仅处理 `SYNTH-*` 前缀的任务
|
||||||
|
- 所有输出必须带 `[synthesizer]` 标识
|
||||||
|
- 仅通过 SendMessage 与 coordinator 通信
|
||||||
|
- Phase 2 读取 shared-memory.json,Phase 5 写入 synthesis_themes
|
||||||
|
|
||||||
|
### MUST NOT
|
||||||
|
|
||||||
|
- ❌ 生成新创意、挑战假设或评分排序
|
||||||
|
- ❌ 直接与其他 worker 角色通信
|
||||||
|
- ❌ 为其他角色创建任务
|
||||||
|
- ❌ 修改 shared-memory.json 中不属于自己的字段
|
||||||
|
|
||||||
|
## Message Types
|
||||||
|
|
||||||
|
| Type | Direction | Trigger | Description |
|
||||||
|
|------|-----------|---------|-------------|
|
||||||
|
| `synthesis_ready` | synthesizer → coordinator | Synthesis completed | 综合整合完成 |
|
||||||
|
| `error` | synthesizer → coordinator | Processing failure | 错误上报 |
|
||||||
|
|
||||||
|
## Execution (5-Phase)
|
||||||
|
|
||||||
|
### Phase 1: Task Discovery
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const tasks = TaskList()
|
||||||
|
const myTasks = tasks.filter(t =>
|
||||||
|
t.subject.startsWith('SYNTH-') &&
|
||||||
|
t.owner === 'synthesizer' &&
|
||||||
|
t.status === 'pending' &&
|
||||||
|
t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
|
||||||
|
if (myTasks.length === 0) return
|
||||||
|
const task = TaskGet({ taskId: myTasks[0].id })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Context Loading + Shared Memory Read
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const sessionMatch = task.description.match(/Session:\s*([^\n]+)/)
|
||||||
|
const sessionFolder = sessionMatch?.[1]?.trim()
|
||||||
|
|
||||||
|
const memoryPath = `${sessionFolder}/shared-memory.json`
|
||||||
|
let sharedMemory = {}
|
||||||
|
try { sharedMemory = JSON.parse(Read(memoryPath)) } catch {}
|
||||||
|
|
||||||
|
// Read all ideas and critiques
|
||||||
|
const ideaFiles = Glob({ pattern: `${sessionFolder}/ideas/*.md` })
|
||||||
|
const critiqueFiles = Glob({ pattern: `${sessionFolder}/critiques/*.md` })
|
||||||
|
const allIdeas = ideaFiles.map(f => Read(f))
|
||||||
|
const allCritiques = critiqueFiles.map(f => Read(f))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Synthesis Execution
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Synthesis process:
|
||||||
|
// 1. Theme Extraction — 识别跨创意的共同主题
|
||||||
|
// 2. Conflict Resolution — 解决相互矛盾的想法
|
||||||
|
// 3. Complementary Grouping — 将互补的创意组合
|
||||||
|
// 4. Gap Identification — 发现未覆盖的视角
|
||||||
|
// 5. Integrated Proposal — 生成1-3个整合方案
|
||||||
|
|
||||||
|
const synthNum = task.subject.match(/SYNTH-(\d+)/)?.[1] || '001'
|
||||||
|
const outputPath = `${sessionFolder}/synthesis/synthesis-${synthNum}.md`
|
||||||
|
|
||||||
|
const synthesisContent = `# Synthesis — Round ${synthNum}
|
||||||
|
|
||||||
|
**Input**: ${ideaFiles.length} idea files, ${critiqueFiles.length} critique files
|
||||||
|
**GC Rounds Completed**: ${sharedMemory.gc_round || 0}
|
||||||
|
|
||||||
|
## Extracted Themes
|
||||||
|
|
||||||
|
${themes.map((theme, i) => `### Theme ${i + 1}: ${theme.name}
|
||||||
|
|
||||||
|
**Description**: ${theme.description}
|
||||||
|
**Supporting Ideas**: ${theme.supportingIdeas.join(', ')}
|
||||||
|
**Strength**: ${theme.strength}/10
|
||||||
|
`).join('\n')}
|
||||||
|
|
||||||
|
## Conflict Resolution
|
||||||
|
|
||||||
|
${conflicts.map(c => `### ${c.idea1} vs ${c.idea2}
|
||||||
|
|
||||||
|
**Nature**: ${c.nature}
|
||||||
|
**Resolution**: ${c.resolution}
|
||||||
|
**Rationale**: ${c.rationale}
|
||||||
|
`).join('\n')}
|
||||||
|
|
||||||
|
## Integrated Proposals
|
||||||
|
|
||||||
|
${proposals.map((p, i) => `### Proposal ${i + 1}: ${p.title}
|
||||||
|
|
||||||
|
**Core Concept**: ${p.concept}
|
||||||
|
**Combines**: ${p.sourceIdeas.join(' + ')}
|
||||||
|
**Addresses Challenges**: ${p.addressedChallenges.join(', ')}
|
||||||
|
**Feasibility**: ${p.feasibility}/10
|
||||||
|
**Innovation**: ${p.innovation}/10
|
||||||
|
|
||||||
|
**Description**:
|
||||||
|
${p.description}
|
||||||
|
|
||||||
|
**Key Benefits**:
|
||||||
|
${p.benefits.map(b => `- ${b}`).join('\n')}
|
||||||
|
|
||||||
|
**Remaining Risks**:
|
||||||
|
${p.risks.map(r => `- ${r}`).join('\n')}
|
||||||
|
`).join('\n')}
|
||||||
|
|
||||||
|
## Coverage Analysis
|
||||||
|
|
||||||
|
| Aspect | Covered | Gaps |
|
||||||
|
|--------|---------|------|
|
||||||
|
${coverageAnalysis.map(a => `| ${a.aspect} | ${a.covered ? '✅' : '❌'} | ${a.gap || '—'} |`).join('\n')}
|
||||||
|
`
|
||||||
|
|
||||||
|
Write(outputPath, synthesisContent)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Quality Check
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Verify synthesis quality
|
||||||
|
const proposalCount = proposals.length
|
||||||
|
const themeCount = themes.length
|
||||||
|
|
||||||
|
if (proposalCount === 0) {
|
||||||
|
// At least one proposal required
|
||||||
|
}
|
||||||
|
|
||||||
|
if (themeCount < 2) {
|
||||||
|
// May need to look for more patterns
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Report to Coordinator + Shared Memory Write
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
sharedMemory.synthesis_themes = themes.map(t => ({
|
||||||
|
name: t.name,
|
||||||
|
strength: t.strength,
|
||||||
|
supporting_ideas: t.supportingIdeas
|
||||||
|
}))
|
||||||
|
Write(memoryPath, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log",
|
||||||
|
team: teamName,
|
||||||
|
from: "synthesizer",
|
||||||
|
to: "coordinator",
|
||||||
|
type: "synthesis_ready",
|
||||||
|
summary: `[synthesizer] Synthesis complete: ${themeCount} themes, ${proposalCount} proposals`,
|
||||||
|
ref: outputPath
|
||||||
|
})
|
||||||
|
|
||||||
|
SendMessage({
|
||||||
|
type: "message",
|
||||||
|
recipient: "coordinator",
|
||||||
|
content: `## [synthesizer] Synthesis Results
|
||||||
|
|
||||||
|
**Task**: ${task.subject}
|
||||||
|
**Themes**: ${themeCount}
|
||||||
|
**Proposals**: ${proposalCount}
|
||||||
|
**Conflicts Resolved**: ${conflicts.length}
|
||||||
|
**Output**: ${outputPath}
|
||||||
|
|
||||||
|
### Top Proposals
|
||||||
|
${proposals.slice(0, 3).map((p, i) => `${i + 1}. **${p.title}** — ${p.concept} (Feasibility: ${p.feasibility}/10, Innovation: ${p.innovation}/10)`).join('\n')}`,
|
||||||
|
summary: `[synthesizer] ${themeCount} themes, ${proposalCount} proposals`
|
||||||
|
})
|
||||||
|
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||||
|
|
||||||
|
const nextTasks = TaskList().filter(t =>
|
||||||
|
t.subject.startsWith('SYNTH-') && t.owner === 'synthesizer' &&
|
||||||
|
t.status === 'pending' && t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
if (nextTasks.length > 0) { /* back to Phase 1 */ }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| No SYNTH-* tasks | Idle, wait for assignment |
|
||||||
|
| No ideas/critiques found | Notify coordinator |
|
||||||
|
| Irreconcilable conflicts | Present both sides, recommend user decision |
|
||||||
|
| Only one idea survives | Create single focused proposal |
|
||||||
86
.claude/skills/team-brainstorm/specs/team-config.json
Normal file
86
.claude/skills/team-brainstorm/specs/team-config.json
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
{
|
||||||
|
"team_name": "team-brainstorm",
|
||||||
|
"team_display_name": "Team Brainstorm",
|
||||||
|
"description": "Head brainstorming team with Generator-Critic loop, shared memory, and dynamic pipeline selection",
|
||||||
|
"version": "1.0.0",
|
||||||
|
|
||||||
|
"roles": {
|
||||||
|
"coordinator": {
|
||||||
|
"task_prefix": null,
|
||||||
|
"responsibility": "Topic clarification, complexity assessment, pipeline selection, convergence monitoring",
|
||||||
|
"message_types": ["pipeline_selected", "gc_loop_trigger", "task_unblocked", "error", "shutdown"]
|
||||||
|
},
|
||||||
|
"ideator": {
|
||||||
|
"task_prefix": "IDEA",
|
||||||
|
"responsibility": "Multi-angle idea generation, concept exploration, divergent thinking",
|
||||||
|
"message_types": ["ideas_ready", "ideas_revised", "error"]
|
||||||
|
},
|
||||||
|
"challenger": {
|
||||||
|
"task_prefix": "CHALLENGE",
|
||||||
|
"responsibility": "Devil's advocate, assumption challenging, feasibility questioning",
|
||||||
|
"message_types": ["critique_ready", "error"]
|
||||||
|
},
|
||||||
|
"synthesizer": {
|
||||||
|
"task_prefix": "SYNTH",
|
||||||
|
"responsibility": "Cross-idea integration, theme extraction, conflict resolution",
|
||||||
|
"message_types": ["synthesis_ready", "error"]
|
||||||
|
},
|
||||||
|
"evaluator": {
|
||||||
|
"task_prefix": "EVAL",
|
||||||
|
"responsibility": "Scoring and ranking, priority recommendation, final selection",
|
||||||
|
"message_types": ["evaluation_ready", "error"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"pipelines": {
|
||||||
|
"quick": {
|
||||||
|
"description": "Simple topic: generate → challenge → synthesize",
|
||||||
|
"task_chain": ["IDEA-001", "CHALLENGE-001", "SYNTH-001"],
|
||||||
|
"gc_loops": 0
|
||||||
|
},
|
||||||
|
"deep": {
|
||||||
|
"description": "Complex topic with Generator-Critic loop (max 2 rounds)",
|
||||||
|
"task_chain": ["IDEA-001", "CHALLENGE-001", "IDEA-002", "CHALLENGE-002", "SYNTH-001", "EVAL-001"],
|
||||||
|
"gc_loops": 2
|
||||||
|
},
|
||||||
|
"full": {
|
||||||
|
"description": "Parallel fan-out ideation + Generator-Critic + evaluation",
|
||||||
|
"task_chain": ["IDEA-001", "IDEA-002", "IDEA-003", "CHALLENGE-001", "IDEA-004", "SYNTH-001", "EVAL-001"],
|
||||||
|
"gc_loops": 1,
|
||||||
|
"parallel_groups": [["IDEA-001", "IDEA-002", "IDEA-003"]]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"innovation_patterns": {
|
||||||
|
"generator_critic": {
|
||||||
|
"generator": "ideator",
|
||||||
|
"critic": "challenger",
|
||||||
|
"max_rounds": 2,
|
||||||
|
"convergence_trigger": "critique.severity < HIGH"
|
||||||
|
},
|
||||||
|
"shared_memory": {
|
||||||
|
"file": "shared-memory.json",
|
||||||
|
"fields": {
|
||||||
|
"ideator": "generated_ideas",
|
||||||
|
"challenger": "critique_insights",
|
||||||
|
"synthesizer": "synthesis_themes",
|
||||||
|
"evaluator": "evaluation_scores"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dynamic_pipeline": {
|
||||||
|
"selector": "coordinator",
|
||||||
|
"criteria": "topic_complexity + scope + time_constraint"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"collaboration_patterns": ["CP-1", "CP-3", "CP-7"],
|
||||||
|
|
||||||
|
"session_dirs": {
|
||||||
|
"base": ".workflow/.team/BRS-{slug}-{YYYY-MM-DD}/",
|
||||||
|
"ideas": "ideas/",
|
||||||
|
"critiques": "critiques/",
|
||||||
|
"synthesis": "synthesis/",
|
||||||
|
"evaluation": "evaluation/",
|
||||||
|
"messages": ".workflow/.team-msg/{team-name}/"
|
||||||
|
}
|
||||||
|
}
|
||||||
372
.claude/skills/team-iterdev/SKILL.md
Normal file
372
.claude/skills/team-iterdev/SKILL.md
Normal file
@@ -0,0 +1,372 @@
|
|||||||
|
---
|
||||||
|
name: team-iterdev
|
||||||
|
description: Unified team skill for iterative development team. All roles invoke this skill with --role arg for role-specific execution. Triggers on "team iterdev".
|
||||||
|
allowed-tools: TeamCreate(*), TeamDelete(*), SendMessage(*), TaskCreate(*), TaskUpdate(*), TaskList(*), TaskGet(*), Task(*), AskUserQuestion(*), Read(*), Write(*), Edit(*), Bash(*), Glob(*), Grep(*)
|
||||||
|
---
|
||||||
|
|
||||||
|
# Team IterDev
|
||||||
|
|
||||||
|
持续迭代开发团队技能。通过 Generator-Critic 循环(developer↔reviewer,最多3轮)、任务账本(task-ledger.json)实时进度追踪、共享记忆(Sprint间学习)和动态管道选择,实现增量交付开发。所有团队成员通过 `--role=xxx` 路由。
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────┐
|
||||||
|
│ Skill(skill="team-iterdev", args="--role=xxx") │
|
||||||
|
└───────────────────┬──────────────────────────────┘
|
||||||
|
│ Role Router
|
||||||
|
┌───────────┬───┼───────────┬───────────┐
|
||||||
|
↓ ↓ ↓ ↓ ↓
|
||||||
|
┌──────────┐┌─────────┐┌─────────┐┌──────┐┌────────┐
|
||||||
|
│coordinator││architect││developer││tester││reviewer│
|
||||||
|
│ roles/ ││ roles/ ││ roles/ ││roles/││ roles/ │
|
||||||
|
└──────────┘└─────────┘└─────────┘└──────┘└────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Role Router
|
||||||
|
|
||||||
|
### Input Parsing
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const args = "$ARGUMENTS"
|
||||||
|
const roleMatch = args.match(/--role[=\s]+(\w+)/)
|
||||||
|
|
||||||
|
if (!roleMatch) {
|
||||||
|
throw new Error("Missing --role argument. Available roles: coordinator, architect, developer, tester, reviewer")
|
||||||
|
}
|
||||||
|
|
||||||
|
const role = roleMatch[1]
|
||||||
|
const teamName = args.match(/--team[=\s]+([\w-]+)/)?.[1] || "iterdev"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Role Dispatch
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const VALID_ROLES = {
|
||||||
|
"coordinator": { file: "roles/coordinator.md", prefix: null },
|
||||||
|
"architect": { file: "roles/architect.md", prefix: "DESIGN" },
|
||||||
|
"developer": { file: "roles/developer.md", prefix: "DEV" },
|
||||||
|
"tester": { file: "roles/tester.md", prefix: "VERIFY" },
|
||||||
|
"reviewer": { file: "roles/reviewer.md", prefix: "REVIEW" }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!VALID_ROLES[role]) {
|
||||||
|
throw new Error(`Unknown role: ${role}. Available: ${Object.keys(VALID_ROLES).join(', ')}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
Read(VALID_ROLES[role].file)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Available Roles
|
||||||
|
|
||||||
|
| Role | Task Prefix | Responsibility | Role File |
|
||||||
|
|------|-------------|----------------|-----------|
|
||||||
|
| `coordinator` | N/A | Sprint规划、积压管理、任务账本维护 | [roles/coordinator.md](roles/coordinator.md) |
|
||||||
|
| `architect` | DESIGN-* | 技术设计、任务分解、架构决策 | [roles/architect.md](roles/architect.md) |
|
||||||
|
| `developer` | DEV-* | 代码实现、增量交付 | [roles/developer.md](roles/developer.md) |
|
||||||
|
| `tester` | VERIFY-* | 测试执行、修复循环、回归检测 | [roles/tester.md](roles/tester.md) |
|
||||||
|
| `reviewer` | REVIEW-* | 代码审查、质量评分、改进建议 | [roles/reviewer.md](roles/reviewer.md) |
|
||||||
|
|
||||||
|
## Shared Infrastructure
|
||||||
|
|
||||||
|
### Role Isolation Rules
|
||||||
|
|
||||||
|
#### Output Tagging(强制)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
SendMessage({ content: `## [${role}] ...`, summary: `[${role}] ...` })
|
||||||
|
mcp__ccw-tools__team_msg({ summary: `[${role}] ...` })
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Coordinator 隔离
|
||||||
|
|
||||||
|
| 允许 | 禁止 |
|
||||||
|
|------|------|
|
||||||
|
| Sprint 规划 (AskUserQuestion) | ❌ 直接编写代码 |
|
||||||
|
| 创建任务链 (TaskCreate) | ❌ 直接执行测试/审查 |
|
||||||
|
| 维护任务账本 (task-ledger.json) | ❌ 调用 code-developer 等实现类 subagent |
|
||||||
|
| 监控进度 | ❌ 绕过 worker 自行完成 |
|
||||||
|
|
||||||
|
#### Worker 隔离
|
||||||
|
|
||||||
|
| 允许 | 禁止 |
|
||||||
|
|------|------|
|
||||||
|
| 处理自己前缀的任务 | ❌ 处理其他角色前缀的任务 |
|
||||||
|
| 读写 shared-memory.json (自己的字段) | ❌ 为其他角色创建任务 |
|
||||||
|
| SendMessage 给 coordinator | ❌ 直接与其他 worker 通信 |
|
||||||
|
|
||||||
|
### Team Configuration
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const TEAM_CONFIG = {
|
||||||
|
name: "iterdev",
|
||||||
|
sessionDir: ".workflow/.team/IDS-{slug}-{date}/",
|
||||||
|
msgDir: ".workflow/.team-msg/iterdev/",
|
||||||
|
sharedMemory: "shared-memory.json",
|
||||||
|
taskLedger: "task-ledger.json"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task Ledger (创新模式 — 任务账本)
|
||||||
|
|
||||||
|
实时追踪所有 Sprint 任务进度:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// task-ledger.json structure
|
||||||
|
{
|
||||||
|
"sprint_id": "sprint-1",
|
||||||
|
"sprint_goal": "...",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"id": "DEV-001",
|
||||||
|
"title": "...",
|
||||||
|
"owner": "developer",
|
||||||
|
"status": "completed", // pending | in_progress | completed | blocked
|
||||||
|
"started_at": "...",
|
||||||
|
"completed_at": "...",
|
||||||
|
"gc_rounds": 0, // Generator-Critic iterations
|
||||||
|
"review_score": null, // reviewer 评分
|
||||||
|
"test_pass_rate": null // tester 通过率
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metrics": {
|
||||||
|
"total": 5,
|
||||||
|
"completed": 3,
|
||||||
|
"in_progress": 1,
|
||||||
|
"blocked": 0,
|
||||||
|
"velocity": 3 // tasks completed this sprint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Coordinator updates ledger at each task transition
|
||||||
|
function updateLedger(sessionFolder, taskId, updates) {
|
||||||
|
const ledger = JSON.parse(Read(`${sessionFolder}/task-ledger.json`))
|
||||||
|
const task = ledger.tasks.find(t => t.id === taskId)
|
||||||
|
if (task) Object.assign(task, updates)
|
||||||
|
// Recalculate metrics
|
||||||
|
ledger.metrics.completed = ledger.tasks.filter(t => t.status === 'completed').length
|
||||||
|
ledger.metrics.in_progress = ledger.tasks.filter(t => t.status === 'in_progress').length
|
||||||
|
Write(`${sessionFolder}/task-ledger.json`, JSON.stringify(ledger, null, 2))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Shared Memory (Sprint间学习)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// shared-memory.json — accumulated across sprints
|
||||||
|
{
|
||||||
|
"sprint_history": [
|
||||||
|
{
|
||||||
|
"sprint_id": "sprint-1",
|
||||||
|
"what_worked": ["..."],
|
||||||
|
"what_failed": ["..."],
|
||||||
|
"patterns_learned": ["..."]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"architecture_decisions": [],
|
||||||
|
"implementation_context": [],
|
||||||
|
"review_feedback_trends": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Message Bus
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log", team: teamName, from: role, to: "coordinator",
|
||||||
|
type: "<type>", summary: `[${role}] <summary>`, ref: "<file_path>"
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
| Role | Types |
|
||||||
|
|------|-------|
|
||||||
|
| coordinator | `sprint_started`, `gc_loop_trigger`, `sprint_complete`, `task_unblocked`, `error`, `shutdown` |
|
||||||
|
| architect | `design_ready`, `design_revision`, `error` |
|
||||||
|
| developer | `dev_complete`, `dev_progress`, `error` |
|
||||||
|
| tester | `verify_passed`, `verify_failed`, `fix_required`, `error` |
|
||||||
|
| reviewer | `review_passed`, `review_revision`, `review_critical`, `error` |
|
||||||
|
|
||||||
|
### CLI Fallback
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Bash(`ccw team log --team "${teamName}" --from "${role}" --to "coordinator" --type "<type>" --summary "<summary>" --json`)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task Lifecycle (All Worker Roles)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const tasks = TaskList()
|
||||||
|
const myTasks = tasks.filter(t =>
|
||||||
|
t.subject.startsWith(`${VALID_ROLES[role].prefix}-`) &&
|
||||||
|
t.owner === role && t.status === 'pending' && t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
if (myTasks.length === 0) return
|
||||||
|
const task = TaskGet({ taskId: myTasks[0].id })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||||
|
// Phase 2-4: Role-specific
|
||||||
|
// Phase 5: Report + Loop
|
||||||
|
```
|
||||||
|
|
||||||
|
## Three-Pipeline Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
Patch (简单修复):
|
||||||
|
DEV-001 → VERIFY-001
|
||||||
|
|
||||||
|
Sprint (标准特性):
|
||||||
|
DESIGN-001 → DEV-001 → [VERIFY-001 + REVIEW-001](parallel)
|
||||||
|
|
||||||
|
Multi-Sprint (大型特性):
|
||||||
|
Sprint 1: DESIGN-001 → DEV-001 → DEV-002(incremental) → VERIFY-001 → DEV-fix → REVIEW-001
|
||||||
|
Sprint 2: DESIGN-002(refined) → DEV-003 → VERIFY-002 → REVIEW-002
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Generator-Critic Loop
|
||||||
|
|
||||||
|
developer ↔ reviewer 循环,最多3轮:
|
||||||
|
|
||||||
|
```
|
||||||
|
DEV → REVIEW → (if review.critical_count > 0 || review.score < 7)
|
||||||
|
→ DEV-fix → REVIEW-2 → (if still issues) → DEV-fix-2 → REVIEW-3
|
||||||
|
→ (max 3 rounds, then accept with warning)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multi-Sprint Dynamic Downgrade
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// If Sprint N is progressing well, downgrade Sprint N+1 complexity
|
||||||
|
if (sprintMetrics.velocity >= expectedVelocity && sprintMetrics.review_avg >= 8) {
|
||||||
|
// Next sprint: skip detailed design, use simplified pipeline
|
||||||
|
nextPipeline = 'sprint' // downgrade from multi-sprint
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Unified Session Directory
|
||||||
|
|
||||||
|
```
|
||||||
|
.workflow/.team/IDS-{slug}-{YYYY-MM-DD}/
|
||||||
|
├── team-session.json
|
||||||
|
├── shared-memory.json # Sprint间学习: what_worked / what_failed / patterns_learned
|
||||||
|
├── task-ledger.json # 实时任务进度账本
|
||||||
|
├── design/ # Architect output
|
||||||
|
│ ├── design-001.md
|
||||||
|
│ └── task-breakdown.json
|
||||||
|
├── code/ # Developer tracking
|
||||||
|
│ └── dev-log.md
|
||||||
|
├── verify/ # Tester output
|
||||||
|
│ └── verify-001.json
|
||||||
|
└── review/ # Reviewer output
|
||||||
|
└── review-001.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Coordinator Spawn Template
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
TeamCreate({ team_name: teamName })
|
||||||
|
|
||||||
|
// Architect
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
team_name: teamName,
|
||||||
|
name: "architect",
|
||||||
|
prompt: `你是 team "${teamName}" 的 ARCHITECT。
|
||||||
|
当你收到 DESIGN-* 任务时,调用 Skill(skill="team-iterdev", args="--role=architect") 执行。
|
||||||
|
当前需求: ${taskDescription}
|
||||||
|
约束: ${constraints}
|
||||||
|
|
||||||
|
## 角色准则(强制)
|
||||||
|
- 你只能处理 DESIGN-* 前缀的任务
|
||||||
|
- 所有输出必须带 [architect] 标识前缀
|
||||||
|
- 仅与 coordinator 通信
|
||||||
|
|
||||||
|
## 消息总线(必须)
|
||||||
|
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||||
|
|
||||||
|
工作流程:
|
||||||
|
1. TaskList → 找到 DESIGN-* 任务
|
||||||
|
2. Skill(skill="team-iterdev", args="--role=architect") 执行
|
||||||
|
3. team_msg log + SendMessage
|
||||||
|
4. TaskUpdate completed → 检查下一个任务`
|
||||||
|
})
|
||||||
|
|
||||||
|
// Developer
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
team_name: teamName,
|
||||||
|
name: "developer",
|
||||||
|
prompt: `你是 team "${teamName}" 的 DEVELOPER。
|
||||||
|
当你收到 DEV-* 任务时,调用 Skill(skill="team-iterdev", args="--role=developer") 执行。
|
||||||
|
当前需求: ${taskDescription}
|
||||||
|
|
||||||
|
## 角色准则(强制)
|
||||||
|
- 你只能处理 DEV-* 前缀的任务
|
||||||
|
- 所有输出必须带 [developer] 标识前缀
|
||||||
|
|
||||||
|
## 消息总线(必须)
|
||||||
|
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||||
|
|
||||||
|
工作流程:
|
||||||
|
1. TaskList → 找到 DEV-* 任务
|
||||||
|
2. Skill(skill="team-iterdev", args="--role=developer") 执行
|
||||||
|
3. team_msg log + SendMessage
|
||||||
|
4. TaskUpdate completed → 检查下一个任务`
|
||||||
|
})
|
||||||
|
|
||||||
|
// Tester
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
team_name: teamName,
|
||||||
|
name: "tester",
|
||||||
|
prompt: `你是 team "${teamName}" 的 TESTER。
|
||||||
|
当你收到 VERIFY-* 任务时,调用 Skill(skill="team-iterdev", args="--role=tester") 执行。
|
||||||
|
当前需求: ${taskDescription}
|
||||||
|
|
||||||
|
## 角色准则(强制)
|
||||||
|
- 你只能处理 VERIFY-* 前缀的任务
|
||||||
|
- 所有输出必须带 [tester] 标识前缀
|
||||||
|
|
||||||
|
## 消息总线(必须)
|
||||||
|
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||||
|
|
||||||
|
工作流程:
|
||||||
|
1. TaskList → 找到 VERIFY-* 任务
|
||||||
|
2. Skill(skill="team-iterdev", args="--role=tester") 执行
|
||||||
|
3. team_msg log + SendMessage
|
||||||
|
4. TaskUpdate completed → 检查下一个任务`
|
||||||
|
})
|
||||||
|
|
||||||
|
// Reviewer
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
team_name: teamName,
|
||||||
|
name: "reviewer",
|
||||||
|
prompt: `你是 team "${teamName}" 的 REVIEWER。
|
||||||
|
当你收到 REVIEW-* 任务时,调用 Skill(skill="team-iterdev", args="--role=reviewer") 执行。
|
||||||
|
当前需求: ${taskDescription}
|
||||||
|
|
||||||
|
## 角色准则(强制)
|
||||||
|
- 你只能处理 REVIEW-* 前缀的任务
|
||||||
|
- 所有输出必须带 [reviewer] 标识前缀
|
||||||
|
|
||||||
|
## 消息总线(必须)
|
||||||
|
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||||
|
|
||||||
|
工作流程:
|
||||||
|
1. TaskList → 找到 REVIEW-* 任务
|
||||||
|
2. Skill(skill="team-iterdev", args="--role=reviewer") 执行
|
||||||
|
3. team_msg log + SendMessage
|
||||||
|
4. TaskUpdate completed → 检查下一个任务`
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| Unknown --role value | Error with available role list |
|
||||||
|
| Missing --role arg | Error with usage hint |
|
||||||
|
| Role file not found | Error with expected path |
|
||||||
|
| GC loop exceeds 3 rounds | Accept with warning, record in shared memory |
|
||||||
|
| Sprint velocity drops below 50% | Coordinator alerts user, suggests scope reduction |
|
||||||
|
| Task ledger corrupted | Rebuild from TaskList state |
|
||||||
179
.claude/skills/team-iterdev/roles/architect.md
Normal file
179
.claude/skills/team-iterdev/roles/architect.md
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
# Role: architect
|
||||||
|
|
||||||
|
技术架构师。负责技术设计、任务分解、架构决策记录。
|
||||||
|
|
||||||
|
## Role Identity
|
||||||
|
|
||||||
|
- **Name**: `architect`
|
||||||
|
- **Task Prefix**: `DESIGN-*`
|
||||||
|
- **Responsibility**: Read-only analysis (技术设计)
|
||||||
|
- **Communication**: SendMessage to coordinator only
|
||||||
|
- **Output Tag**: `[architect]`
|
||||||
|
|
||||||
|
## Role Boundaries
|
||||||
|
|
||||||
|
### MUST
|
||||||
|
|
||||||
|
- 仅处理 `DESIGN-*` 前缀的任务
|
||||||
|
- 所有输出必须带 `[architect]` 标识
|
||||||
|
- Phase 2 读取 shared-memory.json,Phase 5 写入 architecture_decisions
|
||||||
|
|
||||||
|
### MUST NOT
|
||||||
|
|
||||||
|
- ❌ 编写实现代码、执行测试或代码审查
|
||||||
|
- ❌ 直接与其他 worker 通信
|
||||||
|
- ❌ 为其他角色创建任务
|
||||||
|
|
||||||
|
## Message Types
|
||||||
|
|
||||||
|
| Type | Direction | Trigger | Description |
|
||||||
|
|------|-----------|---------|-------------|
|
||||||
|
| `design_ready` | architect → coordinator | Design completed | 设计完成 |
|
||||||
|
| `design_revision` | architect → coordinator | Design revised | 设计修订 |
|
||||||
|
| `error` | architect → coordinator | Processing failure | 错误上报 |
|
||||||
|
|
||||||
|
## Execution (5-Phase)
|
||||||
|
|
||||||
|
### Phase 1: Task Discovery
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const tasks = TaskList()
|
||||||
|
const myTasks = tasks.filter(t =>
|
||||||
|
t.subject.startsWith('DESIGN-') && t.owner === 'architect' &&
|
||||||
|
t.status === 'pending' && t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
if (myTasks.length === 0) return
|
||||||
|
const task = TaskGet({ taskId: myTasks[0].id })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Context Loading + Codebase Exploration
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const sessionMatch = task.description.match(/Session:\s*([^\n]+)/)
|
||||||
|
const sessionFolder = sessionMatch?.[1]?.trim()
|
||||||
|
|
||||||
|
const memoryPath = `${sessionFolder}/shared-memory.json`
|
||||||
|
let sharedMemory = {}
|
||||||
|
try { sharedMemory = JSON.parse(Read(memoryPath)) } catch {}
|
||||||
|
|
||||||
|
// Multi-angle codebase exploration
|
||||||
|
Task({
|
||||||
|
subagent_type: "cli-explore-agent",
|
||||||
|
run_in_background: false,
|
||||||
|
description: "Explore architecture",
|
||||||
|
prompt: `Explore codebase architecture for: ${task.description}
|
||||||
|
Focus on: existing patterns, module structure, dependencies, similar implementations.
|
||||||
|
Report relevant files and integration points.`
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Technical Design + Task Decomposition
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const designNum = task.subject.match(/DESIGN-(\d+)/)?.[1] || '001'
|
||||||
|
const designPath = `${sessionFolder}/design/design-${designNum}.md`
|
||||||
|
const breakdownPath = `${sessionFolder}/design/task-breakdown.json`
|
||||||
|
|
||||||
|
// Generate design document
|
||||||
|
const designContent = `# Technical Design — ${designNum}
|
||||||
|
|
||||||
|
**Requirement**: ${task.description}
|
||||||
|
**Sprint**: ${sharedMemory.sprint_history?.length + 1 || 1}
|
||||||
|
|
||||||
|
## Architecture Decision
|
||||||
|
|
||||||
|
**Approach**: ${selectedApproach}
|
||||||
|
**Rationale**: ${rationale}
|
||||||
|
**Alternatives Considered**: ${alternatives.join(', ')}
|
||||||
|
|
||||||
|
## Component Design
|
||||||
|
|
||||||
|
${components.map(c => `### ${c.name}
|
||||||
|
- **Responsibility**: ${c.responsibility}
|
||||||
|
- **Dependencies**: ${c.dependencies.join(', ')}
|
||||||
|
- **Files**: ${c.files.join(', ')}
|
||||||
|
- **Complexity**: ${c.complexity}
|
||||||
|
`).join('\n')}
|
||||||
|
|
||||||
|
## Task Breakdown
|
||||||
|
|
||||||
|
${taskBreakdown.map((t, i) => `### Task ${i + 1}: ${t.title}
|
||||||
|
- **Files**: ${t.files.join(', ')}
|
||||||
|
- **Estimated Complexity**: ${t.complexity}
|
||||||
|
- **Dependencies**: ${t.dependencies.join(', ') || 'None'}
|
||||||
|
`).join('\n')}
|
||||||
|
|
||||||
|
## Integration Points
|
||||||
|
|
||||||
|
${integrationPoints.map(ip => `- **${ip.name}**: ${ip.description}`).join('\n')}
|
||||||
|
|
||||||
|
## Risks
|
||||||
|
|
||||||
|
${risks.map(r => `- **${r.risk}**: ${r.mitigation}`).join('\n')}
|
||||||
|
`
|
||||||
|
|
||||||
|
Write(designPath, designContent)
|
||||||
|
|
||||||
|
// Generate task breakdown JSON for developer
|
||||||
|
const breakdown = {
|
||||||
|
design_id: `design-${designNum}`,
|
||||||
|
tasks: taskBreakdown.map((t, i) => ({
|
||||||
|
id: `task-${i + 1}`,
|
||||||
|
title: t.title,
|
||||||
|
files: t.files,
|
||||||
|
complexity: t.complexity,
|
||||||
|
dependencies: t.dependencies,
|
||||||
|
acceptance_criteria: t.acceptance
|
||||||
|
})),
|
||||||
|
total_files: [...new Set(taskBreakdown.flatMap(t => t.files))].length,
|
||||||
|
execution_order: taskBreakdown.map((t, i) => `task-${i + 1}`)
|
||||||
|
}
|
||||||
|
Write(breakdownPath, JSON.stringify(breakdown, null, 2))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Design Validation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Verify design completeness
|
||||||
|
const hasComponents = components.length > 0
|
||||||
|
const hasBreakdown = taskBreakdown.length > 0
|
||||||
|
const hasDependencies = components.every(c => c.dependencies !== undefined)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Report to Coordinator + Shared Memory Write
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
sharedMemory.architecture_decisions.push({
|
||||||
|
design_id: `design-${designNum}`,
|
||||||
|
approach: selectedApproach,
|
||||||
|
rationale: rationale,
|
||||||
|
components: components.map(c => c.name),
|
||||||
|
task_count: taskBreakdown.length
|
||||||
|
})
|
||||||
|
Write(memoryPath, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log", team: teamName, from: "architect", to: "coordinator",
|
||||||
|
type: "design_ready",
|
||||||
|
summary: `[architect] Design complete: ${components.length} components, ${taskBreakdown.length} tasks`,
|
||||||
|
ref: designPath
|
||||||
|
})
|
||||||
|
|
||||||
|
SendMessage({
|
||||||
|
type: "message", recipient: "coordinator",
|
||||||
|
content: `## [architect] Design Ready\n\n**Components**: ${components.length}\n**Tasks**: ${taskBreakdown.length}\n**Design**: ${designPath}\n**Breakdown**: ${breakdownPath}`,
|
||||||
|
summary: `[architect] Design: ${taskBreakdown.length} tasks`
|
||||||
|
})
|
||||||
|
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| No DESIGN-* tasks | Idle |
|
||||||
|
| Codebase exploration fails | Design based on task description alone |
|
||||||
|
| Too many components | Simplify, suggest phased approach |
|
||||||
|
| Conflicting patterns found | Document in design, recommend resolution |
|
||||||
266
.claude/skills/team-iterdev/roles/coordinator.md
Normal file
266
.claude/skills/team-iterdev/roles/coordinator.md
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
# Role: coordinator
|
||||||
|
|
||||||
|
持续迭代开发团队协调者。负责 Sprint 规划、积压管理、任务账本维护、Generator-Critic 循环控制(developer↔reviewer,最多3轮)和 Sprint 间学习。
|
||||||
|
|
||||||
|
## Role Identity
|
||||||
|
|
||||||
|
- **Name**: `coordinator`
|
||||||
|
- **Task Prefix**: N/A
|
||||||
|
- **Responsibility**: Orchestration
|
||||||
|
- **Communication**: SendMessage to all teammates
|
||||||
|
- **Output Tag**: `[coordinator]`
|
||||||
|
|
||||||
|
## Role Boundaries
|
||||||
|
|
||||||
|
### MUST
|
||||||
|
|
||||||
|
- 所有输出必须带 `[coordinator]` 标识
|
||||||
|
- 维护 task-ledger.json 实时进度
|
||||||
|
- 管理 developer↔reviewer 的 GC 循环(最多3轮)
|
||||||
|
- Sprint 结束时记录学习到 shared-memory.json
|
||||||
|
|
||||||
|
### MUST NOT
|
||||||
|
|
||||||
|
- ❌ 直接编写代码、设计架构、执行测试或代码审查
|
||||||
|
- ❌ 直接调用实现类 subagent
|
||||||
|
- ❌ 修改源代码
|
||||||
|
|
||||||
|
## Execution
|
||||||
|
|
||||||
|
### Phase 1: Sprint Planning
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const args = "$ARGUMENTS"
|
||||||
|
const teamName = args.match(/--team-name[=\s]+([\w-]+)/)?.[1] || `iterdev-${Date.now().toString(36)}`
|
||||||
|
const taskDescription = args.replace(/--team-name[=\s]+[\w-]+/, '').replace(/--role[=\s]+\w+/, '').trim()
|
||||||
|
|
||||||
|
// Assess complexity for pipeline selection
|
||||||
|
function assessComplexity(desc) {
|
||||||
|
let score = 0
|
||||||
|
const changedFiles = Bash(`git diff --name-only HEAD~1 2>/dev/null || echo ""`).split('\n').filter(Boolean)
|
||||||
|
score += changedFiles.length > 10 ? 3 : changedFiles.length > 3 ? 2 : 0
|
||||||
|
if (/refactor|architect|restructure|system|module/.test(desc)) score += 3
|
||||||
|
if (/multiple|across|cross/.test(desc)) score += 2
|
||||||
|
if (/fix|bug|typo|patch/.test(desc)) score -= 2
|
||||||
|
return { score, fileCount: changedFiles.length }
|
||||||
|
}
|
||||||
|
|
||||||
|
const { score, fileCount } = assessComplexity(taskDescription)
|
||||||
|
const suggestedPipeline = score >= 5 ? 'multi-sprint' : score >= 2 ? 'sprint' : 'patch'
|
||||||
|
|
||||||
|
AskUserQuestion({
|
||||||
|
questions: [{
|
||||||
|
question: "选择开发模式:",
|
||||||
|
header: "Mode",
|
||||||
|
multiSelect: false,
|
||||||
|
options: [
|
||||||
|
{ label: suggestedPipeline === 'patch' ? "patch (推荐)" : "patch", description: "补丁模式:实现→验证(简单修复)" },
|
||||||
|
{ label: suggestedPipeline === 'sprint' ? "sprint (推荐)" : "sprint", description: "Sprint模式:设计→实现→验证+审查" },
|
||||||
|
{ label: suggestedPipeline === 'multi-sprint' ? "multi-sprint (推荐)" : "multi-sprint", description: "多Sprint:增量迭代交付(大型特性)" }
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Create Team + Initialize Ledger
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
TeamCreate({ team_name: teamName })
|
||||||
|
|
||||||
|
const topicSlug = taskDescription.toLowerCase().replace(/[^a-z0-9]+/g, '-').substring(0, 40)
|
||||||
|
const dateStr = new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString().substring(0, 10)
|
||||||
|
const sessionId = `IDS-${topicSlug}-${dateStr}`
|
||||||
|
const sessionFolder = `.workflow/.team/${sessionId}`
|
||||||
|
|
||||||
|
Bash(`mkdir -p "${sessionFolder}/design" "${sessionFolder}/code" "${sessionFolder}/verify" "${sessionFolder}/review"`)
|
||||||
|
|
||||||
|
// Initialize task ledger
|
||||||
|
const taskLedger = {
|
||||||
|
sprint_id: "sprint-1",
|
||||||
|
sprint_goal: taskDescription,
|
||||||
|
pipeline: selectedPipeline,
|
||||||
|
tasks: [],
|
||||||
|
metrics: { total: 0, completed: 0, in_progress: 0, blocked: 0, velocity: 0 }
|
||||||
|
}
|
||||||
|
Write(`${sessionFolder}/task-ledger.json`, JSON.stringify(taskLedger, null, 2))
|
||||||
|
|
||||||
|
// Initialize shared memory with sprint learning
|
||||||
|
const sharedMemory = {
|
||||||
|
sprint_history: [],
|
||||||
|
architecture_decisions: [],
|
||||||
|
implementation_context: [],
|
||||||
|
review_feedback_trends: [],
|
||||||
|
gc_round: 0,
|
||||||
|
max_gc_rounds: 3
|
||||||
|
}
|
||||||
|
Write(`${sessionFolder}/shared-memory.json`, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
const teamSession = {
|
||||||
|
session_id: sessionId, team_name: teamName, task: taskDescription,
|
||||||
|
pipeline: selectedPipeline, status: "active", sprint_number: 1,
|
||||||
|
created_at: new Date().toISOString(), updated_at: new Date().toISOString(),
|
||||||
|
completed_tasks: []
|
||||||
|
}
|
||||||
|
Write(`${sessionFolder}/team-session.json`, JSON.stringify(teamSession, null, 2))
|
||||||
|
```
|
||||||
|
|
||||||
|
Spawn workers (see SKILL.md Coordinator Spawn Template).
|
||||||
|
|
||||||
|
### Phase 3: Create Task Chain + Update Ledger
|
||||||
|
|
||||||
|
#### Patch Pipeline
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
TaskCreate({ subject: "DEV-001: 实现修复", description: `${taskDescription}\n\nSession: ${sessionFolder}`, activeForm: "实现中" })
|
||||||
|
TaskUpdate({ taskId: devId, owner: "developer" })
|
||||||
|
|
||||||
|
TaskCreate({ subject: "VERIFY-001: 验证修复", description: `验证 DEV-001\n\nSession: ${sessionFolder}`, activeForm: "验证中" })
|
||||||
|
TaskUpdate({ taskId: verifyId, owner: "tester", addBlockedBy: [devId] })
|
||||||
|
|
||||||
|
// Update ledger
|
||||||
|
updateLedger(sessionFolder, null, {
|
||||||
|
tasks: [
|
||||||
|
{ id: "DEV-001", title: "实现修复", owner: "developer", status: "pending", gc_rounds: 0 },
|
||||||
|
{ id: "VERIFY-001", title: "验证修复", owner: "tester", status: "pending", gc_rounds: 0 }
|
||||||
|
],
|
||||||
|
metrics: { total: 2, completed: 0, in_progress: 0, blocked: 0, velocity: 0 }
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Sprint Pipeline
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
TaskCreate({ subject: "DESIGN-001: 技术设计与任务分解", description: `${taskDescription}\n\nSession: ${sessionFolder}\n输出: ${sessionFolder}/design/design-001.md + task-breakdown.json`, activeForm: "设计中" })
|
||||||
|
TaskUpdate({ taskId: designId, owner: "architect" })
|
||||||
|
|
||||||
|
TaskCreate({ subject: "DEV-001: 实现设计方案", description: `按设计方案实现\n\nSession: ${sessionFolder}\n设计: design/design-001.md\n分解: design/task-breakdown.json`, activeForm: "实现中" })
|
||||||
|
TaskUpdate({ taskId: devId, owner: "developer", addBlockedBy: [designId] })
|
||||||
|
|
||||||
|
// VERIFY-001 and REVIEW-001 parallel, both blockedBy DEV-001
|
||||||
|
TaskCreate({ subject: "VERIFY-001: 测试验证", description: `验证实现\n\nSession: ${sessionFolder}`, activeForm: "验证中" })
|
||||||
|
TaskUpdate({ taskId: verifyId, owner: "tester", addBlockedBy: [devId] })
|
||||||
|
|
||||||
|
TaskCreate({ subject: "REVIEW-001: 代码审查", description: `审查实现\n\nSession: ${sessionFolder}\n设计: design/design-001.md`, activeForm: "审查中" })
|
||||||
|
TaskUpdate({ taskId: reviewId, owner: "reviewer", addBlockedBy: [devId] })
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Multi-Sprint Pipeline
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Sprint 1 — created dynamically, subsequent sprints created after Sprint N completes
|
||||||
|
// Each sprint: DESIGN → DEV-1..N(incremental) → VERIFY → DEV-fix → REVIEW
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Coordination Loop + GC Control + Ledger Updates
|
||||||
|
|
||||||
|
| Received Message | Action |
|
||||||
|
|-----------------|--------|
|
||||||
|
| architect: design_ready | Read design → update ledger → unblock DEV |
|
||||||
|
| developer: dev_complete | Update ledger → unblock VERIFY + REVIEW |
|
||||||
|
| tester: verify_passed | Update ledger (test_pass_rate) |
|
||||||
|
| tester: verify_failed | Create DEV-fix task |
|
||||||
|
| tester: fix_required | Create DEV-fix task → assign developer |
|
||||||
|
| reviewer: review_passed | Update ledger (review_score) → mark complete |
|
||||||
|
| reviewer: review_revision | **GC loop** → create DEV-fix → REVIEW-next |
|
||||||
|
| reviewer: review_critical | **GC loop** → create DEV-fix → REVIEW-next |
|
||||||
|
|
||||||
|
#### Generator-Critic Loop Control (developer↔reviewer)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
if (msgType === 'review_revision' || msgType === 'review_critical') {
|
||||||
|
const sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`))
|
||||||
|
const gcRound = sharedMemory.gc_round || 0
|
||||||
|
|
||||||
|
if (gcRound < sharedMemory.max_gc_rounds) {
|
||||||
|
sharedMemory.gc_round = gcRound + 1
|
||||||
|
Write(`${sessionFolder}/shared-memory.json`, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
// Create DEV-fix task
|
||||||
|
TaskCreate({
|
||||||
|
subject: `DEV-fix-${gcRound + 1}: 根据审查修订代码`,
|
||||||
|
description: `审查反馈:\n${reviewFeedback}\n\nSession: ${sessionFolder}\n审查: review/review-${reviewNum}.md`,
|
||||||
|
activeForm: "修订代码中"
|
||||||
|
})
|
||||||
|
TaskUpdate({ taskId: fixId, owner: "developer" })
|
||||||
|
|
||||||
|
// Create REVIEW-next task
|
||||||
|
TaskCreate({
|
||||||
|
subject: `REVIEW-${reviewNum + 1}: 验证修订`,
|
||||||
|
description: `验证 DEV-fix-${gcRound + 1} 的修订\n\nSession: ${sessionFolder}`,
|
||||||
|
activeForm: "复审中"
|
||||||
|
})
|
||||||
|
TaskUpdate({ taskId: nextReviewId, owner: "reviewer", addBlockedBy: [fixId] })
|
||||||
|
|
||||||
|
// Update ledger
|
||||||
|
updateLedger(sessionFolder, `DEV-fix-${gcRound + 1}`, { status: 'pending', gc_rounds: gcRound + 1 })
|
||||||
|
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log", team: teamName, from: "coordinator", to: "developer",
|
||||||
|
type: "gc_loop_trigger",
|
||||||
|
summary: `[coordinator] GC round ${gcRound + 1}/${sharedMemory.max_gc_rounds}: review requires revision`
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Max rounds — accept with warning
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log", team: teamName, from: "coordinator", to: "all",
|
||||||
|
type: "sprint_complete",
|
||||||
|
summary: `[coordinator] GC loop exhausted (${gcRound} rounds), accepting current state`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Sprint Retrospective + Persist
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const ledger = JSON.parse(Read(`${sessionFolder}/task-ledger.json`))
|
||||||
|
const sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`))
|
||||||
|
|
||||||
|
// Record sprint learning
|
||||||
|
const sprintRetro = {
|
||||||
|
sprint_id: ledger.sprint_id,
|
||||||
|
velocity: ledger.metrics.velocity,
|
||||||
|
gc_rounds: sharedMemory.gc_round,
|
||||||
|
what_worked: [], // extracted from review/verify feedback
|
||||||
|
what_failed: [], // extracted from failures
|
||||||
|
patterns_learned: [] // derived from GC loop patterns
|
||||||
|
}
|
||||||
|
sharedMemory.sprint_history.push(sprintRetro)
|
||||||
|
Write(`${sessionFolder}/shared-memory.json`, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
SendMessage({
|
||||||
|
content: `## [coordinator] Sprint 完成
|
||||||
|
|
||||||
|
**需求**: ${taskDescription}
|
||||||
|
**管道**: ${selectedPipeline}
|
||||||
|
**完成**: ${ledger.metrics.completed}/${ledger.metrics.total}
|
||||||
|
**GC 轮次**: ${sharedMemory.gc_round}
|
||||||
|
|
||||||
|
### 任务账本
|
||||||
|
${ledger.tasks.map(t => `- ${t.id}: ${t.status} ${t.review_score ? '(Review: ' + t.review_score + '/10)' : ''}`).join('\n')}`,
|
||||||
|
summary: `[coordinator] Sprint complete: ${ledger.metrics.completed}/${ledger.metrics.total}`
|
||||||
|
})
|
||||||
|
|
||||||
|
AskUserQuestion({
|
||||||
|
questions: [{
|
||||||
|
question: "Sprint 已完成。下一步:",
|
||||||
|
header: "Next",
|
||||||
|
multiSelect: false,
|
||||||
|
options: [
|
||||||
|
{ label: "下一个Sprint", description: "继续迭代(携带学习记忆)" },
|
||||||
|
{ label: "新需求", description: "新的开发需求" },
|
||||||
|
{ label: "关闭团队", description: "关闭所有 teammate" }
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| GC 循环超限 (3轮) | 接受当前代码,记录到 sprint_history |
|
||||||
|
| Velocity 低于 50% | 上报用户,建议缩小范围 |
|
||||||
|
| 任务账本损坏 | 从 TaskList 重建 |
|
||||||
|
| 设计被拒 3+ 次 | Coordinator 介入简化设计 |
|
||||||
|
| 测试持续失败 | 创建 DEV-fix 给 developer |
|
||||||
212
.claude/skills/team-iterdev/roles/developer.md
Normal file
212
.claude/skills/team-iterdev/roles/developer.md
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
# Role: developer
|
||||||
|
|
||||||
|
代码实现者。负责按设计方案编码、增量交付。作为 Generator-Critic 循环中的 Generator 角色(与 reviewer 配对)。
|
||||||
|
|
||||||
|
## Role Identity
|
||||||
|
|
||||||
|
- **Name**: `developer`
|
||||||
|
- **Task Prefix**: `DEV-*`
|
||||||
|
- **Responsibility**: Code generation (代码实现)
|
||||||
|
- **Communication**: SendMessage to coordinator only
|
||||||
|
- **Output Tag**: `[developer]`
|
||||||
|
|
||||||
|
## Role Boundaries
|
||||||
|
|
||||||
|
### MUST
|
||||||
|
|
||||||
|
- 仅处理 `DEV-*` 前缀的任务
|
||||||
|
- 所有输出必须带 `[developer]` 标识
|
||||||
|
- Phase 2 读取 shared-memory.json + design,Phase 5 写入 implementation_context
|
||||||
|
- 修订任务(DEV-fix-*)时参考 review 反馈
|
||||||
|
|
||||||
|
### MUST NOT
|
||||||
|
|
||||||
|
- ❌ 执行测试、代码审查或架构设计
|
||||||
|
- ❌ 直接与其他 worker 通信
|
||||||
|
- ❌ 为其他角色创建任务
|
||||||
|
|
||||||
|
## Message Types
|
||||||
|
|
||||||
|
| Type | Direction | Trigger | Description |
|
||||||
|
|------|-----------|---------|-------------|
|
||||||
|
| `dev_complete` | developer → coordinator | Implementation done | 实现完成 |
|
||||||
|
| `dev_progress` | developer → coordinator | Incremental progress | 进度更新 |
|
||||||
|
| `error` | developer → coordinator | Processing failure | 错误上报 |
|
||||||
|
|
||||||
|
## Execution (5-Phase)
|
||||||
|
|
||||||
|
### Phase 1: Task Discovery
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const tasks = TaskList()
|
||||||
|
const myTasks = tasks.filter(t =>
|
||||||
|
t.subject.startsWith('DEV-') && t.owner === 'developer' &&
|
||||||
|
t.status === 'pending' && t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
if (myTasks.length === 0) return
|
||||||
|
const task = TaskGet({ taskId: myTasks[0].id })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Context Loading
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const sessionMatch = task.description.match(/Session:\s*([^\n]+)/)
|
||||||
|
const sessionFolder = sessionMatch?.[1]?.trim()
|
||||||
|
|
||||||
|
const memoryPath = `${sessionFolder}/shared-memory.json`
|
||||||
|
let sharedMemory = {}
|
||||||
|
try { sharedMemory = JSON.parse(Read(memoryPath)) } catch {}
|
||||||
|
|
||||||
|
// Read design and breakdown
|
||||||
|
let design = null, breakdown = null
|
||||||
|
try {
|
||||||
|
design = Read(`${sessionFolder}/design/design-001.md`)
|
||||||
|
breakdown = JSON.parse(Read(`${sessionFolder}/design/task-breakdown.json`))
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
// Check if this is a fix task (GC loop)
|
||||||
|
const isFixTask = task.subject.includes('fix')
|
||||||
|
let reviewFeedback = null
|
||||||
|
if (isFixTask) {
|
||||||
|
const reviewFiles = Glob({ pattern: `${sessionFolder}/review/*.md` })
|
||||||
|
if (reviewFiles.length > 0) {
|
||||||
|
reviewFeedback = Read(reviewFiles[reviewFiles.length - 1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Previous implementation context from shared memory
|
||||||
|
const prevContext = sharedMemory.implementation_context || []
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Code Implementation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Determine complexity and delegation strategy
|
||||||
|
const taskCount = breakdown?.tasks?.length || 1
|
||||||
|
|
||||||
|
if (isFixTask) {
|
||||||
|
// === GC Fix Mode ===
|
||||||
|
// Focus on review feedback items
|
||||||
|
// Parse critical/high issues from review
|
||||||
|
// Fix each issue directly
|
||||||
|
|
||||||
|
Task({
|
||||||
|
subagent_type: "code-developer",
|
||||||
|
run_in_background: false,
|
||||||
|
description: "Fix review issues",
|
||||||
|
prompt: `Fix the following code review issues:
|
||||||
|
|
||||||
|
${reviewFeedback}
|
||||||
|
|
||||||
|
Focus on:
|
||||||
|
1. Critical issues (must fix)
|
||||||
|
2. High issues (should fix)
|
||||||
|
3. Medium issues (if time permits)
|
||||||
|
|
||||||
|
Do NOT change code that wasn't flagged.
|
||||||
|
Maintain existing code style and patterns.`
|
||||||
|
})
|
||||||
|
|
||||||
|
} else if (taskCount <= 2) {
|
||||||
|
// Direct implementation
|
||||||
|
for (const t of (breakdown?.tasks || [])) {
|
||||||
|
for (const file of (t.files || [])) {
|
||||||
|
try { Read(file) } catch {} // Read existing file
|
||||||
|
// Edit or Write as needed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Delegate to code-developer
|
||||||
|
Task({
|
||||||
|
subagent_type: "code-developer",
|
||||||
|
run_in_background: false,
|
||||||
|
description: `Implement ${taskCount} tasks`,
|
||||||
|
prompt: `## Design
|
||||||
|
${design}
|
||||||
|
|
||||||
|
## Task Breakdown
|
||||||
|
${JSON.stringify(breakdown, null, 2)}
|
||||||
|
|
||||||
|
${prevContext.length > 0 ? `## Previous Context\n${prevContext.map(c => `- ${c}`).join('\n')}` : ''}
|
||||||
|
|
||||||
|
Implement each task following the design. Complete tasks in the specified execution order.`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Self-Validation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Syntax check
|
||||||
|
const syntaxResult = Bash(`npx tsc --noEmit 2>&1 || python -m py_compile *.py 2>&1 || true`)
|
||||||
|
const hasSyntaxErrors = syntaxResult.includes('error')
|
||||||
|
|
||||||
|
// List changed files
|
||||||
|
const changedFiles = Bash(`git diff --name-only`).split('\n').filter(Boolean)
|
||||||
|
|
||||||
|
// Log implementation progress
|
||||||
|
const devLog = `# Dev Log — ${task.subject}
|
||||||
|
|
||||||
|
**Changed Files**: ${changedFiles.length}
|
||||||
|
**Syntax Clean**: ${!hasSyntaxErrors}
|
||||||
|
**Fix Task**: ${isFixTask}
|
||||||
|
|
||||||
|
## Files Changed
|
||||||
|
${changedFiles.map(f => `- ${f}`).join('\n')}
|
||||||
|
`
|
||||||
|
Write(`${sessionFolder}/code/dev-log.md`, devLog)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Report to Coordinator + Shared Memory Write
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
sharedMemory.implementation_context.push({
|
||||||
|
task: task.subject,
|
||||||
|
changed_files: changedFiles,
|
||||||
|
is_fix: isFixTask,
|
||||||
|
syntax_clean: !hasSyntaxErrors
|
||||||
|
})
|
||||||
|
Write(memoryPath, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log", team: teamName, from: "developer", to: "coordinator",
|
||||||
|
type: "dev_complete",
|
||||||
|
summary: `[developer] ${isFixTask ? 'Fix' : 'Implementation'} complete: ${changedFiles.length} files changed`,
|
||||||
|
ref: `${sessionFolder}/code/dev-log.md`
|
||||||
|
})
|
||||||
|
|
||||||
|
SendMessage({
|
||||||
|
type: "message", recipient: "coordinator",
|
||||||
|
content: `## [developer] ${isFixTask ? 'Fix' : 'Implementation'} Complete
|
||||||
|
|
||||||
|
**Task**: ${task.subject}
|
||||||
|
**Changed Files**: ${changedFiles.length}
|
||||||
|
**Syntax Clean**: ${!hasSyntaxErrors}
|
||||||
|
${isFixTask ? `**GC Round**: ${sharedMemory.gc_round}` : ''}
|
||||||
|
|
||||||
|
### Files
|
||||||
|
${changedFiles.slice(0, 10).map(f => `- ${f}`).join('\n')}`,
|
||||||
|
summary: `[developer] ${changedFiles.length} files ${isFixTask ? 'fixed' : 'implemented'}`
|
||||||
|
})
|
||||||
|
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||||
|
|
||||||
|
// Check for next task
|
||||||
|
const nextTasks = TaskList().filter(t =>
|
||||||
|
t.subject.startsWith('DEV-') && t.owner === 'developer' &&
|
||||||
|
t.status === 'pending' && t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
if (nextTasks.length > 0) { /* back to Phase 1 */ }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| No DEV-* tasks | Idle |
|
||||||
|
| Design not found | Implement based on task description |
|
||||||
|
| Syntax errors after implementation | Attempt auto-fix, report remaining errors |
|
||||||
|
| Review feedback unclear | Implement best interpretation, note in dev-log |
|
||||||
|
| Code-developer agent fails | Retry once, then implement inline |
|
||||||
206
.claude/skills/team-iterdev/roles/reviewer.md
Normal file
206
.claude/skills/team-iterdev/roles/reviewer.md
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
# Role: reviewer
|
||||||
|
|
||||||
|
代码审查者。负责多维度审查、质量评分、改进建议。作为 Generator-Critic 循环中的 Critic 角色(与 developer 配对)。
|
||||||
|
|
||||||
|
## Role Identity
|
||||||
|
|
||||||
|
- **Name**: `reviewer`
|
||||||
|
- **Task Prefix**: `REVIEW-*`
|
||||||
|
- **Responsibility**: Read-only analysis (代码审查)
|
||||||
|
- **Communication**: SendMessage to coordinator only
|
||||||
|
- **Output Tag**: `[reviewer]`
|
||||||
|
|
||||||
|
## Role Boundaries
|
||||||
|
|
||||||
|
### MUST
|
||||||
|
|
||||||
|
- 仅处理 `REVIEW-*` 前缀的任务
|
||||||
|
- 所有输出必须带 `[reviewer]` 标识
|
||||||
|
- Phase 2 读取 shared-memory.json + design,Phase 5 写入 review_feedback_trends
|
||||||
|
- 标记每个问题的严重度 (CRITICAL/HIGH/MEDIUM/LOW)
|
||||||
|
- 提供质量评分 (1-10)
|
||||||
|
|
||||||
|
### MUST NOT
|
||||||
|
|
||||||
|
- ❌ 编写实现代码、设计架构或执行测试
|
||||||
|
- ❌ 直接与其他 worker 通信
|
||||||
|
- ❌ 为其他角色创建任务
|
||||||
|
|
||||||
|
## Message Types
|
||||||
|
|
||||||
|
| Type | Direction | Trigger | Description |
|
||||||
|
|------|-----------|---------|-------------|
|
||||||
|
| `review_passed` | reviewer → coordinator | No critical issues, score >= 7 | 审查通过 |
|
||||||
|
| `review_revision` | reviewer → coordinator | Issues found, score < 7 | 需要修订 (触发GC) |
|
||||||
|
| `review_critical` | reviewer → coordinator | Critical issues found | 严重问题 (触发GC) |
|
||||||
|
| `error` | reviewer → coordinator | Processing failure | 错误上报 |
|
||||||
|
|
||||||
|
## Execution (5-Phase)
|
||||||
|
|
||||||
|
### Phase 1: Task Discovery
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const tasks = TaskList()
|
||||||
|
const myTasks = tasks.filter(t =>
|
||||||
|
t.subject.startsWith('REVIEW-') && t.owner === 'reviewer' &&
|
||||||
|
t.status === 'pending' && t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
if (myTasks.length === 0) return
|
||||||
|
const task = TaskGet({ taskId: myTasks[0].id })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Context Loading
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const sessionMatch = task.description.match(/Session:\s*([^\n]+)/)
|
||||||
|
const sessionFolder = sessionMatch?.[1]?.trim()
|
||||||
|
|
||||||
|
const memoryPath = `${sessionFolder}/shared-memory.json`
|
||||||
|
let sharedMemory = {}
|
||||||
|
try { sharedMemory = JSON.parse(Read(memoryPath)) } catch {}
|
||||||
|
|
||||||
|
// Read design for requirements alignment
|
||||||
|
let design = null
|
||||||
|
try { design = Read(`${sessionFolder}/design/design-001.md`) } catch {}
|
||||||
|
|
||||||
|
// Get changed files
|
||||||
|
const changedFiles = Bash(`git diff --name-only HEAD~1 2>/dev/null || git diff --name-only --cached`).split('\n').filter(Boolean)
|
||||||
|
|
||||||
|
// Read file contents
|
||||||
|
const fileContents = {}
|
||||||
|
for (const file of changedFiles.slice(0, 20)) {
|
||||||
|
try { fileContents[file] = Read(file) } catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Previous review trends
|
||||||
|
const prevTrends = sharedMemory.review_feedback_trends || []
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Multi-Dimensional Review
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Review dimensions:
|
||||||
|
// 1. Correctness — 逻辑正确性、边界处理
|
||||||
|
// 2. Completeness — 是否覆盖设计要求
|
||||||
|
// 3. Maintainability — 可读性、代码风格、DRY
|
||||||
|
// 4. Security — 安全漏洞、输入验证
|
||||||
|
|
||||||
|
// Optional: CLI-assisted review
|
||||||
|
Bash(`ccw cli -p "PURPOSE: Code review for correctness and security
|
||||||
|
TASK: Review changes in: ${changedFiles.join(', ')}
|
||||||
|
MODE: analysis
|
||||||
|
CONTEXT: @${changedFiles.join(' @')}
|
||||||
|
EXPECTED: Issues with severity (CRITICAL/HIGH/MEDIUM/LOW) and file:line
|
||||||
|
CONSTRAINTS: Focus on correctness and security" --tool gemini --mode analysis`, { run_in_background: true })
|
||||||
|
|
||||||
|
const reviewNum = task.subject.match(/REVIEW-(\d+)/)?.[1] || '001'
|
||||||
|
const outputPath = `${sessionFolder}/review/review-${reviewNum}.md`
|
||||||
|
|
||||||
|
// Scoring
|
||||||
|
const score = calculateScore(findings)
|
||||||
|
const criticalCount = findings.filter(f => f.severity === 'CRITICAL').length
|
||||||
|
const highCount = findings.filter(f => f.severity === 'HIGH').length
|
||||||
|
|
||||||
|
const reviewContent = `# Code Review — Round ${reviewNum}
|
||||||
|
|
||||||
|
**Files Reviewed**: ${changedFiles.length}
|
||||||
|
**Quality Score**: ${score}/10
|
||||||
|
**Critical Issues**: ${criticalCount}
|
||||||
|
**High Issues**: ${highCount}
|
||||||
|
|
||||||
|
## Findings
|
||||||
|
|
||||||
|
${findings.map((f, i) => `### ${i + 1}. [${f.severity}] ${f.title}
|
||||||
|
|
||||||
|
**File**: ${f.file}:${f.line}
|
||||||
|
**Dimension**: ${f.dimension}
|
||||||
|
**Description**: ${f.description}
|
||||||
|
**Suggestion**: ${f.suggestion}
|
||||||
|
`).join('\n')}
|
||||||
|
|
||||||
|
## Scoring Breakdown
|
||||||
|
|
||||||
|
| Dimension | Score | Notes |
|
||||||
|
|-----------|-------|-------|
|
||||||
|
| Correctness | ${scores.correctness}/10 | ${scores.correctnessNotes} |
|
||||||
|
| Completeness | ${scores.completeness}/10 | ${scores.completenessNotes} |
|
||||||
|
| Maintainability | ${scores.maintainability}/10 | ${scores.maintainabilityNotes} |
|
||||||
|
| Security | ${scores.security}/10 | ${scores.securityNotes} |
|
||||||
|
| **Overall** | **${score}/10** | |
|
||||||
|
|
||||||
|
## Signal
|
||||||
|
|
||||||
|
${criticalCount > 0 ? '**CRITICAL** — Critical issues must be fixed before merge'
|
||||||
|
: score < 7 ? '**REVISION_NEEDED** — Quality below threshold (7/10)'
|
||||||
|
: '**APPROVED** — Code meets quality standards'}
|
||||||
|
|
||||||
|
${design ? `## Design Alignment\n${designAlignmentNotes}` : ''}
|
||||||
|
`
|
||||||
|
|
||||||
|
Write(outputPath, reviewContent)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Trend Analysis
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Compare with previous reviews to detect trends
|
||||||
|
const currentIssueTypes = findings.map(f => f.dimension)
|
||||||
|
const trendNote = prevTrends.length > 0
|
||||||
|
? `Recurring: ${findRecurring(prevTrends, currentIssueTypes).join(', ')}`
|
||||||
|
: 'First review'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Report to Coordinator + Shared Memory Write
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
sharedMemory.review_feedback_trends.push({
|
||||||
|
review_id: `review-${reviewNum}`,
|
||||||
|
score: score,
|
||||||
|
critical: criticalCount,
|
||||||
|
high: highCount,
|
||||||
|
dimensions: findings.map(f => f.dimension),
|
||||||
|
gc_round: sharedMemory.gc_round || 0
|
||||||
|
})
|
||||||
|
Write(memoryPath, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
const msgType = criticalCount > 0 ? "review_critical"
|
||||||
|
: score < 7 ? "review_revision"
|
||||||
|
: "review_passed"
|
||||||
|
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log", team: teamName, from: "reviewer", to: "coordinator",
|
||||||
|
type: msgType,
|
||||||
|
summary: `[reviewer] Review ${msgType}: score=${score}/10, ${criticalCount}C/${highCount}H`,
|
||||||
|
ref: outputPath
|
||||||
|
})
|
||||||
|
|
||||||
|
SendMessage({
|
||||||
|
type: "message", recipient: "coordinator",
|
||||||
|
content: `## [reviewer] Code Review Results
|
||||||
|
|
||||||
|
**Task**: ${task.subject}
|
||||||
|
**Score**: ${score}/10
|
||||||
|
**Signal**: ${msgType.toUpperCase()}
|
||||||
|
**Critical**: ${criticalCount}, **High**: ${highCount}
|
||||||
|
**Output**: ${outputPath}
|
||||||
|
|
||||||
|
### Top Issues
|
||||||
|
${findings.filter(f => ['CRITICAL', 'HIGH'].includes(f.severity)).slice(0, 5).map(f =>
|
||||||
|
`- **[${f.severity}]** ${f.title} (${f.file}:${f.line})`
|
||||||
|
).join('\n')}`,
|
||||||
|
summary: `[reviewer] ${msgType}: ${score}/10`
|
||||||
|
})
|
||||||
|
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| No REVIEW-* tasks | Idle |
|
||||||
|
| No changed files | Review files referenced in design |
|
||||||
|
| CLI review fails | Fall back to inline analysis |
|
||||||
|
| All issues LOW | Score high, approve |
|
||||||
|
| Design not found | Review against general quality standards |
|
||||||
146
.claude/skills/team-iterdev/roles/tester.md
Normal file
146
.claude/skills/team-iterdev/roles/tester.md
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
# Role: tester
|
||||||
|
|
||||||
|
测试验证者。负责测试执行、修复循环、回归检测。
|
||||||
|
|
||||||
|
## Role Identity
|
||||||
|
|
||||||
|
- **Name**: `tester`
|
||||||
|
- **Task Prefix**: `VERIFY-*`
|
||||||
|
- **Responsibility**: Validation (测试验证)
|
||||||
|
- **Communication**: SendMessage to coordinator only
|
||||||
|
- **Output Tag**: `[tester]`
|
||||||
|
|
||||||
|
## Role Boundaries
|
||||||
|
|
||||||
|
### MUST
|
||||||
|
|
||||||
|
- 仅处理 `VERIFY-*` 前缀的任务
|
||||||
|
- 所有输出必须带 `[tester]` 标识
|
||||||
|
- Phase 2 读取 shared-memory.json,Phase 5 写入 test_patterns
|
||||||
|
|
||||||
|
### MUST NOT
|
||||||
|
|
||||||
|
- ❌ 编写实现代码、设计架构或代码审查
|
||||||
|
- ❌ 直接与其他 worker 通信
|
||||||
|
- ❌ 为其他角色创建任务
|
||||||
|
|
||||||
|
## Message Types
|
||||||
|
|
||||||
|
| Type | Direction | Trigger | Description |
|
||||||
|
|------|-----------|---------|-------------|
|
||||||
|
| `verify_passed` | tester → coordinator | All tests pass | 验证通过 |
|
||||||
|
| `verify_failed` | tester → coordinator | Tests fail | 验证失败 |
|
||||||
|
| `fix_required` | tester → coordinator | Issues found needing fix | 需要修复 |
|
||||||
|
| `error` | tester → coordinator | Environment failure | 错误上报 |
|
||||||
|
|
||||||
|
## Execution (5-Phase)
|
||||||
|
|
||||||
|
### Phase 1: Task Discovery
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const tasks = TaskList()
|
||||||
|
const myTasks = tasks.filter(t =>
|
||||||
|
t.subject.startsWith('VERIFY-') && t.owner === 'tester' &&
|
||||||
|
t.status === 'pending' && t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
if (myTasks.length === 0) return
|
||||||
|
const task = TaskGet({ taskId: myTasks[0].id })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Context Loading
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const sessionMatch = task.description.match(/Session:\s*([^\n]+)/)
|
||||||
|
const sessionFolder = sessionMatch?.[1]?.trim()
|
||||||
|
|
||||||
|
const memoryPath = `${sessionFolder}/shared-memory.json`
|
||||||
|
let sharedMemory = {}
|
||||||
|
try { sharedMemory = JSON.parse(Read(memoryPath)) } catch {}
|
||||||
|
|
||||||
|
// Detect test framework and test command
|
||||||
|
const testCommand = detectTestCommand()
|
||||||
|
const changedFiles = Bash(`git diff --name-only`).split('\n').filter(Boolean)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Test Execution + Fix Cycle
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
let iteration = 0
|
||||||
|
const MAX_ITERATIONS = 5
|
||||||
|
let lastResult = null
|
||||||
|
let passRate = 0
|
||||||
|
|
||||||
|
while (iteration < MAX_ITERATIONS) {
|
||||||
|
lastResult = Bash(`${testCommand} 2>&1 || true`)
|
||||||
|
passRate = parsePassRate(lastResult)
|
||||||
|
|
||||||
|
if (passRate >= 0.95) break
|
||||||
|
|
||||||
|
if (iteration < MAX_ITERATIONS - 1) {
|
||||||
|
// Delegate fix to code-developer
|
||||||
|
Task({
|
||||||
|
subagent_type: "code-developer",
|
||||||
|
run_in_background: false,
|
||||||
|
description: `Fix test failures (iteration ${iteration + 1})`,
|
||||||
|
prompt: `Test failures:\n${lastResult.substring(0, 3000)}\n\nFix failing tests. Changed files: ${changedFiles.join(', ')}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
iteration++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save verification results
|
||||||
|
const verifyNum = task.subject.match(/VERIFY-(\d+)/)?.[1] || '001'
|
||||||
|
const resultData = {
|
||||||
|
verify_id: `verify-${verifyNum}`,
|
||||||
|
pass_rate: passRate,
|
||||||
|
iterations: iteration,
|
||||||
|
passed: passRate >= 0.95,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
}
|
||||||
|
Write(`${sessionFolder}/verify/verify-${verifyNum}.json`, JSON.stringify(resultData, null, 2))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Regression Check
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Run full test suite for regression
|
||||||
|
const regressionResult = Bash(`${testCommand} --all 2>&1 || true`)
|
||||||
|
const regressionPassed = !regressionResult.includes('FAIL')
|
||||||
|
resultData.regression_passed = regressionPassed
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Report to Coordinator + Shared Memory Write
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
sharedMemory.test_patterns = sharedMemory.test_patterns || []
|
||||||
|
if (passRate >= 0.95) {
|
||||||
|
sharedMemory.test_patterns.push(`verify-${verifyNum}: passed in ${iteration} iterations`)
|
||||||
|
}
|
||||||
|
Write(memoryPath, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
const msgType = resultData.passed ? "verify_passed" : (iteration >= MAX_ITERATIONS ? "fix_required" : "verify_failed")
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log", team: teamName, from: "tester", to: "coordinator",
|
||||||
|
type: msgType,
|
||||||
|
summary: `[tester] ${msgType}: pass_rate=${(passRate*100).toFixed(1)}%, iterations=${iteration}`,
|
||||||
|
ref: `${sessionFolder}/verify/verify-${verifyNum}.json`
|
||||||
|
})
|
||||||
|
|
||||||
|
SendMessage({
|
||||||
|
type: "message", recipient: "coordinator",
|
||||||
|
content: `## [tester] Verification Results\n\n**Pass Rate**: ${(passRate*100).toFixed(1)}%\n**Iterations**: ${iteration}/${MAX_ITERATIONS}\n**Regression**: ${resultData.regression_passed ? '✅' : '❌'}\n**Status**: ${resultData.passed ? '✅ PASSED' : '❌ NEEDS FIX'}`,
|
||||||
|
summary: `[tester] ${resultData.passed ? 'PASSED' : 'FAILED'}: ${(passRate*100).toFixed(1)}%`
|
||||||
|
})
|
||||||
|
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| No VERIFY-* tasks | Idle |
|
||||||
|
| Test command not found | Try common commands (npm test, pytest, vitest) |
|
||||||
|
| Max iterations exceeded | Report fix_required to coordinator |
|
||||||
|
| Test environment broken | Report error, suggest manual fix |
|
||||||
94
.claude/skills/team-iterdev/specs/team-config.json
Normal file
94
.claude/skills/team-iterdev/specs/team-config.json
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
{
|
||||||
|
"team_name": "team-iterdev",
|
||||||
|
"team_display_name": "Team IterDev",
|
||||||
|
"description": "Iterative development team with Generator-Critic loop, task ledger, sprint learning, and dynamic pipeline",
|
||||||
|
"version": "1.0.0",
|
||||||
|
|
||||||
|
"roles": {
|
||||||
|
"coordinator": {
|
||||||
|
"task_prefix": null,
|
||||||
|
"responsibility": "Sprint planning, backlog management, task ledger maintenance, GC loop control",
|
||||||
|
"message_types": ["sprint_started", "gc_loop_trigger", "sprint_complete", "task_unblocked", "error", "shutdown"]
|
||||||
|
},
|
||||||
|
"architect": {
|
||||||
|
"task_prefix": "DESIGN",
|
||||||
|
"responsibility": "Technical design, task decomposition, architecture decisions",
|
||||||
|
"message_types": ["design_ready", "design_revision", "error"]
|
||||||
|
},
|
||||||
|
"developer": {
|
||||||
|
"task_prefix": "DEV",
|
||||||
|
"responsibility": "Code implementation, incremental delivery",
|
||||||
|
"message_types": ["dev_complete", "dev_progress", "error"]
|
||||||
|
},
|
||||||
|
"tester": {
|
||||||
|
"task_prefix": "VERIFY",
|
||||||
|
"responsibility": "Test execution, fix cycle, regression detection",
|
||||||
|
"message_types": ["verify_passed", "verify_failed", "fix_required", "error"]
|
||||||
|
},
|
||||||
|
"reviewer": {
|
||||||
|
"task_prefix": "REVIEW",
|
||||||
|
"responsibility": "Code review, quality scoring, improvement suggestions",
|
||||||
|
"message_types": ["review_passed", "review_revision", "review_critical", "error"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"pipelines": {
|
||||||
|
"patch": {
|
||||||
|
"description": "Simple fix: implement → verify",
|
||||||
|
"task_chain": ["DEV-001", "VERIFY-001"],
|
||||||
|
"gc_loops": 0
|
||||||
|
},
|
||||||
|
"sprint": {
|
||||||
|
"description": "Standard feature: design → implement → verify + review (parallel)",
|
||||||
|
"task_chain": ["DESIGN-001", "DEV-001", "VERIFY-001", "REVIEW-001"],
|
||||||
|
"gc_loops": 3,
|
||||||
|
"parallel_groups": [["VERIFY-001", "REVIEW-001"]]
|
||||||
|
},
|
||||||
|
"multi-sprint": {
|
||||||
|
"description": "Large feature: multiple sprint cycles with incremental delivery",
|
||||||
|
"task_chain": "dynamic — coordinator creates per-sprint chains",
|
||||||
|
"gc_loops": 3,
|
||||||
|
"sprint_count": "dynamic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"innovation_patterns": {
|
||||||
|
"generator_critic": {
|
||||||
|
"generator": "developer",
|
||||||
|
"critic": "reviewer",
|
||||||
|
"max_rounds": 3,
|
||||||
|
"convergence_trigger": "review.critical_count === 0 && review.score >= 7"
|
||||||
|
},
|
||||||
|
"task_ledger": {
|
||||||
|
"file": "task-ledger.json",
|
||||||
|
"updated_by": "coordinator",
|
||||||
|
"tracks": ["status", "gc_rounds", "review_score", "test_pass_rate", "velocity"]
|
||||||
|
},
|
||||||
|
"shared_memory": {
|
||||||
|
"file": "shared-memory.json",
|
||||||
|
"fields": {
|
||||||
|
"architect": "architecture_decisions",
|
||||||
|
"developer": "implementation_context",
|
||||||
|
"tester": "test_patterns",
|
||||||
|
"reviewer": "review_feedback_trends"
|
||||||
|
},
|
||||||
|
"persistent_fields": ["sprint_history", "what_worked", "what_failed", "patterns_learned"]
|
||||||
|
},
|
||||||
|
"dynamic_pipeline": {
|
||||||
|
"selector": "coordinator",
|
||||||
|
"criteria": "file_count + module_count + complexity_assessment",
|
||||||
|
"downgrade_rule": "velocity >= expected && review_avg >= 8 → simplify next sprint"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"collaboration_patterns": ["CP-1", "CP-3", "CP-5", "CP-6"],
|
||||||
|
|
||||||
|
"session_dirs": {
|
||||||
|
"base": ".workflow/.team/IDS-{slug}-{YYYY-MM-DD}/",
|
||||||
|
"design": "design/",
|
||||||
|
"code": "code/",
|
||||||
|
"verify": "verify/",
|
||||||
|
"review": "review/",
|
||||||
|
"messages": ".workflow/.team-msg/{team-name}/"
|
||||||
|
}
|
||||||
|
}
|
||||||
331
.claude/skills/team-testing/SKILL.md
Normal file
331
.claude/skills/team-testing/SKILL.md
Normal file
@@ -0,0 +1,331 @@
|
|||||||
|
---
|
||||||
|
name: team-testing
|
||||||
|
description: Unified team skill for testing team. All roles invoke this skill with --role arg for role-specific execution. Triggers on "team testing".
|
||||||
|
allowed-tools: TeamCreate(*), TeamDelete(*), SendMessage(*), TaskCreate(*), TaskUpdate(*), TaskList(*), TaskGet(*), Task(*), AskUserQuestion(*), Read(*), Write(*), Edit(*), Bash(*), Glob(*), Grep(*)
|
||||||
|
---
|
||||||
|
|
||||||
|
# Team Testing
|
||||||
|
|
||||||
|
测试团队技能。通过 Generator-Critic 循环(generator↔executor)、共享记忆(缺陷模式追踪)和动态层级选择,实现渐进式测试覆盖。所有团队成员通过 `--role=xxx` 路由到角色执行逻辑。
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────────────────────────────────┐
|
||||||
|
│ Skill(skill="team-testing", args="--role=xxx") │
|
||||||
|
└───────────────────┬──────────────────────────────┘
|
||||||
|
│ Role Router
|
||||||
|
┌───────────┬───┼───────────┬───────────┐
|
||||||
|
↓ ↓ ↓ ↓ ↓
|
||||||
|
┌──────────┐┌──────────┐┌─────────┐┌────────┐┌────────┐
|
||||||
|
│coordinator││strategist││generator││executor││analyst │
|
||||||
|
│ roles/ ││ roles/ ││ roles/ ││ roles/ ││ roles/ │
|
||||||
|
└──────────┘└──────────┘└─────────┘└────────┘└────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Role Router
|
||||||
|
|
||||||
|
### Input Parsing
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const args = "$ARGUMENTS"
|
||||||
|
const roleMatch = args.match(/--role[=\s]+(\w+)/)
|
||||||
|
|
||||||
|
if (!roleMatch) {
|
||||||
|
throw new Error("Missing --role argument. Available roles: coordinator, strategist, generator, executor, analyst")
|
||||||
|
}
|
||||||
|
|
||||||
|
const role = roleMatch[1]
|
||||||
|
const teamName = args.match(/--team[=\s]+([\w-]+)/)?.[1] || "testing"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Role Dispatch
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const VALID_ROLES = {
|
||||||
|
"coordinator": { file: "roles/coordinator.md", prefix: null },
|
||||||
|
"strategist": { file: "roles/strategist.md", prefix: "STRATEGY" },
|
||||||
|
"generator": { file: "roles/generator.md", prefix: "TESTGEN" },
|
||||||
|
"executor": { file: "roles/executor.md", prefix: "TESTRUN" },
|
||||||
|
"analyst": { file: "roles/analyst.md", prefix: "TESTANA" }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!VALID_ROLES[role]) {
|
||||||
|
throw new Error(`Unknown role: ${role}. Available: ${Object.keys(VALID_ROLES).join(', ')}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
Read(VALID_ROLES[role].file)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Available Roles
|
||||||
|
|
||||||
|
| Role | Task Prefix | Responsibility | Role File |
|
||||||
|
|------|-------------|----------------|-----------|
|
||||||
|
| `coordinator` | N/A | 变更范围分析、层级选择、质量门控 | [roles/coordinator.md](roles/coordinator.md) |
|
||||||
|
| `strategist` | STRATEGY-* | 分析 git diff、确定测试层级、定义覆盖率目标 | [roles/strategist.md](roles/strategist.md) |
|
||||||
|
| `generator` | TESTGEN-* | 按层级生成测试用例(单元/集成/E2E) | [roles/generator.md](roles/generator.md) |
|
||||||
|
| `executor` | TESTRUN-* | 执行测试、收集覆盖率、自动修复 | [roles/executor.md](roles/executor.md) |
|
||||||
|
| `analyst` | TESTANA-* | 缺陷模式分析、覆盖率差距、质量报告 | [roles/analyst.md](roles/analyst.md) |
|
||||||
|
|
||||||
|
## Shared Infrastructure
|
||||||
|
|
||||||
|
### Role Isolation Rules
|
||||||
|
|
||||||
|
**核心原则**: 每个角色仅能执行自己职责范围内的工作。
|
||||||
|
|
||||||
|
#### Output Tagging(强制)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
SendMessage({ content: `## [${role}] ...`, summary: `[${role}] ...` })
|
||||||
|
mcp__ccw-tools__team_msg({ summary: `[${role}] ...` })
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Coordinator 隔离
|
||||||
|
|
||||||
|
| 允许 | 禁止 |
|
||||||
|
|------|------|
|
||||||
|
| 变更范围分析 | ❌ 直接编写测试 |
|
||||||
|
| 创建任务链 (TaskCreate) | ❌ 直接执行测试 |
|
||||||
|
| 质量门控判断 | ❌ 直接分析覆盖率 |
|
||||||
|
| 监控进度 (消息总线) | ❌ 绕过 worker 自行完成 |
|
||||||
|
|
||||||
|
#### Worker 隔离
|
||||||
|
|
||||||
|
| 允许 | 禁止 |
|
||||||
|
|------|------|
|
||||||
|
| 处理自己前缀的任务 | ❌ 处理其他角色前缀的任务 |
|
||||||
|
| 读写 shared-memory.json (自己的字段) | ❌ 为其他角色创建任务 |
|
||||||
|
| SendMessage 给 coordinator | ❌ 直接与其他 worker 通信 |
|
||||||
|
|
||||||
|
### Team Configuration
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const TEAM_CONFIG = {
|
||||||
|
name: "testing",
|
||||||
|
sessionDir: ".workflow/.team/TST-{slug}-{date}/",
|
||||||
|
msgDir: ".workflow/.team-msg/testing/",
|
||||||
|
sharedMemory: "shared-memory.json",
|
||||||
|
testLayers: {
|
||||||
|
L1: { name: "Unit Tests", coverage_target: 80 },
|
||||||
|
L2: { name: "Integration Tests", coverage_target: 60 },
|
||||||
|
L3: { name: "E2E Tests", coverage_target: 40 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Shared Memory (创新模式)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Phase 2: 读取共享记忆
|
||||||
|
const memoryPath = `${sessionFolder}/shared-memory.json`
|
||||||
|
let sharedMemory = {}
|
||||||
|
try { sharedMemory = JSON.parse(Read(memoryPath)) } catch {}
|
||||||
|
|
||||||
|
// Phase 5: 写入共享记忆(仅更新自己负责的字段)
|
||||||
|
// strategist → sharedMemory.test_strategy
|
||||||
|
// generator → sharedMemory.generated_tests
|
||||||
|
// executor → sharedMemory.execution_results + defect_patterns
|
||||||
|
// analyst → sharedMemory.analysis_report + coverage_history
|
||||||
|
Write(memoryPath, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Message Bus (All Roles)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log",
|
||||||
|
team: teamName,
|
||||||
|
from: role,
|
||||||
|
to: "coordinator",
|
||||||
|
type: "<type>",
|
||||||
|
summary: `[${role}] <summary>`,
|
||||||
|
ref: "<file_path>"
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
| Role | Types |
|
||||||
|
|------|-------|
|
||||||
|
| coordinator | `pipeline_selected`, `gc_loop_trigger`, `quality_gate`, `task_unblocked`, `error`, `shutdown` |
|
||||||
|
| strategist | `strategy_ready`, `error` |
|
||||||
|
| generator | `tests_generated`, `tests_revised`, `error` |
|
||||||
|
| executor | `tests_passed`, `tests_failed`, `coverage_report`, `error` |
|
||||||
|
| analyst | `analysis_ready`, `error` |
|
||||||
|
|
||||||
|
### CLI Fallback
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Bash(`ccw team log --team "${teamName}" --from "${role}" --to "coordinator" --type "<type>" --summary "<summary>" --json`)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task Lifecycle (All Worker Roles)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const tasks = TaskList()
|
||||||
|
const myTasks = tasks.filter(t =>
|
||||||
|
t.subject.startsWith(`${VALID_ROLES[role].prefix}-`) &&
|
||||||
|
t.owner === role &&
|
||||||
|
t.status === 'pending' &&
|
||||||
|
t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
if (myTasks.length === 0) return
|
||||||
|
const task = TaskGet({ taskId: myTasks[0].id })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||||
|
|
||||||
|
// Phase 2-4: Role-specific
|
||||||
|
// Phase 5: Report + Loop
|
||||||
|
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: role, to: "coordinator", type: "...", summary: `[${role}] ...` })
|
||||||
|
SendMessage({ type: "message", recipient: "coordinator", content: `## [${role}] ...`, summary: `[${role}] ...` })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||||
|
```
|
||||||
|
|
||||||
|
## Three-Pipeline Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
Targeted (小范围变更):
|
||||||
|
STRATEGY-001 → TESTGEN-001(L1 unit) → TESTRUN-001
|
||||||
|
|
||||||
|
Standard (渐进式):
|
||||||
|
STRATEGY-001 → TESTGEN-001(L1) → TESTRUN-001(L1) → TESTGEN-002(L2) → TESTRUN-002(L2) → TESTANA-001
|
||||||
|
|
||||||
|
Comprehensive (全覆盖):
|
||||||
|
STRATEGY-001 → [TESTGEN-001(L1) + TESTGEN-002(L2)](parallel) → [TESTRUN-001(L1) + TESTRUN-002(L2)](parallel) → TESTGEN-003(L3) → TESTRUN-003(L3) → TESTANA-001
|
||||||
|
```
|
||||||
|
|
||||||
|
### Generator-Critic Loop
|
||||||
|
|
||||||
|
generator ↔ executor 循环(覆盖率不达标时修订测试):
|
||||||
|
|
||||||
|
```
|
||||||
|
TESTGEN → TESTRUN → (if coverage < target) → TESTGEN-fix → TESTRUN-2
|
||||||
|
(if coverage >= target) → next layer or TESTANA
|
||||||
|
```
|
||||||
|
|
||||||
|
## Unified Session Directory
|
||||||
|
|
||||||
|
```
|
||||||
|
.workflow/.team/TST-{slug}-{YYYY-MM-DD}/
|
||||||
|
├── team-session.json
|
||||||
|
├── shared-memory.json # 缺陷模式 / 有效测试模式 / 覆盖率历史
|
||||||
|
├── strategy/ # Strategist output
|
||||||
|
│ └── test-strategy.md
|
||||||
|
├── tests/ # Generator output
|
||||||
|
│ ├── L1-unit/
|
||||||
|
│ ├── L2-integration/
|
||||||
|
│ └── L3-e2e/
|
||||||
|
├── results/ # Executor output
|
||||||
|
│ ├── run-001.json
|
||||||
|
│ └── coverage-001.json
|
||||||
|
└── analysis/ # Analyst output
|
||||||
|
└── quality-report.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Coordinator Spawn Template
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
TeamCreate({ team_name: teamName })
|
||||||
|
|
||||||
|
// Strategist
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
team_name: teamName,
|
||||||
|
name: "strategist",
|
||||||
|
prompt: `你是 team "${teamName}" 的 STRATEGIST。
|
||||||
|
当你收到 STRATEGY-* 任务时,调用 Skill(skill="team-testing", args="--role=strategist") 执行。
|
||||||
|
当前需求: ${taskDescription}
|
||||||
|
约束: ${constraints}
|
||||||
|
|
||||||
|
## 角色准则(强制)
|
||||||
|
- 你只能处理 STRATEGY-* 前缀的任务
|
||||||
|
- 所有输出必须带 [strategist] 标识前缀
|
||||||
|
- 仅与 coordinator 通信
|
||||||
|
|
||||||
|
## 消息总线(必须)
|
||||||
|
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||||
|
|
||||||
|
工作流程:
|
||||||
|
1. TaskList → 找到 STRATEGY-* 任务
|
||||||
|
2. Skill(skill="team-testing", args="--role=strategist") 执行
|
||||||
|
3. team_msg log + SendMessage 结果给 coordinator
|
||||||
|
4. TaskUpdate completed → 检查下一个任务`
|
||||||
|
})
|
||||||
|
|
||||||
|
// Generator
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
team_name: teamName,
|
||||||
|
name: "generator",
|
||||||
|
prompt: `你是 team "${teamName}" 的 GENERATOR。
|
||||||
|
当你收到 TESTGEN-* 任务时,调用 Skill(skill="team-testing", args="--role=generator") 执行。
|
||||||
|
当前需求: ${taskDescription}
|
||||||
|
|
||||||
|
## 角色准则(强制)
|
||||||
|
- 你只能处理 TESTGEN-* 前缀的任务
|
||||||
|
- 所有输出必须带 [generator] 标识前缀
|
||||||
|
|
||||||
|
## 消息总线(必须)
|
||||||
|
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||||
|
|
||||||
|
工作流程:
|
||||||
|
1. TaskList → 找到 TESTGEN-* 任务
|
||||||
|
2. Skill(skill="team-testing", args="--role=generator") 执行
|
||||||
|
3. team_msg log + SendMessage
|
||||||
|
4. TaskUpdate completed → 检查下一个任务`
|
||||||
|
})
|
||||||
|
|
||||||
|
// Executor
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
team_name: teamName,
|
||||||
|
name: "executor",
|
||||||
|
prompt: `你是 team "${teamName}" 的 EXECUTOR。
|
||||||
|
当你收到 TESTRUN-* 任务时,调用 Skill(skill="team-testing", args="--role=executor") 执行。
|
||||||
|
当前需求: ${taskDescription}
|
||||||
|
|
||||||
|
## 角色准则(强制)
|
||||||
|
- 你只能处理 TESTRUN-* 前缀的任务
|
||||||
|
- 所有输出必须带 [executor] 标识前缀
|
||||||
|
|
||||||
|
## 消息总线(必须)
|
||||||
|
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||||
|
|
||||||
|
工作流程:
|
||||||
|
1. TaskList → 找到 TESTRUN-* 任务
|
||||||
|
2. Skill(skill="team-testing", args="--role=executor") 执行
|
||||||
|
3. team_msg log + SendMessage
|
||||||
|
4. TaskUpdate completed → 检查下一个任务`
|
||||||
|
})
|
||||||
|
|
||||||
|
// Analyst
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
team_name: teamName,
|
||||||
|
name: "analyst",
|
||||||
|
prompt: `你是 team "${teamName}" 的 ANALYST。
|
||||||
|
当你收到 TESTANA-* 任务时,调用 Skill(skill="team-testing", args="--role=analyst") 执行。
|
||||||
|
当前需求: ${taskDescription}
|
||||||
|
|
||||||
|
## 角色准则(强制)
|
||||||
|
- 你只能处理 TESTANA-* 前缀的任务
|
||||||
|
- 所有输出必须带 [analyst] 标识前缀
|
||||||
|
|
||||||
|
## 消息总线(必须)
|
||||||
|
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||||
|
|
||||||
|
工作流程:
|
||||||
|
1. TaskList → 找到 TESTANA-* 任务
|
||||||
|
2. Skill(skill="team-testing", args="--role=analyst") 执行
|
||||||
|
3. team_msg log + SendMessage
|
||||||
|
4. TaskUpdate completed → 检查下一个任务`
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| Unknown --role value | Error with available role list |
|
||||||
|
| Missing --role arg | Error with usage hint |
|
||||||
|
| Role file not found | Error with expected path |
|
||||||
|
| Task prefix conflict | Log warning, proceed |
|
||||||
|
| Coverage never reaches target | After 3 GC loops, accept current coverage with warning |
|
||||||
|
| Test environment broken | Notify user, suggest manual fix |
|
||||||
228
.claude/skills/team-testing/roles/analyst.md
Normal file
228
.claude/skills/team-testing/roles/analyst.md
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
# Role: analyst
|
||||||
|
|
||||||
|
测试质量分析师。负责缺陷模式分析、覆盖率差距识别、质量报告生成。
|
||||||
|
|
||||||
|
## Role Identity
|
||||||
|
|
||||||
|
- **Name**: `analyst`
|
||||||
|
- **Task Prefix**: `TESTANA-*`
|
||||||
|
- **Responsibility**: Read-only analysis (质量分析)
|
||||||
|
- **Communication**: SendMessage to coordinator only
|
||||||
|
- **Output Tag**: `[analyst]`
|
||||||
|
|
||||||
|
## Role Boundaries
|
||||||
|
|
||||||
|
### MUST
|
||||||
|
|
||||||
|
- 仅处理 `TESTANA-*` 前缀的任务
|
||||||
|
- 所有输出必须带 `[analyst]` 标识
|
||||||
|
- Phase 2 读取 shared-memory.json (所有历史数据),Phase 5 写入 analysis_report
|
||||||
|
|
||||||
|
### MUST NOT
|
||||||
|
|
||||||
|
- ❌ 生成测试、执行测试或制定策略
|
||||||
|
- ❌ 直接与其他 worker 通信
|
||||||
|
- ❌ 为其他角色创建任务
|
||||||
|
|
||||||
|
## Message Types
|
||||||
|
|
||||||
|
| Type | Direction | Trigger | Description |
|
||||||
|
|------|-----------|---------|-------------|
|
||||||
|
| `analysis_ready` | analyst → coordinator | Analysis completed | 分析报告完成 |
|
||||||
|
| `error` | analyst → coordinator | Processing failure | 错误上报 |
|
||||||
|
|
||||||
|
## Execution (5-Phase)
|
||||||
|
|
||||||
|
### Phase 1: Task Discovery
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const tasks = TaskList()
|
||||||
|
const myTasks = tasks.filter(t =>
|
||||||
|
t.subject.startsWith('TESTANA-') &&
|
||||||
|
t.owner === 'analyst' &&
|
||||||
|
t.status === 'pending' &&
|
||||||
|
t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
if (myTasks.length === 0) return
|
||||||
|
const task = TaskGet({ taskId: myTasks[0].id })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Context Loading + Shared Memory Read
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const sessionMatch = task.description.match(/Session:\s*([^\n]+)/)
|
||||||
|
const sessionFolder = sessionMatch?.[1]?.trim()
|
||||||
|
|
||||||
|
const memoryPath = `${sessionFolder}/shared-memory.json`
|
||||||
|
let sharedMemory = {}
|
||||||
|
try { sharedMemory = JSON.parse(Read(memoryPath)) } catch {}
|
||||||
|
|
||||||
|
// Read all execution results
|
||||||
|
const resultFiles = Glob({ pattern: `${sessionFolder}/results/run-*.json` })
|
||||||
|
const results = resultFiles.map(f => {
|
||||||
|
try { return JSON.parse(Read(f)) } catch { return null }
|
||||||
|
}).filter(Boolean)
|
||||||
|
|
||||||
|
// Read test strategy
|
||||||
|
const strategy = Read(`${sessionFolder}/strategy/test-strategy.md`)
|
||||||
|
|
||||||
|
// Read test files for pattern analysis
|
||||||
|
const testFiles = Glob({ pattern: `${sessionFolder}/tests/**/*` })
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Quality Analysis
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const outputPath = `${sessionFolder}/analysis/quality-report.md`
|
||||||
|
|
||||||
|
// 1. Coverage Analysis
|
||||||
|
const coverageHistory = sharedMemory.coverage_history || []
|
||||||
|
const layerCoverage = {}
|
||||||
|
coverageHistory.forEach(c => {
|
||||||
|
if (!layerCoverage[c.layer] || c.timestamp > layerCoverage[c.layer].timestamp) {
|
||||||
|
layerCoverage[c.layer] = c
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 2. Defect Pattern Analysis
|
||||||
|
const defectPatterns = sharedMemory.defect_patterns || []
|
||||||
|
const patternFrequency = {}
|
||||||
|
defectPatterns.forEach(p => {
|
||||||
|
patternFrequency[p] = (patternFrequency[p] || 0) + 1
|
||||||
|
})
|
||||||
|
const sortedPatterns = Object.entries(patternFrequency)
|
||||||
|
.sort(([,a], [,b]) => b - a)
|
||||||
|
|
||||||
|
// 3. GC Loop Effectiveness
|
||||||
|
const gcRounds = sharedMemory.gc_round || 0
|
||||||
|
const gcEffectiveness = coverageHistory.length >= 2
|
||||||
|
? coverageHistory[coverageHistory.length - 1].coverage - coverageHistory[0].coverage
|
||||||
|
: 0
|
||||||
|
|
||||||
|
// 4. Test Quality Metrics
|
||||||
|
const totalTests = sharedMemory.generated_tests?.length || 0
|
||||||
|
const effectivePatterns = sharedMemory.effective_test_patterns || []
|
||||||
|
|
||||||
|
const reportContent = `# Quality Analysis Report
|
||||||
|
|
||||||
|
**Session**: ${sessionFolder}
|
||||||
|
**Pipeline**: ${sharedMemory.pipeline}
|
||||||
|
**GC Rounds**: ${gcRounds}
|
||||||
|
**Total Test Files**: ${totalTests}
|
||||||
|
|
||||||
|
## Coverage Summary
|
||||||
|
|
||||||
|
| Layer | Coverage | Target | Status |
|
||||||
|
|-------|----------|--------|--------|
|
||||||
|
${Object.entries(layerCoverage).map(([layer, data]) =>
|
||||||
|
`| ${layer} | ${data.coverage}% | ${data.target}% | ${data.coverage >= data.target ? '✅ Met' : '❌ Below'} |`
|
||||||
|
).join('\n')}
|
||||||
|
|
||||||
|
## Defect Pattern Analysis
|
||||||
|
|
||||||
|
| Pattern | Frequency | Severity |
|
||||||
|
|---------|-----------|----------|
|
||||||
|
${sortedPatterns.map(([pattern, freq]) =>
|
||||||
|
`| ${pattern} | ${freq} | ${freq >= 3 ? 'HIGH' : freq >= 2 ? 'MEDIUM' : 'LOW'} |`
|
||||||
|
).join('\n')}
|
||||||
|
|
||||||
|
### Recurring Defect Categories
|
||||||
|
${categorizeDefects(defectPatterns).map(cat =>
|
||||||
|
`- **${cat.name}**: ${cat.count} occurrences — ${cat.recommendation}`
|
||||||
|
).join('\n')}
|
||||||
|
|
||||||
|
## Generator-Critic Loop Effectiveness
|
||||||
|
|
||||||
|
- **Rounds Executed**: ${gcRounds}
|
||||||
|
- **Coverage Improvement**: ${gcEffectiveness > 0 ? '+' : ''}${gcEffectiveness.toFixed(1)}%
|
||||||
|
- **Effectiveness**: ${gcEffectiveness > 10 ? 'HIGH' : gcEffectiveness > 5 ? 'MEDIUM' : 'LOW'}
|
||||||
|
- **Recommendation**: ${gcRounds > 2 && gcEffectiveness < 5 ? 'Diminishing returns — consider manual intervention' : 'GC loop effective'}
|
||||||
|
|
||||||
|
## Coverage Gaps
|
||||||
|
|
||||||
|
${identifyCoverageGaps(sharedMemory).map(gap =>
|
||||||
|
`### ${gap.area}\n- **Current**: ${gap.current}%\n- **Gap**: ${gap.gap}%\n- **Reason**: ${gap.reason}\n- **Recommendation**: ${gap.recommendation}\n`
|
||||||
|
).join('\n')}
|
||||||
|
|
||||||
|
## Effective Test Patterns
|
||||||
|
|
||||||
|
${effectivePatterns.map(p => `- ${p}`).join('\n')}
|
||||||
|
|
||||||
|
## Recommendations
|
||||||
|
|
||||||
|
### Immediate Actions
|
||||||
|
${immediateActions.map((a, i) => `${i + 1}. ${a}`).join('\n')}
|
||||||
|
|
||||||
|
### Long-term Improvements
|
||||||
|
${longTermActions.map((a, i) => `${i + 1}. ${a}`).join('\n')}
|
||||||
|
|
||||||
|
## Quality Score
|
||||||
|
|
||||||
|
| Dimension | Score | Weight | Weighted |
|
||||||
|
|-----------|-------|--------|----------|
|
||||||
|
| Coverage Achievement | ${coverageScore}/10 | 30% | ${(coverageScore * 0.3).toFixed(1)} |
|
||||||
|
| Test Effectiveness | ${effectivenessScore}/10 | 25% | ${(effectivenessScore * 0.25).toFixed(1)} |
|
||||||
|
| Defect Detection | ${defectScore}/10 | 25% | ${(defectScore * 0.25).toFixed(1)} |
|
||||||
|
| GC Loop Efficiency | ${gcScore}/10 | 20% | ${(gcScore * 0.2).toFixed(1)} |
|
||||||
|
| **Total** | | | **${totalScore.toFixed(1)}/10** |
|
||||||
|
`
|
||||||
|
|
||||||
|
Write(outputPath, reportContent)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Trend Analysis (if historical data available)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Compare with previous sessions if available
|
||||||
|
const previousSessions = Glob({ pattern: '.workflow/.team/TST-*/shared-memory.json' })
|
||||||
|
if (previousSessions.length > 1) {
|
||||||
|
// Track coverage trends, defect pattern evolution
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Report to Coordinator + Shared Memory Write
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
sharedMemory.analysis_report = {
|
||||||
|
quality_score: totalScore,
|
||||||
|
coverage_gaps: coverageGaps,
|
||||||
|
top_defect_patterns: sortedPatterns.slice(0, 5),
|
||||||
|
gc_effectiveness: gcEffectiveness,
|
||||||
|
recommendations: immediateActions
|
||||||
|
}
|
||||||
|
Write(memoryPath, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log", team: teamName, from: "analyst", to: "coordinator",
|
||||||
|
type: "analysis_ready",
|
||||||
|
summary: `[analyst] Quality report: score ${totalScore.toFixed(1)}/10, ${sortedPatterns.length} defect patterns, ${coverageGaps.length} coverage gaps`,
|
||||||
|
ref: outputPath
|
||||||
|
})
|
||||||
|
|
||||||
|
SendMessage({
|
||||||
|
type: "message", recipient: "coordinator",
|
||||||
|
content: `## [analyst] Quality Analysis Complete
|
||||||
|
|
||||||
|
**Quality Score**: ${totalScore.toFixed(1)}/10
|
||||||
|
**Defect Patterns**: ${sortedPatterns.length}
|
||||||
|
**Coverage Gaps**: ${coverageGaps.length}
|
||||||
|
**GC Effectiveness**: ${gcEffectiveness > 0 ? '+' : ''}${gcEffectiveness.toFixed(1)}%
|
||||||
|
**Output**: ${outputPath}
|
||||||
|
|
||||||
|
### Top Issues
|
||||||
|
${immediateActions.slice(0, 3).map((a, i) => `${i + 1}. ${a}`).join('\n')}`,
|
||||||
|
summary: `[analyst] Quality: ${totalScore.toFixed(1)}/10`
|
||||||
|
})
|
||||||
|
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| No TESTANA-* tasks | Idle |
|
||||||
|
| No execution results | Generate report based on strategy only |
|
||||||
|
| Incomplete data | Report available metrics, flag gaps |
|
||||||
|
| Previous session data corrupted | Analyze current session only |
|
||||||
284
.claude/skills/team-testing/roles/coordinator.md
Normal file
284
.claude/skills/team-testing/roles/coordinator.md
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
# Role: coordinator
|
||||||
|
|
||||||
|
测试团队协调者。负责变更范围分析、测试层级选择、Generator-Critic 循环控制(generator↔executor)和质量门控。
|
||||||
|
|
||||||
|
## Role Identity
|
||||||
|
|
||||||
|
- **Name**: `coordinator`
|
||||||
|
- **Task Prefix**: N/A (coordinator creates tasks, doesn't receive them)
|
||||||
|
- **Responsibility**: Orchestration
|
||||||
|
- **Communication**: SendMessage to all teammates
|
||||||
|
- **Output Tag**: `[coordinator]`
|
||||||
|
|
||||||
|
## Role Boundaries
|
||||||
|
|
||||||
|
### MUST
|
||||||
|
|
||||||
|
- 所有输出必须带 `[coordinator]` 标识
|
||||||
|
- 仅负责变更分析、任务创建/分发、质量门控、结果汇报
|
||||||
|
- 管理 Generator-Critic 循环计数(generator↔executor)
|
||||||
|
- 根据覆盖率结果决定是否触发修订循环
|
||||||
|
|
||||||
|
### MUST NOT
|
||||||
|
|
||||||
|
- ❌ **直接编写测试、执行测试或分析覆盖率**
|
||||||
|
- ❌ 直接调用实现类 subagent
|
||||||
|
- ❌ 直接修改测试文件或源代码
|
||||||
|
- ❌ 绕过 worker 角色自行完成应委派的工作
|
||||||
|
|
||||||
|
## Message Types
|
||||||
|
|
||||||
|
| Type | Direction | Trigger | Description |
|
||||||
|
|------|-----------|---------|-------------|
|
||||||
|
| `pipeline_selected` | coordinator → all | Pipeline decided | 通知选定管道模式 |
|
||||||
|
| `gc_loop_trigger` | coordinator → generator | Coverage < target | 触发 generator 修订测试 |
|
||||||
|
| `quality_gate` | coordinator → all | Quality assessment | 质量门控结果 |
|
||||||
|
| `task_unblocked` | coordinator → any | Dependency resolved | 通知 worker 可用任务 |
|
||||||
|
| `error` | coordinator → all | Critical error | 上报用户 |
|
||||||
|
| `shutdown` | coordinator → all | Team dissolving | 关闭信号 |
|
||||||
|
|
||||||
|
## Execution
|
||||||
|
|
||||||
|
### Phase 1: Change Scope Analysis
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const args = "$ARGUMENTS"
|
||||||
|
const teamName = args.match(/--team-name[=\s]+([\w-]+)/)?.[1] || `testing-${Date.now().toString(36)}`
|
||||||
|
const taskDescription = args.replace(/--team-name[=\s]+[\w-]+/, '').replace(/--role[=\s]+\w+/, '').trim()
|
||||||
|
|
||||||
|
// Analyze change scope
|
||||||
|
const changedFiles = Bash(`git diff --name-only HEAD~1 2>/dev/null || git diff --name-only --cached`).split('\n').filter(Boolean)
|
||||||
|
const changedModules = new Set(changedFiles.map(f => f.split('/').slice(0, 2).join('/')))
|
||||||
|
|
||||||
|
function selectPipeline(fileCount, moduleCount) {
|
||||||
|
if (fileCount <= 3 && moduleCount <= 1) return 'targeted'
|
||||||
|
if (fileCount <= 10 && moduleCount <= 3) return 'standard'
|
||||||
|
return 'comprehensive'
|
||||||
|
}
|
||||||
|
|
||||||
|
const suggestedPipeline = selectPipeline(changedFiles.length, changedModules.size)
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
AskUserQuestion({
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
question: `检测到 ${changedFiles.length} 个变更文件,${changedModules.size} 个模块。选择测试模式:`,
|
||||||
|
header: "Mode",
|
||||||
|
multiSelect: false,
|
||||||
|
options: [
|
||||||
|
{ label: suggestedPipeline === 'targeted' ? "targeted (推荐)" : "targeted", description: "目标模式:策略→生成L1→执行(小范围变更)" },
|
||||||
|
{ label: suggestedPipeline === 'standard' ? "standard (推荐)" : "standard", description: "标准模式:L1→L2 渐进式(含分析)" },
|
||||||
|
{ label: suggestedPipeline === 'comprehensive' ? "comprehensive (推荐)" : "comprehensive", description: "全覆盖:并行L1+L2→L3(含分析)" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "覆盖率目标:",
|
||||||
|
header: "Coverage",
|
||||||
|
multiSelect: false,
|
||||||
|
options: [
|
||||||
|
{ label: "标准", description: "L1:80% L2:60% L3:40%" },
|
||||||
|
{ label: "严格", description: "L1:90% L2:75% L3:60%" },
|
||||||
|
{ label: "最低", description: "L1:60% L2:40% L3:20%" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Create Team + Spawn Workers
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
TeamCreate({ team_name: teamName })
|
||||||
|
|
||||||
|
const topicSlug = taskDescription.toLowerCase().replace(/[^a-z0-9]+/g, '-').substring(0, 40)
|
||||||
|
const dateStr = new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString().substring(0, 10)
|
||||||
|
const sessionId = `TST-${topicSlug}-${dateStr}`
|
||||||
|
const sessionFolder = `.workflow/.team/${sessionId}`
|
||||||
|
|
||||||
|
Bash(`mkdir -p "${sessionFolder}/strategy" "${sessionFolder}/tests/L1-unit" "${sessionFolder}/tests/L2-integration" "${sessionFolder}/tests/L3-e2e" "${sessionFolder}/results" "${sessionFolder}/analysis"`)
|
||||||
|
|
||||||
|
// Initialize shared memory
|
||||||
|
const sharedMemory = {
|
||||||
|
task: taskDescription,
|
||||||
|
pipeline: selectedPipeline,
|
||||||
|
changed_files: changedFiles,
|
||||||
|
changed_modules: [...changedModules],
|
||||||
|
coverage_targets: coverageTargets,
|
||||||
|
gc_round: 0,
|
||||||
|
max_gc_rounds: 3,
|
||||||
|
test_strategy: null,
|
||||||
|
generated_tests: [],
|
||||||
|
execution_results: [],
|
||||||
|
defect_patterns: [],
|
||||||
|
effective_test_patterns: [],
|
||||||
|
coverage_history: []
|
||||||
|
}
|
||||||
|
Write(`${sessionFolder}/shared-memory.json`, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
const teamSession = {
|
||||||
|
session_id: sessionId,
|
||||||
|
team_name: teamName,
|
||||||
|
task: taskDescription,
|
||||||
|
pipeline: selectedPipeline,
|
||||||
|
status: "active",
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
|
gc_round: 0,
|
||||||
|
completed_tasks: []
|
||||||
|
}
|
||||||
|
Write(`${sessionFolder}/team-session.json`, JSON.stringify(teamSession, null, 2))
|
||||||
|
```
|
||||||
|
|
||||||
|
Spawn workers (see SKILL.md Coordinator Spawn Template).
|
||||||
|
|
||||||
|
### Phase 3: Create Task Chain
|
||||||
|
|
||||||
|
#### Targeted Pipeline
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
TaskCreate({ subject: "STRATEGY-001: 变更范围分析与测试策略", description: `分析变更: ${changedFiles.join(', ')}\n\nSession: ${sessionFolder}\n输出: ${sessionFolder}/strategy/test-strategy.md\n\n确定测试层级、覆盖目标、优先级`, activeForm: "制定策略中" })
|
||||||
|
TaskUpdate({ taskId: strategyId, owner: "strategist" })
|
||||||
|
|
||||||
|
TaskCreate({ subject: "TESTGEN-001: 生成 L1 单元测试", description: `基于策略生成单元测试\n\nSession: ${sessionFolder}\n层级: L1-unit\n输入: strategy/test-strategy.md\n输出: tests/L1-unit/\n覆盖率目标: ${coverageTargets.L1}%`, activeForm: "生成测试中" })
|
||||||
|
TaskUpdate({ taskId: genId, owner: "generator", addBlockedBy: [strategyId] })
|
||||||
|
|
||||||
|
TaskCreate({ subject: "TESTRUN-001: 执行 L1 单元测试", description: `执行生成的单元测试\n\nSession: ${sessionFolder}\n输入: tests/L1-unit/\n输出: results/run-001.json + coverage-001.json\n覆盖率目标: ${coverageTargets.L1}%`, activeForm: "执行测试中" })
|
||||||
|
TaskUpdate({ taskId: runId, owner: "executor", addBlockedBy: [genId] })
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Standard Pipeline
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// STRATEGY-001 → TESTGEN-001(L1) → TESTRUN-001(L1) → TESTGEN-002(L2) → TESTRUN-002(L2) → TESTANA-001
|
||||||
|
|
||||||
|
// ... STRATEGY-001, TESTGEN-001, TESTRUN-001 same as targeted ...
|
||||||
|
|
||||||
|
TaskCreate({ subject: "TESTGEN-002: 生成 L2 集成测试", description: `基于 L1 结果生成集成测试\n\nSession: ${sessionFolder}\n层级: L2-integration\n输入: strategy/ + results/run-001.json\n输出: tests/L2-integration/\n覆盖率目标: ${coverageTargets.L2}%`, activeForm: "生成集成测试中" })
|
||||||
|
TaskUpdate({ taskId: gen2Id, owner: "generator", addBlockedBy: [run1Id] })
|
||||||
|
|
||||||
|
TaskCreate({ subject: "TESTRUN-002: 执行 L2 集成测试", description: `执行集成测试\n\nSession: ${sessionFolder}\n输入: tests/L2-integration/\n输出: results/run-002.json`, activeForm: "执行集成测试中" })
|
||||||
|
TaskUpdate({ taskId: run2Id, owner: "executor", addBlockedBy: [gen2Id] })
|
||||||
|
|
||||||
|
TaskCreate({ subject: "TESTANA-001: 质量分析报告", description: `分析所有测试结果\n\nSession: ${sessionFolder}\n输入: results/ + shared-memory.json\n输出: analysis/quality-report.md\n\n分析: 缺陷模式、覆盖率差距、测试有效性`, activeForm: "分析中" })
|
||||||
|
TaskUpdate({ taskId: anaId, owner: "analyst", addBlockedBy: [run2Id] })
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Comprehensive Pipeline
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// STRATEGY-001 → [TESTGEN-001(L1) + TESTGEN-002(L2)] → [TESTRUN-001 + TESTRUN-002] → TESTGEN-003(L3) → TESTRUN-003 → TESTANA-001
|
||||||
|
|
||||||
|
// TESTGEN-001 and TESTGEN-002 are parallel (both blockedBy STRATEGY-001)
|
||||||
|
// TESTRUN-001 and TESTRUN-002 are parallel (blockedBy their respective TESTGEN)
|
||||||
|
// TESTGEN-003(L3) blockedBy both TESTRUN-001 and TESTRUN-002
|
||||||
|
// TESTRUN-003 blockedBy TESTGEN-003
|
||||||
|
// TESTANA-001 blockedBy TESTRUN-003
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Coordination Loop + Generator-Critic Control
|
||||||
|
|
||||||
|
| Received Message | Action |
|
||||||
|
|-----------------|--------|
|
||||||
|
| strategist: strategy_ready | Read strategy → team_msg log → TaskUpdate completed |
|
||||||
|
| generator: tests_generated | team_msg log → TaskUpdate completed → unblock TESTRUN |
|
||||||
|
| executor: tests_passed | Read coverage → **质量门控** → proceed to next layer |
|
||||||
|
| executor: tests_failed | **Generator-Critic 判断** → 决定是否触发修订 |
|
||||||
|
| executor: coverage_report | Read coverage data → update shared memory |
|
||||||
|
| analyst: analysis_ready | Read report → team_msg log → Phase 5 |
|
||||||
|
|
||||||
|
#### Generator-Critic Loop Control
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
if (msgType === 'tests_failed' || msgType === 'coverage_report') {
|
||||||
|
const result = JSON.parse(Read(`${sessionFolder}/results/run-${runNum}.json`))
|
||||||
|
const sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`))
|
||||||
|
|
||||||
|
const passRate = result.pass_rate || 0
|
||||||
|
const coverage = result.coverage || 0
|
||||||
|
const target = coverageTargets[currentLayer]
|
||||||
|
const gcRound = sharedMemory.gc_round || 0
|
||||||
|
|
||||||
|
if ((passRate < 0.95 || coverage < target) && gcRound < sharedMemory.max_gc_rounds) {
|
||||||
|
// Trigger generator revision
|
||||||
|
sharedMemory.gc_round = gcRound + 1
|
||||||
|
Write(`${sessionFolder}/shared-memory.json`, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
// Create TESTGEN-fix task
|
||||||
|
TaskCreate({
|
||||||
|
subject: `TESTGEN-fix-${gcRound + 1}: 修订 ${currentLayer} 测试`,
|
||||||
|
description: `基于执行结果修订测试\n\nSession: ${sessionFolder}\n失败原因: ${result.failure_summary}\n覆盖率: ${coverage}% (目标: ${target}%)\n通过率: ${(passRate * 100).toFixed(1)}%`,
|
||||||
|
activeForm: "修订测试中"
|
||||||
|
})
|
||||||
|
TaskUpdate({ taskId: fixGenId, owner: "generator" })
|
||||||
|
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log", team: teamName, from: "coordinator", to: "generator",
|
||||||
|
type: "gc_loop_trigger",
|
||||||
|
summary: `[coordinator] GC round ${gcRound + 1}: coverage ${coverage}% < target ${target}%, revise tests`
|
||||||
|
})
|
||||||
|
} else if (gcRound >= sharedMemory.max_gc_rounds) {
|
||||||
|
// Max rounds exceeded — accept current coverage
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log", team: teamName, from: "coordinator", to: "all",
|
||||||
|
type: "quality_gate",
|
||||||
|
summary: `[coordinator] GC loop exhausted (${gcRound} rounds), accepting coverage ${coverage}%`
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// Coverage met — proceed
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log", team: teamName, from: "coordinator", to: "all",
|
||||||
|
type: "quality_gate",
|
||||||
|
summary: `[coordinator] ${currentLayer} coverage ${coverage}% >= target ${target}%, proceeding`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Report + Persist
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`))
|
||||||
|
const analysisReport = Read(`${sessionFolder}/analysis/quality-report.md`)
|
||||||
|
|
||||||
|
SendMessage({
|
||||||
|
content: `## [coordinator] 测试完成
|
||||||
|
|
||||||
|
**任务**: ${taskDescription}
|
||||||
|
**管道**: ${selectedPipeline}
|
||||||
|
**GC 轮次**: ${sharedMemory.gc_round}
|
||||||
|
**变更文件**: ${changedFiles.length}
|
||||||
|
|
||||||
|
### 覆盖率
|
||||||
|
${sharedMemory.coverage_history.map(c => `- **${c.layer}**: ${c.coverage}% (目标: ${c.target}%)`).join('\n')}
|
||||||
|
|
||||||
|
### 质量报告
|
||||||
|
${analysisReport}`,
|
||||||
|
summary: `[coordinator] Testing complete: ${sharedMemory.gc_round} GC rounds`
|
||||||
|
})
|
||||||
|
|
||||||
|
updateSession(sessionFolder, { status: 'completed', completed_at: new Date().toISOString() })
|
||||||
|
|
||||||
|
AskUserQuestion({
|
||||||
|
questions: [{
|
||||||
|
question: "测试已完成。下一步:",
|
||||||
|
header: "Next",
|
||||||
|
multiSelect: false,
|
||||||
|
options: [
|
||||||
|
{ label: "新测试", description: "对新变更运行测试" },
|
||||||
|
{ label: "深化测试", description: "增加测试层级或提高覆盖率" },
|
||||||
|
{ label: "关闭团队", description: "关闭所有 teammate 并清理" }
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| Teammate 无响应 | 发追踪消息,2次 → 重新 spawn |
|
||||||
|
| GC 循环超限 (3轮) | 接受当前覆盖率,记录到 shared memory |
|
||||||
|
| 测试环境异常 | 上报用户,建议手动修复 |
|
||||||
|
| 所有测试失败 | 检查测试框架配置,通知 analyst 分析 |
|
||||||
|
| 覆盖率工具不可用 | 降级为通过率判断 |
|
||||||
204
.claude/skills/team-testing/roles/executor.md
Normal file
204
.claude/skills/team-testing/roles/executor.md
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
# Role: executor
|
||||||
|
|
||||||
|
测试执行者。执行测试、收集覆盖率、尝试自动修复失败。作为 Generator-Critic 循环中的 Critic 角色。
|
||||||
|
|
||||||
|
## Role Identity
|
||||||
|
|
||||||
|
- **Name**: `executor`
|
||||||
|
- **Task Prefix**: `TESTRUN-*`
|
||||||
|
- **Responsibility**: Validation (测试执行与验证)
|
||||||
|
- **Communication**: SendMessage to coordinator only
|
||||||
|
- **Output Tag**: `[executor]`
|
||||||
|
|
||||||
|
## Role Boundaries
|
||||||
|
|
||||||
|
### MUST
|
||||||
|
|
||||||
|
- 仅处理 `TESTRUN-*` 前缀的任务
|
||||||
|
- 所有输出必须带 `[executor]` 标识
|
||||||
|
- Phase 2 读取 shared-memory.json,Phase 5 写入 execution_results + defect_patterns
|
||||||
|
- 报告覆盖率和通过率供 coordinator 做 GC 判断
|
||||||
|
|
||||||
|
### MUST NOT
|
||||||
|
|
||||||
|
- ❌ 生成新测试、制定策略或分析趋势
|
||||||
|
- ❌ 直接与其他 worker 通信
|
||||||
|
- ❌ 为其他角色创建任务
|
||||||
|
|
||||||
|
## Message Types
|
||||||
|
|
||||||
|
| Type | Direction | Trigger | Description |
|
||||||
|
|------|-----------|---------|-------------|
|
||||||
|
| `tests_passed` | executor → coordinator | All tests pass + coverage met | 测试通过 |
|
||||||
|
| `tests_failed` | executor → coordinator | Tests fail or coverage below target | 测试失败/覆盖不足 |
|
||||||
|
| `coverage_report` | executor → coordinator | Coverage data collected | 覆盖率数据 |
|
||||||
|
| `error` | executor → coordinator | Execution environment failure | 错误上报 |
|
||||||
|
|
||||||
|
## Execution (5-Phase)
|
||||||
|
|
||||||
|
### Phase 1: Task Discovery
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const tasks = TaskList()
|
||||||
|
const myTasks = tasks.filter(t =>
|
||||||
|
t.subject.startsWith('TESTRUN-') &&
|
||||||
|
t.owner === 'executor' &&
|
||||||
|
t.status === 'pending' &&
|
||||||
|
t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
if (myTasks.length === 0) return
|
||||||
|
const task = TaskGet({ taskId: myTasks[0].id })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Context Loading + Shared Memory Read
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const sessionMatch = task.description.match(/Session:\s*([^\n]+)/)
|
||||||
|
const sessionFolder = sessionMatch?.[1]?.trim()
|
||||||
|
|
||||||
|
const memoryPath = `${sessionFolder}/shared-memory.json`
|
||||||
|
let sharedMemory = {}
|
||||||
|
try { sharedMemory = JSON.parse(Read(memoryPath)) } catch {}
|
||||||
|
|
||||||
|
const framework = sharedMemory.test_strategy?.framework || 'Jest'
|
||||||
|
const coverageTarget = parseInt(task.description.match(/覆盖率目标:\s*(\d+)/)?.[1] || '80')
|
||||||
|
|
||||||
|
// Find test files to execute
|
||||||
|
const testDir = task.description.match(/输入:\s*([^\n]+)/)?.[1]?.trim()
|
||||||
|
const testFiles = Glob({ pattern: `${sessionFolder}/${testDir || 'tests'}/**/*` })
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Test Execution + Fix Cycle
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Determine test command based on framework
|
||||||
|
const testCommands = {
|
||||||
|
'Jest': `npx jest --coverage --json --outputFile=${sessionFolder}/results/jest-output.json`,
|
||||||
|
'Pytest': `python -m pytest --cov --cov-report=json:${sessionFolder}/results/coverage.json -v`,
|
||||||
|
'Vitest': `npx vitest run --coverage --reporter=json`
|
||||||
|
}
|
||||||
|
const testCommand = testCommands[framework] || testCommands['Jest']
|
||||||
|
|
||||||
|
// Execute tests with auto-fix cycle (max 3 iterations)
|
||||||
|
let iteration = 0
|
||||||
|
const MAX_FIX_ITERATIONS = 3
|
||||||
|
let lastResult = null
|
||||||
|
let passRate = 0
|
||||||
|
let coverage = 0
|
||||||
|
|
||||||
|
while (iteration < MAX_FIX_ITERATIONS) {
|
||||||
|
lastResult = Bash(`${testCommand} 2>&1 || true`)
|
||||||
|
|
||||||
|
// Parse results
|
||||||
|
const passed = !lastResult.includes('FAIL') && !lastResult.includes('FAILED')
|
||||||
|
passRate = parsePassRate(lastResult)
|
||||||
|
coverage = parseCoverage(lastResult)
|
||||||
|
|
||||||
|
if (passed && coverage >= coverageTarget) break
|
||||||
|
|
||||||
|
if (iteration < MAX_FIX_ITERATIONS - 1 && !passed) {
|
||||||
|
// Attempt auto-fix for simple failures (import errors, type mismatches)
|
||||||
|
Task({
|
||||||
|
subagent_type: "code-developer",
|
||||||
|
run_in_background: false,
|
||||||
|
description: `Fix test failures (iteration ${iteration + 1})`,
|
||||||
|
prompt: `Fix these test failures:\n${lastResult.substring(0, 3000)}\n\nOnly fix the test files, not the source code.`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
iteration++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save results
|
||||||
|
const runNum = task.subject.match(/TESTRUN-(\d+)/)?.[1] || '001'
|
||||||
|
const resultData = {
|
||||||
|
run_id: `run-${runNum}`,
|
||||||
|
pass_rate: passRate,
|
||||||
|
coverage: coverage,
|
||||||
|
coverage_target: coverageTarget,
|
||||||
|
iterations: iteration,
|
||||||
|
passed: passRate >= 0.95 && coverage >= coverageTarget,
|
||||||
|
failure_summary: passRate < 0.95 ? extractFailures(lastResult) : null,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
}
|
||||||
|
|
||||||
|
Write(`${sessionFolder}/results/run-${runNum}.json`, JSON.stringify(resultData, null, 2))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Defect Pattern Extraction
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Extract defect patterns from failures
|
||||||
|
if (resultData.failure_summary) {
|
||||||
|
const newPatterns = extractDefectPatterns(lastResult)
|
||||||
|
// Common patterns: null reference, async timing, import errors, type mismatches
|
||||||
|
resultData.defect_patterns = newPatterns
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record effective test patterns (from passing tests)
|
||||||
|
if (passRate > 0.8) {
|
||||||
|
const effectivePatterns = extractEffectivePatterns(testFiles)
|
||||||
|
resultData.effective_patterns = effectivePatterns
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Report to Coordinator + Shared Memory Write
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Update shared memory
|
||||||
|
sharedMemory.execution_results.push(resultData)
|
||||||
|
if (resultData.defect_patterns) {
|
||||||
|
sharedMemory.defect_patterns = [
|
||||||
|
...sharedMemory.defect_patterns,
|
||||||
|
...resultData.defect_patterns
|
||||||
|
]
|
||||||
|
}
|
||||||
|
if (resultData.effective_patterns) {
|
||||||
|
sharedMemory.effective_test_patterns = [
|
||||||
|
...new Set([...sharedMemory.effective_test_patterns, ...resultData.effective_patterns])
|
||||||
|
]
|
||||||
|
}
|
||||||
|
sharedMemory.coverage_history.push({
|
||||||
|
layer: testDir,
|
||||||
|
coverage: coverage,
|
||||||
|
target: coverageTarget,
|
||||||
|
pass_rate: passRate,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
})
|
||||||
|
Write(memoryPath, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
const msgType = resultData.passed ? "tests_passed" : "tests_failed"
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log", team: teamName, from: "executor", to: "coordinator",
|
||||||
|
type: msgType,
|
||||||
|
summary: `[executor] ${msgType}: pass=${(passRate*100).toFixed(1)}%, coverage=${coverage}% (target: ${coverageTarget}%), iterations=${iteration}`,
|
||||||
|
ref: `${sessionFolder}/results/run-${runNum}.json`
|
||||||
|
})
|
||||||
|
|
||||||
|
SendMessage({
|
||||||
|
type: "message", recipient: "coordinator",
|
||||||
|
content: `## [executor] Test Execution Results
|
||||||
|
|
||||||
|
**Task**: ${task.subject}
|
||||||
|
**Pass Rate**: ${(passRate * 100).toFixed(1)}%
|
||||||
|
**Coverage**: ${coverage}% (target: ${coverageTarget}%)
|
||||||
|
**Fix Iterations**: ${iteration}/${MAX_FIX_ITERATIONS}
|
||||||
|
**Status**: ${resultData.passed ? '✅ PASSED' : '❌ NEEDS REVISION'}
|
||||||
|
|
||||||
|
${resultData.defect_patterns ? `### Defect Patterns\n${resultData.defect_patterns.map(p => `- ${p}`).join('\n')}` : ''}`,
|
||||||
|
summary: `[executor] ${resultData.passed ? 'PASSED' : 'FAILED'}: ${coverage}% coverage`
|
||||||
|
})
|
||||||
|
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| No TESTRUN-* tasks | Idle |
|
||||||
|
| Test command fails to start | Check framework installation, notify coordinator |
|
||||||
|
| Coverage tool unavailable | Report pass rate only |
|
||||||
|
| All tests timeout | Increase timeout, retry once |
|
||||||
|
| Auto-fix makes tests worse | Revert, report original failures |
|
||||||
192
.claude/skills/team-testing/roles/generator.md
Normal file
192
.claude/skills/team-testing/roles/generator.md
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
# Role: generator
|
||||||
|
|
||||||
|
测试用例生成者。按层级(L1单元/L2集成/L3 E2E)生成测试代码。作为 Generator-Critic 循环中的 Generator 角色。
|
||||||
|
|
||||||
|
## Role Identity
|
||||||
|
|
||||||
|
- **Name**: `generator`
|
||||||
|
- **Task Prefix**: `TESTGEN-*`
|
||||||
|
- **Responsibility**: Code generation (测试代码生成)
|
||||||
|
- **Communication**: SendMessage to coordinator only
|
||||||
|
- **Output Tag**: `[generator]`
|
||||||
|
|
||||||
|
## Role Boundaries
|
||||||
|
|
||||||
|
### MUST
|
||||||
|
|
||||||
|
- 仅处理 `TESTGEN-*` 前缀的任务
|
||||||
|
- 所有输出必须带 `[generator]` 标识
|
||||||
|
- Phase 2 读取 shared-memory.json + test strategy,Phase 5 写入 generated_tests
|
||||||
|
- 生成可直接执行的测试代码
|
||||||
|
|
||||||
|
### MUST NOT
|
||||||
|
|
||||||
|
- ❌ 执行测试、分析覆盖率或制定策略
|
||||||
|
- ❌ 直接与其他 worker 通信
|
||||||
|
- ❌ 为其他角色创建任务
|
||||||
|
- ❌ 修改源代码(仅生成测试代码)
|
||||||
|
|
||||||
|
## Message Types
|
||||||
|
|
||||||
|
| Type | Direction | Trigger | Description |
|
||||||
|
|------|-----------|---------|-------------|
|
||||||
|
| `tests_generated` | generator → coordinator | Tests created | 测试生成完成 |
|
||||||
|
| `tests_revised` | generator → coordinator | Tests revised after failure | 修订测试完成 (GC 循环) |
|
||||||
|
| `error` | generator → coordinator | Processing failure | 错误上报 |
|
||||||
|
|
||||||
|
## Execution (5-Phase)
|
||||||
|
|
||||||
|
### Phase 1: Task Discovery
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const tasks = TaskList()
|
||||||
|
const myTasks = tasks.filter(t =>
|
||||||
|
t.subject.startsWith('TESTGEN-') &&
|
||||||
|
t.owner === 'generator' &&
|
||||||
|
t.status === 'pending' &&
|
||||||
|
t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
if (myTasks.length === 0) return
|
||||||
|
const task = TaskGet({ taskId: myTasks[0].id })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Context Loading + Shared Memory Read
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const sessionMatch = task.description.match(/Session:\s*([^\n]+)/)
|
||||||
|
const sessionFolder = sessionMatch?.[1]?.trim()
|
||||||
|
const layerMatch = task.description.match(/层级:\s*(\S+)/)
|
||||||
|
const layer = layerMatch?.[1] || 'L1-unit'
|
||||||
|
|
||||||
|
const memoryPath = `${sessionFolder}/shared-memory.json`
|
||||||
|
let sharedMemory = {}
|
||||||
|
try { sharedMemory = JSON.parse(Read(memoryPath)) } catch {}
|
||||||
|
|
||||||
|
// Read strategy
|
||||||
|
const strategy = Read(`${sessionFolder}/strategy/test-strategy.md`)
|
||||||
|
|
||||||
|
// Read source files to test
|
||||||
|
const targetFiles = sharedMemory.test_strategy?.priority_files || sharedMemory.changed_files || []
|
||||||
|
const sourceContents = {}
|
||||||
|
for (const file of targetFiles.slice(0, 20)) {
|
||||||
|
try { sourceContents[file] = Read(file) } catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is a revision (GC loop)
|
||||||
|
const isRevision = task.subject.includes('fix') || task.subject.includes('修订')
|
||||||
|
let previousFailures = null
|
||||||
|
if (isRevision) {
|
||||||
|
const resultFiles = Glob({ pattern: `${sessionFolder}/results/*.json` })
|
||||||
|
if (resultFiles.length > 0) {
|
||||||
|
try { previousFailures = JSON.parse(Read(resultFiles[resultFiles.length - 1])) } catch {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read existing test patterns from shared memory
|
||||||
|
const effectivePatterns = sharedMemory.effective_test_patterns || []
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Test Generation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const framework = sharedMemory.test_strategy?.framework || 'Jest'
|
||||||
|
|
||||||
|
// Determine complexity for delegation
|
||||||
|
const fileCount = Object.keys(sourceContents).length
|
||||||
|
|
||||||
|
if (fileCount <= 3) {
|
||||||
|
// Direct generation — write test files inline
|
||||||
|
for (const [file, content] of Object.entries(sourceContents)) {
|
||||||
|
const testPath = generateTestPath(file, layer)
|
||||||
|
const testCode = generateTestCode(file, content, layer, framework, {
|
||||||
|
isRevision,
|
||||||
|
previousFailures,
|
||||||
|
effectivePatterns
|
||||||
|
})
|
||||||
|
Write(testPath, testCode)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Delegate to code-developer for batch generation
|
||||||
|
Task({
|
||||||
|
subagent_type: "code-developer",
|
||||||
|
run_in_background: false,
|
||||||
|
description: `Generate ${layer} tests`,
|
||||||
|
prompt: `Generate ${layer} tests using ${framework} for the following files:
|
||||||
|
|
||||||
|
${Object.entries(sourceContents).map(([f, c]) => `### ${f}\n\`\`\`\n${c.substring(0, 2000)}\n\`\`\``).join('\n\n')}
|
||||||
|
|
||||||
|
${isRevision ? `\n## Previous Failures\n${JSON.stringify(previousFailures?.failures?.slice(0, 10), null, 2)}` : ''}
|
||||||
|
|
||||||
|
${effectivePatterns.length > 0 ? `\n## Effective Patterns (from previous rounds)\n${effectivePatterns.map(p => `- ${p}`).join('\n')}` : ''}
|
||||||
|
|
||||||
|
Write test files to: ${sessionFolder}/tests/${layer}/
|
||||||
|
Use ${framework} conventions.
|
||||||
|
Each test file should cover: happy path, edge cases, error handling.`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const generatedTestFiles = Glob({ pattern: `${sessionFolder}/tests/${layer}/**/*` })
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Self-Validation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Verify generated tests are syntactically valid
|
||||||
|
const syntaxCheck = Bash(`cd "${sessionFolder}" && npx tsc --noEmit tests/${layer}/**/*.ts 2>&1 || true`)
|
||||||
|
const hasSyntaxErrors = syntaxCheck.includes('error TS')
|
||||||
|
|
||||||
|
if (hasSyntaxErrors) {
|
||||||
|
// Attempt auto-fix for common issues (imports, types)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify minimum test count
|
||||||
|
const testFileCount = generatedTestFiles.length
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Report to Coordinator + Shared Memory Write
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
sharedMemory.generated_tests = [
|
||||||
|
...sharedMemory.generated_tests,
|
||||||
|
...generatedTestFiles.map(f => ({
|
||||||
|
file: f,
|
||||||
|
layer: layer,
|
||||||
|
round: isRevision ? sharedMemory.gc_round : 0,
|
||||||
|
revised: isRevision
|
||||||
|
}))
|
||||||
|
]
|
||||||
|
Write(memoryPath, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
const msgType = isRevision ? "tests_revised" : "tests_generated"
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log", team: teamName, from: "generator", to: "coordinator",
|
||||||
|
type: msgType,
|
||||||
|
summary: `[generator] ${isRevision ? 'Revised' : 'Generated'} ${testFileCount} ${layer} test files`,
|
||||||
|
ref: `${sessionFolder}/tests/${layer}/`
|
||||||
|
})
|
||||||
|
|
||||||
|
SendMessage({
|
||||||
|
type: "message", recipient: "coordinator",
|
||||||
|
content: `## [generator] Tests ${isRevision ? 'Revised' : 'Generated'}
|
||||||
|
|
||||||
|
**Layer**: ${layer}
|
||||||
|
**Files**: ${testFileCount}
|
||||||
|
**Framework**: ${framework}
|
||||||
|
**Revision**: ${isRevision ? 'Yes (GC round ' + sharedMemory.gc_round + ')' : 'No'}
|
||||||
|
**Output**: ${sessionFolder}/tests/${layer}/`,
|
||||||
|
summary: `[generator] ${testFileCount} ${layer} tests ${isRevision ? 'revised' : 'generated'}`
|
||||||
|
})
|
||||||
|
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| No TESTGEN-* tasks | Idle |
|
||||||
|
| Source file not found | Skip, notify coordinator |
|
||||||
|
| Test framework unknown | Default to Jest patterns |
|
||||||
|
| Revision with no failure data | Generate additional tests instead of revising |
|
||||||
|
| Syntax errors in generated tests | Auto-fix imports and types |
|
||||||
179
.claude/skills/team-testing/roles/strategist.md
Normal file
179
.claude/skills/team-testing/roles/strategist.md
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
# Role: strategist
|
||||||
|
|
||||||
|
测试策略制定者。分析 git diff、确定测试层级、定义覆盖率目标和测试优先级。
|
||||||
|
|
||||||
|
## Role Identity
|
||||||
|
|
||||||
|
- **Name**: `strategist`
|
||||||
|
- **Task Prefix**: `STRATEGY-*`
|
||||||
|
- **Responsibility**: Read-only analysis (策略分析)
|
||||||
|
- **Communication**: SendMessage to coordinator only
|
||||||
|
- **Output Tag**: `[strategist]`
|
||||||
|
|
||||||
|
## Role Boundaries
|
||||||
|
|
||||||
|
### MUST
|
||||||
|
|
||||||
|
- 仅处理 `STRATEGY-*` 前缀的任务
|
||||||
|
- 所有输出必须带 `[strategist]` 标识
|
||||||
|
- Phase 2 读取 shared-memory.json,Phase 5 写入 test_strategy
|
||||||
|
|
||||||
|
### MUST NOT
|
||||||
|
|
||||||
|
- ❌ 生成测试代码、执行测试或分析结果
|
||||||
|
- ❌ 直接与其他 worker 通信
|
||||||
|
- ❌ 为其他角色创建任务
|
||||||
|
|
||||||
|
## Message Types
|
||||||
|
|
||||||
|
| Type | Direction | Trigger | Description |
|
||||||
|
|------|-----------|---------|-------------|
|
||||||
|
| `strategy_ready` | strategist → coordinator | Strategy completed | 策略制定完成 |
|
||||||
|
| `error` | strategist → coordinator | Processing failure | 错误上报 |
|
||||||
|
|
||||||
|
## Execution (5-Phase)
|
||||||
|
|
||||||
|
### Phase 1: Task Discovery
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const tasks = TaskList()
|
||||||
|
const myTasks = tasks.filter(t =>
|
||||||
|
t.subject.startsWith('STRATEGY-') &&
|
||||||
|
t.owner === 'strategist' &&
|
||||||
|
t.status === 'pending' &&
|
||||||
|
t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
if (myTasks.length === 0) return
|
||||||
|
const task = TaskGet({ taskId: myTasks[0].id })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Context Loading + Shared Memory Read
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const sessionMatch = task.description.match(/Session:\s*([^\n]+)/)
|
||||||
|
const sessionFolder = sessionMatch?.[1]?.trim()
|
||||||
|
|
||||||
|
const memoryPath = `${sessionFolder}/shared-memory.json`
|
||||||
|
let sharedMemory = {}
|
||||||
|
try { sharedMemory = JSON.parse(Read(memoryPath)) } catch {}
|
||||||
|
|
||||||
|
const changedFiles = sharedMemory.changed_files || []
|
||||||
|
const changedModules = sharedMemory.changed_modules || []
|
||||||
|
|
||||||
|
// Read git diff for detailed analysis
|
||||||
|
const gitDiff = Bash(`git diff HEAD~1 -- ${changedFiles.join(' ')} 2>/dev/null || git diff --cached -- ${changedFiles.join(' ')}`)
|
||||||
|
|
||||||
|
// Detect test framework
|
||||||
|
const hasJest = Bash(`test -f jest.config.js || test -f jest.config.ts && echo "yes" || echo "no"`).trim() === 'yes'
|
||||||
|
const hasPytest = Bash(`test -f pytest.ini || test -f pyproject.toml && echo "yes" || echo "no"`).trim() === 'yes'
|
||||||
|
const hasVitest = Bash(`test -f vitest.config.ts || test -f vitest.config.js && echo "yes" || echo "no"`).trim() === 'yes'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Strategy Formulation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Analyze changes by type:
|
||||||
|
// - New files → need new tests
|
||||||
|
// - Modified functions → need updated tests
|
||||||
|
// - Deleted files → need test cleanup
|
||||||
|
// - Config changes → may need integration tests
|
||||||
|
|
||||||
|
const outputPath = `${sessionFolder}/strategy/test-strategy.md`
|
||||||
|
|
||||||
|
const strategyContent = `# Test Strategy
|
||||||
|
|
||||||
|
**Changed Files**: ${changedFiles.length}
|
||||||
|
**Changed Modules**: ${changedModules.join(', ')}
|
||||||
|
**Test Framework**: ${hasJest ? 'Jest' : hasPytest ? 'Pytest' : hasVitest ? 'Vitest' : 'Unknown'}
|
||||||
|
|
||||||
|
## Change Analysis
|
||||||
|
|
||||||
|
| File | Change Type | Impact | Priority |
|
||||||
|
|------|------------|--------|----------|
|
||||||
|
${changeAnalysis.map(c => `| ${c.file} | ${c.type} | ${c.impact} | ${c.priority} |`).join('\n')}
|
||||||
|
|
||||||
|
## Test Layer Recommendations
|
||||||
|
|
||||||
|
### L1: Unit Tests
|
||||||
|
- **Scope**: ${l1Scope.join(', ')}
|
||||||
|
- **Coverage Target**: ${coverageTargets.L1}%
|
||||||
|
- **Priority Files**: ${l1Priority.join(', ')}
|
||||||
|
- **Test Patterns**: ${l1Patterns.join(', ')}
|
||||||
|
|
||||||
|
### L2: Integration Tests
|
||||||
|
- **Scope**: ${l2Scope.join(', ')}
|
||||||
|
- **Coverage Target**: ${coverageTargets.L2}%
|
||||||
|
- **Integration Points**: ${integrationPoints.join(', ')}
|
||||||
|
|
||||||
|
### L3: E2E Tests
|
||||||
|
- **Scope**: ${l3Scope.join(', ')}
|
||||||
|
- **Coverage Target**: ${coverageTargets.L3}%
|
||||||
|
- **User Scenarios**: ${userScenarios.join(', ')}
|
||||||
|
|
||||||
|
## Risk Assessment
|
||||||
|
|
||||||
|
| Risk | Probability | Impact | Mitigation |
|
||||||
|
|------|------------|--------|------------|
|
||||||
|
${risks.map(r => `| ${r.risk} | ${r.probability} | ${r.impact} | ${r.mitigation} |`).join('\n')}
|
||||||
|
|
||||||
|
## Test Execution Order
|
||||||
|
|
||||||
|
1. L1 unit tests for high-priority changed files
|
||||||
|
2. L1 unit tests for dependent modules
|
||||||
|
3. L2 integration tests for cross-module interactions
|
||||||
|
4. L3 E2E tests for affected user scenarios
|
||||||
|
`
|
||||||
|
|
||||||
|
Write(outputPath, strategyContent)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Self-Validation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Verify strategy completeness
|
||||||
|
const hasAllLayers = l1Scope.length > 0
|
||||||
|
const hasCoverageTargets = coverageTargets.L1 > 0
|
||||||
|
const hasPriorityFiles = l1Priority.length > 0
|
||||||
|
|
||||||
|
if (!hasAllLayers || !hasCoverageTargets) {
|
||||||
|
// Fill gaps
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Report to Coordinator + Shared Memory Write
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
sharedMemory.test_strategy = {
|
||||||
|
framework: hasJest ? 'Jest' : hasPytest ? 'Pytest' : hasVitest ? 'Vitest' : 'Unknown',
|
||||||
|
layers: { L1: l1Scope, L2: l2Scope, L3: l3Scope },
|
||||||
|
coverage_targets: coverageTargets,
|
||||||
|
priority_files: l1Priority,
|
||||||
|
risks: risks
|
||||||
|
}
|
||||||
|
Write(memoryPath, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log", team: teamName, from: "strategist", to: "coordinator",
|
||||||
|
type: "strategy_ready",
|
||||||
|
summary: `[strategist] Strategy complete: ${changedFiles.length} files, L1-L3 layers defined`,
|
||||||
|
ref: outputPath
|
||||||
|
})
|
||||||
|
|
||||||
|
SendMessage({
|
||||||
|
type: "message", recipient: "coordinator",
|
||||||
|
content: `## [strategist] Test Strategy Ready\n\n**Files**: ${changedFiles.length}\n**Layers**: L1(${l1Scope.length} targets), L2(${l2Scope.length}), L3(${l3Scope.length})\n**Framework**: ${sharedMemory.test_strategy.framework}\n**Output**: ${outputPath}`,
|
||||||
|
summary: `[strategist] Strategy ready`
|
||||||
|
})
|
||||||
|
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| No STRATEGY-* tasks | Idle |
|
||||||
|
| No changed files | Analyze full codebase, recommend smoke tests |
|
||||||
|
| Unknown test framework | Recommend Jest/Pytest based on project language |
|
||||||
|
| All files are config | Recommend integration tests only |
|
||||||
93
.claude/skills/team-testing/specs/team-config.json
Normal file
93
.claude/skills/team-testing/specs/team-config.json
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
{
|
||||||
|
"team_name": "team-testing",
|
||||||
|
"team_display_name": "Team Testing",
|
||||||
|
"description": "Testing team with Generator-Critic loop, shared defect memory, and progressive test layers",
|
||||||
|
"version": "1.0.0",
|
||||||
|
|
||||||
|
"roles": {
|
||||||
|
"coordinator": {
|
||||||
|
"task_prefix": null,
|
||||||
|
"responsibility": "Change scope analysis, layer selection, quality gating",
|
||||||
|
"message_types": ["pipeline_selected", "gc_loop_trigger", "quality_gate", "task_unblocked", "error", "shutdown"]
|
||||||
|
},
|
||||||
|
"strategist": {
|
||||||
|
"task_prefix": "STRATEGY",
|
||||||
|
"responsibility": "Analyze git diff, determine test layers, define coverage targets",
|
||||||
|
"message_types": ["strategy_ready", "error"]
|
||||||
|
},
|
||||||
|
"generator": {
|
||||||
|
"task_prefix": "TESTGEN",
|
||||||
|
"responsibility": "Generate test cases by layer (unit/integration/E2E)",
|
||||||
|
"message_types": ["tests_generated", "tests_revised", "error"]
|
||||||
|
},
|
||||||
|
"executor": {
|
||||||
|
"task_prefix": "TESTRUN",
|
||||||
|
"responsibility": "Execute tests, collect coverage, auto-fix failures",
|
||||||
|
"message_types": ["tests_passed", "tests_failed", "coverage_report", "error"]
|
||||||
|
},
|
||||||
|
"analyst": {
|
||||||
|
"task_prefix": "TESTANA",
|
||||||
|
"responsibility": "Defect pattern analysis, coverage gap analysis, quality report",
|
||||||
|
"message_types": ["analysis_ready", "error"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"pipelines": {
|
||||||
|
"targeted": {
|
||||||
|
"description": "Small scope: strategy → generate L1 → run",
|
||||||
|
"task_chain": ["STRATEGY-001", "TESTGEN-001", "TESTRUN-001"],
|
||||||
|
"gc_loops": 0
|
||||||
|
},
|
||||||
|
"standard": {
|
||||||
|
"description": "Progressive: L1 → L2 with analysis",
|
||||||
|
"task_chain": ["STRATEGY-001", "TESTGEN-001", "TESTRUN-001", "TESTGEN-002", "TESTRUN-002", "TESTANA-001"],
|
||||||
|
"gc_loops": 1
|
||||||
|
},
|
||||||
|
"comprehensive": {
|
||||||
|
"description": "Full coverage: parallel L1+L2, then L3 with analysis",
|
||||||
|
"task_chain": ["STRATEGY-001", "TESTGEN-001", "TESTGEN-002", "TESTRUN-001", "TESTRUN-002", "TESTGEN-003", "TESTRUN-003", "TESTANA-001"],
|
||||||
|
"gc_loops": 2,
|
||||||
|
"parallel_groups": [["TESTGEN-001", "TESTGEN-002"], ["TESTRUN-001", "TESTRUN-002"]]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"innovation_patterns": {
|
||||||
|
"generator_critic": {
|
||||||
|
"generator": "generator",
|
||||||
|
"critic": "executor",
|
||||||
|
"max_rounds": 3,
|
||||||
|
"convergence_trigger": "coverage >= target && pass_rate >= 0.95"
|
||||||
|
},
|
||||||
|
"shared_memory": {
|
||||||
|
"file": "shared-memory.json",
|
||||||
|
"fields": {
|
||||||
|
"strategist": "test_strategy",
|
||||||
|
"generator": "generated_tests",
|
||||||
|
"executor": "execution_results",
|
||||||
|
"analyst": "analysis_report"
|
||||||
|
},
|
||||||
|
"persistent_fields": ["defect_patterns", "effective_test_patterns", "coverage_history"]
|
||||||
|
},
|
||||||
|
"dynamic_pipeline": {
|
||||||
|
"selector": "coordinator",
|
||||||
|
"criteria": "changed_file_count + module_count + change_type"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"test_layers": {
|
||||||
|
"L1": { "name": "Unit Tests", "coverage_target": 80, "description": "Function-level isolation tests" },
|
||||||
|
"L2": { "name": "Integration Tests", "coverage_target": 60, "description": "Module interaction tests" },
|
||||||
|
"L3": { "name": "E2E Tests", "coverage_target": 40, "description": "User scenario end-to-end tests" }
|
||||||
|
},
|
||||||
|
|
||||||
|
"collaboration_patterns": ["CP-1", "CP-3", "CP-5"],
|
||||||
|
|
||||||
|
"session_dirs": {
|
||||||
|
"base": ".workflow/.team/TST-{slug}-{YYYY-MM-DD}/",
|
||||||
|
"strategy": "strategy/",
|
||||||
|
"tests": "tests/",
|
||||||
|
"results": "results/",
|
||||||
|
"analysis": "analysis/",
|
||||||
|
"messages": ".workflow/.team-msg/{team-name}/"
|
||||||
|
}
|
||||||
|
}
|
||||||
378
.claude/skills/team-uidesign/SKILL.md
Normal file
378
.claude/skills/team-uidesign/SKILL.md
Normal file
@@ -0,0 +1,378 @@
|
|||||||
|
---
|
||||||
|
name: team-uidesign
|
||||||
|
description: Unified team skill for UI design team. All roles invoke this skill with --role arg for role-specific execution. CP-9 Dual-Track design+implementation.
|
||||||
|
allowed-tools: TeamCreate(*), TeamDelete(*), SendMessage(*), TaskCreate(*), TaskUpdate(*), TaskList(*), TaskGet(*), Task(*), AskUserQuestion(*), TodoWrite(*), Read(*), Write(*), Edit(*), Bash(*), Glob(*), Grep(*), WebFetch(*), WebSearch(*)
|
||||||
|
---
|
||||||
|
|
||||||
|
# Team UI Design
|
||||||
|
|
||||||
|
Unified team skill for UI design covering design system analysis, token definition, component specification, accessibility audit, and code implementation. All team members invoke this skill with `--role=xxx` to route to role-specific execution.
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────┐
|
||||||
|
│ Skill(skill="team-uidesign", args="--role=xxx") │
|
||||||
|
└───────────────────┬─────────────────────────────┘
|
||||||
|
│ Role Router
|
||||||
|
┌───────┬───────┼───────┬───────┐
|
||||||
|
↓ ↓ ↓ ↓ ↓
|
||||||
|
┌──────────┐┌──────────┐┌──────────┐┌──────────┐┌──────────┐
|
||||||
|
│coordinator││researcher││ designer ││ reviewer ││implementer│
|
||||||
|
│ roles/ ││ roles/ ││ roles/ ││ roles/ ││ roles/ │
|
||||||
|
└──────────┘└──────────┘└──────────┘└──────────┘└───────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Role Router
|
||||||
|
|
||||||
|
### Input Parsing
|
||||||
|
|
||||||
|
Parse `$ARGUMENTS` to extract `--role`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const args = "$ARGUMENTS"
|
||||||
|
const roleMatch = args.match(/--role[=\s]+(\w+)/)
|
||||||
|
|
||||||
|
if (!roleMatch) {
|
||||||
|
throw new Error("Missing --role argument. Available roles: coordinator, researcher, designer, reviewer, implementer")
|
||||||
|
}
|
||||||
|
|
||||||
|
const role = roleMatch[1]
|
||||||
|
const teamName = args.match(/--team[=\s]+([\w-]+)/)?.[1] || "uidesign"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Role Dispatch
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const VALID_ROLES = {
|
||||||
|
"coordinator": { file: "roles/coordinator.md", prefix: null },
|
||||||
|
"researcher": { file: "roles/researcher.md", prefix: "RESEARCH" },
|
||||||
|
"designer": { file: "roles/designer.md", prefix: "DESIGN" },
|
||||||
|
"reviewer": { file: "roles/reviewer.md", prefix: "AUDIT" },
|
||||||
|
"implementer": { file: "roles/implementer.md", prefix: "BUILD" }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!VALID_ROLES[role]) {
|
||||||
|
throw new Error(`Unknown role: ${role}. Available: ${Object.keys(VALID_ROLES).join(', ')}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read and execute role-specific logic
|
||||||
|
Read(VALID_ROLES[role].file)
|
||||||
|
// → Execute the 5-phase process defined in that file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Available Roles
|
||||||
|
|
||||||
|
| Role | Task Prefix | Responsibility | Role File |
|
||||||
|
|------|-------------|----------------|-----------|
|
||||||
|
| `coordinator` | N/A | Scope assessment, dual-track orchestration, sync point management | [roles/coordinator.md](roles/coordinator.md) |
|
||||||
|
| `researcher` | RESEARCH-* | Design system analysis, component inventory, accessibility audit | [roles/researcher.md](roles/researcher.md) |
|
||||||
|
| `designer` | DESIGN-* | Design token definition, component specs, layout design | [roles/designer.md](roles/designer.md) |
|
||||||
|
| `reviewer` | AUDIT-* | Design consistency, accessibility compliance, visual audit | [roles/reviewer.md](roles/reviewer.md) |
|
||||||
|
| `implementer` | BUILD-* | Component code implementation, CSS generation, design token consumption | [roles/implementer.md](roles/implementer.md) |
|
||||||
|
|
||||||
|
## Shared Infrastructure
|
||||||
|
|
||||||
|
### Role Isolation Rules
|
||||||
|
|
||||||
|
**核心原则**: 每个角色仅能执行自己职责范围内的工作。
|
||||||
|
|
||||||
|
#### Output Tagging(强制)
|
||||||
|
|
||||||
|
所有角色的输出必须带 `[role_name]` 标识前缀:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
SendMessage({
|
||||||
|
content: `## [${role}] ...`,
|
||||||
|
summary: `[${role}] ...`
|
||||||
|
})
|
||||||
|
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
summary: `[${role}] ...`
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Coordinator 隔离
|
||||||
|
|
||||||
|
| 允许 | 禁止 |
|
||||||
|
|------|------|
|
||||||
|
| 需求澄清 (AskUserQuestion) | ❌ 直接编写/修改代码 |
|
||||||
|
| 创建任务链 (TaskCreate) | ❌ 调用实现类 subagent |
|
||||||
|
| 分发任务给 worker | ❌ 直接执行分析/测试/审查 |
|
||||||
|
| 监控进度 (消息总线) | ❌ 绕过 worker 自行完成任务 |
|
||||||
|
| 汇报结果给用户 | ❌ 修改源代码或产物文件 |
|
||||||
|
|
||||||
|
#### Worker 隔离
|
||||||
|
|
||||||
|
| 允许 | 禁止 |
|
||||||
|
|------|------|
|
||||||
|
| 处理自己前缀的任务 | ❌ 处理其他角色前缀的任务 |
|
||||||
|
| SendMessage 给 coordinator | ❌ 直接与其他 worker 通信 |
|
||||||
|
| 使用 Toolbox 中声明的工具 | ❌ 为其他角色创建任务 (TaskCreate) |
|
||||||
|
|
||||||
|
### Message Bus (All Roles)
|
||||||
|
|
||||||
|
Every SendMessage **before**, must call `mcp__ccw-tools__team_msg` to log:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log",
|
||||||
|
team: teamName,
|
||||||
|
from: role,
|
||||||
|
to: "coordinator",
|
||||||
|
type: "<type>",
|
||||||
|
summary: `[${role}] <summary>`,
|
||||||
|
ref: "<file_path>"
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
**Message types by role**:
|
||||||
|
|
||||||
|
| Role | Types |
|
||||||
|
|------|-------|
|
||||||
|
| coordinator | `task_unblocked`, `sync_checkpoint`, `fix_required`, `error`, `shutdown` |
|
||||||
|
| researcher | `research_ready`, `research_progress`, `error` |
|
||||||
|
| designer | `design_ready`, `design_revision`, `design_progress`, `error` |
|
||||||
|
| reviewer | `audit_result`, `audit_passed`, `fix_required`, `error` |
|
||||||
|
| implementer | `build_complete`, `build_progress`, `error` |
|
||||||
|
|
||||||
|
### CLI Fallback
|
||||||
|
|
||||||
|
当 `mcp__ccw-tools__team_msg` MCP 不可用时:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
Bash(`ccw team log --team "${teamName}" --from "${role}" --to "coordinator" --type "<type>" --summary "<summary>" --json`)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task Lifecycle (All Worker Roles)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Standard task lifecycle every worker role follows
|
||||||
|
// Phase 1: Discovery
|
||||||
|
const tasks = TaskList()
|
||||||
|
const myTasks = tasks.filter(t =>
|
||||||
|
t.subject.startsWith(`${VALID_ROLES[role].prefix}-`) &&
|
||||||
|
t.owner === role &&
|
||||||
|
t.status === 'pending' &&
|
||||||
|
t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
if (myTasks.length === 0) return // idle
|
||||||
|
const task = TaskGet({ taskId: myTasks[0].id })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||||
|
|
||||||
|
// Phase 2-4: Role-specific (see roles/{role}.md)
|
||||||
|
|
||||||
|
// Phase 5: Report + Loop — 所有输出必须带 [role] 标识
|
||||||
|
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: role, to: "coordinator", type: "...", summary: `[${role}] ...` })
|
||||||
|
SendMessage({ type: "message", recipient: "coordinator", content: `## [${role}] ...`, summary: `[${role}] ...` })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||||
|
// Check for next task → back to Phase 1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Three-Pipeline Architecture
|
||||||
|
|
||||||
|
### CP-9 Dual-Track Concept
|
||||||
|
|
||||||
|
```
|
||||||
|
Track A (Design): RESEARCH → DESIGN(tokens) → DESIGN(components) → ...
|
||||||
|
│ │
|
||||||
|
Sync Point 1 Sync Point 2
|
||||||
|
│ │
|
||||||
|
Track B (Build): BUILD(tokens) ──→ BUILD(components) → ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Design and implementation proceed in parallel after sync checkpoints. Each sync point validates that design artifacts are stable enough for implementation to consume.
|
||||||
|
|
||||||
|
### Pipeline Modes
|
||||||
|
|
||||||
|
```
|
||||||
|
component (单组件):
|
||||||
|
RESEARCH-001 → DESIGN-001 → AUDIT-001 → BUILD-001
|
||||||
|
|
||||||
|
system (设计系统 - 双轨):
|
||||||
|
Track A: RESEARCH-001 → DESIGN-001(tokens) → DESIGN-002(components)
|
||||||
|
Sync-1: AUDIT-001 (tokens审查)
|
||||||
|
Track B: BUILD-001(tokens, blockedBy AUDIT-001) ∥ DESIGN-002(components)
|
||||||
|
Sync-2: AUDIT-002 (components审查)
|
||||||
|
Track B: BUILD-002(components, blockedBy AUDIT-002)
|
||||||
|
|
||||||
|
full-system (完整设计系统):
|
||||||
|
RESEARCH-001 → DESIGN-001(tokens) → AUDIT-001
|
||||||
|
→ [DESIGN-002(components) + BUILD-001(tokens)](并行, blockedBy AUDIT-001)
|
||||||
|
→ AUDIT-002 → BUILD-002(components) → AUDIT-003(最终)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Generator-Critic Loop
|
||||||
|
|
||||||
|
designer ↔ reviewer 循环,确保设计一致性和可访问性:
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────┐ DESIGN artifact ┌──────────┐
|
||||||
|
│ designer │ ──────────────────────→ │ reviewer │
|
||||||
|
│(Generator)│ │ (Critic) │
|
||||||
|
│ │ ←────────────────────── │ │
|
||||||
|
└──────────┘ AUDIT feedback └──────────┘
|
||||||
|
(max 2 rounds)
|
||||||
|
|
||||||
|
Convergence: audit.score >= 8 && audit.critical_count === 0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Shared Memory
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"design_token_registry": {
|
||||||
|
"colors": {}, "typography": {}, "spacing": {}, "shadows": {}
|
||||||
|
},
|
||||||
|
"style_decisions": [],
|
||||||
|
"component_inventory": [],
|
||||||
|
"accessibility_patterns": [],
|
||||||
|
"audit_history": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
每个角色在 Phase 2 读取,Phase 5 写入自己负责的字段。
|
||||||
|
|
||||||
|
## Session Directory
|
||||||
|
|
||||||
|
```
|
||||||
|
.workflow/.team/UDS-{slug}-{YYYY-MM-DD}/
|
||||||
|
├── team-session.json # Session state
|
||||||
|
├── shared-memory.json # Cross-role accumulated knowledge
|
||||||
|
├── research/ # Researcher output
|
||||||
|
│ ├── design-system-analysis.json
|
||||||
|
│ ├── component-inventory.json
|
||||||
|
│ └── accessibility-audit.json
|
||||||
|
├── design/ # Designer output
|
||||||
|
│ ├── design-tokens.json
|
||||||
|
│ ├── component-specs/
|
||||||
|
│ │ └── {component-name}.md
|
||||||
|
│ └── layout-specs/
|
||||||
|
│ └── {layout-name}.md
|
||||||
|
├── audit/ # Reviewer output
|
||||||
|
│ └── audit-{NNN}.md
|
||||||
|
└── build/ # Implementer output
|
||||||
|
├── token-files/
|
||||||
|
└── component-files/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Coordinator Spawn Template
|
||||||
|
|
||||||
|
When coordinator creates teammates:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
TeamCreate({ team_name: teamName })
|
||||||
|
|
||||||
|
// Researcher
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
team_name: teamName,
|
||||||
|
name: "researcher",
|
||||||
|
prompt: `你是 team "${teamName}" 的 RESEARCHER。
|
||||||
|
当你收到 RESEARCH-* 任务时,调用 Skill(skill="team-uidesign", args="--role=researcher") 执行。
|
||||||
|
当前需求: ${taskDescription}
|
||||||
|
约束: ${constraints}
|
||||||
|
Session: ${sessionFolder}
|
||||||
|
|
||||||
|
## 角色准则(强制)
|
||||||
|
- 你只能处理 RESEARCH-* 前缀的任务
|
||||||
|
- 所有输出必须带 [researcher] 标识前缀
|
||||||
|
- 仅与 coordinator 通信
|
||||||
|
|
||||||
|
## 消息总线(必须)
|
||||||
|
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||||
|
|
||||||
|
工作流程:
|
||||||
|
1. TaskList → 找到 RESEARCH-* 任务
|
||||||
|
2. Skill(skill="team-uidesign", args="--role=researcher") 执行
|
||||||
|
3. team_msg log + SendMessage 结果给 coordinator
|
||||||
|
4. TaskUpdate completed → 检查下一个任务`
|
||||||
|
})
|
||||||
|
|
||||||
|
// Designer
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
team_name: teamName,
|
||||||
|
name: "designer",
|
||||||
|
prompt: `你是 team "${teamName}" 的 DESIGNER。
|
||||||
|
当你收到 DESIGN-* 任务时,调用 Skill(skill="team-uidesign", args="--role=designer") 执行。
|
||||||
|
当前需求: ${taskDescription}
|
||||||
|
Session: ${sessionFolder}
|
||||||
|
|
||||||
|
## 角色准则(强制)
|
||||||
|
- 你只能处理 DESIGN-* 前缀的任务
|
||||||
|
- 所有输出必须带 [designer] 标识前缀
|
||||||
|
- 仅与 coordinator 通信
|
||||||
|
|
||||||
|
## 消息总线(必须)
|
||||||
|
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||||
|
|
||||||
|
工作流程:
|
||||||
|
1. TaskList → 找到 DESIGN-* 任务
|
||||||
|
2. Skill(skill="team-uidesign", args="--role=designer") 执行
|
||||||
|
3. team_msg log + SendMessage 结果给 coordinator
|
||||||
|
4. TaskUpdate completed → 检查下一个任务`
|
||||||
|
})
|
||||||
|
|
||||||
|
// Reviewer (AUDIT)
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
team_name: teamName,
|
||||||
|
name: "reviewer",
|
||||||
|
prompt: `你是 team "${teamName}" 的 REVIEWER (审查员)。
|
||||||
|
当你收到 AUDIT-* 任务时,调用 Skill(skill="team-uidesign", args="--role=reviewer") 执行。
|
||||||
|
当前需求: ${taskDescription}
|
||||||
|
Session: ${sessionFolder}
|
||||||
|
|
||||||
|
## 角色准则(强制)
|
||||||
|
- 你只能处理 AUDIT-* 前缀的任务
|
||||||
|
- 所有输出必须带 [reviewer] 标识前缀
|
||||||
|
- 仅与 coordinator 通信
|
||||||
|
|
||||||
|
## 消息总线(必须)
|
||||||
|
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||||
|
|
||||||
|
工作流程:
|
||||||
|
1. TaskList → 找到 AUDIT-* 任务
|
||||||
|
2. Skill(skill="team-uidesign", args="--role=reviewer") 执行
|
||||||
|
3. team_msg log + SendMessage 结果给 coordinator
|
||||||
|
4. TaskUpdate completed → 检查下一个任务`
|
||||||
|
})
|
||||||
|
|
||||||
|
// Implementer (BUILD)
|
||||||
|
Task({
|
||||||
|
subagent_type: "general-purpose",
|
||||||
|
team_name: teamName,
|
||||||
|
name: "implementer",
|
||||||
|
prompt: `你是 team "${teamName}" 的 IMPLEMENTER (实现者)。
|
||||||
|
当你收到 BUILD-* 任务时,调用 Skill(skill="team-uidesign", args="--role=implementer") 执行。
|
||||||
|
当前需求: ${taskDescription}
|
||||||
|
Session: ${sessionFolder}
|
||||||
|
|
||||||
|
## 角色准则(强制)
|
||||||
|
- 你只能处理 BUILD-* 前缀的任务
|
||||||
|
- 所有输出必须带 [implementer] 标识前缀
|
||||||
|
- 仅与 coordinator 通信
|
||||||
|
|
||||||
|
## 消息总线(必须)
|
||||||
|
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||||
|
|
||||||
|
工作流程:
|
||||||
|
1. TaskList → 找到 BUILD-* 任务
|
||||||
|
2. Skill(skill="team-uidesign", args="--role=implementer") 执行
|
||||||
|
3. team_msg log + SendMessage 结果给 coordinator
|
||||||
|
4. TaskUpdate completed → 检查下一个任务`
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| Unknown --role value | Error with available role list |
|
||||||
|
| Missing --role arg | Error with usage hint |
|
||||||
|
| Role file not found | Error with expected path |
|
||||||
|
| AUDIT score < 6 超过 2 轮 GC | Coordinator 上报用户 |
|
||||||
|
| 双轨同步失败 | 回退到单轨顺序执行 |
|
||||||
|
| 设计令牌冲突 | Reviewer 仲裁,Coordinator 介入 |
|
||||||
|
| BUILD 找不到设计文件 | 等待 Sync Point 或上报 |
|
||||||
377
.claude/skills/team-uidesign/roles/coordinator.md
Normal file
377
.claude/skills/team-uidesign/roles/coordinator.md
Normal file
@@ -0,0 +1,377 @@
|
|||||||
|
# Role: coordinator
|
||||||
|
|
||||||
|
UI Design team coordinator. Orchestrates design pipelines across three modes: component, system (dual-track), and full-system. Manages sync points between design and implementation tracks, controls Generator-Critic loops between designer and reviewer.
|
||||||
|
|
||||||
|
## Role Identity
|
||||||
|
|
||||||
|
- **Name**: `coordinator`
|
||||||
|
- **Task Prefix**: N/A (coordinator creates tasks, doesn't receive them)
|
||||||
|
- **Responsibility**: Dual-track orchestration, sync point management
|
||||||
|
- **Communication**: SendMessage to all teammates
|
||||||
|
|
||||||
|
## Message Types
|
||||||
|
|
||||||
|
| Type | Direction | Trigger | Description |
|
||||||
|
|------|-----------|---------|-------------|
|
||||||
|
| `task_unblocked` | coordinator → any | Dependency resolved / sync point passed | Notify worker of available task |
|
||||||
|
| `sync_checkpoint` | coordinator → all | Audit passed at sync point | Design artifacts stable for consumption |
|
||||||
|
| `fix_required` | coordinator → designer | Audit found issues | Create DESIGN-fix task |
|
||||||
|
| `error` | coordinator → all | Critical system error | Escalation to user |
|
||||||
|
| `shutdown` | coordinator → all | Team being dissolved | Clean shutdown signal |
|
||||||
|
|
||||||
|
## Execution
|
||||||
|
|
||||||
|
### Phase 0: Session Resume Check
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const args = "$ARGUMENTS"
|
||||||
|
const isResume = /--resume|--continue/.test(args)
|
||||||
|
|
||||||
|
if (isResume) {
|
||||||
|
const sessionDirs = Glob({ pattern: '.workflow/.team/UDS-*/team-session.json' })
|
||||||
|
const resumable = sessionDirs.map(f => {
|
||||||
|
try {
|
||||||
|
const session = JSON.parse(Read(f))
|
||||||
|
if (session.status === 'active' || session.status === 'paused') return session
|
||||||
|
} catch {}
|
||||||
|
return null
|
||||||
|
}).filter(Boolean)
|
||||||
|
|
||||||
|
if (resumable.length === 1) {
|
||||||
|
var resumedSession = resumable[0]
|
||||||
|
} else if (resumable.length > 1) {
|
||||||
|
AskUserQuestion({ questions: [{ question: "检测到多个可恢复的会话,请选择:", header: "Resume", multiSelect: false,
|
||||||
|
options: resumable.slice(0, 4).map(s => ({ label: s.session_id, description: `${s.topic} (${s.current_phase}, ${s.status})` }))
|
||||||
|
}]})
|
||||||
|
var resumedSession = resumable.find(s => s.session_id === userChoice)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resumedSession) {
|
||||||
|
// Restore and rebuild team, skip to Phase 4
|
||||||
|
const teamName = resumedSession.team_name
|
||||||
|
const sessionFolder = `.workflow/.team/${resumedSession.session_id}`
|
||||||
|
TeamCreate({ team_name: teamName })
|
||||||
|
// Spawn workers, create remaining tasks, jump to coordination loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 1: Requirement Clarification
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const args = "$ARGUMENTS"
|
||||||
|
const teamNameMatch = args.match(/--team-name[=\s]+([\w-]+)/)
|
||||||
|
const teamName = teamNameMatch ? teamNameMatch[1] : `uidesign-${Date.now().toString(36)}`
|
||||||
|
const taskDescription = args.replace(/--team-name[=\s]+[\w-]+/, '').replace(/--role[=\s]+\w+/, '').replace(/--resume|--continue/, '').trim()
|
||||||
|
```
|
||||||
|
|
||||||
|
Assess scope and select pipeline:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
AskUserQuestion({
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
question: "UI 设计范围:",
|
||||||
|
header: "Scope",
|
||||||
|
multiSelect: false,
|
||||||
|
options: [
|
||||||
|
{ label: "单组件", description: "设计并实现一个独立组件" },
|
||||||
|
{ label: "组件系统", description: "多组件 + 设计令牌系统" },
|
||||||
|
{ label: "完整设计系统", description: "从零构建完整设计系统(令牌 + 组件 + 布局)" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "设计约束:",
|
||||||
|
header: "Constraint",
|
||||||
|
multiSelect: true,
|
||||||
|
options: [
|
||||||
|
{ label: "现有设计系统", description: "必须兼容现有设计令牌和组件" },
|
||||||
|
{ label: "WCAG AA", description: "必须满足 WCAG 2.1 AA 可访问性标准" },
|
||||||
|
{ label: "响应式", description: "必须支持 mobile/tablet/desktop" },
|
||||||
|
{ label: "暗色模式", description: "必须支持 light/dark 主题切换" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
// Map scope to pipeline
|
||||||
|
const pipelineMap = {
|
||||||
|
'单组件': 'component',
|
||||||
|
'组件系统': 'system',
|
||||||
|
'完整设计系统': 'full-system'
|
||||||
|
}
|
||||||
|
const pipeline = pipelineMap[scopeChoice]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Create Team + Spawn Workers
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
TeamCreate({ team_name: teamName })
|
||||||
|
|
||||||
|
// Session setup
|
||||||
|
const topicSlug = taskDescription.toLowerCase().replace(/[^a-z0-9]+/g, '-').substring(0, 40)
|
||||||
|
const dateStr = new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString().substring(0, 10)
|
||||||
|
const sessionId = `UDS-${topicSlug}-${dateStr}`
|
||||||
|
const sessionFolder = `.workflow/.team/${sessionId}`
|
||||||
|
|
||||||
|
// Create directory structure
|
||||||
|
Bash(`mkdir -p "${sessionFolder}/research" "${sessionFolder}/design/component-specs" "${sessionFolder}/design/layout-specs" "${sessionFolder}/audit" "${sessionFolder}/build/token-files" "${sessionFolder}/build/component-files"`)
|
||||||
|
|
||||||
|
// Initialize shared-memory.json
|
||||||
|
const sharedMemory = {
|
||||||
|
design_token_registry: { colors: {}, typography: {}, spacing: {}, shadows: {}, borders: {} },
|
||||||
|
style_decisions: [],
|
||||||
|
component_inventory: [],
|
||||||
|
accessibility_patterns: [],
|
||||||
|
audit_history: [],
|
||||||
|
_metadata: { created_at: new Date().toISOString(), pipeline: pipeline }
|
||||||
|
}
|
||||||
|
Write(`${sessionFolder}/shared-memory.json`, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
// Create team-session.json
|
||||||
|
const teamSession = {
|
||||||
|
session_id: sessionId,
|
||||||
|
team_name: teamName,
|
||||||
|
topic: taskDescription,
|
||||||
|
pipeline: pipeline,
|
||||||
|
status: "active",
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
|
current_phase: "research",
|
||||||
|
completed_tasks: [],
|
||||||
|
sync_points: [],
|
||||||
|
gc_state: { round: 0, max_rounds: 2, converged: false },
|
||||||
|
user_preferences: { scope: scopeChoice, constraints: constraintChoices },
|
||||||
|
pipeline_progress: {
|
||||||
|
total: pipeline === 'component' ? 4 : pipeline === 'system' ? 6 : 7,
|
||||||
|
completed: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write(`${sessionFolder}/team-session.json`, JSON.stringify(teamSession, null, 2))
|
||||||
|
|
||||||
|
// Spawn 4 workers (see SKILL.md Coordinator Spawn Template)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Create Task Chain
|
||||||
|
|
||||||
|
#### Component Pipeline
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// RESEARCH-001: Design system analysis
|
||||||
|
TaskCreate({ subject: "RESEARCH-001: 设计系统分析与组件调研", description: `${taskDescription}\n\nSession: ${sessionFolder}\n输出: ${sessionFolder}/research/\n\n任务:\n- 分析现有设计系统(如有)\n- 组件盘点\n- 可访问性基线审计\n- 竞品参考收集`, activeForm: "调研设计系统中" })
|
||||||
|
TaskUpdate({ taskId: researchId, owner: "researcher" })
|
||||||
|
|
||||||
|
// DESIGN-001: Component design
|
||||||
|
TaskCreate({ subject: "DESIGN-001: 组件设计与规格定义", description: `${taskDescription}\n\nSession: ${sessionFolder}\n输入: ${sessionFolder}/research/\n输出: ${sessionFolder}/design/component-specs/\n\n任务:\n- 定义设计令牌(如需)\n- 组件状态定义 (default/hover/focus/active/disabled)\n- 响应式断点\n- 交互规格`, activeForm: "设计组件中" })
|
||||||
|
TaskUpdate({ taskId: design1Id, owner: "designer", addBlockedBy: [researchId] })
|
||||||
|
|
||||||
|
// AUDIT-001: Design audit
|
||||||
|
TaskCreate({ subject: "AUDIT-001: 设计审查", description: `${taskDescription}\n\nSession: ${sessionFolder}\n输入: ${sessionFolder}/design/\n输出: ${sessionFolder}/audit/audit-001.md\n\n审查维度:\n- 设计一致性\n- 可访问性合规 (WCAG AA)\n- 状态完整性\n- 令牌使用规范`, activeForm: "审查设计中" })
|
||||||
|
TaskUpdate({ taskId: audit1Id, owner: "reviewer", addBlockedBy: [design1Id] })
|
||||||
|
|
||||||
|
// BUILD-001: Component build
|
||||||
|
TaskCreate({ subject: "BUILD-001: 组件代码实现", description: `${taskDescription}\n\nSession: ${sessionFolder}\n输入: ${sessionFolder}/design/component-specs/ + ${sessionFolder}/audit/audit-001.md\n输出: ${sessionFolder}/build/component-files/\n\n任务:\n- 消费设计令牌\n- 实现组件代码\n- CSS/样式生成\n- 可访问性属性 (ARIA)`, activeForm: "实现组件中" })
|
||||||
|
TaskUpdate({ taskId: build1Id, owner: "implementer", addBlockedBy: [audit1Id] })
|
||||||
|
```
|
||||||
|
|
||||||
|
#### System Pipeline (Dual-Track)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// RESEARCH-001: same as component
|
||||||
|
TaskCreate({ subject: "RESEARCH-001: 设计系统全面分析", description: `...\n输出: ${sessionFolder}/research/`, activeForm: "调研设计系统中" })
|
||||||
|
TaskUpdate({ taskId: researchId, owner: "researcher" })
|
||||||
|
|
||||||
|
// DESIGN-001: Design Tokens
|
||||||
|
TaskCreate({ subject: "DESIGN-001: 设计令牌系统定义", description: `定义完整设计令牌系统\n\nSession: ${sessionFolder}\n输入: ${sessionFolder}/research/\n输出: ${sessionFolder}/design/design-tokens.json\n\n包含:\n- 颜色系统 (primary/secondary/neutral/semantic)\n- 排版系统 (font-family/size/weight/line-height)\n- 间距系统 (4px grid)\n- 阴影系统\n- 边框系统\n- 断点定义`, activeForm: "定义设计令牌中" })
|
||||||
|
TaskUpdate({ taskId: design1Id, owner: "designer", addBlockedBy: [researchId] })
|
||||||
|
|
||||||
|
// AUDIT-001: Token Audit (Sync Point 1)
|
||||||
|
TaskCreate({ subject: "AUDIT-001: 设计令牌审查 [同步点1]", description: `审查设计令牌系统\n\nSession: ${sessionFolder}\n⚡ 同步点: 通过后将解锁 BUILD-001(令牌实现) 和 DESIGN-002(组件设计) 并行执行\n\n审查维度:\n- 令牌命名规范\n- 值域合理性\n- 主题兼容性\n- 可访问性 (对比度比值)`, activeForm: "审查令牌系统中" })
|
||||||
|
TaskUpdate({ taskId: audit1Id, owner: "reviewer", addBlockedBy: [design1Id] })
|
||||||
|
|
||||||
|
// === 双轨并行段 (blockedBy AUDIT-001) ===
|
||||||
|
|
||||||
|
// DESIGN-002: Component Specs (Track A continues)
|
||||||
|
TaskCreate({ subject: "DESIGN-002: 组件规格设计", description: `基于令牌系统设计组件\n\nSession: ${sessionFolder}\n输入: ${sessionFolder}/design/design-tokens.json\n输出: ${sessionFolder}/design/component-specs/\n\n⚡ 双轨: 与 BUILD-001(令牌实现) 并行执行`, activeForm: "设计组件规格中" })
|
||||||
|
TaskUpdate({ taskId: design2Id, owner: "designer", addBlockedBy: [audit1Id] })
|
||||||
|
|
||||||
|
// BUILD-001: Token Implementation (Track B starts)
|
||||||
|
TaskCreate({ subject: "BUILD-001: 设计令牌代码实现", description: `实现设计令牌为 CSS/JS 代码\n\nSession: ${sessionFolder}\n输入: ${sessionFolder}/design/design-tokens.json\n输出: ${sessionFolder}/build/token-files/\n\n⚡ 双轨: 与 DESIGN-002(组件设计) 并行执行`, activeForm: "实现设计令牌中" })
|
||||||
|
TaskUpdate({ taskId: build1Id, owner: "implementer", addBlockedBy: [audit1Id] })
|
||||||
|
|
||||||
|
// AUDIT-002: Component Audit (Sync Point 2)
|
||||||
|
TaskCreate({ subject: "AUDIT-002: 组件设计审查 [同步点2]", description: `审查组件设计规格\n\nSession: ${sessionFolder}\n⚡ 同步点: 通过后解锁 BUILD-002(组件实现)\n\n审查维度:\n- 令牌引用正确性\n- 状态完整性\n- 响应式规格\n- 可访问性模式`, activeForm: "审查组件设计中" })
|
||||||
|
TaskUpdate({ taskId: audit2Id, owner: "reviewer", addBlockedBy: [design2Id] })
|
||||||
|
|
||||||
|
// BUILD-002: Component Implementation
|
||||||
|
TaskCreate({ subject: "BUILD-002: 组件代码实现", description: `实现组件代码\n\nSession: ${sessionFolder}\n输入: ${sessionFolder}/design/component-specs/ + ${sessionFolder}/build/token-files/\n输出: ${sessionFolder}/build/component-files/`, activeForm: "实现组件代码中" })
|
||||||
|
TaskUpdate({ taskId: build2Id, owner: "implementer", addBlockedBy: [audit2Id, build1Id] })
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Full-System Pipeline
|
||||||
|
|
||||||
|
同 System Pipeline,但增加 AUDIT-003 最终审查:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// [Same as System Pipeline tasks above]
|
||||||
|
// + AUDIT-003: Final comprehensive audit
|
||||||
|
TaskCreate({ subject: "AUDIT-003: 最终设计系统审查", description: `全面审查设计系统\n\nSession: ${sessionFolder}\n\n审查维度:\n- 令牌 ↔ 组件一致性\n- 代码 ↔ 设计规格一致性\n- 跨组件一致性\n- 可访问性全面检查\n- 响应式全面检查\n\n输出: ${sessionFolder}/audit/audit-003.md + 最终评分`, activeForm: "最终审查中" })
|
||||||
|
TaskUpdate({ taskId: audit3Id, owner: "reviewer", addBlockedBy: [build2Id] })
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Coordination Loop
|
||||||
|
|
||||||
|
#### Message Handling
|
||||||
|
|
||||||
|
| Received Message | Action |
|
||||||
|
|-----------------|--------|
|
||||||
|
| Researcher: research_ready | Read research output → team_msg log → TaskUpdate completed (auto-unblocks DESIGN) |
|
||||||
|
| Designer: design_ready | Read design artifacts → team_msg log → TaskUpdate completed (auto-unblocks AUDIT) |
|
||||||
|
| Designer: design_revision | GC loop: update round count, re-assign DESIGN-fix task |
|
||||||
|
| Reviewer: audit_passed (score >= 8) | **Sync Point**: team_msg log(sync_checkpoint) → TaskUpdate completed → unblock parallel tasks |
|
||||||
|
| Reviewer: audit_result (score 6-7) | GC round < max → Create DESIGN-fix → assign designer |
|
||||||
|
| Reviewer: fix_required (score < 6) | GC round < max → Create DESIGN-fix with severity CRITICAL → assign designer |
|
||||||
|
| Reviewer: audit_result + GC round >= max | Escalate to user: "设计审查未通过 {max} 轮,需要人工介入" |
|
||||||
|
| Implementer: build_complete | team_msg log → TaskUpdate completed → check if next AUDIT unblocked |
|
||||||
|
| All tasks completed | → Phase 5 |
|
||||||
|
|
||||||
|
#### Generator-Critic Loop Control
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
if (msgType === 'audit_result' || msgType === 'fix_required') {
|
||||||
|
const auditScore = msg.data.score
|
||||||
|
const criticalCount = msg.data.critical_count
|
||||||
|
const gcState = session.gc_state
|
||||||
|
|
||||||
|
if (auditScore >= 8 && criticalCount === 0) {
|
||||||
|
// Converged → proceed (mark as sync_checkpoint)
|
||||||
|
gcState.converged = true
|
||||||
|
team_msg({ type: 'sync_checkpoint', summary: `[coordinator] Sync point passed: ${msg.ref}` })
|
||||||
|
} else if (gcState.round < gcState.max_rounds) {
|
||||||
|
// Not converged → another round
|
||||||
|
gcState.round++
|
||||||
|
TaskCreate({
|
||||||
|
subject: `DESIGN-fix-${gcState.round}: 根据审查反馈修订设计`,
|
||||||
|
description: `审查反馈: ${msg.data.feedback}\n分数: ${auditScore}/10\n严重问题: ${criticalCount}\n\nSession: ${sessionFolder}\n修复后重新提交审查`,
|
||||||
|
activeForm: "修订设计中"
|
||||||
|
})
|
||||||
|
TaskUpdate({ taskId: fixTaskId, owner: "designer" })
|
||||||
|
// After designer completes fix → re-run same AUDIT task
|
||||||
|
} else {
|
||||||
|
// Exceeded max rounds → escalate
|
||||||
|
AskUserQuestion({
|
||||||
|
questions: [{
|
||||||
|
question: `设计审查 ${gcState.round} 轮后仍未通过 (分数: ${auditScore}/10, 严重问题: ${criticalCount})。如何处理?`,
|
||||||
|
header: "GC Escalation",
|
||||||
|
multiSelect: false,
|
||||||
|
options: [
|
||||||
|
{ label: "接受当前设计", description: "跳过剩余审查,继续实现" },
|
||||||
|
{ label: "再试一轮", description: "额外给一轮 GC 循环机会" },
|
||||||
|
{ label: "终止流程", description: "停止并手动处理" }
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSession(sessionFolder, { gc_state: gcState })
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Dual-Track Sync Point Management
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// When AUDIT at a sync point completes with PASS:
|
||||||
|
if (isSyncPoint && auditPassed) {
|
||||||
|
// Record sync point
|
||||||
|
session.sync_points.push({
|
||||||
|
audit_task: auditTaskId,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
score: auditScore
|
||||||
|
})
|
||||||
|
|
||||||
|
// Unblock parallel tasks on both tracks
|
||||||
|
// e.g., AUDIT-001 passed → unblock both DESIGN-002 and BUILD-001
|
||||||
|
team_msg({ type: 'sync_checkpoint', summary: `[coordinator] 同步点 ${auditTaskId} 通过,双轨任务已解锁` })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dual-track failure fallback:
|
||||||
|
if (dualTrackFailed) {
|
||||||
|
// Convert remaining parallel tasks to sequential
|
||||||
|
// Remove parallel dependencies, add sequential blockedBy
|
||||||
|
team_msg({ type: 'error', summary: '[coordinator] 双轨同步失败,回退到顺序执行' })
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Report
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Summary based on pipeline
|
||||||
|
const report = {
|
||||||
|
pipeline: pipeline,
|
||||||
|
tasks_completed: session.completed_tasks.length,
|
||||||
|
gc_rounds: session.gc_state.round,
|
||||||
|
sync_points_passed: session.sync_points.length,
|
||||||
|
final_audit_score: lastAuditScore,
|
||||||
|
artifacts: {
|
||||||
|
research: `${sessionFolder}/research/`,
|
||||||
|
design: `${sessionFolder}/design/`,
|
||||||
|
audit: `${sessionFolder}/audit/`,
|
||||||
|
build: `${sessionFolder}/build/`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AskUserQuestion({
|
||||||
|
questions: [{
|
||||||
|
question: "UI 设计任务已完成。下一步:",
|
||||||
|
header: "Next",
|
||||||
|
multiSelect: false,
|
||||||
|
options: [
|
||||||
|
{ label: "新组件", description: "设计新的组件(复用团队)" },
|
||||||
|
{ label: "集成测试", description: "验证组件在实际页面中的表现" },
|
||||||
|
{ label: "关闭团队", description: "关闭所有 teammate 并清理" }
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
|
||||||
|
// Update session
|
||||||
|
updateSession(sessionFolder, {
|
||||||
|
status: 'completed',
|
||||||
|
completed_at: new Date().toISOString()
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Session State Tracking
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function updateSession(sessionFolder, updates) {
|
||||||
|
const session = JSON.parse(Read(`${sessionFolder}/team-session.json`))
|
||||||
|
Object.assign(session, updates, { updated_at: new Date().toISOString() })
|
||||||
|
Write(`${sessionFolder}/team-session.json`, JSON.stringify(session, null, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
// On task completion:
|
||||||
|
updateSession(sessionFolder, {
|
||||||
|
completed_tasks: [...session.completed_tasks, taskPrefix],
|
||||||
|
pipeline_progress: { ...session.pipeline_progress, completed: session.pipeline_progress.completed + 1 }
|
||||||
|
})
|
||||||
|
|
||||||
|
// On sync point passed:
|
||||||
|
updateSession(sessionFolder, {
|
||||||
|
sync_points: [...session.sync_points, { audit: auditId, timestamp: new Date().toISOString() }]
|
||||||
|
})
|
||||||
|
|
||||||
|
// On GC round:
|
||||||
|
updateSession(sessionFolder, {
|
||||||
|
gc_state: { ...session.gc_state, round: session.gc_state.round + 1 }
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| 审查分数 < 6 且 GC 轮次耗尽 | 上报用户决定 |
|
||||||
|
| 双轨同步点失败 | 回退到单轨顺序执行 |
|
||||||
|
| BUILD 找不到设计文件 | 等待设计完成或上报 |
|
||||||
|
| 设计令牌冲突 | Reviewer 仲裁 |
|
||||||
|
| Worker 无响应 | 追踪消息,2次无响应 → 重新 spawn |
|
||||||
330
.claude/skills/team-uidesign/roles/designer.md
Normal file
330
.claude/skills/team-uidesign/roles/designer.md
Normal file
@@ -0,0 +1,330 @@
|
|||||||
|
# Role: designer
|
||||||
|
|
||||||
|
Design token architect and component specification author. Defines visual language, component behavior, and responsive layouts. Acts as Generator in the designer↔reviewer Generator-Critic loop.
|
||||||
|
|
||||||
|
## Role Identity
|
||||||
|
|
||||||
|
- **Name**: `designer`
|
||||||
|
- **Task Prefix**: `DESIGN`
|
||||||
|
- **Responsibility Type**: Code generation (design artifacts)
|
||||||
|
- **Responsibility**: Design token definition, component specs, layout design
|
||||||
|
- **Toolbox**: Read, Write, Edit, Glob, Grep, Task(code-developer, universal-executor)
|
||||||
|
|
||||||
|
## Message Types
|
||||||
|
|
||||||
|
| Type | When | Content |
|
||||||
|
|------|------|---------|
|
||||||
|
| `design_ready` | Design artifact complete | Summary + file references |
|
||||||
|
| `design_revision` | GC fix iteration complete | What changed + audit feedback addressed |
|
||||||
|
| `design_progress` | Intermediate update | Current progress |
|
||||||
|
| `error` | Failure | Error details |
|
||||||
|
|
||||||
|
## Execution
|
||||||
|
|
||||||
|
### Phase 1: Task Discovery
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const tasks = TaskList()
|
||||||
|
const myTasks = tasks.filter(t =>
|
||||||
|
t.subject.startsWith('DESIGN-') &&
|
||||||
|
t.owner === 'designer' &&
|
||||||
|
t.status === 'pending' &&
|
||||||
|
t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
if (myTasks.length === 0) return
|
||||||
|
const task = TaskGet({ taskId: myTasks[0].id })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||||
|
|
||||||
|
// Detect task type
|
||||||
|
const isTokenTask = task.subject.includes('令牌') || task.subject.includes('token')
|
||||||
|
const isComponentTask = task.subject.includes('组件') || task.subject.includes('component')
|
||||||
|
const isFixTask = task.subject.includes('fix') || task.subject.includes('修订')
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Context Loading + Shared Memory Read
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const sessionFolder = task.description.match(/Session:\s*(.+)/)?.[1]?.trim()
|
||||||
|
|
||||||
|
// Read shared memory
|
||||||
|
let sharedMemory = {}
|
||||||
|
try {
|
||||||
|
sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`))
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
// Read research findings
|
||||||
|
let research = {}
|
||||||
|
try {
|
||||||
|
research = {
|
||||||
|
designSystem: JSON.parse(Read(`${sessionFolder}/research/design-system-analysis.json`)),
|
||||||
|
inventory: JSON.parse(Read(`${sessionFolder}/research/component-inventory.json`)),
|
||||||
|
a11y: JSON.parse(Read(`${sessionFolder}/research/accessibility-audit.json`))
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
// If GC fix task, read audit feedback
|
||||||
|
let auditFeedback = null
|
||||||
|
if (isFixTask) {
|
||||||
|
const feedbackMatch = task.description.match(/审查反馈:\s*(.+)/s)
|
||||||
|
auditFeedback = feedbackMatch?.[1]?.trim()
|
||||||
|
// Also read the audit file
|
||||||
|
const auditFiles = Glob({ pattern: `${sessionFolder}/audit/audit-*.md` })
|
||||||
|
if (auditFiles.length > 0) {
|
||||||
|
auditFeedback = Read(auditFiles[auditFiles.length - 1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Core Execution
|
||||||
|
|
||||||
|
#### Token System Design (DESIGN-001 in system/full-system pipeline)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
if (isTokenTask) {
|
||||||
|
const existingTokens = research.designSystem?.existing_tokens || {}
|
||||||
|
const stylingApproach = research.designSystem?.styling_approach || 'css-variables'
|
||||||
|
|
||||||
|
// Generate design tokens following W3C Design Tokens Format
|
||||||
|
const designTokens = {
|
||||||
|
"$schema": "https://design-tokens.github.io/community-group/format/",
|
||||||
|
"color": {
|
||||||
|
"primary": {
|
||||||
|
"$type": "color",
|
||||||
|
"$value": { "light": "#1976d2", "dark": "#90caf9" }
|
||||||
|
},
|
||||||
|
"secondary": {
|
||||||
|
"$type": "color",
|
||||||
|
"$value": { "light": "#dc004e", "dark": "#f48fb1" }
|
||||||
|
},
|
||||||
|
"background": {
|
||||||
|
"$type": "color",
|
||||||
|
"$value": { "light": "#ffffff", "dark": "#121212" }
|
||||||
|
},
|
||||||
|
"surface": {
|
||||||
|
"$type": "color",
|
||||||
|
"$value": { "light": "#f5f5f5", "dark": "#1e1e1e" }
|
||||||
|
},
|
||||||
|
"text": {
|
||||||
|
"primary": {
|
||||||
|
"$type": "color",
|
||||||
|
"$value": { "light": "rgba(0,0,0,0.87)", "dark": "rgba(255,255,255,0.87)" }
|
||||||
|
},
|
||||||
|
"secondary": {
|
||||||
|
"$type": "color",
|
||||||
|
"$value": { "light": "rgba(0,0,0,0.6)", "dark": "rgba(255,255,255,0.6)" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ... extend based on research findings
|
||||||
|
},
|
||||||
|
"typography": {
|
||||||
|
"font-family": {
|
||||||
|
"base": { "$type": "fontFamily", "$value": ["Inter", "system-ui", "sans-serif"] },
|
||||||
|
"mono": { "$type": "fontFamily", "$value": ["JetBrains Mono", "monospace"] }
|
||||||
|
},
|
||||||
|
"font-size": {
|
||||||
|
"xs": { "$type": "dimension", "$value": "0.75rem" },
|
||||||
|
"sm": { "$type": "dimension", "$value": "0.875rem" },
|
||||||
|
"base": { "$type": "dimension", "$value": "1rem" },
|
||||||
|
"lg": { "$type": "dimension", "$value": "1.125rem" },
|
||||||
|
"xl": { "$type": "dimension", "$value": "1.25rem" },
|
||||||
|
"2xl": { "$type": "dimension", "$value": "1.5rem" },
|
||||||
|
"3xl": { "$type": "dimension", "$value": "1.875rem" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"spacing": {
|
||||||
|
"unit": { "$type": "dimension", "$value": "4px" },
|
||||||
|
"xs": { "$type": "dimension", "$value": "4px" },
|
||||||
|
"sm": { "$type": "dimension", "$value": "8px" },
|
||||||
|
"md": { "$type": "dimension", "$value": "16px" },
|
||||||
|
"lg": { "$type": "dimension", "$value": "24px" },
|
||||||
|
"xl": { "$type": "dimension", "$value": "32px" },
|
||||||
|
"2xl": { "$type": "dimension", "$value": "48px" }
|
||||||
|
},
|
||||||
|
"shadow": {
|
||||||
|
"sm": { "$type": "shadow", "$value": "0 1px 2px rgba(0,0,0,0.05)" },
|
||||||
|
"md": { "$type": "shadow", "$value": "0 4px 6px rgba(0,0,0,0.1)" },
|
||||||
|
"lg": { "$type": "shadow", "$value": "0 10px 15px rgba(0,0,0,0.1)" }
|
||||||
|
},
|
||||||
|
"border": {
|
||||||
|
"radius": {
|
||||||
|
"sm": { "$type": "dimension", "$value": "4px" },
|
||||||
|
"md": { "$type": "dimension", "$value": "8px" },
|
||||||
|
"lg": { "$type": "dimension", "$value": "12px" },
|
||||||
|
"full": { "$type": "dimension", "$value": "9999px" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"breakpoint": {
|
||||||
|
"mobile": { "$type": "dimension", "$value": "320px" },
|
||||||
|
"tablet": { "$type": "dimension", "$value": "768px" },
|
||||||
|
"desktop": { "$type": "dimension", "$value": "1024px" },
|
||||||
|
"wide": { "$type": "dimension", "$value": "1280px" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapt tokens based on existing design system if present
|
||||||
|
if (Object.keys(existingTokens).length > 0) {
|
||||||
|
// Merge/extend rather than replace
|
||||||
|
}
|
||||||
|
|
||||||
|
Write(`${sessionFolder}/design/design-tokens.json`, JSON.stringify(designTokens, null, 2))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Component Specification (DESIGN-002 or DESIGN-001 in component pipeline)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
if (isComponentTask) {
|
||||||
|
const tokens = JSON.parse(Read(`${sessionFolder}/design/design-tokens.json`))
|
||||||
|
const componentList = sharedMemory.component_inventory || []
|
||||||
|
|
||||||
|
// For each component to design, create a spec file
|
||||||
|
// Component spec includes: states, props, tokens consumed, responsive behavior, a11y
|
||||||
|
const componentSpec = `# Component Spec: {ComponentName}
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
- **Type**: atom | molecule | organism
|
||||||
|
- **Purpose**: Brief description
|
||||||
|
|
||||||
|
## Design Tokens Consumed
|
||||||
|
| Token | Usage | Value Reference |
|
||||||
|
|-------|-------|-----------------|
|
||||||
|
| color.primary | Button background | {color.primary} |
|
||||||
|
| spacing.md | Internal padding | {spacing.md} |
|
||||||
|
|
||||||
|
## States
|
||||||
|
| State | Visual Changes | Interaction |
|
||||||
|
|-------|---------------|-------------|
|
||||||
|
| default | Base appearance | — |
|
||||||
|
| hover | Background lighten 10% | Mouse over |
|
||||||
|
| focus | 2px outline, offset 2px | Tab navigation |
|
||||||
|
| active | Background darken 5% | Mouse down |
|
||||||
|
| disabled | Opacity 0.5 | cursor: not-allowed |
|
||||||
|
|
||||||
|
## Responsive Behavior
|
||||||
|
| Breakpoint | Changes |
|
||||||
|
|------------|---------|
|
||||||
|
| mobile (<768px) | Full width, stacked |
|
||||||
|
| tablet (768-1024px) | Auto width |
|
||||||
|
| desktop (>1024px) | Fixed width |
|
||||||
|
|
||||||
|
## Accessibility
|
||||||
|
- **Role**: button | link | tab | ...
|
||||||
|
- **ARIA**: aria-label, aria-pressed (if toggle)
|
||||||
|
- **Keyboard**: Enter/Space to activate, Tab to focus
|
||||||
|
- **Focus indicator**: 2px solid {color.primary}, offset 2px
|
||||||
|
- **Contrast**: Text on background >= 4.5:1 (AA)
|
||||||
|
|
||||||
|
## Variants
|
||||||
|
| Variant | Description | Token Override |
|
||||||
|
|---------|-------------|----------------|
|
||||||
|
| primary | Main action | color.primary |
|
||||||
|
| secondary | Secondary action | color.secondary |
|
||||||
|
| outline | Ghost style | border only |
|
||||||
|
`
|
||||||
|
|
||||||
|
// Write spec for each component
|
||||||
|
// Actual implementation adapts to task requirements
|
||||||
|
Write(`${sessionFolder}/design/component-specs/{component-name}.md`, componentSpec)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### GC Fix Mode (DESIGN-fix-N)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
if (isFixTask && auditFeedback) {
|
||||||
|
// Parse audit feedback for specific issues
|
||||||
|
// Re-read the affected design artifacts
|
||||||
|
// Apply fixes based on audit feedback:
|
||||||
|
// - Token value adjustments (contrast ratios, spacing)
|
||||||
|
// - Missing state definitions
|
||||||
|
// - Accessibility gaps
|
||||||
|
// - Naming convention fixes
|
||||||
|
|
||||||
|
// Re-write affected files with corrections
|
||||||
|
// Signal design_revision instead of design_ready
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Validation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Self-check design artifacts
|
||||||
|
const checks = {
|
||||||
|
tokens_valid: true, // All token values are valid
|
||||||
|
states_complete: true, // All interactive states defined
|
||||||
|
a11y_specified: true, // Accessibility attributes defined
|
||||||
|
responsive_defined: true, // Responsive breakpoints specified
|
||||||
|
token_refs_valid: true // All token references resolve
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token reference integrity check
|
||||||
|
if (isTokenTask) {
|
||||||
|
// Verify all $value fields are non-empty
|
||||||
|
// Verify light/dark mode values exist for colors
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isComponentTask) {
|
||||||
|
// Verify all token references ({token.path}) match defined tokens
|
||||||
|
// Verify all states are defined
|
||||||
|
// Verify a11y section is complete
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Report + Shared Memory Write
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Update shared memory
|
||||||
|
if (isTokenTask) {
|
||||||
|
sharedMemory.design_token_registry = {
|
||||||
|
colors: Object.keys(designTokens.color || {}),
|
||||||
|
typography: Object.keys(designTokens.typography || {}),
|
||||||
|
spacing: Object.keys(designTokens.spacing || {}),
|
||||||
|
shadows: Object.keys(designTokens.shadow || {}),
|
||||||
|
borders: Object.keys(designTokens.border || {})
|
||||||
|
}
|
||||||
|
sharedMemory.style_decisions.push({
|
||||||
|
decision: `Token system defined with ${stylingApproach} approach`,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isComponentTask) {
|
||||||
|
sharedMemory.style_decisions.push({
|
||||||
|
decision: `Component specs created for ${componentCount} components`,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Write(`${sessionFolder}/shared-memory.json`, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
// Report
|
||||||
|
const msgType = isFixTask ? 'design_revision' : 'design_ready'
|
||||||
|
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log",
|
||||||
|
team: teamName,
|
||||||
|
from: "designer",
|
||||||
|
to: "coordinator",
|
||||||
|
type: msgType,
|
||||||
|
summary: `[designer] ${isFixTask ? '设计修订完成' : '设计完成'}: ${isTokenTask ? '令牌系统' : '组件规格'}`,
|
||||||
|
ref: `${sessionFolder}/design/`
|
||||||
|
})
|
||||||
|
|
||||||
|
SendMessage({
|
||||||
|
type: "message",
|
||||||
|
recipient: "coordinator",
|
||||||
|
content: `## [designer] ${isFixTask ? '设计修订完成' : '设计产出就绪'}\n\n${isTokenTask ? '令牌系统已定义' : '组件规格已完成'}\n产出: ${sessionFolder}/design/`,
|
||||||
|
summary: `[designer] ${msgType}`
|
||||||
|
})
|
||||||
|
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| 研究数据缺失 | 使用默认令牌 + 标记待确认 |
|
||||||
|
| 令牌冲突 | 记录决策依据,提交审查仲裁 |
|
||||||
|
| GC 修复无法满足所有审查意见 | 标记权衡取舍,让 coordinator 决定 |
|
||||||
|
| 组件数量过多 | 优先 MVP 组件,标记 post-MVP |
|
||||||
296
.claude/skills/team-uidesign/roles/implementer.md
Normal file
296
.claude/skills/team-uidesign/roles/implementer.md
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
# Role: implementer
|
||||||
|
|
||||||
|
Component code builder responsible for translating design specifications into production code. Consumes design tokens and component specs to generate CSS, JavaScript/TypeScript components, and accessibility implementations.
|
||||||
|
|
||||||
|
## Role Identity
|
||||||
|
|
||||||
|
- **Name**: `implementer`
|
||||||
|
- **Task Prefix**: `BUILD`
|
||||||
|
- **Responsibility Type**: Code generation
|
||||||
|
- **Responsibility**: Component code implementation, CSS generation, design token consumption
|
||||||
|
- **Toolbox**: Read, Write, Edit, Glob, Grep, Bash, Task(code-developer)
|
||||||
|
|
||||||
|
## Message Types
|
||||||
|
|
||||||
|
| Type | When | Content |
|
||||||
|
|------|------|---------|
|
||||||
|
| `build_complete` | Implementation finished | Changed files + summary |
|
||||||
|
| `build_progress` | Intermediate update | Current progress |
|
||||||
|
| `error` | Failure | Error details |
|
||||||
|
|
||||||
|
## Execution
|
||||||
|
|
||||||
|
### Phase 1: Task Discovery
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const tasks = TaskList()
|
||||||
|
const myTasks = tasks.filter(t =>
|
||||||
|
t.subject.startsWith('BUILD-') &&
|
||||||
|
t.owner === 'implementer' &&
|
||||||
|
t.status === 'pending' &&
|
||||||
|
t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
if (myTasks.length === 0) return
|
||||||
|
const task = TaskGet({ taskId: myTasks[0].id })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||||
|
|
||||||
|
// Detect build type
|
||||||
|
const isTokenBuild = task.subject.includes('令牌') || task.subject.includes('token')
|
||||||
|
const isComponentBuild = task.subject.includes('组件') || task.subject.includes('component')
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Context Loading + Shared Memory Read
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const sessionFolder = task.description.match(/Session:\s*(.+)/)?.[1]?.trim()
|
||||||
|
|
||||||
|
// Read shared memory
|
||||||
|
let sharedMemory = {}
|
||||||
|
try {
|
||||||
|
sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`))
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
const tokenRegistry = sharedMemory.design_token_registry || {}
|
||||||
|
const styleDecisions = sharedMemory.style_decisions || []
|
||||||
|
|
||||||
|
// Read design artifacts
|
||||||
|
let designTokens = null
|
||||||
|
try {
|
||||||
|
designTokens = JSON.parse(Read(`${sessionFolder}/design/design-tokens.json`))
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
// Read component specs (for component build)
|
||||||
|
let componentSpecs = []
|
||||||
|
if (isComponentBuild) {
|
||||||
|
const specFiles = Glob({ pattern: `${sessionFolder}/design/component-specs/*.md` })
|
||||||
|
componentSpecs = specFiles.map(f => ({ path: f, content: Read(f), name: f.match(/([^/\\]+)\.md$/)?.[1] }))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read audit reports for approved changes
|
||||||
|
const auditFiles = Glob({ pattern: `${sessionFolder}/audit/audit-*.md` })
|
||||||
|
const latestAudit = auditFiles.length > 0 ? Read(auditFiles[auditFiles.length - 1]) : null
|
||||||
|
|
||||||
|
// Detect project tech stack from codebase
|
||||||
|
// Read existing project patterns for code style alignment
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Core Execution
|
||||||
|
|
||||||
|
#### Token Implementation (BUILD-001: Token Files)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
if (isTokenBuild && designTokens) {
|
||||||
|
// Detect styling approach from codebase
|
||||||
|
const stylingApproach = sharedMemory.style_decisions?.find(d => d.decision.includes('approach'))
|
||||||
|
|| 'css-variables'
|
||||||
|
|
||||||
|
// Delegate to code-developer for implementation
|
||||||
|
Task({
|
||||||
|
subagent_type: "code-developer",
|
||||||
|
run_in_background: false,
|
||||||
|
prompt: `
|
||||||
|
## Design Token Implementation
|
||||||
|
|
||||||
|
Convert the following design tokens into production code.
|
||||||
|
|
||||||
|
### Design Tokens (W3C Format)
|
||||||
|
${JSON.stringify(designTokens, null, 2)}
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
1. Generate CSS custom properties (variables) for all tokens
|
||||||
|
2. Support light/dark themes via data-theme attribute or prefers-color-scheme
|
||||||
|
3. Generate TypeScript type definitions for token paths
|
||||||
|
4. Follow project's existing styling patterns
|
||||||
|
|
||||||
|
### Output Files
|
||||||
|
Write to: ${sessionFolder}/build/token-files/
|
||||||
|
|
||||||
|
Files to create:
|
||||||
|
- tokens.css — CSS custom properties with :root and [data-theme="dark"]
|
||||||
|
- tokens.ts — TypeScript constants/types for programmatic access
|
||||||
|
- README.md — Token usage guide
|
||||||
|
|
||||||
|
### Example CSS Output
|
||||||
|
:root {
|
||||||
|
--color-primary: #1976d2;
|
||||||
|
--color-text-primary: rgba(0,0,0,0.87);
|
||||||
|
--spacing-md: 16px;
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] {
|
||||||
|
--color-primary: #90caf9;
|
||||||
|
--color-text-primary: rgba(255,255,255,0.87);
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
### Constraints
|
||||||
|
- Use semantic token names matching the design tokens
|
||||||
|
- Ensure all color tokens have both light and dark values
|
||||||
|
- Use CSS custom properties for runtime theming
|
||||||
|
- TypeScript types should enable autocomplete
|
||||||
|
`
|
||||||
|
})
|
||||||
|
|
||||||
|
// Track output files
|
||||||
|
const tokenFiles = Glob({ pattern: `${sessionFolder}/build/token-files/*` })
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Component Implementation (BUILD-002: Component Code)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
if (isComponentBuild && componentSpecs.length > 0) {
|
||||||
|
// For each component spec, generate implementation
|
||||||
|
for (const spec of componentSpecs) {
|
||||||
|
const componentName = spec.name
|
||||||
|
|
||||||
|
Task({
|
||||||
|
subagent_type: "code-developer",
|
||||||
|
run_in_background: false,
|
||||||
|
prompt: `
|
||||||
|
## Component Implementation: ${componentName}
|
||||||
|
|
||||||
|
### Design Specification
|
||||||
|
${spec.content}
|
||||||
|
|
||||||
|
### Design Tokens Available
|
||||||
|
Token file: ${sessionFolder}/build/token-files/tokens.css
|
||||||
|
Token types: ${sessionFolder}/build/token-files/tokens.ts
|
||||||
|
|
||||||
|
### Audit Feedback (if any)
|
||||||
|
${latestAudit ? 'Latest audit notes:\n' + latestAudit : 'No audit feedback'}
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
1. Implement component following the design spec exactly
|
||||||
|
2. Consume design tokens via CSS custom properties (var(--token-name))
|
||||||
|
3. Implement ALL states: default, hover, focus, active, disabled
|
||||||
|
4. Add ARIA attributes as specified in the design spec
|
||||||
|
5. Support responsive breakpoints from the spec
|
||||||
|
6. Follow project's component patterns (detect from existing codebase)
|
||||||
|
|
||||||
|
### Output
|
||||||
|
Write to: ${sessionFolder}/build/component-files/${componentName}/
|
||||||
|
|
||||||
|
Files:
|
||||||
|
- ${componentName}.tsx (or .vue/.svelte based on project)
|
||||||
|
- ${componentName}.css (or .module.css / styled-components)
|
||||||
|
- ${componentName}.test.tsx (basic render + state tests)
|
||||||
|
- index.ts (re-export)
|
||||||
|
|
||||||
|
### Accessibility Requirements
|
||||||
|
- Keyboard navigation must work
|
||||||
|
- Screen reader support via ARIA
|
||||||
|
- Focus indicator visible (use design token)
|
||||||
|
- Color contrast meets WCAG AA (4.5:1 text, 3:1 UI)
|
||||||
|
|
||||||
|
### Constraints
|
||||||
|
- NO hardcoded colors/spacing — all from design tokens
|
||||||
|
- Follow existing codebase patterns for component structure
|
||||||
|
- Include basic accessibility tests
|
||||||
|
`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const componentFiles = Glob({ pattern: `${sessionFolder}/build/component-files/**/*` })
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Validation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Verify build outputs exist
|
||||||
|
if (isTokenBuild) {
|
||||||
|
const requiredTokenFiles = ['tokens.css', 'tokens.ts']
|
||||||
|
const missing = requiredTokenFiles.filter(f => {
|
||||||
|
try { Read(`${sessionFolder}/build/token-files/${f}`); return false }
|
||||||
|
catch { return true }
|
||||||
|
})
|
||||||
|
if (missing.length > 0) {
|
||||||
|
// Re-run token generation for missing files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isComponentBuild) {
|
||||||
|
// Verify each component has at minimum: .tsx + .css + index.ts
|
||||||
|
componentSpecs.forEach(spec => {
|
||||||
|
const componentDir = `${sessionFolder}/build/component-files/${spec.name}`
|
||||||
|
const files = Glob({ pattern: `${componentDir}/*` })
|
||||||
|
if (files.length < 3) {
|
||||||
|
// Re-run component generation
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token reference check: verify CSS uses var(--token-*) not hardcoded values
|
||||||
|
if (isComponentBuild) {
|
||||||
|
const cssFiles = Glob({ pattern: `${sessionFolder}/build/component-files/**/*.css` })
|
||||||
|
cssFiles.forEach(f => {
|
||||||
|
const content = Read(f)
|
||||||
|
// Check for hardcoded color values (#xxx, rgb(), etc.)
|
||||||
|
const hardcoded = content.match(/#[0-9a-fA-F]{3,8}|rgb\(|rgba\(/g) || []
|
||||||
|
if (hardcoded.length > 0) {
|
||||||
|
// Flag as warning — should use design tokens
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Report + Shared Memory Write
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Update shared memory with implementation details
|
||||||
|
if (isComponentBuild) {
|
||||||
|
// Update component inventory with implementation paths
|
||||||
|
componentSpecs.forEach(spec => {
|
||||||
|
const existing = sharedMemory.component_inventory.find(c => c.name === spec.name)
|
||||||
|
if (existing) {
|
||||||
|
existing.implementation_path = `${sessionFolder}/build/component-files/${spec.name}/`
|
||||||
|
existing.implemented = true
|
||||||
|
} else {
|
||||||
|
sharedMemory.component_inventory.push({
|
||||||
|
name: spec.name,
|
||||||
|
implementation_path: `${sessionFolder}/build/component-files/${spec.name}/`,
|
||||||
|
implemented: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Write(`${sessionFolder}/shared-memory.json`, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
// Collect output summary
|
||||||
|
const outputFiles = isTokenBuild
|
||||||
|
? Glob({ pattern: `${sessionFolder}/build/token-files/*` })
|
||||||
|
: Glob({ pattern: `${sessionFolder}/build/component-files/**/*` })
|
||||||
|
|
||||||
|
// Report
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log",
|
||||||
|
team: teamName,
|
||||||
|
from: "implementer",
|
||||||
|
to: "coordinator",
|
||||||
|
type: "build_complete",
|
||||||
|
summary: `[implementer] ${isTokenBuild ? '令牌代码' : '组件代码'}实现完成, ${outputFiles.length} 个文件`,
|
||||||
|
ref: `${sessionFolder}/build/`
|
||||||
|
})
|
||||||
|
|
||||||
|
SendMessage({
|
||||||
|
type: "message",
|
||||||
|
recipient: "coordinator",
|
||||||
|
content: `## [implementer] 构建完成\n\n- 类型: ${isTokenBuild ? '设计令牌实现' : '组件代码实现'}\n- 输出文件: ${outputFiles.length}\n- 目录: ${sessionFolder}/build/${isTokenBuild ? 'token-files/' : 'component-files/'}\n\n### 产出文件\n${outputFiles.map(f => `- ${f}`).join('\n')}`,
|
||||||
|
summary: `[implementer] build_complete: ${outputFiles.length} files`
|
||||||
|
})
|
||||||
|
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| 设计令牌文件不存在 | 等待 sync point 或报告 error |
|
||||||
|
| 组件规格不完整 | 使用默认值 + 标记待确认 |
|
||||||
|
| 代码生成失败 | 重试 1 次,仍失败则上报 |
|
||||||
|
| 检测到硬编码值 | 自动替换为令牌引用 |
|
||||||
|
| 项目技术栈未知 | 默认 React + CSS Modules |
|
||||||
229
.claude/skills/team-uidesign/roles/researcher.md
Normal file
229
.claude/skills/team-uidesign/roles/researcher.md
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
# Role: researcher
|
||||||
|
|
||||||
|
Design system analyst responsible for current state assessment, component inventory, accessibility baseline, and competitive research.
|
||||||
|
|
||||||
|
## Role Identity
|
||||||
|
|
||||||
|
- **Name**: `researcher`
|
||||||
|
- **Task Prefix**: `RESEARCH`
|
||||||
|
- **Responsibility Type**: Read-only analysis
|
||||||
|
- **Responsibility**: Design system analysis, component inventory, accessibility audit
|
||||||
|
- **Toolbox**: Read, Glob, Grep, Bash(read-only), Task(cli-explore-agent), WebSearch, WebFetch
|
||||||
|
|
||||||
|
## Message Types
|
||||||
|
|
||||||
|
| Type | When | Content |
|
||||||
|
|------|------|---------|
|
||||||
|
| `research_ready` | Research complete | Summary of findings + file references |
|
||||||
|
| `research_progress` | Intermediate update | Current progress status |
|
||||||
|
| `error` | Failure | Error details |
|
||||||
|
|
||||||
|
## Execution
|
||||||
|
|
||||||
|
### Phase 1: Task Discovery
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const tasks = TaskList()
|
||||||
|
const myTasks = tasks.filter(t =>
|
||||||
|
t.subject.startsWith('RESEARCH-') &&
|
||||||
|
t.owner === 'researcher' &&
|
||||||
|
t.status === 'pending' &&
|
||||||
|
t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
if (myTasks.length === 0) return
|
||||||
|
const task = TaskGet({ taskId: myTasks[0].id })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Context Loading + Shared Memory Read
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Extract session folder from task description
|
||||||
|
const sessionFolder = task.description.match(/Session:\s*(.+)/)?.[1]?.trim()
|
||||||
|
|
||||||
|
// Read shared memory for accumulated knowledge
|
||||||
|
let sharedMemory = {}
|
||||||
|
try {
|
||||||
|
sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`))
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
// Read existing component inventory if any
|
||||||
|
const existingInventory = sharedMemory.component_inventory || []
|
||||||
|
const existingPatterns = sharedMemory.accessibility_patterns || []
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Core Execution
|
||||||
|
|
||||||
|
Research is divided into 3 parallel analysis streams:
|
||||||
|
|
||||||
|
#### Stream 1: Design System Analysis
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Use cli-explore-agent for codebase analysis
|
||||||
|
Task({
|
||||||
|
subagent_type: "cli-explore-agent",
|
||||||
|
run_in_background: false,
|
||||||
|
prompt: `
|
||||||
|
## Design System Analysis
|
||||||
|
Topic: ${task.description}
|
||||||
|
Session: ${sessionFolder}
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
1. Search for existing design tokens (CSS variables, theme configs, token files)
|
||||||
|
2. Identify styling patterns (CSS-in-JS, CSS modules, utility classes, SCSS)
|
||||||
|
3. Map color palette, typography scale, spacing system
|
||||||
|
4. Find component library usage (MUI, Ant Design, custom, etc.)
|
||||||
|
|
||||||
|
## Output
|
||||||
|
Write to: ${sessionFolder}/research/design-system-analysis.json
|
||||||
|
|
||||||
|
Schema:
|
||||||
|
{
|
||||||
|
"existing_tokens": { "colors": [], "typography": [], "spacing": [], "shadows": [] },
|
||||||
|
"styling_approach": "css-modules | css-in-js | utility | scss | mixed",
|
||||||
|
"component_library": { "name": "", "version": "", "usage_count": 0 },
|
||||||
|
"custom_components": [],
|
||||||
|
"inconsistencies": [],
|
||||||
|
"_metadata": { "timestamp": "..." }
|
||||||
|
}
|
||||||
|
`
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Stream 2: Component Inventory
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Discover all UI components in the codebase
|
||||||
|
Task({
|
||||||
|
subagent_type: "Explore",
|
||||||
|
run_in_background: false,
|
||||||
|
prompt: `
|
||||||
|
Find all UI components in the codebase. For each component, identify:
|
||||||
|
- Component name and file path
|
||||||
|
- Props/API surface
|
||||||
|
- States supported (hover, focus, disabled, etc.)
|
||||||
|
- Accessibility attributes (ARIA labels, roles, etc.)
|
||||||
|
- Dependencies on other components
|
||||||
|
|
||||||
|
Write findings to: ${sessionFolder}/research/component-inventory.json
|
||||||
|
|
||||||
|
Schema:
|
||||||
|
{
|
||||||
|
"components": [{
|
||||||
|
"name": "", "path": "", "type": "atom|molecule|organism|template",
|
||||||
|
"props": [], "states": [], "aria_attributes": [],
|
||||||
|
"dependencies": [], "usage_count": 0
|
||||||
|
}],
|
||||||
|
"patterns": {
|
||||||
|
"naming_convention": "",
|
||||||
|
"file_structure": "",
|
||||||
|
"state_management": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Stream 3: Accessibility Baseline
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Assess current accessibility state
|
||||||
|
Task({
|
||||||
|
subagent_type: "Explore",
|
||||||
|
run_in_background: false,
|
||||||
|
prompt: `
|
||||||
|
Perform accessibility baseline audit:
|
||||||
|
1. Check for ARIA attributes usage patterns
|
||||||
|
2. Identify keyboard navigation support
|
||||||
|
3. Check color contrast ratios (if design tokens found)
|
||||||
|
4. Find focus management patterns
|
||||||
|
5. Check semantic HTML usage
|
||||||
|
|
||||||
|
Write to: ${sessionFolder}/research/accessibility-audit.json
|
||||||
|
|
||||||
|
Schema:
|
||||||
|
{
|
||||||
|
"wcag_level": "none|partial-A|A|partial-AA|AA",
|
||||||
|
"aria_coverage": { "labeled": 0, "total": 0, "percentage": 0 },
|
||||||
|
"keyboard_nav": { "supported": [], "missing": [] },
|
||||||
|
"contrast_issues": [],
|
||||||
|
"focus_management": { "pattern": "", "coverage": "" },
|
||||||
|
"semantic_html": { "score": 0, "issues": [] },
|
||||||
|
"recommendations": []
|
||||||
|
}
|
||||||
|
`
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Validation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Verify all 3 research outputs exist
|
||||||
|
const requiredFiles = [
|
||||||
|
'design-system-analysis.json',
|
||||||
|
'component-inventory.json',
|
||||||
|
'accessibility-audit.json'
|
||||||
|
]
|
||||||
|
|
||||||
|
const missing = requiredFiles.filter(f => {
|
||||||
|
try { Read(`${sessionFolder}/research/${f}`); return false }
|
||||||
|
catch { return true }
|
||||||
|
})
|
||||||
|
|
||||||
|
if (missing.length > 0) {
|
||||||
|
// Re-run missing streams
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile research summary
|
||||||
|
const designAnalysis = JSON.parse(Read(`${sessionFolder}/research/design-system-analysis.json`))
|
||||||
|
const inventory = JSON.parse(Read(`${sessionFolder}/research/component-inventory.json`))
|
||||||
|
const a11yAudit = JSON.parse(Read(`${sessionFolder}/research/accessibility-audit.json`))
|
||||||
|
|
||||||
|
const researchSummary = {
|
||||||
|
design_system_exists: !!designAnalysis.component_library?.name,
|
||||||
|
styling_approach: designAnalysis.styling_approach,
|
||||||
|
total_components: inventory.components?.length || 0,
|
||||||
|
accessibility_level: a11yAudit.wcag_level,
|
||||||
|
key_findings: [],
|
||||||
|
recommendations: []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Report + Shared Memory Write
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Update shared memory
|
||||||
|
sharedMemory.component_inventory = inventory.components || []
|
||||||
|
sharedMemory.accessibility_patterns = a11yAudit.recommendations || []
|
||||||
|
Write(`${sessionFolder}/shared-memory.json`, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
// Log and report
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log",
|
||||||
|
team: teamName,
|
||||||
|
from: "researcher",
|
||||||
|
to: "coordinator",
|
||||||
|
type: "research_ready",
|
||||||
|
summary: `[researcher] 调研完成: ${researchSummary.total_components} 个组件, 可访问性等级 ${researchSummary.accessibility_level}, 样式方案 ${researchSummary.styling_approach}`,
|
||||||
|
ref: `${sessionFolder}/research/`
|
||||||
|
})
|
||||||
|
|
||||||
|
SendMessage({
|
||||||
|
type: "message",
|
||||||
|
recipient: "coordinator",
|
||||||
|
content: `## [researcher] 设计系统调研完成\n\n- 现有组件: ${researchSummary.total_components}\n- 样式方案: ${researchSummary.styling_approach}\n- 可访问性等级: ${researchSummary.accessibility_level}\n- 组件库: ${designAnalysis.component_library?.name || '无'}\n\n产出目录: ${sessionFolder}/research/`,
|
||||||
|
summary: `[researcher] 调研完成`
|
||||||
|
})
|
||||||
|
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||||
|
|
||||||
|
// Check for next task
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| 无法检测设计系统 | 报告为 "greenfield",建议从零构建 |
|
||||||
|
| 组件盘点超时 | 报告已发现部分 + 标记未扫描区域 |
|
||||||
|
| 可访问性工具不可用 | 手动抽样检查 + 降级报告 |
|
||||||
294
.claude/skills/team-uidesign/roles/reviewer.md
Normal file
294
.claude/skills/team-uidesign/roles/reviewer.md
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
# Role: reviewer
|
||||||
|
|
||||||
|
Design auditor responsible for consistency, accessibility compliance, and visual quality review. Acts as Critic in the designer↔reviewer Generator-Critic loop. Serves as sync point gatekeeper in dual-track pipelines.
|
||||||
|
|
||||||
|
## Role Identity
|
||||||
|
|
||||||
|
- **Name**: `reviewer`
|
||||||
|
- **Task Prefix**: `AUDIT`
|
||||||
|
- **Responsibility Type**: Read-only analysis (Validation)
|
||||||
|
- **Responsibility**: Design consistency audit, accessibility compliance, visual review
|
||||||
|
- **Toolbox**: Read, Glob, Grep, Bash(read-only), Task(Explore)
|
||||||
|
|
||||||
|
## Message Types
|
||||||
|
|
||||||
|
| Type | When | Content |
|
||||||
|
|------|------|---------|
|
||||||
|
| `audit_passed` | Score >= 8, no critical issues | Audit report + score |
|
||||||
|
| `audit_result` | Score 6-7, non-critical issues | Feedback for GC revision |
|
||||||
|
| `fix_required` | Score < 6, critical issues found | Critical issues list |
|
||||||
|
| `error` | Failure | Error details |
|
||||||
|
|
||||||
|
## Execution
|
||||||
|
|
||||||
|
### Phase 1: Task Discovery
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const tasks = TaskList()
|
||||||
|
const myTasks = tasks.filter(t =>
|
||||||
|
t.subject.startsWith('AUDIT-') &&
|
||||||
|
t.owner === 'reviewer' &&
|
||||||
|
t.status === 'pending' &&
|
||||||
|
t.blockedBy.length === 0
|
||||||
|
)
|
||||||
|
if (myTasks.length === 0) return
|
||||||
|
const task = TaskGet({ taskId: myTasks[0].id })
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||||
|
|
||||||
|
// Detect audit type
|
||||||
|
const isTokenAudit = task.subject.includes('令牌') || task.subject.includes('token')
|
||||||
|
const isComponentAudit = task.subject.includes('组件') || task.subject.includes('component')
|
||||||
|
const isFinalAudit = task.subject.includes('最终') || task.subject.includes('final')
|
||||||
|
const isSyncPoint = task.subject.includes('同步点') || task.subject.includes('Sync')
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Context Loading + Shared Memory Read
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const sessionFolder = task.description.match(/Session:\s*(.+)/)?.[1]?.trim()
|
||||||
|
|
||||||
|
// Read shared memory for audit history
|
||||||
|
let sharedMemory = {}
|
||||||
|
try {
|
||||||
|
sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`))
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
const auditHistory = sharedMemory.audit_history || []
|
||||||
|
const tokenRegistry = sharedMemory.design_token_registry || {}
|
||||||
|
|
||||||
|
// Read design artifacts to audit
|
||||||
|
let designTokens = null
|
||||||
|
let componentSpecs = []
|
||||||
|
try {
|
||||||
|
designTokens = JSON.parse(Read(`${sessionFolder}/design/design-tokens.json`))
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
const specFiles = Glob({ pattern: `${sessionFolder}/design/component-specs/*.md` })
|
||||||
|
componentSpecs = specFiles.map(f => ({ path: f, content: Read(f) }))
|
||||||
|
|
||||||
|
// Read build artifacts if final audit
|
||||||
|
let buildArtifacts = []
|
||||||
|
if (isFinalAudit) {
|
||||||
|
const buildFiles = Glob({ pattern: `${sessionFolder}/build/**/*` })
|
||||||
|
buildArtifacts = buildFiles
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Core Execution
|
||||||
|
|
||||||
|
#### Audit Dimensions
|
||||||
|
|
||||||
|
4 dimensions scored on 1-10 scale:
|
||||||
|
|
||||||
|
| Dimension | Weight | Criteria |
|
||||||
|
|-----------|--------|----------|
|
||||||
|
| Consistency | 25% | Token usage, naming conventions, visual uniformity |
|
||||||
|
| Accessibility | 30% | WCAG AA compliance, ARIA attributes, keyboard nav, contrast |
|
||||||
|
| Completeness | 25% | All states defined, responsive specs, edge cases |
|
||||||
|
| Quality | 20% | Token reference integrity, documentation clarity, maintainability |
|
||||||
|
|
||||||
|
#### Token Audit (AUDIT for token systems)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
if (isTokenAudit && designTokens) {
|
||||||
|
const tokenAudit = {
|
||||||
|
consistency: { score: 0, issues: [] },
|
||||||
|
accessibility: { score: 0, issues: [] },
|
||||||
|
completeness: { score: 0, issues: [] },
|
||||||
|
quality: { score: 0, issues: [] }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consistency checks
|
||||||
|
// - Naming convention (kebab-case, semantic names)
|
||||||
|
// - Value patterns (consistent units: rem/px/%)
|
||||||
|
// - Theme completeness (light + dark for all colors)
|
||||||
|
|
||||||
|
// Accessibility checks
|
||||||
|
// - Color contrast ratios (text on background >= 4.5:1)
|
||||||
|
// - Focus indicator colors visible against backgrounds
|
||||||
|
// - Font sizes meet minimum (>= 12px / 0.75rem)
|
||||||
|
|
||||||
|
// Completeness checks
|
||||||
|
// - All token categories present (color, typography, spacing, shadow, border)
|
||||||
|
// - Breakpoints defined
|
||||||
|
// - Semantic color tokens (success, warning, error, info)
|
||||||
|
|
||||||
|
// Quality checks
|
||||||
|
// - $type metadata present (W3C format)
|
||||||
|
// - Values are valid (CSS-parseable)
|
||||||
|
// - No duplicate definitions
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Component Audit
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
if (isComponentAudit && componentSpecs.length > 0) {
|
||||||
|
componentSpecs.forEach(spec => {
|
||||||
|
// Consistency: token references resolve, naming matches convention
|
||||||
|
// Accessibility: ARIA roles defined, keyboard behavior specified, focus indicator
|
||||||
|
// Completeness: all 5 states (default/hover/focus/active/disabled), responsive breakpoints
|
||||||
|
// Quality: clear descriptions, variant system, interaction specs
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Final Audit (Cross-cutting)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
if (isFinalAudit) {
|
||||||
|
// Token ↔ Component consistency
|
||||||
|
// - All token references in components resolve to defined tokens
|
||||||
|
// - No hardcoded values in component specs
|
||||||
|
|
||||||
|
// Code ↔ Design consistency (if build artifacts exist)
|
||||||
|
// - CSS variables match design tokens
|
||||||
|
// - Component implementation matches spec states
|
||||||
|
// - ARIA attributes implemented as specified
|
||||||
|
|
||||||
|
// Cross-component consistency
|
||||||
|
// - Consistent spacing patterns
|
||||||
|
// - Consistent color usage for similar elements
|
||||||
|
// - Consistent interaction patterns
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Score Calculation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const weights = { consistency: 0.25, accessibility: 0.30, completeness: 0.25, quality: 0.20 }
|
||||||
|
const overallScore = Math.round(
|
||||||
|
tokenAudit.consistency.score * weights.consistency +
|
||||||
|
tokenAudit.accessibility.score * weights.accessibility +
|
||||||
|
tokenAudit.completeness.score * weights.completeness +
|
||||||
|
tokenAudit.quality.score * weights.quality
|
||||||
|
)
|
||||||
|
|
||||||
|
// Severity classification
|
||||||
|
const criticalIssues = allIssues.filter(i => i.severity === 'CRITICAL')
|
||||||
|
const highIssues = allIssues.filter(i => i.severity === 'HIGH')
|
||||||
|
const mediumIssues = allIssues.filter(i => i.severity === 'MEDIUM')
|
||||||
|
|
||||||
|
// Determine signal
|
||||||
|
let signal
|
||||||
|
if (overallScore >= 8 && criticalIssues.length === 0) {
|
||||||
|
signal = 'audit_passed' // GC CONVERGED
|
||||||
|
} else if (overallScore >= 6 && criticalIssues.length === 0) {
|
||||||
|
signal = 'audit_result' // GC REVISION NEEDED
|
||||||
|
} else {
|
||||||
|
signal = 'fix_required' // GC CRITICAL FIX NEEDED
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Audit Report Generation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const auditNumber = task.subject.match(/AUDIT-(\d+)/)?.[1] || '001'
|
||||||
|
const auditReport = `# Audit Report: AUDIT-${auditNumber}
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
- **Overall Score**: ${overallScore}/10
|
||||||
|
- **Signal**: ${signal}
|
||||||
|
- **Critical Issues**: ${criticalIssues.length}
|
||||||
|
- **High Issues**: ${highIssues.length}
|
||||||
|
- **Medium Issues**: ${mediumIssues.length}
|
||||||
|
${isSyncPoint ? `\n**⚡ Sync Point**: ${signal === 'audit_passed' ? 'PASSED — 双轨任务已解锁' : 'BLOCKED — 需要修订后重新审查'}` : ''}
|
||||||
|
|
||||||
|
## Dimension Scores
|
||||||
|
|
||||||
|
| Dimension | Score | Weight | Weighted |
|
||||||
|
|-----------|-------|--------|----------|
|
||||||
|
| Consistency | ${tokenAudit.consistency.score}/10 | 25% | ${(tokenAudit.consistency.score * 0.25).toFixed(1)} |
|
||||||
|
| Accessibility | ${tokenAudit.accessibility.score}/10 | 30% | ${(tokenAudit.accessibility.score * 0.30).toFixed(1)} |
|
||||||
|
| Completeness | ${tokenAudit.completeness.score}/10 | 25% | ${(tokenAudit.completeness.score * 0.25).toFixed(1)} |
|
||||||
|
| Quality | ${tokenAudit.quality.score}/10 | 20% | ${(tokenAudit.quality.score * 0.20).toFixed(1)} |
|
||||||
|
|
||||||
|
## Critical Issues
|
||||||
|
${criticalIssues.map(i => `- **[CRITICAL]** ${i.description}\n Location: ${i.location}\n Fix: ${i.suggestion}`).join('\n')}
|
||||||
|
|
||||||
|
## High Issues
|
||||||
|
${highIssues.map(i => `- **[HIGH]** ${i.description}\n Fix: ${i.suggestion}`).join('\n')}
|
||||||
|
|
||||||
|
## Medium Issues
|
||||||
|
${mediumIssues.map(i => `- [MEDIUM] ${i.description}`).join('\n')}
|
||||||
|
|
||||||
|
## Recommendations
|
||||||
|
${recommendations.join('\n')}
|
||||||
|
|
||||||
|
## GC Loop Status
|
||||||
|
- Signal: ${signal}
|
||||||
|
- ${signal === 'audit_passed' ? '✅ 设计通过审查' : `⚠️ 需要 designer 修订: ${criticalIssues.length + highIssues.length} 个问题需修复`}
|
||||||
|
`
|
||||||
|
|
||||||
|
Write(`${sessionFolder}/audit/audit-${auditNumber}.md`, auditReport)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Validation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Verify audit report written
|
||||||
|
try {
|
||||||
|
Read(`${sessionFolder}/audit/audit-${auditNumber}.md`)
|
||||||
|
} catch {
|
||||||
|
// Re-write audit report
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cross-reference with previous audits for trend
|
||||||
|
if (auditHistory.length > 0) {
|
||||||
|
const previousScore = auditHistory[auditHistory.length - 1].score
|
||||||
|
const trend = overallScore > previousScore ? 'improving' : overallScore === previousScore ? 'stable' : 'declining'
|
||||||
|
// Include trend in report
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Report + Shared Memory Write
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Update shared memory
|
||||||
|
sharedMemory.audit_history.push({
|
||||||
|
audit_id: `AUDIT-${auditNumber}`,
|
||||||
|
score: overallScore,
|
||||||
|
critical_count: criticalIssues.length,
|
||||||
|
signal: signal,
|
||||||
|
is_sync_point: isSyncPoint,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
})
|
||||||
|
Write(`${sessionFolder}/shared-memory.json`, JSON.stringify(sharedMemory, null, 2))
|
||||||
|
|
||||||
|
// Report to coordinator
|
||||||
|
mcp__ccw-tools__team_msg({
|
||||||
|
operation: "log",
|
||||||
|
team: teamName,
|
||||||
|
from: "reviewer",
|
||||||
|
to: "coordinator",
|
||||||
|
type: signal,
|
||||||
|
summary: `[reviewer] AUDIT-${auditNumber}: 分数 ${overallScore}/10, 严重问题 ${criticalIssues.length}${isSyncPoint ? ' [同步点]' : ''}`,
|
||||||
|
ref: `${sessionFolder}/audit/audit-${auditNumber}.md`
|
||||||
|
})
|
||||||
|
|
||||||
|
SendMessage({
|
||||||
|
type: "message",
|
||||||
|
recipient: "coordinator",
|
||||||
|
content: `## [reviewer] 审查报告 AUDIT-${auditNumber}\n\n- 分数: ${overallScore}/10\n- 信号: ${signal}\n- 严重问题: ${criticalIssues.length}\n- 高级问题: ${highIssues.length}\n${isSyncPoint ? `\n⚡ **同步点**: ${signal === 'audit_passed' ? '通过' : '未通过'}` : ''}\n\n${signal !== 'audit_passed' ? `### 需修复:\n${criticalIssues.concat(highIssues).map(i => `- ${i.description}`).join('\n')}` : ''}`,
|
||||||
|
summary: `[reviewer] AUDIT-${auditNumber}: ${overallScore}/10, ${signal}`
|
||||||
|
})
|
||||||
|
|
||||||
|
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||||
|
```
|
||||||
|
|
||||||
|
## Severity Classification
|
||||||
|
|
||||||
|
| Severity | Criteria | GC Impact |
|
||||||
|
|----------|----------|-----------|
|
||||||
|
| CRITICAL | 可访问性不合规 (对比度 <3:1), 缺少关键状态 | 阻塞 GC 收敛 |
|
||||||
|
| HIGH | 令牌引用不一致, 缺少 ARIA 属性, 部分状态缺失 | 计入 GC 评分 |
|
||||||
|
| MEDIUM | 命名不规范, 文档不完整, 次要样式问题 | 建议修复 |
|
||||||
|
| LOW | 代码风格, 可选优化 | 信息性 |
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Scenario | Resolution |
|
||||||
|
|----------|------------|
|
||||||
|
| 设计文件不存在 | 报告 error,通知 coordinator |
|
||||||
|
| 令牌格式无法解析 | 降级为文本审查 |
|
||||||
|
| 审查维度无法评估 | 标记为 N/A,不计入总分 |
|
||||||
107
.claude/skills/team-uidesign/specs/team-config.json
Normal file
107
.claude/skills/team-uidesign/specs/team-config.json
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
{
|
||||||
|
"team_name": "uidesign",
|
||||||
|
"team_display_name": "UI Design",
|
||||||
|
"description": "UI design team with CP-9 Dual-Track for parallel design and implementation",
|
||||||
|
"version": "1.0.0",
|
||||||
|
|
||||||
|
"roles": {
|
||||||
|
"coordinator": {
|
||||||
|
"task_prefix": null,
|
||||||
|
"responsibility": "Scope assessment, dual-track orchestration, sync point management, GC loop control",
|
||||||
|
"message_types": ["task_unblocked", "sync_checkpoint", "fix_required", "error", "shutdown"]
|
||||||
|
},
|
||||||
|
"researcher": {
|
||||||
|
"task_prefix": "RESEARCH",
|
||||||
|
"responsibility": "Design system analysis, component inventory, accessibility baseline audit",
|
||||||
|
"message_types": ["research_ready", "research_progress", "error"]
|
||||||
|
},
|
||||||
|
"designer": {
|
||||||
|
"task_prefix": "DESIGN",
|
||||||
|
"responsibility": "Design token definition, component specifications, layout design",
|
||||||
|
"message_types": ["design_ready", "design_revision", "design_progress", "error"]
|
||||||
|
},
|
||||||
|
"reviewer": {
|
||||||
|
"task_prefix": "AUDIT",
|
||||||
|
"additional_prefixes": [],
|
||||||
|
"responsibility": "Design consistency audit, accessibility compliance, visual review",
|
||||||
|
"message_types": ["audit_result", "audit_passed", "fix_required", "error"]
|
||||||
|
},
|
||||||
|
"implementer": {
|
||||||
|
"task_prefix": "BUILD",
|
||||||
|
"responsibility": "Component code implementation, CSS generation, design token consumption",
|
||||||
|
"message_types": ["build_complete", "build_progress", "error"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"pipelines": {
|
||||||
|
"component": {
|
||||||
|
"description": "Single component: research → design → audit → build",
|
||||||
|
"task_chain": ["RESEARCH-001", "DESIGN-001", "AUDIT-001", "BUILD-001"],
|
||||||
|
"complexity": "low"
|
||||||
|
},
|
||||||
|
"system": {
|
||||||
|
"description": "Design system with dual-track: design tokens → audit → parallel build+components → audit → build components",
|
||||||
|
"task_chain": [
|
||||||
|
"RESEARCH-001",
|
||||||
|
"DESIGN-001:tokens", "AUDIT-001",
|
||||||
|
"DESIGN-002:components || BUILD-001:tokens",
|
||||||
|
"AUDIT-002", "BUILD-002:components"
|
||||||
|
],
|
||||||
|
"sync_points": ["AUDIT-001", "AUDIT-002"],
|
||||||
|
"complexity": "medium"
|
||||||
|
},
|
||||||
|
"full-system": {
|
||||||
|
"description": "Complete design system with 3 audit checkpoints",
|
||||||
|
"task_chain": [
|
||||||
|
"RESEARCH-001",
|
||||||
|
"DESIGN-001:tokens", "AUDIT-001",
|
||||||
|
"DESIGN-002:components || BUILD-001:tokens",
|
||||||
|
"AUDIT-002",
|
||||||
|
"BUILD-002:components", "AUDIT-003"
|
||||||
|
],
|
||||||
|
"sync_points": ["AUDIT-001", "AUDIT-002", "AUDIT-003"],
|
||||||
|
"complexity": "high"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"innovation_patterns": {
|
||||||
|
"generator_critic": {
|
||||||
|
"generator": "designer",
|
||||||
|
"critic": "reviewer",
|
||||||
|
"max_rounds": 2,
|
||||||
|
"convergence": "audit.score >= 8 && audit.critical_count === 0",
|
||||||
|
"escalation": "Coordinator intervenes after max rounds"
|
||||||
|
},
|
||||||
|
"shared_memory": {
|
||||||
|
"file": "shared-memory.json",
|
||||||
|
"fields": {
|
||||||
|
"researcher": ["component_inventory", "accessibility_patterns"],
|
||||||
|
"designer": ["design_token_registry", "style_decisions"],
|
||||||
|
"reviewer": ["audit_history"],
|
||||||
|
"implementer": ["component_inventory"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dynamic_pipeline": {
|
||||||
|
"criteria": {
|
||||||
|
"component": "scope.component_count <= 1",
|
||||||
|
"system": "scope.component_count <= 5 && scope.has_token_system",
|
||||||
|
"full-system": "scope.component_count > 5 || scope.is_full_redesign"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dual_track": {
|
||||||
|
"pattern": "CP-9",
|
||||||
|
"description": "Design and implementation proceed in parallel after sync checkpoints",
|
||||||
|
"sync_mechanism": "AUDIT tasks serve as sync points between tracks",
|
||||||
|
"fallback": "If dual-track fails, coordinator falls back to sequential execution"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"session_dirs": {
|
||||||
|
"base": ".workflow/.team/UDS-{slug}-{YYYY-MM-DD}/",
|
||||||
|
"research": "research/",
|
||||||
|
"design": "design/",
|
||||||
|
"audit": "audit/",
|
||||||
|
"build": "build/",
|
||||||
|
"messages": ".workflow/.team-msg/{team-name}/"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user