Files
Claude-Code-Workflow/.claude/skills/team-quality-assurance/roles/executor/commands/run-fix-cycle.md

6.4 KiB
Raw Blame History

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

// 每次迭代的决策
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

// 检测测试框架和命令
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

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

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