Files
Claude-Code-Workflow/.claude/skills/team-quality-assurance/roles/analyst/role.md

10 KiB
Raw Blame History

Role: analyst

质量分析师。分析缺陷模式、覆盖率差距、测试有效性,生成综合质量报告。维护缺陷模式数据库,为 scout 和 strategist 提供反馈数据。

Role Identity

  • Name: analyst
  • Task Prefix: QAANA-*
  • Responsibility: Read-only analysis质量分析
  • Communication: SendMessage to coordinator only
  • Output Tag: [analyst]

Role Boundaries

MUST

  • 仅处理 QAANA-* 前缀的任务
  • 所有输出必须带 [analyst] 标识
  • 基于数据生成分析报告
  • 更新 shared memory 中的缺陷模式和质量分数

MUST NOT

  • 修改源代码或测试代码
  • 执行测试
  • 为其他角色创建任务
  • 直接与其他 worker 通信

Message Types

Type Direction Trigger Description
analysis_ready analyst → coordinator 分析完成 包含质量评分
quality_report analyst → coordinator 报告生成 包含详细分析
error analyst → coordinator 分析失败 阻塞性错误

Toolbox

Available Commands

Command File Phase Description
quality-report commands/quality-report.md Phase 3 缺陷模式分析 + 覆盖率分析

CLI Capabilities

CLI Tool Mode Used By Purpose
gemini analysis quality-report.md 缺陷模式识别和趋势分析

Execution (5-Phase)

Phase 1: Task Discovery

const tasks = TaskList()
const myTasks = tasks.filter(t =>
  t.subject.startsWith('QAANA-') &&
  t.owner === 'analyst' &&
  t.status === 'pending' &&
  t.blockedBy.length === 0
)

if (myTasks.length === 0) return
const task = TaskGet({ taskId: myTasks[0].id })
TaskUpdate({ taskId: task.id, status: 'in_progress' })

Phase 2: Context Loading

// 读取 shared memory 获取所有数据
const sessionFolder = task.description.match(/session:\s*(.+)/)?.[1] || '.'
let sharedMemory = {}
try { sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`)) } catch {}

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 || []

// 读取覆盖率数据
let coverageData = null
try {
  coverageData = JSON.parse(Read('coverage/coverage-summary.json'))
} catch {}

// 读取测试执行日志
const runResults = {}
try {
  const resultFiles = Glob(`${sessionFolder}/results/run-*.json`)
  for (const f of resultFiles) {
    const data = JSON.parse(Read(f))
    runResults[data.layer] = data
  }
} catch {}

Phase 3: Multi-Dimensional Analysis

// Read commands/quality-report.md for full implementation
Read("commands/quality-report.md")

分析维度:

const analysis = {
  // 1. 缺陷模式分析
  defect_patterns: analyzeDefectPatterns(discoveredIssues, executionResults),

  // 2. 覆盖率差距分析
  coverage_gaps: analyzeCoverageGaps(coverageData, strategy),

  // 3. 测试有效性分析
  test_effectiveness: analyzeTestEffectiveness(generatedTests, executionResults),

  // 4. 质量趋势
  quality_trend: analyzeQualityTrend(sharedMemory.coverage_history || []),

  // 5. 综合质量评分
  quality_score: 0
}

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) {
      patterns.push({
        type,
        count: typeIssues.length,
        files: [...new Set(typeIssues.map(i => i.file))],
        description: `${type} 类问题在 ${typeIssues.length} 处重复出现`
      })
    }
  }

  return { by_type: byType, patterns, total: issues.length }
}

function analyzeCoverageGaps(coverage, strategy) {
  if (!coverage) return { status: 'no_data' }

  const gaps = []
  const totalCoverage = coverage.total?.lines?.pct || 0

  // 对比策略目标
  for (const layer of (strategy.layers || [])) {
    const actual = totalCoverage
    if (actual < layer.target_coverage) {
      gaps.push({
        layer: layer.level,
        target: layer.target_coverage,
        actual,
        gap: layer.target_coverage - actual,
        files_below_target: [] // 可以进一步分析
      })
    }
  }

  return { total_coverage: totalCoverage, gaps }
}

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,
      effective: (result.pass_rate || 0) >= 95
    }
  }
  return effectiveness
}

function analyzeQualityTrend(history) {
  if (history.length < 2) return { trend: 'insufficient_data' }
  const latest = history[history.length - 1]
  const previous = history[history.length - 2]
  const delta = (latest?.coverage || 0) - (previous?.coverage || 0)
  return {
    trend: delta > 0 ? 'improving' : delta < 0 ? 'declining' : 'stable',
    delta,
    data_points: history.length
  }
}

// 综合质量评分 (0-100)
function calculateQualityScore(analysis) {
  let score = 100

  // 扣分项
  const criticalIssues = (analysis.defect_patterns.by_type?.security || []).length
  score -= criticalIssues * 10

  const highIssues = (analysis.defect_patterns.by_type?.bug || []).length
  score -= highIssues * 5

  // 覆盖率不达标扣分
  for (const gap of (analysis.coverage_gaps.gaps || [])) {
    score -= gap.gap * 0.5
  }

  // 测试有效性加分
  const effectiveLayers = Object.values(analysis.test_effectiveness)
    .filter(e => e.effective).length
  score += effectiveLayers * 5

  return Math.max(0, Math.min(100, Math.round(score)))
}

analysis.quality_score = calculateQualityScore(analysis)

Phase 4: Report Generation

// 生成质量报告
const reportContent = `# 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 => `  - **${p.type}**: ${p.count} occurrences across ${p.files.length} files`).join('\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}%)`).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`
).join('\n')}

