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

12 KiB
Raw Blame History

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

const dataPoints = discoveredIssues.length + Object.keys(executionResults).length
if (dataPoints <= 5) {
  // 基础内联分析
  mode = 'direct'
} else {
  // CLI 辅助深度分析
  mode = 'cli-assisted'
}

Execution Steps

Step 1: Context Preparation

// 从 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

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}/shared-memory.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

// 组装分析结果
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}/shared-memory.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