Refactor team collaboration skills and update documentation

- Renamed `team-lifecycle-v5` to `team-lifecycle` across various documentation files for consistency.
- Updated references in code examples and usage sections to reflect the new skill name.
- Added a new command file for the `monitor` functionality in the `team-iterdev` skill, detailing the coordinator's monitoring events and task management.
- Introduced new components for dynamic pipeline visualization and session coordinates display in the frontend.
- Implemented utility functions for pipeline stage detection and status derivation based on message history.
- Enhanced the team role panel to map members to their respective pipeline roles with status indicators.
- Updated Chinese documentation to reflect the changes in skill names and descriptions.
This commit is contained in:
catlog22
2026-03-04 11:07:48 +08:00
parent 5e96722c09
commit ffd5282932
132 changed files with 2938 additions and 18916 deletions

View File

@@ -11,23 +11,23 @@ Unified team skill: quality assurance combining issue discovery and software tes
## Architecture
```
┌──────────────────────────────────────────────────────────┐
Skill(skill="team-quality-assurance")
args="<task-description>" or args="--role=xxx"
└────────────────────────────┬─────────────────────────────┘
│ Role Router
┌──── --role present? ────┐
│ NO │ YES
↓ ↓
Orchestration Mode Role Dispatch
(auto -> coordinator) (route to role.md)
┌─────┬──────┴──────┬───────────┬──────────┬──────────┐
↓ ↓ ↓ ↓ ↓ ↓
┌────────┐┌───────┐┌──────────┐┌─────────┐┌────────┐┌────────┐
│ coord ││scout ││strategist││generator││executor││analyst │
│ ││SCOUT-*││QASTRAT-* ││QAGEN-* ││QARUN-* ││QAANA-* │
└────────┘└───────┘└──────────┘└─────────┘└────────┘└────────┘
+---------------------------------------------------+
| Skill(skill="team-quality-assurance") |
| args="<task-description>" |
+-------------------+-------------------------------+
|
Orchestration Mode (auto -> coordinator)
|
Coordinator (inline)
Phase 0-5 orchestration
|
+-----+-----+-----+-----+-----+
v v v v v
[tw] [tw] [tw] [tw] [tw]
scout stra- gene- execu- analy-
tegist rator tor st
(tw) = team-worker agent
```
## Command Architecture
@@ -71,14 +71,14 @@ Parse `$ARGUMENTS` to extract `--role`. If absent -> Orchestration Mode (auto ro
### Role Registry
| Role | File | Task Prefix | Type | Compact |
|------|------|-------------|------|---------|
| coordinator | [roles/coordinator/role.md](roles/coordinator/role.md) | (none) | orchestrator | **compressed -> must re-read** |
| scout | [roles/scout/role.md](roles/scout/role.md) | SCOUT-* | pipeline | compressed -> must re-read |
| strategist | [roles/strategist/role.md](roles/strategist/role.md) | QASTRAT-* | pipeline | compressed -> must re-read |
| generator | [roles/generator/role.md](roles/generator/role.md) | QAGEN-* | pipeline | compressed -> must re-read |
| executor | [roles/executor/role.md](roles/executor/role.md) | QARUN-* | pipeline | compressed -> must re-read |
| analyst | [roles/analyst/role.md](roles/analyst/role.md) | QAANA-* | pipeline | compressed -> must re-read |
| Role | Spec | Task Prefix | Inner Loop |
|------|------|-------------|------------|
| coordinator | [roles/coordinator/role.md](roles/coordinator/role.md) | (none) | - |
| scout | [role-specs/scout.md](role-specs/scout.md) | SCOUT-* | false |
| strategist | [role-specs/strategist.md](role-specs/strategist.md) | QASTRAT-* | false |
| generator | [role-specs/generator.md](role-specs/generator.md) | QAGEN-* | false |
| executor | [role-specs/executor.md](role-specs/executor.md) | QARUN-* | true |
| analyst | [role-specs/analyst.md](role-specs/analyst.md) | QAANA-* | false |
> **COMPACT PROTECTION**: Role files are execution documents, not reference material. When context compression occurs and role instructions are reduced to summaries, **you MUST immediately `Read` the corresponding role.md to reload before continuing execution**. Do not execute any Phase based on summaries.
@@ -354,42 +354,38 @@ Beat 1 2 3 4 5 6
## Coordinator Spawn Template
When coordinator spawns workers, use background mode (Spawn-and-Stop):
### v5 Worker Spawn (all roles)
When coordinator spawns workers, use `team-worker` agent with role-spec path:
```
Task({
subagent_type: "general-purpose",
subagent_type: "team-worker",
description: "Spawn <role> worker",
team_name: <team-name>,
team_name: "quality-assurance",
name: "<role>",
run_in_background: true,
prompt: `You are team "<team-name>" <ROLE>.
prompt: `## Role Assignment
role: <role>
role_spec: .claude/skills/team-quality-assurance/role-specs/<role>.md
session: <session-folder>
session_id: <session-id>
team_name: quality-assurance
requirement: <task-description>
inner_loop: <true|false>
## Primary Directive
All your work must be executed through Skill to load role definition:
Skill(skill="team-quality-assurance", args="--role=<role>")
Current requirement: <task-description>
Session: <session-folder>
## Role Guidelines
- Only process <PREFIX>-* tasks, do not execute other role work
- All output prefixed with [<role>] identifier
- Only communicate with coordinator
- Do not use TaskCreate for other roles
- Call mcp__ccw-tools__team_msg before every SendMessage
## Workflow
1. Call Skill -> load role definition and execution logic
2. Follow role.md 5-Phase flow
3. team_msg + SendMessage results to coordinator
4. TaskUpdate completed -> check next task`
Read role_spec file to load Phase 2-4 domain instructions.
Execute built-in Phase 1 (task discovery) -> role-spec Phase 2-4 -> built-in Phase 5 (report).`
})
```
**Inner Loop roles** (executor): Set `inner_loop: true`.
**Single-task roles** (scout, strategist, generator, analyst): Set `inner_loop: false`.
### Parallel Spawn (N agents for same role)
> When pipeline has parallel tasks assigned to the same role, spawn N distinct agents with unique names. A single agent can only process tasks serially.
> When pipeline has parallel tasks assigned to the same role, spawn N distinct team-worker agents with unique names.
**Parallel detection**:
@@ -402,30 +398,54 @@ Session: <session-folder>
```
Task({
subagent_type: "general-purpose",
subagent_type: "team-worker",
description: "Spawn <role>-<N> worker",
team_name: <team-name>,
team_name: "quality-assurance",
name: "<role>-<N>",
run_in_background: true,
prompt: `You are team "<team-name>" <ROLE> (<role>-<N>).
Your agent name is "<role>-<N>", use this name for task discovery owner matching.
prompt: `## Role Assignment
role: <role>
role_spec: .claude/skills/team-quality-assurance/role-specs/<role>.md
session: <session-folder>
session_id: <session-id>
team_name: quality-assurance
requirement: <task-description>
agent_name: <role>-<N>
inner_loop: <true|false>
## Primary Directive
Skill(skill="team-quality-assurance", args="--role=<role> --agent-name=<role>-<N>")
## Role Guidelines
- Only process tasks where owner === "<role>-<N>" with <PREFIX>-* prefix
- All output prefixed with [<role>] identifier
## Workflow
1. TaskList -> find tasks where owner === "<role>-<N>" with <PREFIX>-* prefix
2. Skill -> execute role definition
3. team_msg + SendMessage results to coordinator
4. TaskUpdate completed -> check next task`
Read role_spec file to load Phase 2-4 domain instructions.
Execute built-in Phase 1 (task discovery, owner=<role>-<N>) -> role-spec Phase 2-4 -> built-in Phase 5 (report).`
})
```
**Dispatch must match agent names**: In dispatch, parallel tasks use instance-specific owner: `<role>-<N>`. In role.md, task discovery uses --agent-name for owner matching.
**Dispatch must match agent names**: In dispatch, parallel tasks use instance-specific owner: `<role>-<N>`.
---
## Completion Action
When the pipeline completes (all tasks done, coordinator Phase 5):
```
AskUserQuestion({
questions: [{
question: "Quality Assurance pipeline complete. What would you like to do?",
header: "Completion",
multiSelect: false,
options: [
{ label: "Archive & Clean (Recommended)", description: "Archive session, clean up tasks and team resources" },
{ label: "Keep Active", description: "Keep session active for follow-up work or inspection" },
{ label: "Export Results", description: "Export deliverables to a specified location, then clean" }
]
}]
})
```
| Choice | Action |
|--------|--------|
| Archive & Clean | Update session status="completed" -> TeamDelete(quality-assurance) -> output final summary |
| Keep Active | Update session status="paused" -> output resume instructions: `Skill(skill="team-quality-assurance", args="resume")` |
| Export Results | AskUserQuestion for target path -> copy deliverables -> Archive & Clean |
## Unified Session Directory

View File

@@ -1,360 +0,0 @@
# Command: quality-report
> 缺陷模式分析 + 覆盖率分析 + 综合质量报告。多维度分析 QA 数据,生成质量评分和改进建议。
## When to Use
- Phase 3 of Analyst
- 测试执行完成,需要分析结果
- 需要识别缺陷模式和覆盖率趋势
**Trigger conditions**:
- QAANA-* 任务进入执行阶段
- 所有 QARUN 任务已完成
- Coordinator 请求质量报告
## Strategy
### Delegation Mode
**Mode**: CLI Fan-out深度分析/ Direct基础分析
**CLI Tool**: `gemini` (primary)
**CLI Mode**: `analysis`
### Decision Logic
```javascript
const dataPoints = discoveredIssues.length + Object.keys(executionResults).length
if (dataPoints <= 5) {
// 基础内联分析
mode = 'direct'
} else {
// CLI 辅助深度分析
mode = 'cli-assisted'
}
```
## Execution Steps
### Step 1: Context Preparation
```javascript
// 从 shared memory 加载所有 QA 数据
const discoveredIssues = sharedMemory.discovered_issues || []
const strategy = sharedMemory.test_strategy || {}
const generatedTests = sharedMemory.generated_tests || {}
const executionResults = sharedMemory.execution_results || {}
const historicalPatterns = sharedMemory.defect_patterns || []
const coverageHistory = sharedMemory.coverage_history || []
// 读取覆盖率详细数据
let coverageData = null
try {
coverageData = JSON.parse(Read('coverage/coverage-summary.json'))
} catch {}
// 读取各层级执行结果
const layerResults = {}
try {
const resultFiles = Glob(`${sessionFolder}/results/run-*.json`)
for (const f of resultFiles) {
const data = JSON.parse(Read(f))
layerResults[data.layer] = data
}
} catch {}
```
### Step 2: Execute Strategy
```javascript
if (mode === 'direct') {
// 基础内联分析
analysis = performDirectAnalysis()
} else {
// CLI 辅助深度分析
const analysisContext = JSON.stringify({
issues: discoveredIssues.slice(0, 20),
execution: layerResults,
coverage: coverageData?.total || {},
strategy: { layers: strategy.layers?.map(l => ({ level: l.level, target: l.target_coverage })) }
}, null, 2)
Bash(`ccw cli -p "PURPOSE: Perform deep quality analysis on QA results to identify defect patterns, coverage trends, and improvement opportunities
TASK: • Classify defects by root cause pattern (logic errors, integration issues, missing validation, etc.) • Identify files with highest defect density • Analyze coverage gaps vs risk levels • Compare actual coverage to targets • Generate actionable improvement recommendations
MODE: analysis
CONTEXT: @${sessionFolder}/.msg/meta.json @${sessionFolder}/results/**/*
EXPECTED: Structured analysis with: defect pattern taxonomy, risk-coverage matrix, quality score rationale, top 5 improvement recommendations with expected impact
CONSTRAINTS: Be data-driven, avoid speculation without evidence" --tool gemini --mode analysis --rule analysis-analyze-code-patterns`, {
run_in_background: true
})
// 等待 CLI 完成
}
// ===== 分析维度 =====
// 1. 缺陷模式分析
function analyzeDefectPatterns(issues, results) {
const byType = {}
for (const issue of issues) {
const type = issue.perspective || 'unknown'
if (!byType[type]) byType[type] = []
byType[type].push(issue)
}
// 识别重复模式
const patterns = []
for (const [type, typeIssues] of Object.entries(byType)) {
if (typeIssues.length >= 2) {
// 分析共同特征
const commonFiles = findCommonPatterns(typeIssues.map(i => i.file))
patterns.push({
type,
count: typeIssues.length,
files: [...new Set(typeIssues.map(i => i.file))],
common_pattern: commonFiles,
description: `${type} 类问题在 ${typeIssues.length} 处重复出现`,
recommendation: generateRecommendation(type, typeIssues)
})
}
}
return { by_type: byType, patterns, total: issues.length }
}
// 2. 覆盖率差距分析
function analyzeCoverageGaps(coverage, strategy) {
if (!coverage) return { status: 'no_data', gaps: [] }
const totalCoverage = coverage.total?.lines?.pct || 0
const gaps = []
for (const layer of (strategy.layers || [])) {
if (totalCoverage < layer.target_coverage) {
gaps.push({
layer: layer.level,
target: layer.target_coverage,
actual: totalCoverage,
gap: Math.round(layer.target_coverage - totalCoverage),
severity: (layer.target_coverage - totalCoverage) > 20 ? 'high' : 'medium'
})
}
}
// 按文件分析覆盖率
const fileGaps = []
if (coverage && typeof coverage === 'object') {
for (const [file, data] of Object.entries(coverage)) {
if (file === 'total') continue
const linePct = data?.lines?.pct || 0
if (linePct < 50) {
fileGaps.push({ file, coverage: linePct, severity: linePct < 20 ? 'critical' : 'high' })
}
}
}
return { total_coverage: totalCoverage, gaps, file_gaps: fileGaps.slice(0, 10) }
}
// 3. 测试有效性分析
function analyzeTestEffectiveness(generated, results) {
const effectiveness = {}
for (const [layer, data] of Object.entries(generated)) {
const result = results[layer] || {}
effectiveness[layer] = {
files_generated: data.files?.length || 0,
pass_rate: result.pass_rate || 0,
iterations_needed: result.iterations || 0,
coverage_achieved: result.coverage || 0,
effective: (result.pass_rate || 0) >= 95 && (result.iterations || 0) <= 2
}
}
return effectiveness
}
// 4. 质量趋势分析
function analyzeQualityTrend(history) {
if (history.length < 2) return { trend: 'insufficient_data', confidence: 'low' }
const latest = history[history.length - 1]
const previous = history[history.length - 2]
const delta = (latest?.coverage || 0) - (previous?.coverage || 0)
return {
trend: delta > 5 ? 'improving' : delta < -5 ? 'declining' : 'stable',
delta: Math.round(delta * 10) / 10,
data_points: history.length,
confidence: history.length >= 5 ? 'high' : history.length >= 3 ? 'medium' : 'low'
}
}
// 5. 综合质量评分
function calculateQualityScore(analysis) {
let score = 100
// 扣分: 安全问题
const securityIssues = (analysis.defect_patterns.by_type?.security || []).length
score -= securityIssues * 10
// 扣分: Bug
const bugIssues = (analysis.defect_patterns.by_type?.bug || []).length
score -= bugIssues * 5
// 扣分: 覆盖率差距
for (const gap of (analysis.coverage_gaps.gaps || [])) {
score -= gap.gap * 0.5
}
// 扣分: 测试失败
for (const [layer, eff] of Object.entries(analysis.test_effectiveness)) {
if (eff.pass_rate < 100) score -= (100 - eff.pass_rate) * 0.3
}
// 加分: 有效测试层
const effectiveLayers = Object.values(analysis.test_effectiveness)
.filter(e => e.effective).length
score += effectiveLayers * 5
// 加分: 改善趋势
if (analysis.quality_trend.trend === 'improving') score += 3
return Math.max(0, Math.min(100, Math.round(score)))
}
// 辅助函数
function findCommonPatterns(files) {
const dirs = files.map(f => f.split('/').slice(0, -1).join('/'))
const commonDir = dirs.reduce((a, b) => {
const partsA = a.split('/')
const partsB = b.split('/')
const common = []
for (let i = 0; i < Math.min(partsA.length, partsB.length); i++) {
if (partsA[i] === partsB[i]) common.push(partsA[i])
else break
}
return common.join('/')
})
return commonDir || 'scattered'
}
function generateRecommendation(type, issues) {
const recommendations = {
'security': '加强输入验证和安全审计,考虑引入 SAST 工具',
'bug': '改进错误处理和边界检查,增加防御性编程',
'test-coverage': '补充缺失的测试用例,聚焦未覆盖的分支',
'code-quality': '重构复杂函数,消除代码重复',
'ux': '统一错误提示和加载状态处理'
}
return recommendations[type] || '进一步分析并制定改进计划'
}
```
### Step 3: Result Processing
```javascript
// 组装分析结果
const analysis = {
defect_patterns: analyzeDefectPatterns(discoveredIssues, layerResults),
coverage_gaps: analyzeCoverageGaps(coverageData, strategy),
test_effectiveness: analyzeTestEffectiveness(generatedTests, layerResults),
quality_trend: analyzeQualityTrend(coverageHistory),
quality_score: 0
}
analysis.quality_score = calculateQualityScore(analysis)
// 生成报告文件
const reportContent = generateReportMarkdown(analysis)
Bash(`mkdir -p "${sessionFolder}/analysis"`)
Write(`${sessionFolder}/analysis/quality-report.md`, reportContent)
// 更新 shared memory
sharedMemory.defect_patterns = analysis.defect_patterns.patterns
sharedMemory.quality_score = analysis.quality_score
sharedMemory.coverage_history = sharedMemory.coverage_history || []
sharedMemory.coverage_history.push({
date: new Date().toISOString(),
coverage: analysis.coverage_gaps.total_coverage || 0,
quality_score: analysis.quality_score,
issues: analysis.defect_patterns.total
})
Write(`${sessionFolder}/.msg/meta.json`, JSON.stringify(sharedMemory, null, 2))
function generateReportMarkdown(analysis) {
return `# Quality Assurance Report
## Quality Score: ${analysis.quality_score}/100
---
## 1. Defect Pattern Analysis
- Total issues found: ${analysis.defect_patterns.total}
- Recurring patterns: ${analysis.defect_patterns.patterns.length}
${analysis.defect_patterns.patterns.map(p =>
`### Pattern: ${p.type} (${p.count} occurrences)
- Files: ${p.files.join(', ')}
- Common location: ${p.common_pattern}
- Recommendation: ${p.recommendation}`
).join('\n\n')}
## 2. Coverage Analysis
- Overall coverage: ${analysis.coverage_gaps.total_coverage || 'N/A'}%
- Coverage gaps: ${(analysis.coverage_gaps.gaps || []).length}
${(analysis.coverage_gaps.gaps || []).map(g =>
`- **${g.layer}**: target ${g.target}% vs actual ${g.actual}% (gap: ${g.gap}%, severity: ${g.severity})`
).join('\n')}
### Low Coverage Files
${(analysis.coverage_gaps.file_gaps || []).map(f =>
`- ${f.file}: ${f.coverage}% [${f.severity}]`
).join('\n')}
## 3. Test Effectiveness
${Object.entries(analysis.test_effectiveness).map(([layer, data]) =>
`- **${layer}**: ${data.files_generated} files, pass rate ${data.pass_rate}%, ${data.iterations_needed} fix iterations, ${data.effective ? 'EFFECTIVE' : 'NEEDS IMPROVEMENT'}`
).join('\n')}
## 4. Quality Trend
- Trend: ${analysis.quality_trend.trend}
${analysis.quality_trend.delta !== undefined ? `- Coverage delta: ${analysis.quality_trend.delta > 0 ? '+' : ''}${analysis.quality_trend.delta}%` : ''}
- Confidence: ${analysis.quality_trend.confidence}
## 5. Recommendations
${analysis.quality_score >= 80 ? '- Quality is **GOOD**. Maintain current testing practices.' : ''}
${analysis.quality_score >= 60 && analysis.quality_score < 80 ? '- Quality needs **IMPROVEMENT**. Focus on coverage gaps and recurring patterns.' : ''}
${analysis.quality_score < 60 ? '- Quality is **CONCERNING**. Recommend comprehensive review and testing effort.' : ''}
${analysis.defect_patterns.patterns.map(p => `- [${p.type}] ${p.recommendation}`).join('\n')}
${(analysis.coverage_gaps.gaps || []).map(g => `- Close ${g.layer} coverage gap: +${g.gap}% needed`).join('\n')}
`
}
```
## Output Format
```
## Quality Analysis Results
### Quality Score: [score]/100
### Dimensions
1. Defect Patterns: [count] recurring
2. Coverage Gaps: [count] layers below target
3. Test Effectiveness: [effective_count]/[total_layers] effective
4. Quality Trend: [improving|stable|declining]
### Report Location
[session]/analysis/quality-report.md
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| No coverage data available | Score based on other dimensions only |
| No execution results | Analyze only scout findings and strategy |
| Shared memory empty/corrupt | Generate minimal report with available data |
| CLI analysis fails | Fall back to direct inline analysis |
| Insufficient history for trend | Report 'insufficient_data', skip trend scoring |
| Agent/CLI failure | Retry once, then fallback to inline execution |
| Timeout (>5 min) | Report partial results, notify coordinator |