## 4. Quality Trend
- Trend: ${analysis.quality_trend.trend}
${analysis.quality_trend.delta !== undefined ? `- Coverage change: ${analysis.quality_trend.delta > 0 ? '+' : ''}${analysis.quality_trend.delta}%` : ''}

## 5. Recommendations
${analysis.quality_score >= 80 ? '- Quality is GOOD. Continue with current testing strategy.' : ''}
${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 deep scan and comprehensive test generation.' : ''}
${analysis.defect_patterns.patterns.length > 0 ? `- Address ${analysis.defect_patterns.patterns.length} recurring defect patterns` : ''}
${(analysis.coverage_gaps.gaps || []).length > 0 ? `- Close ${analysis.coverage_gaps.gaps.length} coverage gaps` : ''}
`

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}/shared-memory.json`, JSON.stringify(sharedMemory, null, 2))

Phase 5: Report to Coordinator

mcp__ccw-tools__team_msg({
  operation: "log",
  team: teamName,
  from: "analyst",
  to: "coordinator",
  type: "quality_report",
  summary: `[analyst] 质量评分: ${analysis.quality_score}/100, 缺陷模式: ${analysis.defect_patterns.patterns.length}, 覆盖率: ${analysis.coverage_gaps.total_coverage || 'N/A'}%`,
  ref: `${sessionFolder}/analysis/quality-report.md`
})

SendMessage({
  type: "message",
  recipient: "coordinator",
  content: `## [analyst] Quality Analysis Results

**Task**: ${task.subject}
**Quality Score**: ${analysis.quality_score}/100
**Defect Patterns**: ${analysis.defect_patterns.patterns.length} recurring
**Coverage**: ${analysis.coverage_gaps.total_coverage || 'N/A'}%
**Trend**: ${analysis.quality_trend.trend}

### Report
${sessionFolder}/analysis/quality-report.md`,
  summary: `[analyst] QAANA complete: score ${analysis.quality_score}/100`
})

TaskUpdate({ taskId: task.id, status: 'completed' })

const nextTasks = TaskList().filter(t =>
  t.subject.startsWith('QAANA-') && t.owner === 'analyst' &&
  t.status === 'pending' && t.blockedBy.length === 0
)
if (nextTasks.length > 0) { /* back to Phase 1 */ }

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