diff --git a/.claude/skills/team-brainstorm/SKILL.md b/.claude/skills/team-brainstorm/SKILL.md new file mode 100644 index 00000000..7ddd0ded --- /dev/null +++ b/.claude/skills/team-brainstorm/SKILL.md @@ -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: "", + summary: `[${role}] `, + ref: "" +}) +``` + +**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 "" --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 | diff --git a/.claude/skills/team-brainstorm/roles/challenger.md b/.claude/skills/team-brainstorm/roles/challenger.md new file mode 100644 index 00000000..e77dca29 --- /dev/null +++ b/.claude/skills/team-brainstorm/roles/challenger.md @@ -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 | diff --git a/.claude/skills/team-brainstorm/roles/coordinator.md b/.claude/skills/team-brainstorm/roles/coordinator.md new file mode 100644 index 00000000..80471518 --- /dev/null +++ b/.claude/skills/team-brainstorm/roles/coordinator.md @@ -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 决定方向 | diff --git a/.claude/skills/team-brainstorm/roles/evaluator.md b/.claude/skills/team-brainstorm/roles/evaluator.md new file mode 100644 index 00000000..6fdf24f2 --- /dev/null +++ b/.claude/skills/team-brainstorm/roles/evaluator.md @@ -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 | diff --git a/.claude/skills/team-brainstorm/roles/ideator.md b/.claude/skills/team-brainstorm/roles/ideator.md new file mode 100644 index 00000000..3d7de836 --- /dev/null +++ b/.claude/skills/team-brainstorm/roles/ideator.md @@ -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 | diff --git a/.claude/skills/team-brainstorm/roles/synthesizer.md b/.claude/skills/team-brainstorm/roles/synthesizer.md new file mode 100644 index 00000000..6c73172a --- /dev/null +++ b/.claude/skills/team-brainstorm/roles/synthesizer.md @@ -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 | diff --git a/.claude/skills/team-brainstorm/specs/team-config.json b/.claude/skills/team-brainstorm/specs/team-config.json new file mode 100644 index 00000000..07829b5b --- /dev/null +++ b/.claude/skills/team-brainstorm/specs/team-config.json @@ -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}/" + } +} diff --git a/.claude/skills/team-iterdev/SKILL.md b/.claude/skills/team-iterdev/SKILL.md new file mode 100644 index 00000000..8e01bac8 --- /dev/null +++ b/.claude/skills/team-iterdev/SKILL.md @@ -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: "", summary: `[${role}] `, ref: "" +}) +``` + +| 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 "" --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 | diff --git a/.claude/skills/team-iterdev/roles/architect.md b/.claude/skills/team-iterdev/roles/architect.md new file mode 100644 index 00000000..91aabc03 --- /dev/null +++ b/.claude/skills/team-iterdev/roles/architect.md @@ -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 | diff --git a/.claude/skills/team-iterdev/roles/coordinator.md b/.claude/skills/team-iterdev/roles/coordinator.md new file mode 100644 index 00000000..3cbbac2f --- /dev/null +++ b/.claude/skills/team-iterdev/roles/coordinator.md @@ -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 | diff --git a/.claude/skills/team-iterdev/roles/developer.md b/.claude/skills/team-iterdev/roles/developer.md new file mode 100644 index 00000000..3f622222 --- /dev/null +++ b/.claude/skills/team-iterdev/roles/developer.md @@ -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 | diff --git a/.claude/skills/team-iterdev/roles/reviewer.md b/.claude/skills/team-iterdev/roles/reviewer.md new file mode 100644 index 00000000..b3816d24 --- /dev/null +++ b/.claude/skills/team-iterdev/roles/reviewer.md @@ -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 | diff --git a/.claude/skills/team-iterdev/roles/tester.md b/.claude/skills/team-iterdev/roles/tester.md new file mode 100644 index 00000000..1cc30ed0 --- /dev/null +++ b/.claude/skills/team-iterdev/roles/tester.md @@ -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 | diff --git a/.claude/skills/team-iterdev/specs/team-config.json b/.claude/skills/team-iterdev/specs/team-config.json new file mode 100644 index 00000000..dabf54a2 --- /dev/null +++ b/.claude/skills/team-iterdev/specs/team-config.json @@ -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}/" + } +} diff --git a/.claude/skills/team-testing/SKILL.md b/.claude/skills/team-testing/SKILL.md new file mode 100644 index 00000000..f0f827ad --- /dev/null +++ b/.claude/skills/team-testing/SKILL.md @@ -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: "", + summary: `[${role}] `, + ref: "" +}) +``` + +| 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 "" --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 | diff --git a/.claude/skills/team-testing/roles/analyst.md b/.claude/skills/team-testing/roles/analyst.md new file mode 100644 index 00000000..4b89be18 --- /dev/null +++ b/.claude/skills/team-testing/roles/analyst.md @@ -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 | diff --git a/.claude/skills/team-testing/roles/coordinator.md b/.claude/skills/team-testing/roles/coordinator.md new file mode 100644 index 00000000..d721c50c --- /dev/null +++ b/.claude/skills/team-testing/roles/coordinator.md @@ -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 分析 | +| 覆盖率工具不可用 | 降级为通过率判断 | diff --git a/.claude/skills/team-testing/roles/executor.md b/.claude/skills/team-testing/roles/executor.md new file mode 100644 index 00000000..e70f03e3 --- /dev/null +++ b/.claude/skills/team-testing/roles/executor.md @@ -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 | diff --git a/.claude/skills/team-testing/roles/generator.md b/.claude/skills/team-testing/roles/generator.md new file mode 100644 index 00000000..8c321cda --- /dev/null +++ b/.claude/skills/team-testing/roles/generator.md @@ -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 | diff --git a/.claude/skills/team-testing/roles/strategist.md b/.claude/skills/team-testing/roles/strategist.md new file mode 100644 index 00000000..42500f33 --- /dev/null +++ b/.claude/skills/team-testing/roles/strategist.md @@ -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 | diff --git a/.claude/skills/team-testing/specs/team-config.json b/.claude/skills/team-testing/specs/team-config.json new file mode 100644 index 00000000..3249e308 --- /dev/null +++ b/.claude/skills/team-testing/specs/team-config.json @@ -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}/" + } +} diff --git a/.claude/skills/team-uidesign/SKILL.md b/.claude/skills/team-uidesign/SKILL.md new file mode 100644 index 00000000..e0a6bd22 --- /dev/null +++ b/.claude/skills/team-uidesign/SKILL.md @@ -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: "", + summary: `[${role}] `, + ref: "" +}) +``` + +**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 "" --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 或上报 | diff --git a/.claude/skills/team-uidesign/roles/coordinator.md b/.claude/skills/team-uidesign/roles/coordinator.md new file mode 100644 index 00000000..f8c34ec2 --- /dev/null +++ b/.claude/skills/team-uidesign/roles/coordinator.md @@ -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 | diff --git a/.claude/skills/team-uidesign/roles/designer.md b/.claude/skills/team-uidesign/roles/designer.md new file mode 100644 index 00000000..1faeef72 --- /dev/null +++ b/.claude/skills/team-uidesign/roles/designer.md @@ -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 | diff --git a/.claude/skills/team-uidesign/roles/implementer.md b/.claude/skills/team-uidesign/roles/implementer.md new file mode 100644 index 00000000..20bd0d5b --- /dev/null +++ b/.claude/skills/team-uidesign/roles/implementer.md @@ -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 | diff --git a/.claude/skills/team-uidesign/roles/researcher.md b/.claude/skills/team-uidesign/roles/researcher.md new file mode 100644 index 00000000..0ee5e41b --- /dev/null +++ b/.claude/skills/team-uidesign/roles/researcher.md @@ -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",建议从零构建 | +| 组件盘点超时 | 报告已发现部分 + 标记未扫描区域 | +| 可访问性工具不可用 | 手动抽样检查 + 降级报告 | diff --git a/.claude/skills/team-uidesign/roles/reviewer.md b/.claude/skills/team-uidesign/roles/reviewer.md new file mode 100644 index 00000000..9f6296e2 --- /dev/null +++ b/.claude/skills/team-uidesign/roles/reviewer.md @@ -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,不计入总分 | diff --git a/.claude/skills/team-uidesign/specs/team-config.json b/.claude/skills/team-uidesign/specs/team-config.json new file mode 100644 index 00000000..3f115154 --- /dev/null +++ b/.claude/skills/team-uidesign/specs/team-config.json @@ -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}/" + } +}