View File

@@ -1,183 +0,0 @@
# Analyst Role
Quality analyst. Analyze defect patterns, coverage gaps, test effectiveness, and generate comprehensive quality reports. Maintain defect pattern database and provide feedback data for scout and strategist.
## Identity
- **Name**: `analyst` | **Tag**: `[analyst]`
- **Task Prefix**: `QAANA-*`
- **Responsibility**: Read-only analysis (quality analysis)
## Boundaries
### MUST
- Only process `QAANA-*` prefixed tasks
- All output (SendMessage, team_msg, logs) must carry `[analyst]` identifier
- Only communicate with coordinator via SendMessage
- Generate analysis reports based on data
- Update defect patterns and quality score in shared memory
- Work strictly within quality analysis responsibility scope
### MUST NOT
- Execute work outside this role's responsibility scope
- Modify source code or test code
- Execute tests
- Communicate directly with other worker roles (must go through coordinator)
- Create tasks for other roles (TaskCreate is coordinator-exclusive)
- Omit `[analyst]` identifier in any output
---
## Toolbox
### Available Commands
| Command | File | Phase | Description |
|---------|------|-------|-------------|
| `quality-report` | [commands/quality-report.md](commands/quality-report.md) | Phase 3 | Defect pattern + coverage analysis |
### Tool Capabilities
| Tool | Type | Used By | Purpose |
|------|------|---------|---------|
| `gemini` | CLI | quality-report.md | Defect pattern recognition and trend analysis |
---
## Message Types
| Type | Direction | Trigger | Description |
|------|-----------|---------|-------------|
| `analysis_ready` | analyst -> coordinator | Analysis complete | Contains quality score |
| `quality_report` | analyst -> coordinator | Report generated | Contains detailed analysis |
| `error` | analyst -> coordinator | Analysis failed | Blocking error |
## Message Bus
Before every SendMessage, log via `mcp__ccw-tools__team_msg`:
```
mcp__ccw-tools__team_msg({
operation: "log",
session_id: <session-id>,
from: "analyst",
type: <message-type>,
data: { ref: <report-path> }
})
```
**CLI fallback** (when MCP unavailable):
```
Bash("ccw team log --session-id <session-id> --from analyst --type <message-type> --json")
```
---
## Execution (5-Phase)
### Phase 1: Task Discovery
> See SKILL.md Shared Infrastructure -> Worker Phase 1: Task Discovery
Standard task discovery flow: TaskList -> filter by prefix `QAANA-*` + owner match + pending + unblocked -> TaskGet -> TaskUpdate in_progress.
### Phase 2: Context Loading
**Loading steps**:
1. Extract session path from task description
2. Read shared memory to get all accumulated data
| Input | Source | Required |
|-------|--------|----------|
| Shared memory | <session-folder>/.msg/meta.json | Yes |
| Discovered issues | sharedMemory.discovered_issues | No |
| Test strategy | sharedMemory.test_strategy | No |
| Generated tests | sharedMemory.generated_tests | No |
| Execution results | sharedMemory.execution_results | No |
| Historical patterns | sharedMemory.defect_patterns | No |
3. Read coverage data from `coverage/coverage-summary.json` if available
4. Read test execution logs from `<session-folder>/results/run-*.json`
### Phase 3: Multi-Dimensional Analysis
Delegate to `commands/quality-report.md` if available, otherwise execute inline.
**Analysis Dimensions**:
| Dimension | Description |
|-----------|-------------|
| Defect Patterns | Group issues by type, identify recurring patterns |
| Coverage Gaps | Compare actual vs target coverage per layer |
| Test Effectiveness | Evaluate test generation and execution results |
| Quality Trend | Analyze coverage history over time |
| Quality Score | Calculate comprehensive score (0-100) |
**Defect Pattern Analysis**:
- Group issues by perspective/type
- Identify patterns with >= 2 occurrences
- Record pattern type, count, affected files
**Coverage Gap Analysis**:
- Compare total coverage vs layer targets
- Record gaps: layer, target, actual, gap percentage
**Test Effectiveness Analysis**:
- Files generated, pass rate, iterations needed
- Effective if pass_rate >= 95%
**Quality Score Calculation**:
| Factor | Impact |
|--------|--------|
| Critical issues (security) | -10 per issue |
| High issues (bug) | -5 per issue |
| Coverage gap | -0.5 per gap percentage |
| Effective test layers | +5 per layer |
### Phase 4: Report Generation
**Report Structure**:
1. Quality Score (0-100)
2. Defect Pattern Analysis (total issues, recurring patterns)
3. Coverage Analysis (overall coverage, gaps by layer)
4. Test Effectiveness (per layer stats)
5. Quality Trend (improving/declining/stable)
6. Recommendations (based on score range)
**Score-based Recommendations**:
| Score Range | Recommendation |
|-------------|----------------|
| >= 80 | Quality is GOOD. Continue with current testing strategy. |
| 60-79 | Quality needs IMPROVEMENT. Focus on coverage gaps and recurring patterns. |
| < 60 | Quality is CONCERNING. Recommend deep scan and comprehensive test generation. |
Write report to `<session-folder>/analysis/quality-report.md`.
Update shared memory:
- `defect_patterns`: identified patterns
- `quality_score`: calculated score
- `coverage_history`: append new data point
### Phase 5: Report to Coordinator
> See SKILL.md Shared Infrastructure -> Worker Phase 5: Report
Standard report flow: team_msg log -> SendMessage with `[analyst]` prefix -> TaskUpdate completed -> Loop to Phase 1 for next task.
---
## Error Handling
| Scenario | Resolution |
|----------|------------|
| No QAANA-* tasks available | Idle, wait for coordinator |
| Coverage data not found | Report quality score based on other dimensions |
| Shared memory empty | Generate minimal report with available data |
| No execution results | Analyze only scout findings and strategy coverage |
| CLI analysis fails | Fall back to inline pattern analysis |
| Critical issue beyond scope | SendMessage error to coordinator |

