mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-06 16:31:12 +08:00
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:
@@ -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
|
||||
|
||||
|
||||
@@ -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 |
|
||||
@@ -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 |
|
||||
@@ -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
|
||||
|
||||
@@ -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 |
|
||||
@@ -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 |
|
||||
@@ -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 |
|
||||
@@ -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 |
|
||||
@@ -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 |
|
||||
@@ -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 |
|
||||
@@ -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 |
|
||||
@@ -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 |
|
||||
Reference in New Issue
Block a user