diff --git a/.claude/skills/team-lifecycle-v2/SKILL.md b/.claude/skills/team-lifecycle-v2/SKILL.md index 44e4cd2f..85e9a94b 100644 --- a/.claude/skills/team-lifecycle-v2/SKILL.md +++ b/.claude/skills/team-lifecycle-v2/SKILL.md @@ -29,6 +29,12 @@ Unified team skill covering specification, implementation, testing, and review. │coordinator││analyst││writer││discussant││planner││executor││tester││reviewer│ │ roles/ ││roles/ ││roles/││ roles/ ││roles/ ││ roles/ ││roles/││ roles/ │ └──────────┘└───────┘└──────┘└──────────┘└───────┘└────────┘└──────┘└────────┘ + ↑ ↑ + on-demand by coordinator + ┌──────────┐ ┌─────────┐ + │ explorer │ │architect│ + │ (service)│ │(consult)│ + └──────────┘ └─────────┘ ``` ## Command Architecture @@ -65,11 +71,17 @@ roles/ │ ├── role.md │ └── commands/ │ └── validate.md # Test-fix cycle -└── reviewer/ - ├── role.md +├── reviewer/ +│ ├── role.md +│ └── commands/ +│ ├── code-review.md # 4-dimension code review +│ └── spec-quality.md # 5-dimension spec quality check +├── explorer/ # Service role (on-demand) +│ └── role.md # Multi-strategy code search & pattern discovery +└── architect/ # Consulting role (on-demand) + ├── role.md # Multi-mode architecture assessment └── commands/ - ├── code-review.md # 4-dimension code review - └── spec-quality.md # 5-dimension spec quality check + └── assess.md # Mode-specific assessment strategies ``` **Design principle**: role.md keeps Phase 1 (Task Discovery) and Phase 5 (Report) inline. Phases 2-4 either stay inline (simple logic) or delegate to `commands/*.md` via `Read("commands/xxx.md")` when they involve subagent delegation, CLI fan-out, or complex strategies. @@ -106,7 +118,9 @@ const VALID_ROLES = { "planner": { file: "roles/planner/role.md", prefix: "PLAN" }, "executor": { file: "roles/executor/role.md", prefix: "IMPL" }, "tester": { file: "roles/tester/role.md", prefix: "TEST" }, - "reviewer": { file: "roles/reviewer/role.md", prefix: ["REVIEW", "QUALITY"] } + "reviewer": { file: "roles/reviewer/role.md", prefix: ["REVIEW", "QUALITY"] }, + "explorer": { file: "roles/explorer/role.md", prefix: "EXPLORE", type: "service" }, + "architect": { file: "roles/architect/role.md", prefix: "ARCH", type: "consulting" } } if (!VALID_ROLES[role]) { @@ -183,6 +197,8 @@ if (!roleMatch) { | `executor` | IMPL-* | Code implementation following plans | [roles/executor/role.md](roles/executor/role.md) | | `tester` | TEST-* | Adaptive test-fix cycles, quality gates | [roles/tester/role.md](roles/tester/role.md) | | `reviewer` | `REVIEW-*` + `QUALITY-*` | Code review + Spec quality validation (auto-switch by prefix) | [roles/reviewer/role.md](roles/reviewer/role.md) | +| `explorer` | EXPLORE-* | Code search, pattern discovery, dependency tracing (service role, on-demand) | [roles/explorer/role.md](roles/explorer/role.md) | +| `architect` | ARCH-* | Architecture assessment, tech feasibility, design review (consulting role, on-demand) | [roles/architect/role.md](roles/architect/role.md) | ## Shared Infrastructure @@ -254,6 +270,8 @@ mcp__ccw-tools__team_msg({ | executor | `impl_complete`, `impl_progress`, `error` | | tester | `test_result`, `impl_progress`, `fix_required`, `error` | | reviewer | `review_result`, `quality_result`, `fix_required`, `error` | +| explorer | `explore_ready`, `explore_progress`, `task_failed` | +| architect | `arch_ready`, `arch_concern`, `arch_progress`, `error` | ### CLI Fallback @@ -265,6 +283,59 @@ Bash(`ccw team list --team "${teamName}" --last 10 --json`) Bash(`ccw team status --team "${teamName}" --json`) ``` +### Wisdom Accumulation (All Roles) + +跨任务知识积累机制。Coordinator 在 session 初始化时创建 `wisdom/` 目录,所有 worker 在执行过程中读取和贡献 wisdom。 + +**目录结构**: +``` +{sessionFolder}/wisdom/ +├── learnings.md # 发现的模式和洞察 +├── decisions.md # 架构和设计决策 +├── conventions.md # 代码库约定 +└── issues.md # 已知风险和问题 +``` + +**Phase 2 加载(所有 worker)**: +```javascript +// Load wisdom context at start of Phase 2 +const sessionFolder = task.description.match(/Session:\s*([^\n]+)/)?.[1]?.trim() +let wisdom = {} +if (sessionFolder) { + try { wisdom.learnings = Read(`${sessionFolder}/wisdom/learnings.md`) } catch {} + try { wisdom.decisions = Read(`${sessionFolder}/wisdom/decisions.md`) } catch {} + try { wisdom.conventions = Read(`${sessionFolder}/wisdom/conventions.md`) } catch {} + try { wisdom.issues = Read(`${sessionFolder}/wisdom/issues.md`) } catch {} +} +``` + +**Phase 4/5 贡献(任务完成时)**: +```javascript +// Contribute wisdom after task completion +if (sessionFolder) { + const timestamp = new Date().toISOString().substring(0, 10) + + // Role-specific contributions: + // analyst → learnings (exploration dimensions, codebase patterns) + // writer → conventions (document structure, naming patterns) + // planner → decisions (task decomposition rationale) + // executor → learnings (implementation patterns), issues (bugs encountered) + // tester → issues (test failures, edge cases), learnings (test patterns) + // reviewer → conventions (code quality patterns), issues (review findings) + // explorer → conventions (codebase patterns), learnings (dependency insights) + // architect → decisions (architecture choices), issues (architectural risks) + + try { + const targetFile = `${sessionFolder}/wisdom/${wisdomTarget}.md` + const existing = Read(targetFile) + const entry = `- [${timestamp}] [${role}] ${wisdomEntry}` + Write(targetFile, existing + '\n' + entry) + } catch {} // wisdom not initialized +} +``` + +**Coordinator 注入**: Coordinator 在 spawn worker 时通过 task description 传递 `Session: {sessionFolder}`,worker 据此定位 wisdom 目录。已有 wisdom 内容为后续 worker 提供上下文,实现跨任务知识传递。 + ### Task Lifecycle (All Worker Roles) ```javascript @@ -343,12 +414,21 @@ All session artifacts are stored under a single session folder: │ └── spec-summary.md ├── discussions/ # Discussion records (discussant output) │ └── discuss-001..006.md -└── plan/ # Plan artifacts (planner output) - ├── exploration-{angle}.json - ├── explorations-manifest.json - ├── plan.json - └── .task/ - └── TASK-*.json +├── plan/ # Plan artifacts (planner output) +│ ├── exploration-{angle}.json +│ ├── explorations-manifest.json +│ ├── plan.json +│ └── .task/ +│ └── TASK-*.json +├── explorations/ # Explorer output (cached for cross-role reuse) +│ └── explore-*.json +├── architecture/ # Architect output (assessment reports) +│ └── arch-*.json +└── wisdom/ # Cross-task accumulated knowledge + ├── learnings.md # Patterns and insights discovered + ├── decisions.md # Architectural decisions made + ├── conventions.md # Codebase conventions found + └── issues.md # Known issues and risks ``` Messages remain at `.workflow/.team-msg/{team-name}/` (unchanged). diff --git a/.claude/skills/team-lifecycle-v2/roles/architect/commands/assess.md b/.claude/skills/team-lifecycle-v2/roles/architect/commands/assess.md new file mode 100644 index 00000000..8fd76f66 --- /dev/null +++ b/.claude/skills/team-lifecycle-v2/roles/architect/commands/assess.md @@ -0,0 +1,271 @@ +# Assess Command + +## Purpose +Multi-mode architecture assessment with mode-specific analysis strategies. Delegated from architect role.md Phase 3. + +## Input Context + +```javascript +// Provided by role.md Phase 2 +const { consultMode, sessionFolder, wisdom, explorations, projectTech, task } = context +``` + +## Mode Strategies + +### spec-review (ARCH-SPEC-*) + +审查架构文档的技术合理性。 + +```javascript +const dimensions = [ + { name: 'consistency', weight: 0.25 }, + { name: 'scalability', weight: 0.25 }, + { name: 'security', weight: 0.25 }, + { name: 'tech-fitness', weight: 0.25 } +] + +// Load architecture documents +const archIndex = Read(`${sessionFolder}/spec/architecture/_index.md`) +const adrFiles = Glob({ pattern: `${sessionFolder}/spec/architecture/ADR-*.md` }) +const adrs = adrFiles.map(f => ({ path: f, content: Read(f) })) + +// Check ADR consistency +const adrDecisions = adrs.map(adr => { + const status = adr.content.match(/status:\s*(\w+)/i)?.[1] + const context = adr.content.match(/## Context\n([\s\S]*?)##/)?.[1]?.trim() + const decision = adr.content.match(/## Decision\n([\s\S]*?)##/)?.[1]?.trim() + return { path: adr.path, status, context, decision } +}) + +// Cross-reference: ADR decisions vs architecture index +// Flag contradictions between ADRs +// Check if tech choices align with project-tech.json + +for (const dim of dimensions) { + const score = evaluateDimension(dim.name, archIndex, adrs, projectTech) + assessment.dimensions.push({ name: dim.name, score, weight: dim.weight }) +} +``` + +### plan-review (ARCH-PLAN-*) + +审查实现计划的架构合理性。 + +```javascript +const plan = JSON.parse(Read(`${sessionFolder}/plan/plan.json`)) +const taskFiles = Glob({ pattern: `${sessionFolder}/plan/.task/TASK-*.json` }) +const tasks = taskFiles.map(f => JSON.parse(Read(f))) + +// 1. Dependency cycle detection +function detectCycles(tasks) { + const graph = {} + tasks.forEach(t => { graph[t.id] = t.depends_on || [] }) + const visited = new Set(), inStack = new Set() + function dfs(node) { + if (inStack.has(node)) return true // cycle + if (visited.has(node)) return false + visited.add(node); inStack.add(node) + for (const dep of (graph[node] || [])) { + if (dfs(dep)) return true + } + inStack.delete(node) + return false + } + return Object.keys(graph).filter(n => dfs(n)) +} +const cycles = detectCycles(tasks) +if (cycles.length > 0) { + assessment.concerns.push({ + severity: 'high', + concern: `Circular dependency detected: ${cycles.join(' → ')}`, + suggestion: 'Break cycle by extracting shared interface or reordering tasks' + }) +} + +// 2. Task granularity check +tasks.forEach(t => { + const fileCount = (t.files || []).length + if (fileCount > 8) { + assessment.concerns.push({ + severity: 'medium', + task: t.id, + concern: `Task touches ${fileCount} files — may be too coarse`, + suggestion: 'Split into smaller tasks with clearer boundaries' + }) + } +}) + +// 3. Convention compliance (from wisdom) +if (wisdom.conventions) { + // Check if plan follows discovered conventions +} + +// 4. Architecture alignment (from wisdom.decisions) +if (wisdom.decisions) { + // Verify plan doesn't contradict previous architectural decisions +} +``` + +### code-review (ARCH-CODE-*) + +评估代码变更的架构影响。 + +```javascript +const changedFiles = Bash(`git diff --name-only HEAD~1 2>/dev/null || git diff --name-only --cached`) + .split('\n').filter(Boolean) + +// 1. Layer violation detection +function detectLayerViolation(file, content) { + // Check import depth — deeper layers should not import from shallower + const imports = (content.match(/from\s+['"]([^'"]+)['"]/g) || []) + .map(i => i.match(/['"]([^'"]+)['"]/)?.[1]).filter(Boolean) + return imports.filter(imp => isUpwardImport(file, imp)) +} + +// 2. New dependency analysis +const pkgChanges = changedFiles.filter(f => f.includes('package.json')) +if (pkgChanges.length > 0) { + for (const pkg of pkgChanges) { + const diff = Bash(`git diff HEAD~1 -- ${pkg} 2>/dev/null || git diff --cached -- ${pkg}`) + const newDeps = (diff.match(/\+\s+"([^"]+)":\s+"[^"]+"/g) || []) + .map(d => d.match(/"([^"]+)"/)?.[1]).filter(Boolean) + if (newDeps.length > 0) { + assessment.recommendations.push({ + area: 'dependencies', + suggestion: `New dependencies added: ${newDeps.join(', ')}. Verify license compatibility and bundle size impact.` + }) + } + } +} + +// 3. Module boundary changes +const indexChanges = changedFiles.filter(f => f.endsWith('index.ts') || f.endsWith('index.js')) +if (indexChanges.length > 0) { + assessment.concerns.push({ + severity: 'medium', + concern: `Module boundary files modified: ${indexChanges.join(', ')}`, + suggestion: 'Verify public API changes are intentional and backward compatible' + }) +} + +// 4. Architectural impact scoring +assessment.architectural_impact = changedFiles.length > 10 ? 'high' + : indexChanges.length > 0 || pkgChanges.length > 0 ? 'medium' : 'low' +``` + +### consult (ARCH-CONSULT-*) + +回答架构决策咨询。 + +```javascript +const question = task.description + .replace(/Session:.*\n?/g, '') + .replace(/Requester:.*\n?/g, '') + .trim() + +const isComplex = question.length > 200 || + /architect|design|pattern|refactor|migrate|scalab/i.test(question) + +if (isComplex) { + // Use cli-explore-agent for deep exploration + Task({ + subagent_type: "cli-explore-agent", + run_in_background: false, + description: `Architecture consultation: ${question.substring(0, 80)}`, + prompt: `## Architecture Consultation + +Question: ${question} + +## Steps +1. Run: ccw tool exec get_modules_by_depth '{}' +2. Search for relevant architectural patterns in codebase +3. Read .workflow/project-tech.json (if exists) +4. Analyze architectural implications + +## Output +Write to: ${sessionFolder}/architecture/consult-exploration.json +Schema: { relevant_files[], patterns[], architectural_implications[], options[] }` + }) + + // Parse exploration results into assessment + try { + const exploration = JSON.parse(Read(`${sessionFolder}/architecture/consult-exploration.json`)) + assessment.recommendations = (exploration.options || []).map(opt => ({ + area: 'architecture', + suggestion: `${opt.name}: ${opt.description}`, + trade_offs: opt.trade_offs || [] + })) + } catch {} +} else { + // Simple consultation — direct analysis + assessment.recommendations.push({ + area: 'architecture', + suggestion: `Direct answer based on codebase context and wisdom` + }) +} +``` + +### feasibility (ARCH-FEASIBILITY-*) + +技术可行性评估。 + +```javascript +const proposal = task.description + .replace(/Session:.*\n?/g, '') + .replace(/Requester:.*\n?/g, '') + .trim() + +// 1. Tech stack compatibility +const techStack = projectTech?.tech_stack || {} +// Check if proposal requires technologies not in current stack + +// 2. Codebase readiness +// Use ACE search to find relevant integration points +const searchResults = mcp__ace-tool__search_context({ + project_root_path: '.', + query: proposal +}) + +// 3. Effort estimation +const touchPoints = (searchResults?.relevant_files || []).length +const effort = touchPoints > 20 ? 'high' : touchPoints > 5 ? 'medium' : 'low' + +// 4. Risk assessment +assessment.verdict = 'FEASIBLE' // FEASIBLE | RISKY | INFEASIBLE +assessment.effort_estimate = effort +assessment.prerequisites = [] +assessment.risks = [] + +if (touchPoints > 20) { + assessment.verdict = 'RISKY' + assessment.risks.push({ + risk: 'High touch-point count suggests significant refactoring', + mitigation: 'Phase the implementation, start with core module' + }) +} +``` + +## Verdict Logic + +```javascript +function determineVerdict(assessment) { + const highConcerns = (assessment.concerns || []).filter(c => c.severity === 'high') + const mediumConcerns = (assessment.concerns || []).filter(c => c.severity === 'medium') + + if (highConcerns.length >= 2) return 'BLOCK' + if (highConcerns.length >= 1 || mediumConcerns.length >= 3) return 'CONCERN' + return 'APPROVE' +} + +assessment.overall_verdict = determineVerdict(assessment) +``` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| Architecture docs not found | Assess from available context, note limitation in report | +| Plan file missing | Report to coordinator via arch_concern | +| Git diff fails (no commits) | Use staged changes or skip code-review mode | +| CLI exploration timeout | Provide partial assessment, flag as incomplete | +| Exploration results unparseable | Fall back to direct analysis without exploration | diff --git a/.claude/skills/team-lifecycle-v2/roles/architect/role.md b/.claude/skills/team-lifecycle-v2/roles/architect/role.md new file mode 100644 index 00000000..37b0e25f --- /dev/null +++ b/.claude/skills/team-lifecycle-v2/roles/architect/role.md @@ -0,0 +1,368 @@ +# Role: architect + +架构顾问。提供架构决策咨询、技术可行性评估、设计模式建议。咨询角色,在 spec 和 impl 流程关键节点提供专业判断。 + +## Role Identity + +- **Name**: `architect` +- **Task Prefix**: `ARCH-*` +- **Responsibility**: Context loading → Mode detection → Architecture analysis → Package assessment → Report +- **Communication**: SendMessage to coordinator only +- **Output Tag**: `[architect]` +- **Role Type**: Consulting(咨询角色,不阻塞主链路,输出被引用) + +## Role Boundaries + +### MUST + +- 仅处理 `ARCH-*` 前缀的任务 +- 所有输出(SendMessage、team_msg、日志)必须带 `[architect]` 标识 +- 仅通过 SendMessage 与 coordinator 通信 +- 输出结构化评估报告供调用方消费 +- 根据任务前缀自动切换咨询模式 + +### MUST NOT + +- ❌ 直接修改源代码文件 +- ❌ 执行需求分析、代码实现、测试等其他角色职责 +- ❌ 直接与其他 worker 角色通信 +- ❌ 为其他角色创建任务 +- ❌ 做最终决策(仅提供建议,决策权在 coordinator/用户) +- ❌ 在输出中省略 `[architect]` 标识 + +## Message Types + +| Type | Direction | Trigger | Description | +|------|-----------|---------|-------------| +| `arch_ready` | architect → coordinator | Consultation complete | 架构评估/建议已就绪 | +| `arch_concern` | architect → coordinator | Significant risk found | 发现重大架构风险 | +| `arch_progress` | architect → coordinator | Long analysis progress | 复杂分析进度更新 | +| `error` | architect → coordinator | Analysis failure | 分析失败或上下文不足 | + +## Message Bus + +每次 SendMessage **前**,必须调用 `mcp__ccw-tools__team_msg` 记录消息: + +```javascript +// Consultation complete +mcp__ccw-tools__team_msg({ + operation: "log", team: teamName, + from: "architect", to: "coordinator", + type: "arch_ready", + summary: "[architect] ARCH complete: 3 recommendations, 1 concern", + ref: outputPath +}) + +// Risk alert +mcp__ccw-tools__team_msg({ + operation: "log", team: teamName, + from: "architect", to: "coordinator", + type: "arch_concern", + summary: "[architect] RISK: circular dependency in module graph" +}) +``` + +### CLI 回退 + +当 `mcp__ccw-tools__team_msg` MCP 不可用时,使用 `ccw team` CLI 作为等效回退: + +```javascript +Bash(`ccw team log --team "${teamName}" --from "architect" --to "coordinator" --type "arch_ready" --summary "[architect] ARCH complete" --ref "${outputPath}" --json`) +``` + +**参数映射**: `team_msg(params)` → `ccw team log --team --from architect --to coordinator --type --summary "" [--ref ] [--json]` + +## Toolbox + +### Available Commands +- `commands/assess.md` — Multi-mode architecture assessment (Phase 3) + +### Subagent Capabilities + +| Agent Type | Used By | Purpose | +|------------|---------|---------| +| `cli-explore-agent` | commands/assess.md | 深度架构探索(模块依赖、分层结构) | + +### CLI Capabilities + +| CLI Tool | Mode | Used By | Purpose | +|----------|------|---------|---------| +| `ccw cli --tool gemini --mode analysis` | analysis | commands/assess.md | 架构分析、模式评估 | + +## Consultation Modes + +根据任务 subject 前缀自动切换: + +| Mode | Task Pattern | Focus | Output | +|------|-------------|-------|--------| +| `spec-review` | ARCH-SPEC-* | 审查架构文档(ADR、组件图) | 架构评审报告 | +| `plan-review` | ARCH-PLAN-* | 审查实现计划的架构合理性 | 计划评审意见 | +| `code-review` | ARCH-CODE-* | 评估代码变更的架构影响 | 架构影响分析 | +| `consult` | ARCH-CONSULT-* | 回答架构决策咨询 | 决策建议 | +| `feasibility` | ARCH-FEASIBILITY-* | 技术可行性评估 | 可行性报告 | + +## Execution (5-Phase) + +### Phase 1: Task Discovery + +```javascript +const tasks = TaskList() +const myTasks = tasks.filter(t => + t.subject.startsWith('ARCH-') && + t.owner === 'architect' && + 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 & Mode Detection + +```javascript +const sessionFolder = task.description.match(/Session:\s*([^\n]+)/)?.[1]?.trim() + +// Auto-detect consultation mode from task subject +const MODE_MAP = { + 'ARCH-SPEC': 'spec-review', + 'ARCH-PLAN': 'plan-review', + 'ARCH-CODE': 'code-review', + 'ARCH-CONSULT': 'consult', + 'ARCH-FEASIBILITY': 'feasibility' +} +const modePrefix = Object.keys(MODE_MAP).find(p => task.subject.startsWith(p)) +const consultMode = modePrefix ? MODE_MAP[modePrefix] : 'consult' + +// Load wisdom (accumulated knowledge from previous tasks) +let wisdom = {} +if (sessionFolder) { + try { wisdom.learnings = Read(`${sessionFolder}/wisdom/learnings.md`) } catch {} + try { wisdom.decisions = Read(`${sessionFolder}/wisdom/decisions.md`) } catch {} + try { wisdom.conventions = Read(`${sessionFolder}/wisdom/conventions.md`) } catch {} +} + +// Load project tech context +let projectTech = {} +try { projectTech = JSON.parse(Read('.workflow/project-tech.json')) } catch {} + +// Load exploration results if available +let explorations = [] +if (sessionFolder) { + try { + const exploreFiles = Glob({ pattern: `${sessionFolder}/explorations/*.json` }) + explorations = exploreFiles.map(f => { + try { return JSON.parse(Read(f)) } catch { return null } + }).filter(Boolean) + } catch {} +} +``` + +### Phase 3: Architecture Assessment + +Delegate to command file for mode-specific analysis: + +```javascript +try { + const assessCommand = Read("commands/assess.md") + // Execute mode-specific strategy defined in command file + // Input: consultMode, sessionFolder, wisdom, explorations, projectTech + // Output: assessment object +} catch { + // Fallback: inline execution (see below) +} +``` + +**Command**: [commands/assess.md](commands/assess.md) + +**Inline Fallback** (when command file unavailable): + +```javascript +const assessment = { + mode: consultMode, + overall_verdict: 'APPROVE', // APPROVE | CONCERN | BLOCK + dimensions: [], + concerns: [], + recommendations: [], + _metadata: { timestamp: new Date().toISOString(), wisdom_loaded: Object.keys(wisdom).length > 0 } +} + +// Mode-specific analysis +if (consultMode === 'spec-review') { + // Load architecture documents, check ADR consistency, scalability, security + const archIndex = Read(`${sessionFolder}/spec/architecture/_index.md`) + const adrFiles = Glob({ pattern: `${sessionFolder}/spec/architecture/ADR-*.md` }) + // Score dimensions: consistency, scalability, security, tech-fitness +} + +if (consultMode === 'plan-review') { + // Load plan.json, check task granularity, dependency cycles, convention compliance + const plan = JSON.parse(Read(`${sessionFolder}/plan/plan.json`)) + // Detect circular dependencies, oversized tasks, missing risk assessment +} + +if (consultMode === 'code-review') { + // Analyze changed files for layer violations, new deps, module boundary changes + const changedFiles = Bash(`git diff --name-only HEAD~1 2>/dev/null || git diff --name-only --cached`) + .split('\n').filter(Boolean) + // Check import depth, package.json changes, index.ts modifications +} + +if (consultMode === 'consult') { + // Free-form consultation — use CLI for complex questions + const question = task.description.replace(/Session:.*\n?/g, '').replace(/Requester:.*\n?/g, '').trim() + const isComplex = question.length > 200 || /architect|design|pattern|refactor|migrate/i.test(question) + if (isComplex) { + Bash({ + command: `ccw cli -p "PURPOSE: Architecture consultation — ${question} +TASK: • Analyze architectural implications • Identify options with trade-offs • Recommend approach +MODE: analysis +CONTEXT: @**/* +EXPECTED: Structured analysis with options, trade-offs, recommendation +CONSTRAINTS: Architecture-level only" --tool gemini --mode analysis --rule analysis-review-architecture`, + run_in_background: true + }) + // Wait for result, parse into assessment + } +} + +if (consultMode === 'feasibility') { + // Assess technical feasibility against current codebase + // Output: verdict (FEASIBLE|RISKY|INFEASIBLE), risks, effort estimate, prerequisites +} +``` + +### Phase 4: Package & Wisdom Contribution + +```javascript +// Write assessment to session +const outputPath = sessionFolder + ? `${sessionFolder}/architecture/arch-${task.subject.replace(/[^a-zA-Z0-9-]/g, '-').toLowerCase()}.json` + : '.workflow/.tmp/arch-assessment.json' + +Bash(`mkdir -p "$(dirname '${outputPath}')"`) +Write(outputPath, JSON.stringify(assessment, null, 2)) + +// Contribute to wisdom: record architectural decisions +if (sessionFolder && assessment.recommendations?.length > 0) { + try { + const decisionsPath = `${sessionFolder}/wisdom/decisions.md` + const existing = Read(decisionsPath) + const newDecisions = assessment.recommendations + .map(r => `- [${new Date().toISOString().substring(0, 10)}] ${r.area || r.dimension}: ${r.suggestion}`) + .join('\n') + Write(decisionsPath, existing + '\n' + newDecisions) + } catch {} // wisdom not initialized +} +``` + +### Phase 5: Report to Coordinator + +```javascript +const verdict = assessment.overall_verdict || assessment.verdict || 'N/A' +const concernCount = (assessment.concerns || []).length +const highConcerns = (assessment.concerns || []).filter(c => c.severity === 'high').length +const recCount = (assessment.recommendations || []).length + +mcp__ccw-tools__team_msg({ + operation: "log", team: teamName, + from: "architect", to: "coordinator", + type: highConcerns > 0 ? "arch_concern" : "arch_ready", + summary: `[architect] ARCH ${consultMode}: ${verdict}, ${concernCount} concerns, ${recCount} recommendations`, + ref: outputPath +}) + +SendMessage({ + type: "message", + recipient: "coordinator", + content: `[architect] ## Architecture Assessment + +**Task**: ${task.subject} +**Mode**: ${consultMode} +**Verdict**: ${verdict} + +### Summary +- **Concerns**: ${concernCount} (${highConcerns} high) +- **Recommendations**: ${recCount} +${assessment.architectural_impact ? `- **Impact**: ${assessment.architectural_impact}` : ''} + +${assessment.dimensions?.length > 0 ? `### Dimension Scores +${assessment.dimensions.map(d => `- **${d.name}**: ${d.score}%`).join('\n')}` : ''} + +${concernCount > 0 ? `### Concerns +${assessment.concerns.map(c => `- [${(c.severity || 'medium').toUpperCase()}] ${c.task || c.file || ''}: ${c.concern}`).join('\n')}` : ''} + +### Recommendations +${(assessment.recommendations || []).map(r => `- ${r.area || r.dimension || ''}: ${r.suggestion}`).join('\n') || 'None'} + +### Output: ${outputPath}`, + summary: `[architect] ARCH ${consultMode}: ${verdict}` +}) + +TaskUpdate({ taskId: task.id, status: 'completed' }) + +// Check for next ARCH task → back to Phase 1 +const nextTasks = TaskList().filter(t => + t.subject.startsWith('ARCH-') && + t.owner === 'architect' && + t.status === 'pending' && + t.blockedBy.length === 0 +) +if (nextTasks.length > 0) { + // Continue → back to Phase 1 +} +``` + +## Coordinator Integration + +Architect 由 coordinator 在关键节点按需创建 ARCH-* 任务: + +### Spec Pipeline (after DRAFT-003, before DISCUSS-004) + +```javascript +TaskCreate({ + subject: 'ARCH-SPEC-001: 架构文档专业评审', + description: `评审架构文档的技术合理性\n\nSession: ${sessionFolder}\n输入: ${sessionFolder}/spec/architecture/`, + activeForm: '架构评审中' +}) +TaskUpdate({ taskId: archSpecId, owner: 'architect' }) +// DISCUSS-004 addBlockedBy [archSpecId] +``` + +### Impl Pipeline (after PLAN-001, before IMPL-001) + +```javascript +TaskCreate({ + subject: 'ARCH-PLAN-001: 实现计划架构审查', + description: `审查实现计划的架构合理性\n\nSession: ${sessionFolder}\nPlan: ${sessionFolder}/plan/plan.json`, + activeForm: '计划审查中' +}) +TaskUpdate({ taskId: archPlanId, owner: 'architect' }) +// IMPL-001 addBlockedBy [archPlanId] +``` + +### On-Demand (any point via coordinator) + +```javascript +TaskCreate({ + subject: 'ARCH-CONSULT-001: 架构决策咨询', + description: `${question}\n\nSession: ${sessionFolder}\nRequester: ${role}`, + activeForm: '架构咨询中' +}) +TaskUpdate({ taskId: archConsultId, owner: 'architect' }) +``` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| No ARCH-* tasks available | Idle, wait for coordinator assignment | +| Architecture documents not found | Assess from available context, note limitation | +| Plan file not found | Report to coordinator, request location | +| CLI analysis timeout | Provide partial assessment, note incomplete | +| Insufficient context | Request explorer to gather more context via coordinator | +| Conflicting requirements | Flag as concern, provide options | +| Command file not found | Fall back to inline execution | +| Unexpected error | Log error via team_msg, report to coordinator | diff --git a/.claude/skills/team-lifecycle-v2/roles/coordinator/role.md b/.claude/skills/team-lifecycle-v2/roles/coordinator/role.md index 22869894..1f2d1218 100644 --- a/.claude/skills/team-lifecycle-v2/roles/coordinator/role.md +++ b/.claude/skills/team-lifecycle-v2/roles/coordinator/role.md @@ -366,6 +366,14 @@ TeamCreate({ Output(`[coordinator] Team created: ${teamId}`) +// Initialize wisdom directory +const wisdomDir = `${sessionFolder}/wisdom` +Bash(`mkdir -p "${wisdomDir}"`) +Write(`${wisdomDir}/learnings.md`, `# Learnings\n\n\n`) +Write(`${wisdomDir}/decisions.md`, `# Decisions\n\n\n`) +Write(`${wisdomDir}/conventions.md`, `# Conventions\n\n\n\n`) +Write(`${wisdomDir}/issues.md`, `# Known Issues\n\n\n`) + // Initialize session file const sessionFile = `D:/Claude_dms3/.workflow/.sessions/${sessionId}.json` const sessionData = { diff --git a/.claude/skills/team-lifecycle-v2/roles/explorer/role.md b/.claude/skills/team-lifecycle-v2/roles/explorer/role.md new file mode 100644 index 00000000..90e369f8 --- /dev/null +++ b/.claude/skills/team-lifecycle-v2/roles/explorer/role.md @@ -0,0 +1,301 @@ +# Explorer Role + +专职代码搜索与模式发现。服务角色,被 analyst/planner/executor/discussant 按需调用。 + +## 1. Role Identity + +- **Name**: explorer +- **Task Prefix**: EXPLORE-* +- **Output Tag**: `[explorer]` +- **Role Type**: Service(按需调用,不占主链路位置) +- **Responsibility**: Parse request → Multi-strategy search → Dependency trace → Package results → Report + +## 2. Role Boundaries + +### MUST +- Only process EXPLORE-* tasks +- Output structured JSON for downstream consumption +- Use priority-ordered search strategies (ACE → Grep → cli-explore-agent) +- Tag all outputs with `[explorer]` +- Cache results in `{session}/explorations/` for cross-role reuse + +### MUST NOT +- Create tasks +- Contact other workers directly +- Modify any source code files +- Execute analysis, planning, or implementation +- Make architectural decisions (only discover patterns) + +## 3. Message Types + +| Type | Direction | Purpose | Format | +|------|-----------|---------|--------| +| `explore_ready` | TO coordinator | Search complete | `{ type: "explore_ready", task_id, file_count, pattern_count, output_path }` | +| `explore_progress` | TO coordinator | Multi-angle progress | `{ type: "explore_progress", task_id, angle, status }` | +| `task_failed` | TO coordinator | Search failure | `{ type: "task_failed", task_id, error, fallback_used }` | + +## 4. Message Bus + +**Primary**: Use `team_msg` for all coordinator communication with `[explorer]` tag: +```javascript +team_msg({ + to: "coordinator", + type: "explore_ready", + task_id: "EXPLORE-001", + file_count: 15, + pattern_count: 3, + output_path: `${sessionFolder}/explorations/explore-001.json` +}, "[explorer]") +``` + +**CLI Fallback**: When message bus unavailable: +```bash +ccw team log --team "${teamName}" --from "explorer" --to "coordinator" --type "explore_ready" --summary "[explorer] 15 files, 3 patterns" --json +``` + +## 5. Toolbox + +### Available Commands +- None (inline execution, search logic is straightforward) + +### Search Tools (priority order) + +| Tool | Priority | Use Case | +|------|----------|----------| +| `mcp__ace-tool__search_context` | P0 | Semantic code search | +| `Grep` / `Glob` | P1 | Pattern matching, file discovery | +| `Read` | P1 | File content reading | +| `Bash` (rg, find) | P2 | Structured search fallback | +| `WebSearch` | P3 | External docs/best practices | + +### Subagent Capabilities +- `cli-explore-agent` — Deep multi-angle codebase exploration + +## 6. Execution (5-Phase) + +### Phase 1: Task Discovery & Request Parsing + +```javascript +const tasks = TaskList() +const myTasks = tasks.filter(t => + t.subject.startsWith('EXPLORE-') && + t.owner === 'explorer' && + 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' }) + +// Parse structured request from task description +const sessionFolder = task.description.match(/Session:\s*([^\n]+)/)?.[1]?.trim() +const exploreMode = task.description.match(/Mode:\s*([^\n]+)/)?.[1]?.trim() || 'codebase' +const angles = (task.description.match(/Angles:\s*([^\n]+)/)?.[1] || 'general').split(',').map(a => a.trim()) +const keywords = (task.description.match(/Keywords:\s*([^\n]+)/)?.[1] || '').split(',').map(k => k.trim()).filter(Boolean) +const requester = task.description.match(/Requester:\s*([^\n]+)/)?.[1]?.trim() || 'coordinator' + +const outputDir = sessionFolder ? `${sessionFolder}/explorations` : '.workflow/.tmp' +Bash(`mkdir -p "${outputDir}"`) +``` + +### Phase 2: Multi-Strategy Search + +```javascript +const findings = { + relevant_files: [], // { path, rationale, role, discovery_source, key_symbols } + patterns: [], // { name, description, files } + dependencies: [], // { file, imports[] } + external_refs: [], // { keyword, results[] } + _metadata: { angles, mode: exploreMode, requester, timestamp: new Date().toISOString() } +} + +// === Strategy 1: ACE Semantic Search (P0) === +if (exploreMode !== 'external') { + for (const kw of keywords) { + try { + const results = mcp__ace-tool__search_context({ project_root_path: '.', query: kw }) + // Deduplicate and add to findings.relevant_files with discovery_source: 'ace-search' + } catch { /* ACE unavailable, fall through */ } + } +} + +// === Strategy 2: Grep Pattern Scan (P1) === +if (exploreMode !== 'external') { + for (const kw of keywords) { + // Find imports/exports/definitions + const defResults = Grep({ + pattern: `(class|function|const|export|interface|type)\\s+.*${kw}`, + glob: '*.{ts,tsx,js,jsx,py,go,rs}', + '-n': true, output_mode: 'content' + }) + // Add to findings with discovery_source: 'grep-scan' + } +} + +// === Strategy 3: Dependency Tracing === +if (exploreMode !== 'external') { + for (const file of findings.relevant_files.slice(0, 10)) { + try { + const content = Read(file.path) + const imports = (content.match(/from\s+['"]([^'"]+)['"]/g) || []) + .map(i => i.match(/['"]([^'"]+)['"]/)?.[1]).filter(Boolean) + if (imports.length > 0) { + findings.dependencies.push({ file: file.path, imports }) + } + } catch {} + } +} + +// === Strategy 4: Deep Exploration (multi-angle, via cli-explore-agent) === +if (angles.length > 1 && exploreMode !== 'external') { + for (const angle of angles) { + Task({ + subagent_type: "cli-explore-agent", + run_in_background: false, + description: `Explore: ${angle}`, + prompt: `## Exploration: ${angle} angle +Keywords: ${keywords.join(', ')} + +## Steps +1. rg -l "${keywords[0]}" --type-add 'code:*.{ts,tsx,js,py,go,rs}' --type code +2. Read .workflow/project-tech.json (if exists) +3. Focus on ${angle} perspective + +## Output +Write to: ${outputDir}/exploration-${angle}.json +Schema: { relevant_files[], patterns[], dependencies[] }` + }) + // Merge angle results into main findings + try { + const angleData = JSON.parse(Read(`${outputDir}/exploration-${angle}.json`)) + findings.relevant_files.push(...(angleData.relevant_files || [])) + findings.patterns.push(...(angleData.patterns || [])) + } catch {} + } +} + +// === Strategy 5: External Search (P3) === +if (exploreMode === 'external' || exploreMode === 'hybrid') { + for (const kw of keywords.slice(0, 3)) { + try { + const results = WebSearch({ query: `${kw} best practices documentation` }) + findings.external_refs.push({ keyword: kw, results }) + } catch {} + } +} + +// Deduplicate relevant_files by path +const seen = new Set() +findings.relevant_files = findings.relevant_files.filter(f => { + if (seen.has(f.path)) return false + seen.add(f.path) + return true +}) +``` + +### Phase 3: Wisdom Contribution + +```javascript +// If wisdom directory exists, contribute discovered patterns +if (sessionFolder) { + try { + const conventionsPath = `${sessionFolder}/wisdom/conventions.md` + const existing = Read(conventionsPath) + if (findings.patterns.length > 0) { + const newPatterns = findings.patterns + .map(p => `- ${p.name}: ${p.description || ''}`) + .join('\n') + Edit({ + file_path: conventionsPath, + old_string: '', + new_string: `\n${newPatterns}` + }) + } + } catch {} // wisdom not initialized +} +``` + +### Phase 4: Package Results + +```javascript +const outputPath = `${outputDir}/explore-${task.subject.replace(/[^a-zA-Z0-9-]/g, '-').toLowerCase()}.json` +Write(outputPath, JSON.stringify(findings, null, 2)) +``` + +### Phase 5: Report to Coordinator + +```javascript +const summary = `${findings.relevant_files.length} files, ${findings.patterns.length} patterns, ${findings.dependencies.length} deps` + +mcp__ccw-tools__team_msg({ + operation: "log", team: teamName, + from: "explorer", to: "coordinator", + type: "explore_ready", + summary: `[explorer] EXPLORE complete: ${summary}`, + ref: outputPath +}) + +SendMessage({ + type: "message", + recipient: "coordinator", + content: `[explorer] ## Exploration Results + +**Task**: ${task.subject} +**Mode**: ${exploreMode} | **Angles**: ${angles.join(', ')} | **Requester**: ${requester} + +### Files: ${findings.relevant_files.length} +${findings.relevant_files.slice(0, 8).map(f => `- \`${f.path}\` (${f.role}) — ${f.rationale}`).join('\n')} + +### Patterns: ${findings.patterns.length} +${findings.patterns.slice(0, 5).map(p => `- ${p.name}: ${p.description || ''}`).join('\n') || 'None'} + +### Output: ${outputPath}`, + summary: `[explorer] ${summary}` +}) + +TaskUpdate({ taskId: task.id, status: 'completed' }) +// Check for next EXPLORE task → back to Phase 1 +``` + +## 7. Coordinator Integration + +Explorer 是服务角色,coordinator 在以下场景按需创建 EXPLORE-* 任务: + +| Trigger | Task Example | Requester | +|---------|-------------|-----------| +| RESEARCH-001 需要代码库上下文 | `EXPLORE-001: 代码库上下文搜索` | analyst | +| PLAN-001 需要多角度探索 | `EXPLORE-002: 实现相关代码探索` | planner | +| DISCUSS-004 需要外部最佳实践 | `EXPLORE-003: 外部文档搜索` | discussant | +| IMPL-001 遇到未知代码 | `EXPLORE-004: 依赖追踪` | executor | + +**Task Description Template**: +``` +搜索描述 + +Session: {sessionFolder} +Mode: codebase|external|hybrid +Angles: architecture,patterns,dependencies +Keywords: auth,middleware,session +Requester: analyst +``` + +## 8. Result Caching + +``` +{sessionFolder}/explorations/ +├── explore-explore-001-*.json # Consolidated results +├── exploration-architecture.json # Angle-specific (from cli-explore-agent) +└── exploration-patterns.json +``` + +后续角色 Phase 2 可直接读取已有探索结果,避免重复搜索。 + +## 9. Error Handling + +| Error Type | Recovery Strategy | Escalation | +|------------|-------------------|------------| +| ACE unavailable | Fallback to Grep + rg | Continue with degraded results | +| cli-explore-agent failure | Fallback to direct search | Report partial results | +| No results found | Report empty, suggest broader keywords | Coordinator decides | +| Web search fails | Skip external refs | Continue with codebase results | +| Session folder missing | Use .workflow/.tmp | Notify coordinator | diff --git a/.claude/skills/team-lifecycle-v2/specs/team-config.json b/.claude/skills/team-lifecycle-v2/specs/team-config.json index ea3fd242..7158ead6 100644 --- a/.claude/skills/team-lifecycle-v2/specs/team-config.json +++ b/.claude/skills/team-lifecycle-v2/specs/team-config.json @@ -47,6 +47,19 @@ "additional_prefixes": ["QUALITY"], "responsibility": "Code review (REVIEW-*) + Spec quality validation (QUALITY-*)", "message_types": ["review_result", "quality_result", "fix_required", "error"] + }, + "explorer": { + "task_prefix": "EXPLORE", + "responsibility": "Code search, pattern discovery, dependency tracing. Service role — on-demand by coordinator", + "role_type": "service", + "message_types": ["explore_ready", "explore_progress", "task_failed"] + }, + "architect": { + "task_prefix": "ARCH", + "responsibility": "Architecture assessment, tech feasibility, design pattern review. Consulting role — on-demand by coordinator", + "role_type": "consulting", + "consultation_modes": ["spec-review", "plan-review", "code-review", "consult", "feasibility"], + "message_types": ["arch_ready", "arch_concern", "arch_progress", "error"] } }, @@ -77,6 +90,9 @@ "spec": "spec/", "discussions": "discussions/", "plan": "plan/", + "explorations": "explorations/", + "architecture": "architecture/", + "wisdom": "wisdom/", "messages": ".workflow/.team-msg/{team-name}/" } }