View File

@@ -1,317 +1,204 @@
# Command: monitor
# Command: Monitor
> 阶段驱动的协调循环。按 pipeline 阶段顺序等待 worker 完成,路由消息,触发 GC 循环,执行质量门控。
Handle all coordinator monitoring events: worker callbacks, status checks, pipeline advancement, GC loops, and completion.
## Constants
## When to Use
| Key | Value |
|-----|-------|
| SPAWN_MODE | background |
| ONE_STEP_PER_INVOCATION | true |
| WORKER_AGENT | team-worker |
| MAX_GC_ROUNDS | 3 |
- Phase 4 of Coordinator
- 任务链已创建并分发
- 需要持续监控直到所有任务完成
## Phase 2: Context Loading
**Trigger conditions**:
- dispatch 完成后立即启动
- GC 循环创建新任务后重新进入
| Input | Source | Required |
|-------|--------|----------|
| Session state | <session>/session.json | Yes |
| Task list | TaskList() | Yes |
| Trigger event | From Entry Router detection | Yes |
| Meta state | <session>/.msg/meta.json | Yes |
| Pipeline definition | From SKILL.md | Yes |
## Strategy
1. Load session.json for current state, `pipeline_mode`, `gc_rounds`
2. Run TaskList() to get current task statuses
3. Identify trigger event type from Entry Router
### Delegation Mode
### Role Detection Table
**Mode**: Stage-driven按阶段顺序等待非轮询
| Message Pattern | Role Detection |
|----------------|---------------|
| `[scout]` or task ID `SCOUT-*` | scout |
| `[strategist]` or task ID `QASTRAT-*` | strategist |
| `[generator]` or task ID `QAGEN-*` | generator |
| `[executor]` or task ID `QARUN-*` | executor |
| `[analyst]` or task ID `QAANA-*` | analyst |
### 设计原则
### Pipeline Stage Order
> **模型执行没有时间概念,禁止任何形式的轮询等待。**
>
> - ❌ 禁止: `while` 循环 + `sleep` + 检查状态(空转浪费 API 轮次)
> - ❌ 禁止: `Bash(sleep N)` / `Bash(timeout /t N)` 作为等待手段
> - ✅ 采用: 同步 `Task()` 调用(`run_in_background: false`call 本身即等待
> - ✅ 采用: Worker 返回 = 阶段完成信号(天然回调)
>
> **原理**: `Task(run_in_background: false)` 是阻塞调用coordinator 自动挂起直到 worker 返回。
> 无需 sleep无需轮询无需消息总线监控。Worker 的返回就是回调。
### Decision Logic
```javascript
// 消息路由表
const routingTable = {
// Scout 完成
'scan_ready': { action: 'Mark SCOUT complete, unblock QASTRAT' },
'issues_found': { action: 'Mark SCOUT complete with issues, unblock QASTRAT' },
// Strategist 完成
'strategy_ready': { action: 'Mark QASTRAT complete, unblock QAGEN' },
// Generator 完成
'tests_generated': { action: 'Mark QAGEN complete, unblock QARUN' },
'tests_revised': { action: 'Mark QAGEN-fix complete, unblock QARUN-gc' },
// Executor 完成
'tests_passed': { action: 'Mark QARUN complete, check coverage, unblock next', special: 'check_coverage' },
'tests_failed': { action: 'Evaluate failures, decide GC loop or continue', special: 'gc_decision' },
// Analyst 完成
'analysis_ready': { action: 'Mark QAANA complete, evaluate quality gate', special: 'quality_gate' },
'quality_report': { action: 'Quality report received, prepare final report', special: 'finalize' },
// 错误
'error': { action: 'Assess severity, retry or escalate', special: 'error_handler' }
}
```
SCOUT -> QASTRAT -> QAGEN -> QARUN -> QAANA
```
### Stage-Worker 映射表
## Phase 3: Event Handlers
### handleCallback
Triggered when a worker sends completion message.
1. Parse message to identify role and task ID using Role Detection Table
2. Mark task as completed:
```
TaskUpdate({ taskId: "<task-id>", status: "completed" })
```
3. Record completion in session state
4. **GC Loop Check** (when executor QARUN completes):
Read `<session>/.msg/meta.json` for execution results.
| Condition | Action |
|-----------|--------|
| Coverage >= target OR no coverage data | Proceed to handleSpawnNext |
| Coverage < target AND gc_rounds < 3 | Create GC fix tasks, increment gc_rounds |
| Coverage < target AND gc_rounds >= 3 | Accept current coverage, proceed to handleSpawnNext |
**GC Fix Task Creation** (when coverage below target):
```
TaskCreate({
subject: "QAGEN-fix-<round>",
description: "PURPOSE: Fix failing tests and improve coverage | Success: Coverage meets target
TASK:
- Load execution results and failing test details
- Fix broken tests and add missing coverage
- Re-validate fixes
CONTEXT:
- Session: <session-folder>
- Upstream artifacts: <session>/.msg/meta.json
EXPECTED: Fixed test files | Improved coverage
CONSTRAINTS: Targeted fixes only | Do not introduce regressions",
blockedBy: [],
status: "pending"
})
TaskCreate({
subject: "QARUN-recheck-<round>",
description: "PURPOSE: Re-execute tests after fixes | Success: Coverage >= target
TASK:
- Execute test suite on fixed code
- Measure coverage
- Report results
CONTEXT:
- Session: <session-folder>
EXPECTED: Execution results with coverage metrics
CONSTRAINTS: Read-only execution",
blockedBy: ["QAGEN-fix-<round>"],
status: "pending"
})
```
5. Proceed to handleSpawnNext
### handleSpawnNext
Find and spawn the next ready tasks.
1. Scan task list for tasks where:
- Status is "pending"
- All blockedBy tasks have status "completed"
2. If no ready tasks and all tasks completed, proceed to handleComplete
3. If no ready tasks but some still in_progress, STOP and wait
4. For each ready task, determine role from task subject prefix:
```javascript
const STAGE_WORKER_MAP = {
'SCOUT': { role: 'scout', skillArgs: '--role=scout' },
'QASTRAT': { role: 'strategist', skillArgs: '--role=strategist' },
'QAGEN': { role: 'generator', skillArgs: '--role=generator' },
'QARUN': { role: 'executor', skillArgs: '--role=executor' },
'QAANA': { role: 'analyst', skillArgs: '--role=analyst' }
'SCOUT': { role: 'scout' },
'QASTRAT': { role: 'strategist' },
'QAGEN': { role: 'generator' },
'QARUN': { role: 'executor' },
'QAANA': { role: 'analyst' }
}
// ★ 统一 auto mode 检测:-y/--yes 从 $ARGUMENTS 或 ccw 传播
const autoYes = /\b(-y|--yes)\b/.test(args)
```
## Execution Steps
5. Spawn team-worker (one at a time for sequential pipeline):
### Step 1: Context Preparation
```javascript
// 从 shared memory 获取覆盖率目标
const sharedMemory = JSON.parse(Read(`${sessionFolder}/.msg/meta.json`))
const strategy = sharedMemory.test_strategy || {}
const coverageTargets = {}
for (const layer of (strategy.layers || [])) {
coverageTargets[layer.level] = layer.target_coverage
}
let gcIteration = 0
const MAX_GC_ITERATIONS = 3
// 获取 pipeline 阶段列表(来自 dispatch 创建的任务链)
const allTasks = TaskList()
const pipelineTasks = allTasks
.filter(t => t.owner && t.owner !== 'coordinator')
.sort((a, b) => Number(a.id) - Number(b.id))
```
### Step 2: Sequential Stage Execution (Stop-Wait)
> **核心**: 逐阶段 spawn worker同步阻塞等待返回。
> Worker 返回 = 阶段完成。无 sleep、无轮询、无消息总线监控。
```javascript
// 按依赖顺序处理每个阶段
for (const stageTask of pipelineTasks) {
// 1. 提取阶段前缀 → 确定 worker 角色
const stagePrefix = stageTask.subject.match(/^([\w-]+)-\d/)?.[1]?.replace(/-L\d$/, '')
const workerConfig = STAGE_WORKER_MAP[stagePrefix]
if (!workerConfig) {
mcp__ccw-tools__team_msg({
operation: "log", session_id: teamName, from: "coordinator",
})
continue
}
// 2. 标记任务为执行中
TaskUpdate({ taskId: stageTask.id, status: 'in_progress' })
mcp__ccw-tools__team_msg({
operation: "log", session_id: teamName, from: "coordinator",
to: workerConfig.role, type: "task_unblocked",
})
// 3. 同步 spawn worker — 阻塞直到 worker 返回Stop-Wait 核心)
const workerResult = Task({
subagent_type: "team-worker",
description: `Spawn ${workerConfig.role} worker for ${stageTask.subject}`,
team_name: teamName,
name: workerConfig.role,
prompt: `## Role Assignment
role: ${workerConfig.role}
role_spec: .claude/skills/team-quality-assurance/role-specs/${workerConfig.role}.md
session: ${sessionFolder}
session_id: ${sessionId}
team_name: ${teamName}
requirement: ${stageTask.description || taskDescription}
Task({
subagent_type: "team-worker",
description: "Spawn <role> worker for <task-id>",
team_name: "quality-assurance",
name: "<role>",
run_in_background: true,
prompt: `## Role Assignment
role: <role>
role_spec: .claude/skills/team-quality-assurance/role-specs/<role>.md
session: <session-folder>
session_id: <session-id>
team_name: quality-assurance
requirement: <task-description>
inner_loop: false
## Current Task
- Task ID: ${stageTask.id}
- Task: ${stageTask.subject}
- Task ID: <task-id>
- Task: <task-subject>
Read role_spec file to load Phase 2-4 domain instructions.
Execute built-in Phase 1 -> role-spec Phase 2-4 -> built-in Phase 5.`,
run_in_background: false
})
// 4. Worker 已返回 — 直接处理结果
const taskState = TaskGet({ taskId: stageTask.id })
if (taskState.status !== 'completed') {
// Worker 返回但未标记 completed → 异常处理
if (autoYes) {
mcp__ccw-tools__team_msg({
operation: "log", session_id: teamName, from: "coordinator",
type: "error",
})
TaskUpdate({ taskId: stageTask.id, status: 'deleted' })
continue
}
const decision = AskUserQuestion({
questions: [{
question: `阶段 "${stageTask.subject}" worker 返回但未完成。如何处理?`,
header: "Stage Fail",
multiSelect: false,
options: [
{ label: "重试", description: "重新 spawn worker 执行此阶段" },
{ label: "跳过", description: "标记为跳过,继续后续流水线" },
{ label: "终止", description: "停止整个 QA 流程,汇报当前结果" }
]
}]
})
const answer = decision["Stage Fail"]
if (answer === "跳过") {
TaskUpdate({ taskId: stageTask.id, status: 'deleted' })
continue
} else if (answer === "终止") {
mcp__ccw-tools__team_msg({
operation: "log", session_id: teamName, from: "coordinator",
to: "user", type: "shutdown",
})
break
}
// 重试: continue to next iteration will re-process if logic wraps
} else {
mcp__ccw-tools__team_msg({
operation: "log", session_id: teamName, from: "coordinator",
})
}
// 5. 阶段间检查QARUN 阶段检查覆盖率,决定 GC 循环)
if (stagePrefix === 'QARUN') {
const latestMemory = JSON.parse(Read(`${sessionFolder}/.msg/meta.json`))
const coverage = latestMemory.execution_results?.coverage || 0
const targetLayer = stageTask.metadata?.layer || 'L1'
const target = coverageTargets[targetLayer] || 80
if (coverage < target && gcIteration < MAX_GC_ITERATIONS) {
gcIteration++
mcp__ccw-tools__team_msg({
operation: "log", session_id: teamName, from: "coordinator",
to: "generator", type: "gc_loop_trigger",
})
// 创建 GC 修复任务追加到 pipeline
}
}
}
Execute built-in Phase 1 -> role-spec Phase 2-4 -> built-in Phase 5.`
})
```
### Step 2.1: Message Processing (processMessage)
6. STOP after spawning -- wait for next callback
```javascript
function processMessage(msg, handler) {
switch (handler.special) {
case 'check_coverage': {
const coverage = msg.data?.coverage || 0
const targetLayer = msg.data?.layer || 'L1'
const target = coverageTargets[targetLayer] || 80
### handleCheck
if (coverage < target) {
handleGCDecision(coverage, targetLayer)
}
// 覆盖率达标则不做额外处理,流水线自然流转
break
}
case 'gc_decision': {
const coverage = msg.data?.coverage || 0
const targetLayer = msg.data?.layer || 'L1'
handleGCDecision(coverage, targetLayer)
break
}
case 'quality_gate': {
// 重新读取最新 shared memory
const latestMemory = JSON.parse(Read(`${sessionFolder}/.msg/meta.json`))
const qualityScore = latestMemory.quality_score || 0
let status = 'PASS'
if (qualityScore < 60) status = 'FAIL'
else if (qualityScore < 80) status = 'CONDITIONAL'
mcp__ccw-tools__team_msg({
operation: "log", session_id: teamName, from: "coordinator",
to: "user", type: "quality_gate",
})
break
}
case 'error_handler': {
const severity = msg.data?.severity || 'medium'
if (severity === 'critical') {
SendMessage({
content: `## [coordinator] Critical Error from ${msg.from}\n\n${msg.summary}`,
})
}
break
}
}
}
function handleGCDecision(coverage, targetLayer) {
if (gcIteration < MAX_GC_ITERATIONS) {
gcIteration++
mcp__ccw-tools__team_msg({
operation: "log", session_id: teamName, from: "coordinator",
data: { iteration: gcIteration, layer: targetLayer, coverage }
})
// 创建 GC 修复任务(参见 dispatch.md createGCLoopTasks
} else {
mcp__ccw-tools__team_msg({
operation: "log", session_id: teamName, from: "coordinator",
})
}
}
```
### Step 3: Result Processing
```javascript
// 汇总所有结果
const finalSharedMemory = JSON.parse(Read(`${sessionFolder}/.msg/meta.json`))
const allFinalTasks = TaskList()
const workerTasks = allFinalTasks.filter(t => t.owner && t.owner !== 'coordinator')
const summary = {
total_tasks: workerTasks.length,
completed_tasks: workerTasks.filter(t => t.status === 'completed').length,
gc_iterations: gcIteration,
quality_score: finalSharedMemory.quality_score,
coverage: finalSharedMemory.execution_results?.coverage
}
```
## Output Format
Output current pipeline status.
```
## Coordination Summary
Pipeline Status:
[DONE] SCOUT-001 (scout) -> scan complete
[DONE] QASTRAT-001 (strategist) -> strategy ready
[RUN] QAGEN-001 (generator) -> generating tests...
[WAIT] QARUN-001 (executor) -> blocked by QAGEN-001
[WAIT] QAANA-001 (analyst) -> blocked by QARUN-001
### Pipeline Status: COMPLETE
### Tasks: [completed]/[total]
### GC Iterations: [count]
### Quality Score: [score]/100
### Coverage: [percent]%
### Message Log (last 10)
- [timestamp] [from] → [to]: [type] - [summary]
GC Rounds: 0/3
Session: <session-id>
```
## Error Handling
Output status -- do NOT advance pipeline.
| Scenario | Resolution |
|----------|------------|
| Worker 返回但未 completed (交互模式) | AskUserQuestion: 重试 / 跳过 / 终止 |
| Worker 返回但未 completed (自动模式) | 自动跳过,记录日志 |
| Worker spawn 失败 | 重试一次,仍失败则上报用户 |
| Quality gate FAIL | Report to user, suggest targeted re-run |
| GC loop stuck >3 iterations | Accept current coverage, continue pipeline |
### handleResume
Resume pipeline after user pause or interruption.
1. Audit task list for inconsistencies:
- Tasks stuck in "in_progress" -> reset to "pending"
- Tasks with completed blockers but still "pending" -> include in spawn list
2. Proceed to handleSpawnNext
### handleComplete
Triggered when all pipeline tasks are completed.
1. Verify all tasks (including any GC fix/recheck tasks) have status "completed"
2. If any tasks not completed, return to handleSpawnNext
3. If all completed:
- Read final state from `<session>/.msg/meta.json`
- Compile summary: total tasks, completed, gc_rounds, quality_score, coverage
- Transition to coordinator Phase 5
## Phase 4: State Persistence
After every handler execution:
1. Update session.json with current state (active tasks, gc_rounds, last event)
2. Verify task list consistency
3. STOP and wait for next event

View File

@@ -1,220 +0,0 @@
# Command: run-fix-cycle
> 迭代测试执行与自动修复。运行测试套件,解析结果,失败时委派 code-developer 修复,最多迭代 5 次。
## When to Use
- Phase 3 of Executor
- 测试代码已生成,需要执行并验证
- GC 循环中重新执行修复后的测试
**Trigger conditions**:
- QARUN-* 任务进入执行阶段
- Generator 报告测试生成完成
- GC 循环中 coordinator 创建的重新执行任务
## Strategy
### Delegation Mode
**Mode**: Sequential Delegation修复时/ Direct执行时
**Agent Type**: `code-developer`(仅用于修复)
**Max Iterations**: 5
### Decision Logic
```javascript
// 每次迭代的决策
function shouldContinue(iteration, passRate, testsFailed) {
if (iteration >= MAX_ITERATIONS) return false
if (testsFailed === 0) return false // 全部通过
if (passRate >= 95 && iteration >= 2) return false // 足够好
return true
}
```
## Execution Steps
### Step 1: Context Preparation
```javascript
// 检测测试框架和命令
const strategy = sharedMemory.test_strategy || {}
const framework = strategy.test_framework || 'vitest'
const targetLayer = task.description.match(/layer:\s*(L[123])/)?.[1] || 'L1'
// 构建测试命令
function buildTestCommand(framework, layer) {
const layerFilter = {
'L1': 'unit',
'L2': 'integration',
'L3': 'e2e'
}
const commands = {
'vitest': `npx vitest run --coverage --reporter=json --outputFile=test-results.json`,
'jest': `npx jest --coverage --json --outputFile=test-results.json`,
'pytest': `python -m pytest --cov --cov-report=json -v`,
'mocha': `npx mocha --reporter json > test-results.json`
}
let cmd = commands[framework] || 'npm test -- --coverage'
// 添加层级过滤(如果测试文件按目录组织)
const filter = layerFilter[layer]
if (filter && framework === 'vitest') {
cmd += ` --testPathPattern="${filter}"`
}
return cmd
}
const testCommand = buildTestCommand(framework, targetLayer)
// 获取关联的测试文件
const generatedTests = sharedMemory.generated_tests?.[targetLayer]?.files || []
```
### Step 2: Execute Strategy
```javascript
let iteration = 0
const MAX_ITERATIONS = 5
let lastOutput = ''
let passRate = 0
let coverage = 0
let testsPassed = 0
let testsFailed = 0
while (iteration < MAX_ITERATIONS) {
// ===== EXECUTE TESTS =====
lastOutput = Bash(`${testCommand} 2>&1 || true`)
// ===== PARSE RESULTS =====
// 解析通过/失败数
const passedMatch = lastOutput.match(/(\d+)\s*(?:passed|passing)/)
const failedMatch = lastOutput.match(/(\d+)\s*(?:failed|failing)/)
testsPassed = passedMatch ? parseInt(passedMatch[1]) : 0
testsFailed = failedMatch ? parseInt(failedMatch[1]) : 0
const testsTotal = testsPassed + testsFailed
passRate = testsTotal > 0 ? Math.round(testsPassed / testsTotal * 100) : 0
// 解析覆盖率
try {
const coverageJson = JSON.parse(Read('coverage/coverage-summary.json'))
coverage = coverageJson.total?.lines?.pct || 0
} catch {
// 尝试从输出解析
const covMatch = lastOutput.match(/(?:Lines|Stmts|All files)\s*[:|]\s*(\d+\.?\d*)%/)
coverage = covMatch ? parseFloat(covMatch[1]) : 0
}
// ===== CHECK PASS =====
if (testsFailed === 0) {
break // 全部通过
}
// ===== SHOULD CONTINUE? =====
if (!shouldContinue(iteration + 1, passRate, testsFailed)) {
break
}
// ===== AUTO-FIX =====
iteration++
// 提取失败详情
const failureLines = lastOutput.split('\n')
.filter(l => /FAIL|Error|AssertionError|Expected|Received|TypeError|ReferenceError/.test(l))
.slice(0, 30)
.join('\n')
// 委派修复给 code-developer
Task({
subagent_type: "code-developer",
run_in_background: false,
description: `Fix ${testsFailed} test failures (iteration ${iteration}/${MAX_ITERATIONS})`,
prompt: `## Goal
Fix failing tests. ONLY modify test files, NEVER modify source code.
## Test Output
\`\`\`
${failureLines}
\`\`\`
## Test Files to Fix
${generatedTests.map(f => `- ${f}`).join('\n')}
## Rules
- Read each failing test file before modifying
- Fix: incorrect assertions, missing imports, wrong mocks, setup issues
- Do NOT: skip tests, add \`@ts-ignore\`, use \`as any\`, modify source code
- Keep existing test structure and naming
- If a test is fundamentally wrong about expected behavior, fix the assertion to match actual source behavior`
})
}
```
### Step 3: Result Processing
```javascript
const resultData = {
layer: targetLayer,
framework: framework,
iterations: iteration,
pass_rate: passRate,
coverage: coverage,
tests_passed: testsPassed,
tests_failed: testsFailed,
all_passed: testsFailed === 0,
max_iterations_reached: iteration >= MAX_ITERATIONS
}
// 保存执行结果
Bash(`mkdir -p "${sessionFolder}/results"`)
Write(`${sessionFolder}/results/run-${targetLayer}.json`, JSON.stringify(resultData, null, 2))
// 保存最后一次测试输出(截取关键部分)
const outputSummary = lastOutput.split('\n').slice(-30).join('\n')
Write(`${sessionFolder}/results/output-${targetLayer}.txt`, outputSummary)
// 更新 shared memory
sharedMemory.execution_results = sharedMemory.execution_results || {}
sharedMemory.execution_results[targetLayer] = resultData
sharedMemory.execution_results.pass_rate = passRate
sharedMemory.execution_results.coverage = coverage
Write(`${sessionFolder}/.msg/meta.json`, JSON.stringify(sharedMemory, null, 2))
```
## Output Format
```
## Test Execution Results
### Layer: [L1|L2|L3]
### Framework: [vitest|jest|pytest]
### Status: [PASS|FAIL]
### Results
- Tests passed: [count]
- Tests failed: [count]
- Pass rate: [percent]%
- Coverage: [percent]%
- Fix iterations: [count]/[max]
### Failure Details (if any)
- [test name]: [error description]
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| Test command not found | Try fallback: npm test → npx vitest → npx jest → pytest |
| Test environment broken | Report error to coordinator, suggest manual fix |
| Max iterations reached with failures | Report current state, let coordinator decide (GC loop or accept) |
| Coverage data unavailable | Report 0%, note coverage collection failure |
| Sub-agent fix introduces new failures | Revert last fix, try different approach |
| No test files to run | Report empty, notify coordinator |
| Agent/CLI failure | Retry once, then fallback to inline execution |
| Timeout (>5 min) | Report partial results, notify coordinator |

View File

@@ -1,175 +0,0 @@
# Executor Role
Test executor. Run test suites, collect coverage data, and perform automatic fix cycles when tests fail. Implement the execution side of the Generator-Executor (GC) loop.
## Identity
- **Name**: `executor` | **Tag**: `[executor]`
- **Task Prefix**: `QARUN-*`
- **Responsibility**: Validation (test execution and fix)
## Boundaries
### MUST
- Only process `QARUN-*` prefixed tasks
- All output (SendMessage, team_msg, logs) must carry `[executor]` identifier
- Only communicate with coordinator via SendMessage
- Execute tests and collect coverage
- Attempt automatic fix on failure
- Work strictly within test execution responsibility scope
### MUST NOT
- Execute work outside this role's responsibility scope
- Generate new tests from scratch (that's generator's responsibility)
- Modify source code (unless fixing tests themselves)
- Communicate directly with other worker roles (must go through coordinator)
- Create tasks for other roles (TaskCreate is coordinator-exclusive)
- Omit `[executor]` identifier in any output
---
## Toolbox
### Available Commands
| Command | File | Phase | Description |
|---------|------|-------|-------------|
| `run-fix-cycle` | [commands/run-fix-cycle.md](commands/run-fix-cycle.md) | Phase 3 | Iterative test execution and auto-fix |
### Tool Capabilities
| Tool | Type | Used By | Purpose |
|------|------|---------|---------|
| `code-developer` | subagent | run-fix-cycle.md | Test failure auto-fix |
---
## Message Types
| Type | Direction | Trigger | Description |
|------|-----------|---------|-------------|
| `tests_passed` | executor -> coordinator | All tests pass | Contains coverage data |
| `tests_failed` | executor -> coordinator | Tests fail | Contains failure details and fix attempts |
| `coverage_report` | executor -> coordinator | Coverage collected | Coverage data |
| `error` | executor -> coordinator | Execution environment error | Blocking error |
## Message Bus
Before every SendMessage, log via `mcp__ccw-tools__team_msg`:
```
mcp__ccw-tools__team_msg({
operation: "log",
session_id: <session-id>,
from: "executor",
type: <message-type>,
data: { ref: <results-file>, pass_rate, coverage, iterations }
})
```
**CLI fallback** (when MCP unavailable):
```
Bash("ccw team log --session-id <session-id> --from executor --type <message-type> --json")
```
---
## Execution (5-Phase)
### Phase 1: Task Discovery
> See SKILL.md Shared Infrastructure -> Worker Phase 1: Task Discovery
Standard task discovery flow: TaskList -> filter by prefix `QARUN-*` + owner match + pending + unblocked -> TaskGet -> TaskUpdate in_progress.
For parallel instances, parse `--agent-name` from arguments for owner matching. Falls back to `executor` for single-instance execution.
### Phase 2: Environment Detection
**Detection steps**:
1. Extract session path from task description
2. Read shared memory for strategy and generated tests
| Input | Source | Required |
|-------|--------|----------|
| Shared memory | <session-folder>/.msg/meta.json | Yes |
| Test strategy | sharedMemory.test_strategy | Yes |
| Generated tests | sharedMemory.generated_tests | Yes |
| Target layer | task description | Yes |
3. Detect test command based on framework:
| Framework | Command Pattern |
|-----------|-----------------|
| jest | `npx jest --coverage --testPathPattern="<layer>"` |
| vitest | `npx vitest run --coverage --reporter=json` |
| pytest | `python -m pytest --cov --cov-report=json` |
| mocha | `npx mocha --reporter json` |
| unknown | `npm test -- --coverage` |
4. Get changed test files from generated_tests[targetLayer].files
### Phase 3: Execution & Fix Cycle
Delegate to `commands/run-fix-cycle.md` if available, otherwise execute inline.
**Iterative Test-Fix Cycle**:
| Step | Action |
|------|--------|
| 1 | Run test command |
| 2 | Parse results -> check pass rate |
| 3 | Pass rate >= 95% -> exit loop (success) |
| 4 | Extract failing test details |
| 5 | Delegate fix to code-developer subagent |
| 6 | Increment iteration counter |
| 7 | iteration >= MAX (5) -> exit loop (report failures) |
| 8 | Go to Step 1 |
**Fix Agent Prompt Structure**:
- Goal: Fix failing tests
- Constraint: Do NOT modify source code, only fix test files
- Input: Failure details, test file list
- Instructions: Read failing tests, fix assertions/imports/setup, do NOT skip/ignore tests
### Phase 4: Result Analysis
**Analyze test outcomes**:
| Metric | Source | Threshold |
|--------|--------|-----------|
| Pass rate | Test output parser | >= 95% |
| Coverage | Coverage tool output | Per layer target |
| Flaky tests | Compare runs | 0 flaky |
**Result Data Structure**:
- layer, iterations, pass_rate, coverage
- tests_passed, tests_failed, all_passed
Save results to `<session-folder>/results/run-<layer>.json`.
Update shared memory with `execution_results` field.
### Phase 5: Report to Coordinator
> See SKILL.md Shared Infrastructure -> Worker Phase 5: Report
Standard report flow: team_msg log -> SendMessage with `[executor]` prefix -> TaskUpdate completed -> Loop to Phase 1 for next task.
Message type selection: `tests_passed` if all_passed, else `tests_failed`.
---
## Error Handling
| Scenario | Resolution |
|----------|------------|
| No QARUN-* tasks available | Idle, wait for coordinator |
| Test command fails to execute | Try fallback: `npm test`, `npx vitest run`, `pytest` |
| Max iterations reached | Report current pass rate, let coordinator decide |
| Coverage data unavailable | Report 0%, note coverage collection failure |
| Test environment broken | SendMessage error to coordinator, suggest manual fix |
| Sub-agent fix introduces new failures | Revert fix, try next failure |

View File

@@ -1,258 +0,0 @@
# Command: generate-tests
> 按层级生成测试代码。根据 strategist 策略和项目现有测试模式,生成 L1/L2/L3 测试用例。
## When to Use
- Phase 3 of Generator
- 策略已制定,需要生成对应层级的测试代码
- GC 循环中修订失败的测试
**Trigger conditions**:
- QAGEN-* 任务进入执行阶段
- 测试策略中包含当前层级
- GC 循环触发修复任务QAGEN-fix-*
## Strategy
### Delegation Mode
**Mode**: Sequential Delegation复杂时/ Direct简单时
**Agent Type**: `code-developer`
**Delegation Scope**: Per-layer
### Decision Logic
```javascript
const focusFiles = layerConfig.focus_files || []
const isGCFix = task.subject.includes('fix')
if (isGCFix) {
// GC 修复模式:读取失败信息,针对性修复
mode = 'gc-fix'
} else if (focusFiles.length <= 3) {
// 直接生成:内联 Read → 分析 → Write
mode = 'direct'
} else {
// 委派给 code-developer
mode = 'delegate'
}
```
## Execution Steps
### Step 1: Context Preparation
```javascript
// 从 shared memory 获取策略
const strategy = sharedMemory.test_strategy || {}
const targetLayer = task.description.match(/layer:\s*(L[123])/)?.[1] || 'L1'
// 确定层级配置
const layerConfig = strategy.layers?.find(l => l.level === targetLayer) || {
level: targetLayer,
name: targetLayer === 'L1' ? 'Unit Tests' : targetLayer === 'L2' ? 'Integration Tests' : 'E2E Tests',
target_coverage: targetLayer === 'L1' ? 80 : targetLayer === 'L2' ? 60 : 40,
focus_files: []
}
// 学习现有测试模式(必须找 3 个相似测试文件)
const existingTests = Glob(`**/*.{test,spec}.{ts,tsx,js,jsx}`)
const testPatterns = existingTests.slice(0, 3).map(f => ({
path: f,
content: Read(f)
}))
// 检测测试约定
const testConventions = detectTestConventions(testPatterns)
```
### Step 2: Execute Strategy
```javascript
if (mode === 'gc-fix') {
// GC 修复模式
// 读取失败信息
const failedTests = sharedMemory.execution_results?.[targetLayer]
const failureOutput = Read(`${sessionFolder}/results/run-${targetLayer}.json`)
Task({
subagent_type: "code-developer",
run_in_background: false,
description: `Fix failing ${targetLayer} tests (GC iteration)`,
prompt: `## Goal
Fix the failing tests based on execution results. Do NOT modify source code.
## Test Execution Results
${JSON.stringify(failedTests, null, 2)}
## Test Conventions
${JSON.stringify(testConventions, null, 2)}
## Instructions
- Read each failing test file
- Fix assertions, imports, mocks, or test setup
- Ensure tests match actual source behavior
- Do NOT skip or ignore tests
- Do NOT modify source files`
})
} else if (mode === 'direct') {
// 直接生成模式
const focusFiles = layerConfig.focus_files || []
for (const sourceFile of focusFiles) {
const sourceContent = Read(sourceFile)
// 确定测试文件路径(遵循项目约定)
const testPath = determineTestPath(sourceFile, testConventions)
// 检查是否已有测试
let existingTest = null
try { existingTest = Read(testPath) } catch {}
if (existingTest) {
// 补充现有测试:分析缺失的测试用例
const missingCases = analyzeMissingCases(sourceContent, existingTest)
if (missingCases.length > 0) {
// 追加测试用例
Edit({
file_path: testPath,
old_string: findLastTestBlock(existingTest),
new_string: `${findLastTestBlock(existingTest)}\n\n${generateCases(missingCases, testConventions)}`
})
}
} else {
// 创建新测试文件
const testContent = generateFullTestFile(sourceFile, sourceContent, testConventions, targetLayer)
Write(testPath, testContent)
}
}
} else {
// 委派模式
const focusFiles = layerConfig.focus_files || []
Task({
subagent_type: "code-developer",
run_in_background: false,
description: `Generate ${targetLayer} tests for ${focusFiles.length} files`,
prompt: `## Goal
Generate ${layerConfig.name} for the following source files.
## Test Framework
${strategy.test_framework || 'vitest'}
## Existing Test Patterns (MUST follow these exactly)
${testPatterns.map(t => `### ${t.path}\n\`\`\`\n${t.content.substring(0, 800)}\n\`\`\``).join('\n\n')}
## Test Conventions
- Test file location: ${testConventions.location}
- Import style: ${testConventions.importStyle}
- Describe/it nesting: ${testConventions.nesting}
## Source Files to Test
${focusFiles.map(f => `- ${f}`).join('\n')}
## Requirements
- Follow existing test patterns exactly (import style, naming, structure)
- Cover: happy path + edge cases + error cases
- Target coverage: ${layerConfig.target_coverage}%
- Do NOT modify source files, only create/modify test files
- Do NOT use \`any\` type assertions
- Do NOT skip or mark tests as TODO without implementation`
})
}
// 辅助函数
function determineTestPath(sourceFile, conventions) {
if (conventions.location === 'colocated') {
return sourceFile.replace(/\.(ts|tsx|js|jsx)$/, `.test.$1`)
} else if (conventions.location === '__tests__') {
const dir = sourceFile.substring(0, sourceFile.lastIndexOf('/'))
const name = sourceFile.substring(sourceFile.lastIndexOf('/') + 1)
return `${dir}/__tests__/${name.replace(/\.(ts|tsx|js|jsx)$/, `.test.$1`)}`
}
return sourceFile.replace(/\.(ts|tsx|js|jsx)$/, `.test.$1`)
}
function detectTestConventions(patterns) {
const conventions = {
location: 'colocated', // or '__tests__'
importStyle: 'named', // or 'default'
nesting: 'describe-it', // or 'test-only'
framework: 'vitest'
}
for (const p of patterns) {
if (p.path.includes('__tests__')) conventions.location = '__tests__'
if (p.content.includes("import { describe")) conventions.nesting = 'describe-it'
if (p.content.includes("from 'vitest'")) conventions.framework = 'vitest'
if (p.content.includes("from '@jest'") || p.content.includes("from 'jest'")) conventions.framework = 'jest'
}
return conventions
}
```
### Step 3: Result Processing
```javascript
// 收集生成/修改的测试文件
const generatedTests = Bash(`git diff --name-only`).split('\n')
.filter(f => /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(f))
// TypeScript 语法检查
const syntaxResult = Bash(`npx tsc --noEmit ${generatedTests.join(' ')} 2>&1 || true`)
const hasSyntaxErrors = syntaxResult.includes('error TS')
// 自动修复语法错误(最多 3 次)
if (hasSyntaxErrors) {
let fixAttempt = 0
while (fixAttempt < 3 && syntaxResult.includes('error TS')) {
const errors = syntaxResult.split('\n').filter(l => l.includes('error TS')).slice(0, 5)
// 尝试修复每个错误...
fixAttempt++
}
}
// 更新 shared memory
const testInfo = {
layer: targetLayer,
files: generatedTests,
count: generatedTests.length,
syntax_clean: !hasSyntaxErrors,
mode: mode,
gc_fix: mode === 'gc-fix'
}
sharedMemory.generated_tests = sharedMemory.generated_tests || {}
sharedMemory.generated_tests[targetLayer] = testInfo
Write(`${sessionFolder}/.msg/meta.json`, JSON.stringify(sharedMemory, null, 2))
```
## Output Format
```
## Test Generation Results
### Layer: [L1|L2|L3]
### Mode: [direct|delegate|gc-fix]
### Files Generated: [count]
- [test file path]
### Syntax Check: PASS/FAIL
### Conventions Applied: [framework], [location], [nesting]
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| No focus files in strategy | Generate L1 tests for all source files in scope |
| No existing test patterns | Use framework defaults (vitest/jest/pytest) |
| Sub-agent failure | Retry once, fallback to direct generation |
| Syntax errors persist after 3 fixes | Report errors, proceed with available tests |
| Source file not found | Skip file, log warning |
| Agent/CLI failure | Retry once, then fallback to inline execution |
| Timeout (>5 min) | Report partial results, notify coordinator |

View File

@@ -1,169 +0,0 @@
# Generator Role
Test case generator. Generate test code according to strategist's strategy and layers. Support L1 unit tests, L2 integration tests, L3 E2E tests. Follow project's existing test patterns and framework conventions.
## Identity
- **Name**: `generator` | **Tag**: `[generator]`
- **Task Prefix**: `QAGEN-*`
- **Responsibility**: Code generation (test code generation)
## Boundaries
### MUST
- Only process `QAGEN-*` prefixed tasks
- All output (SendMessage, team_msg, logs) must carry `[generator]` identifier
- Only communicate with coordinator via SendMessage
- Follow project's existing test framework and patterns
- Generated tests must be runnable
- Work strictly within test code generation responsibility scope
### MUST NOT
- Execute work outside this role's responsibility scope
- Modify source code (only generate test code)
- Execute tests
- Communicate directly with other worker roles (must go through coordinator)
- Create tasks for other roles (TaskCreate is coordinator-exclusive)
- Omit `[generator]` identifier in any output
---
## Toolbox
### Available Commands
| Command | File | Phase | Description |
|---------|------|-------|-------------|
| `generate-tests` | [commands/generate-tests.md](commands/generate-tests.md) | Phase 3 | Layer-based test code generation |
### Tool Capabilities
| Tool | Type | Used By | Purpose |
|------|------|---------|---------|
| `code-developer` | subagent | generate-tests.md | Complex test code generation |
| `gemini` | CLI | generate-tests.md | Analyze existing test patterns |
---
## Message Types
| Type | Direction | Trigger | Description |
|------|-----------|---------|-------------|
| `tests_generated` | generator -> coordinator | Test generation complete | Contains generated test file list |
| `tests_revised` | generator -> coordinator | Test revision complete | After revision in GC loop |
| `error` | generator -> coordinator | Generation failed | Blocking error |
## Message Bus
Before every SendMessage, log via `mcp__ccw-tools__team_msg`:
```
mcp__ccw-tools__team_msg({
operation: "log",
session_id: <session-id>,
from: "generator",
type: <message-type>,
data: { ref: <first-test-file> }
})
```
**CLI fallback** (when MCP unavailable):
```
Bash("ccw team log --session-id <session-id> --from generator --type <message-type> --json")
```
---
## Execution (5-Phase)
### Phase 1: Task Discovery
> See SKILL.md Shared Infrastructure -> Worker Phase 1: Task Discovery
Standard task discovery flow: TaskList -> filter by prefix `QAGEN-*` + owner match + pending + unblocked -> TaskGet -> TaskUpdate in_progress.
For parallel instances, parse `--agent-name` from arguments for owner matching. Falls back to `generator` for single-instance execution.
### Phase 2: Strategy & Pattern Loading
**Loading steps**:
1. Extract session path from task description
2. Read shared memory to get strategy
| Input | Source | Required |
|-------|--------|----------|
| Shared memory | <session-folder>/.msg/meta.json | Yes |
| Test strategy | sharedMemory.test_strategy | Yes |
| Target layer | task description or strategy.layers[0] | Yes |
3. Determine target layer config:
| Layer | Name | Coverage Target |
|-------|------|-----------------|
| L1 | Unit Tests | 80% |
| L2 | Integration Tests | 60% |
| L3 | E2E Tests | 40% |
4. Learn existing test patterns (find 3 similar test files)
5. Detect test framework and configuration
### Phase 3: Test Generation
Delegate to `commands/generate-tests.md` if available, otherwise execute inline.
**Implementation Strategy Selection**:
| Focus File Count | Complexity | Strategy |
|------------------|------------|----------|
| <= 3 files | Low | Direct: inline Edit/Write |
| 3-5 files | Medium | Single code-developer agent |
| > 5 files | High | Batch by module, one agent per batch |
**Direct Generation Flow**:
1. Read source file content
2. Determine test file path (follow project convention)
3. Check if test already exists -> supplement, else create new
4. Generate test content based on source exports and existing patterns
**Test Content Generation**:
- Import source exports
- Create describe blocks per export
- Include happy path, edge cases, error cases tests
### Phase 4: Self-Validation
**Validation Checks**:
| Check | Method | Pass Criteria |
|-------|--------|---------------|
| Syntax | TypeScript check | No errors |
| File existence | Verify all planned files exist | All files present |
| Import resolution | Check no broken imports | All imports resolve |
If validation fails -> attempt auto-fix (max 2 attempts) -> report remaining issues.
Update shared memory with `generated_tests` field for this layer.
### Phase 5: Report to Coordinator
> See SKILL.md Shared Infrastructure -> Worker Phase 5: Report
Standard report flow: team_msg log -> SendMessage with `[generator]` prefix -> TaskUpdate completed -> Loop to Phase 1 for next task.
Message type selection: `tests_generated` for new generation, `tests_revised` for fix iterations.
---
## Error Handling
| Scenario | Resolution |
|----------|------------|
| No QAGEN-* tasks available | Idle, wait for coordinator |
| Strategy not found in shared memory | Generate L1 unit tests for changed files |
| No existing test patterns found | Use framework defaults |
| Sub-agent failure | Retry once, fallback to direct generation |
| Syntax errors in generated tests | Auto-fix up to 3 attempts, report remaining |
| Source file not found | Skip file, report to coordinator |

View File

@@ -1,216 +0,0 @@
# Command: scan
> 多视角 CLI Fan-out 扫描。从 bug、安全、测试覆盖、代码质量、UX 等视角并行分析代码,发现潜在问题。
## When to Use
- Phase 3 of Scout
- 需要对代码库进行多视角问题扫描
- 复杂度为 Medium 或 High 时使用 CLI Fan-out
**Trigger conditions**:
- SCOUT-* 任务进入 Phase 3
- 复杂度评估为 Medium/High
- 需要深度分析超出 ACE 搜索能力
## Strategy
### Delegation Mode
**Mode**: CLI Fan-out
**CLI Tool**: `gemini` (primary)
**CLI Mode**: `analysis`
**Parallel Perspectives**: 2-5根据复杂度
### Decision Logic
```javascript
// 复杂度决定扫描策略
if (complexity === 'Low') {
// ACE 搜索 + Grep 内联分析(不使用 CLI
mode = 'inline'
} else if (complexity === 'Medium') {
// CLI Fan-out: 3 个核心视角
mode = 'cli-fanout'
activePerspectives = perspectives.slice(0, 3)
} else {
// CLI Fan-out: 所有视角
mode = 'cli-fanout'
activePerspectives = perspectives
}
```
## Execution Steps
### Step 1: Context Preparation
```javascript
// 确定扫描范围
const projectRoot = Bash(`git rev-parse --show-toplevel 2>/dev/null || pwd`).trim()
const scanScope = task.description.match(/scope:\s*(.+)/)?.[1] || '**/*'
// 获取变更文件用于聚焦扫描
const changedFiles = Bash(`git diff --name-only HEAD~5 2>/dev/null || echo ""`)
.split('\n').filter(Boolean)
// 构建文件上下文
const fileContext = changedFiles.length > 0
? changedFiles.map(f => `@${f}`).join(' ')
: `@${scanScope}`
// 已知缺陷模式(来自 shared memory
const knownPatternsText = knownPatterns.length > 0
? `\nKnown defect patterns to verify: ${knownPatterns.map(p => p.description).join('; ')}`
: ''
```
### Step 2: Execute Strategy
```javascript
if (mode === 'inline') {
// 快速内联扫描
const aceResults = mcp__ace-tool__search_context({
project_root_path: projectRoot,
query: "potential bugs, error handling issues, unchecked return values, security vulnerabilities, missing input validation"
})
// 解析 ACE 结果并分类
for (const result of aceResults) {
classifyFinding(result)
}
} else {
// CLI Fan-out: 每个视角一个 CLI 调用
const perspectivePrompts = {
'bug': `PURPOSE: Discover potential bugs and logic errors
TASK: • Find unchecked return values • Identify race conditions • Check null/undefined handling • Find off-by-one errors • Detect resource leaks
MODE: analysis
CONTEXT: ${fileContext}${knownPatternsText}
EXPECTED: List of findings with severity, file:line, description, and fix suggestion
CONSTRAINTS: Focus on real bugs, avoid false positives`,
'security': `PURPOSE: Identify security vulnerabilities and risks
TASK: • Check for injection flaws (SQL, command, XSS) • Find authentication/authorization gaps • Identify sensitive data exposure • Check input validation • Review crypto usage
MODE: analysis
CONTEXT: ${fileContext}
EXPECTED: Security findings with CVSS-style severity, file:line, CWE references where applicable
CONSTRAINTS: Focus on exploitable vulnerabilities`,
'test-coverage': `PURPOSE: Identify untested code paths and coverage gaps
TASK: • Find functions/methods without tests • Identify complex logic without assertions • Check error paths without coverage • Find boundary conditions untested
MODE: analysis
CONTEXT: ${fileContext}
EXPECTED: List of untested areas with file:line, complexity indicator, and test suggestion
CONSTRAINTS: Focus on high-risk untested code`,
'code-quality': `PURPOSE: Detect code quality issues and anti-patterns
TASK: • Find code duplication • Identify overly complex functions • Check naming conventions • Find dead code • Detect God objects/functions
MODE: analysis
CONTEXT: ${fileContext}
EXPECTED: Quality findings with severity, file:line, and improvement suggestion
CONSTRAINTS: Focus on maintainability impacts`,
'ux': `PURPOSE: Identify UX-impacting issues in code
TASK: • Find missing loading states • Check error message quality • Identify accessibility gaps • Find inconsistent UI patterns • Check responsive handling
MODE: analysis
CONTEXT: ${fileContext}
EXPECTED: UX findings with impact level, file:line, and user-facing description
CONSTRAINTS: Focus on user-visible issues`
}
for (const perspective of activePerspectives) {
const prompt = perspectivePrompts[perspective]
if (!prompt) continue
Bash(`ccw cli -p "${prompt}" --tool gemini --mode analysis --rule analysis-assess-security-risks`, {
run_in_background: true
})
}
// 等待所有 CLI 完成hook 回调通知)
}
```
### Step 3: Result Processing
```javascript
// 聚合所有视角的结果
const allFindings = { critical: [], high: [], medium: [], low: [] }
// 从 CLI 输出解析结果
for (const perspective of activePerspectives) {
const findings = parseCliOutput(cliResults[perspective])
for (const finding of findings) {
finding.perspective = perspective
allFindings[finding.severity].push(finding)
}
}
// 去重:相同 file:line 的发现合并
function deduplicateFindings(findings) {
const seen = new Set()
const unique = []
for (const f of findings) {
const key = `${f.file}:${f.line}`
if (!seen.has(key)) {
seen.add(key)
unique.push(f)
} else {
// 合并视角信息到已有条目
const existing = unique.find(u => `${u.file}:${u.line}` === key)
if (existing) existing.perspectives = [...(existing.perspectives || [existing.perspective]), f.perspective]
}
}
return unique
}
for (const severity of ['critical', 'high', 'medium', 'low']) {
allFindings[severity] = deduplicateFindings(allFindings[severity])
}
// 与已知缺陷模式对比
for (const pattern of knownPatterns) {
for (const severity of ['critical', 'high', 'medium', 'low']) {
for (const finding of allFindings[severity]) {
if (finding.file === pattern.file || finding.description.includes(pattern.type)) {
finding.known_pattern = true
}
}
}
}
```
## Output Format
```
## Scan Results
### Perspectives Scanned: [list]
### Complexity: [Low|Medium|High]
### Findings by Severity
#### Critical ([count])
- [file:line] [perspective] - [description]
#### High ([count])
- [file:line] [perspective] - [description]
#### Medium ([count])
- [file:line] - [description]
#### Low ([count])
- [file:line] - [description]
### Known Pattern Matches: [count]
### New Findings: [count]
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| CLI tool unavailable | Fall back to ACE search + Grep inline analysis |
| CLI returns empty for a perspective | Note incomplete perspective, continue others |
| Too many findings (>50) | Prioritize critical/high, summarize medium/low |
| Timeout on CLI call | Use partial results, note incomplete perspectives |
| Agent/CLI failure | Retry once, then fallback to inline execution |
| Timeout (>5 min) | Report partial results, notify coordinator |

View File

@@ -1,242 +0,0 @@
# Role: scout
多视角问题侦察员。主动扫描代码库,从 bug、安全、UX、测试覆盖、代码质量等多个视角发现潜在问题创建结构化 issue。融合 issue-discover 的多视角扫描能力。
## Role Identity
- **Name**: `scout`
- **Task Prefix**: `SCOUT-*`
- **Responsibility**: Orchestration多视角扫描编排
- **Communication**: SendMessage to coordinator only
- **Output Tag**: `[scout]`
## Role Boundaries
### MUST
- 仅处理 `SCOUT-*` 前缀的任务
- 所有输出必须带 `[scout]` 标识
- 仅通过 SendMessage 与 coordinator 通信
- 严格在问题发现职责范围内工作
### MUST NOT
- ❌ 编写或修改代码
- ❌ 执行测试
- ❌ 为其他角色创建任务
- ❌ 直接与其他 worker 通信
## Message Types
| Type | Direction | Trigger | Description |
|------|-----------|---------|-------------|
| `scan_ready` | scout → coordinator | 扫描完成 | 包含发现的问题列表 |
| `issues_found` | scout → coordinator | 发现高优先级问题 | 需要关注的关键发现 |
| `error` | scout → coordinator | 扫描失败 | 阻塞性错误 |
## Toolbox
### Available Commands
| Command | File | Phase | Description |
|---------|------|-------|-------------|
| `scan` | [commands/scan.md](commands/scan.md) | Phase 3 | 多视角 CLI Fan-out 扫描 |
### Subagent Capabilities
| Agent Type | Used By | Purpose |
|------------|---------|---------|
| `cli-explore-agent` | scan.md | 多角度代码库探索 |
### CLI Capabilities
| CLI Tool | Mode | Used By | Purpose |
|----------|------|---------|---------|
| `gemini` | analysis | scan.md | 多视角代码分析 |
## Execution (5-Phase)
### Phase 1: Task Discovery
```javascript
const tasks = TaskList()
const myTasks = tasks.filter(t =>
t.subject.startsWith('SCOUT-') &&
t.owner === 'scout' &&
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 & Scope Assessment
```javascript
// 确定扫描范围
const scanScope = task.description.match(/scope:\s*(.+)/)?.[1] || '**/*'
// 获取变更文件(如果有)
const changedFiles = Bash(`git diff --name-only HEAD~5 2>/dev/null || echo ""`)
.split('\n').filter(Boolean)
// 读取 shared memory 获取历史缺陷模式
const sessionFolder = task.description.match(/session:\s*(.+)/)?.[1] || '.'
let sharedMemory = {}
try { sharedMemory = JSON.parse(Read(`${sessionFolder}/.msg/meta.json`)) } catch {}
const knownPatterns = sharedMemory.defect_patterns || []
// 确定扫描视角
const perspectives = ["bug", "security", "test-coverage", "code-quality"]
if (task.description.includes('ux')) perspectives.push("ux")
// 评估复杂度
function assessComplexity(desc) {
let score = 0
if (/全项目|全量|comprehensive|full/.test(desc)) score += 3
if (/security|安全/.test(desc)) score += 1
if (/multiple|across|cross|多模块/.test(desc)) score += 2
return score >= 4 ? 'High' : score >= 2 ? 'Medium' : 'Low'
}
const complexity = assessComplexity(task.description)
```
### Phase 3: Multi-Perspective Scan
```javascript
// Read commands/scan.md for full CLI Fan-out implementation
Read("commands/scan.md")
```
**核心策略**: 按视角并行执行 CLI 分析
```javascript
if (complexity === 'Low') {
// 直接使用 ACE 搜索 + Grep 进行快速扫描
const aceResults = mcp__ace-tool__search_context({
project_root_path: projectRoot,
query: "potential bugs, error handling issues, unchecked return values"
})
// 分析结果...
} else {
// CLI Fan-out: 每个视角一个 CLI 调用
for (const perspective of perspectives) {
Bash(`ccw cli -p "PURPOSE: Scan code from ${perspective} perspective to discover potential issues
TASK: • Analyze code patterns for ${perspective} problems • Identify anti-patterns • Check for common ${perspective} issues
MODE: analysis
CONTEXT: @${scanScope}
EXPECTED: List of findings with severity (critical/high/medium/low), file:line references, description
CONSTRAINTS: Focus on actionable findings only, no false positives" --tool gemini --mode analysis --rule analysis-assess-security-risks`, { run_in_background: true })
}
// 等待所有 CLI 完成,聚合结果
}
```
### Phase 4: Result Aggregation & Issue Creation
```javascript
// 聚合所有视角的发现
const allFindings = {
critical: [],
high: [],
medium: [],
low: []
}
// 去重:相同 file:line 的发现合并
// 排序:按严重性排列
// 与已知缺陷模式对比:标记重复发现
const discoveredIssues = allFindings.critical
.concat(allFindings.high)
.map((f, i) => ({
id: `SCOUT-ISSUE-${i + 1}`,
severity: f.severity,
perspective: f.perspective,
file: f.file,
line: f.line,
description: f.description,
suggestion: f.suggestion
}))
// 更新 shared memory
sharedMemory.discovered_issues = discoveredIssues
Write(`${sessionFolder}/.msg/meta.json`, JSON.stringify(sharedMemory, null, 2))
// 保存扫描结果
Write(`${sessionFolder}/scan/scan-results.json`, JSON.stringify({
scan_date: new Date().toISOString(),
perspectives: perspectives,
total_findings: Object.values(allFindings).flat().length,
by_severity: {
critical: allFindings.critical.length,
high: allFindings.high.length,
medium: allFindings.medium.length,
low: allFindings.low.length
},
findings: allFindings,
issues_created: discoveredIssues.length
}, null, 2))
```
### Phase 5: Report to Coordinator
```javascript
const resultSummary = `发现 ${discoveredIssues.length} 个问题Critical: ${allFindings.critical.length}, High: ${allFindings.high.length}, Medium: ${allFindings.medium.length}, Low: ${allFindings.low.length}`
mcp__ccw-tools__team_msg({
operation: "log",
session_id: teamName,
from: "scout",
type: discoveredIssues.length > 0 ? "issues_found" : "scan_ready",
data: { ref: `${sessionFolder}/scan/scan-results.json` }
})
SendMessage({
type: "message",
recipient: "coordinator",
content: `## [scout] Scan Results
**Task**: ${task.subject}
**Perspectives**: ${perspectives.join(', ')}
**Status**: ${discoveredIssues.length > 0 ? 'Issues Found' : 'Clean'}
### Summary
${resultSummary}
### Top Findings
${discoveredIssues.slice(0, 5).map(i => `- **[${i.severity}]** ${i.file}:${i.line} - ${i.description}`).join('\n')}
### Scan Report
${sessionFolder}/scan/scan-results.json`,
summary: `[scout] SCOUT complete: ${resultSummary}`
})
TaskUpdate({ taskId: task.id, status: 'completed' })
// Check for next task
const nextTasks = TaskList().filter(t =>
t.subject.startsWith('SCOUT-') &&
t.owner === 'scout' &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (nextTasks.length > 0) {
// Continue with next task → back to Phase 1
}
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| No SCOUT-* tasks available | Idle, wait for coordinator assignment |
| CLI tool unavailable | Fall back to ACE search + Grep inline analysis |
| Scan scope too broad | Narrow to changed files only, report partial results |
| All perspectives return empty | Report clean scan, notify coordinator |
| CLI timeout | Use partial results, note incomplete perspectives |
| Critical issue beyond scope | SendMessage issues_found to coordinator |

View File

@@ -1,221 +0,0 @@
# Command: analyze-scope
> 变更范围分析 + 测试策略制定。分析代码变更、scout 发现和项目结构,确定测试层级和覆盖率目标。
## When to Use
- Phase 2-3 of Strategist
- 需要分析代码变更范围
- 需要将 scout 发现转化为测试策略
**Trigger conditions**:
- QASTRAT-* 任务进入执行阶段
- 变更文件数 > 5 需要 CLI 辅助分析
- 存在 scout 发现的高优先级问题
## Strategy
### Delegation Mode
**Mode**: CLI Fan-out复杂项目/ Direct简单项目
**CLI Tool**: `gemini` (primary)
**CLI Mode**: `analysis`
### Decision Logic
```javascript
const totalScope = changedFiles.length + discoveredIssues.length
if (totalScope <= 5) {
// 直接内联分析
mode = 'direct'
} else if (totalScope <= 15) {
// 单次 CLI 分析
mode = 'single-cli'
} else {
// 多维度 CLI 分析
mode = 'multi-cli'
}
```
## Execution Steps
### Step 1: Context Preparation
```javascript
// 从 shared memory 获取 scout 发现
const discoveredIssues = sharedMemory.discovered_issues || []
// 分析 git diff 获取变更范围
const changedFiles = Bash(`git diff --name-only HEAD~5 2>/dev/null || git diff --name-only --cached 2>/dev/null || echo ""`)
.split('\n').filter(Boolean)
// 分类变更文件
const fileCategories = {
source: changedFiles.filter(f => /\.(ts|tsx|js|jsx|py|java|go|rs)$/.test(f)),
test: changedFiles.filter(f => /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(f) || /test_/.test(f)),
config: changedFiles.filter(f => /\.(json|yaml|yml|toml|env)$/.test(f)),
style: changedFiles.filter(f => /\.(css|scss|less)$/.test(f)),
docs: changedFiles.filter(f => /\.(md|txt|rst)$/.test(f))
}
// 检测项目测试框架
const packageJson = Read('package.json')
const testFramework = detectFramework(packageJson)
// 获取已有测试覆盖率基线
let baselineCoverage = null
try {
const coverageSummary = JSON.parse(Read('coverage/coverage-summary.json'))
baselineCoverage = coverageSummary.total?.lines?.pct || null
} catch {}
```
### Step 2: Execute Strategy
```javascript
if (mode === 'direct') {
// 内联分析:直接构建策略
buildStrategyDirect(fileCategories, discoveredIssues, testFramework)
} else if (mode === 'single-cli') {
// 单次 CLI 综合分析
Bash(`ccw cli -p "PURPOSE: Analyze code changes and scout findings to determine optimal test strategy
TASK: • Classify ${changedFiles.length} changed files by risk level • Map ${discoveredIssues.length} scout issues to test requirements • Identify integration points between changed modules • Recommend test layers (L1/L2/L3) with coverage targets
MODE: analysis
CONTEXT: @${changedFiles.slice(0, 20).join(' @')} | Memory: Scout found ${discoveredIssues.length} issues, baseline coverage ${baselineCoverage || 'unknown'}%
EXPECTED: JSON with layers array, each containing level, name, target_coverage, focus_files, rationale
CONSTRAINTS: Be conservative with L3 E2E tests | Focus L1 on changed source files" --tool gemini --mode analysis --rule analysis-analyze-code-patterns`, {
run_in_background: true
})
// 等待 CLI 完成
} else {
// 多维度分析
// Dimension 1: 变更风险分析
Bash(`ccw cli -p "PURPOSE: Assess risk level of code changes
TASK: • Classify each file by change risk (high/medium/low) • Identify files touching critical paths • Map dependency chains
MODE: analysis
CONTEXT: @${fileCategories.source.join(' @')}
EXPECTED: Risk matrix with file:risk_level mapping
CONSTRAINTS: Focus on source files only" --tool gemini --mode analysis`, {
run_in_background: true
})
// Dimension 2: 测试覆盖差距分析
Bash(`ccw cli -p "PURPOSE: Identify test coverage gaps for changed code
TASK: • Find changed functions without tests • Map test files to source files • Identify missing integration test scenarios
MODE: analysis
CONTEXT: @${[...fileCategories.source, ...fileCategories.test].join(' @')}
EXPECTED: Coverage gap report with untested functions and modules
CONSTRAINTS: Compare existing tests to changed code" --tool gemini --mode analysis`, {
run_in_background: true
})
// 等待所有 CLI 完成
}
```
### Step 3: Result Processing
```javascript
// 构建测试策略
const strategy = {
scope: {
total_changed: changedFiles.length,
source_files: fileCategories.source.length,
test_files: fileCategories.test.length,
issue_count: discoveredIssues.length,
baseline_coverage: baselineCoverage
},
test_framework: testFramework,
layers: [],
coverage_targets: {}
}
// 层级选择算法
// L1: Unit Tests - 所有有源码变更的文件
if (fileCategories.source.length > 0 || discoveredIssues.length > 0) {
const l1Files = fileCategories.source.length > 0
? fileCategories.source
: [...new Set(discoveredIssues.map(i => i.file))]
strategy.layers.push({
level: 'L1',
name: 'Unit Tests',
target_coverage: 80,
focus_files: l1Files,
rationale: fileCategories.source.length > 0
? '所有变更的源文件需要单元测试覆盖'
: 'Scout 发现的问题需要测试覆盖'
})
}
// L2: Integration Tests - 多模块变更或关键问题
if (fileCategories.source.length >= 3 || discoveredIssues.some(i => i.severity === 'critical')) {
const integrationPoints = fileCategories.source
.filter(f => /service|controller|handler|middleware|route|api/.test(f))
if (integrationPoints.length > 0) {
strategy.layers.push({
level: 'L2',
name: 'Integration Tests',
target_coverage: 60,
focus_areas: integrationPoints,
rationale: '多文件变更涉及模块间交互,需要集成测试'
})
}
}
// L3: E2E Tests - 大量高优先级问题
const criticalHighCount = discoveredIssues
.filter(i => i.severity === 'critical' || i.severity === 'high').length
if (criticalHighCount >= 3) {
strategy.layers.push({
level: 'L3',
name: 'E2E Tests',
target_coverage: 40,
focus_flows: [...new Set(discoveredIssues
.filter(i => i.severity === 'critical' || i.severity === 'high')
.map(i => i.file.split('/')[1] || 'main'))],
rationale: `${criticalHighCount} 个高优先级问题需要端到端验证`
})
}
// 设置覆盖率目标
for (const layer of strategy.layers) {
strategy.coverage_targets[layer.level] = layer.target_coverage
}
```
## Output Format
```
## Test Strategy
### Scope Analysis
- Changed files: [count]
- Source files: [count]
- Scout issues: [count]
- Baseline coverage: [percent]%
### Test Layers
#### L1: Unit Tests
- Coverage target: 80%
- Focus files: [list]
#### L2: Integration Tests (if applicable)
- Coverage target: 60%
- Focus areas: [list]
#### L3: E2E Tests (if applicable)
- Coverage target: 40%
- Focus flows: [list]
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| No changed files | Use scout issues as scope |
| No scout issues | Generate L1 tests for all source files |
| Test framework unknown | Default to Jest/Vitest (JS/TS) or pytest (Python) |
| CLI analysis returns unusable results | Fall back to heuristic-based strategy |
| Agent/CLI failure | Retry once, then fallback to inline execution |
| Timeout (>5 min) | Report partial results, notify coordinator |

View File

@@ -1,163 +0,0 @@
# Strategist Role
Test strategist. Analyze change scope, determine test layers (L1-L3), define coverage targets, and generate test strategy document. Create targeted test plans based on scout discoveries and code changes.
## Identity
- **Name**: `strategist` | **Tag**: `[strategist]`
- **Task Prefix**: `QASTRAT-*`
- **Responsibility**: Orchestration (strategy formulation)
## Boundaries
### MUST
- Only process `QASTRAT-*` prefixed tasks
- All output (SendMessage, team_msg, logs) must carry `[strategist]` identifier
- Only communicate with coordinator via SendMessage
- Work strictly within strategy formulation responsibility scope
### MUST NOT
- Execute work outside this role's responsibility scope
- Write test code
- Execute tests
- Communicate directly with other worker roles (must go through coordinator)
- Create tasks for other roles (TaskCreate is coordinator-exclusive)
- Modify source code
- Omit `[strategist]` identifier in any output
---
## Toolbox
### Available Commands
| Command | File | Phase | Description |
|---------|------|-------|-------------|
| `analyze-scope` | [commands/analyze-scope.md](commands/analyze-scope.md) | Phase 2-3 | Change scope analysis + strategy formulation |
### Tool Capabilities
| Tool | Type | Used By | Purpose |
|------|------|---------|---------|
| `cli-explore-agent` | subagent | analyze-scope.md | Code structure and dependency analysis |
| `gemini` | CLI | analyze-scope.md | Test strategy analysis |
---
## Message Types
| Type | Direction | Trigger | Description |
|------|-----------|---------|-------------|
| `strategy_ready` | strategist -> coordinator | Strategy complete | Contains layer selection and coverage targets |
| `error` | strategist -> coordinator | Strategy failed | Blocking error |
## Message Bus
Before every SendMessage, log via `mcp__ccw-tools__team_msg`:
```
mcp__ccw-tools__team_msg({
operation: "log",
session_id: <session-id>,
from: "strategist",
type: <message-type>,
data: { ref: <artifact-path> }
})
```
**CLI fallback** (when MCP unavailable):
```
Bash("ccw team log --session-id <session-id> --from strategist --type <message-type> --json")
```
---
## Execution (5-Phase)
### Phase 1: Task Discovery
> See SKILL.md Shared Infrastructure -> Worker Phase 1: Task Discovery
Standard task discovery flow: TaskList -> filter by prefix `QASTRAT-*` + owner match + pending + unblocked -> TaskGet -> TaskUpdate in_progress.
### Phase 2: Context & Change Analysis
**Loading steps**:
1. Extract session path from task description
2. Read shared memory to get scout discoveries
| Input | Source | Required |
|-------|--------|----------|
| Shared memory | <session-folder>/.msg/meta.json | Yes |
| Discovered issues | sharedMemory.discovered_issues | No |
| Defect patterns | sharedMemory.defect_patterns | No |
3. Analyze change scope:
```
Bash("git diff --name-only HEAD~5 2>/dev/null || git diff --name-only --cached 2>/dev/null || echo \"\"")
```
4. Categorize changed files:
| Category | Pattern |
|----------|---------|
| Source | `/\.(ts|tsx|js|jsx|py|java|go|rs)$/` |
| Test | `/\.(test|spec)\.(ts|tsx|js|jsx)$/` or `/test_/` |
| Config | `/\.(json|yaml|yml|toml|env)$/` |
| Style | `/\.(css|scss|less)$/` |
5. Detect test framework from project files
6. Check existing coverage data if available
### Phase 3: Strategy Generation
**Layer Selection Logic**:
| Condition | Layer | Coverage Target |
|-----------|-------|-----------------|
| Has source file changes | L1: Unit Tests | 80% |
| >= 3 source files OR critical issues found | L2: Integration Tests | 60% |
| >= 3 critical/high severity issues | L3: E2E Tests | 40% |
| No changes but has scout issues | L1 focused on issue files | 80% |
**Strategy Document Structure**:
- Scope Analysis: changed files count, source files, scout issues, test framework
- Test Layers: level, name, coverage target, focus files/areas, rationale
- Priority Issues: top 10 issues from scout
Write strategy document to `<session-folder>/strategy/test-strategy.md`.
Update shared memory with `test_strategy` field.
### Phase 4: Strategy Validation
**Validation Checks**:
| Check | Criteria |
|-------|----------|
| has_layers | strategy.layers.length > 0 |
| has_targets | All layers have target_coverage > 0 |
| covers_issues | Discovered issues covered by focus_files |
| framework_detected | testFramework !== 'unknown' |
### Phase 5: Report to Coordinator
> See SKILL.md Shared Infrastructure -> Worker Phase 5: Report
Standard report flow: team_msg log -> SendMessage with `[strategist]` prefix -> TaskUpdate completed -> Loop to Phase 1 for next task.
---
## Error Handling
| Scenario | Resolution |
|----------|------------|
| No QASTRAT-* tasks available | Idle, wait for coordinator |
| No changed files detected | Use scout issues as scope, or scan full project |
| Test framework unknown | Default to Jest/Vitest for JS/TS, pytest for Python |
| Shared memory not found | Create with defaults, proceed |
| Critical issue beyond scope | SendMessage error to coordinator |