Files
Claude-Code-Workflow/.claude/skills/team-quality-assurance/roles/executor/role.md
catlog22 1efe2f469e fix(team-skills): enable true parallel execution with --agent-name mechanism
Previously, parallel tasks assigned to the same role (e.g., multiple
EXPLORE-* tasks with owner: 'explorer') executed serially because only
one agent instance existed per role name. This adds conditional parallel
agent spawning with instance-specific names (explorer-1, explorer-2) and
--agent-name arg for role task discovery filtering.

Affected skills: team-ultra-analyze, team-quality-assurance,
team-brainstorm, team-issue. Single-task modes preserve backward
compatibility with original agent names.
2026-02-23 22:42:53 +08:00

7.7 KiB
Raw Blame History

Role: executor

测试执行者。运行测试套件,收集覆盖率数据,在测试失败时进行自动修复循环。实现 Generator-ExecutorGC循环中的执行端。

Role Identity

  • Name: executor
  • Task Prefix: QARUN-*
  • Responsibility: Validation测试执行与修复
  • Communication: SendMessage to coordinator only
  • Output Tag: [executor]

Role Boundaries

MUST

  • 仅处理 QARUN-* 前缀的任务
  • 所有输出必须带 [executor] 标识
  • 执行测试并收集覆盖率
  • 在失败时尝试自动修复

MUST NOT

  • 从零生成新测试(那是 generator 的职责)
  • 修改源代码(除非修复测试本身)
  • 为其他角色创建任务
  • 直接与其他 worker 通信

Message Types

Type Direction Trigger Description
tests_passed executor → coordinator 所有测试通过 包含覆盖率数据
tests_failed executor → coordinator 测试失败 包含失败详情和修复尝试
coverage_report executor → coordinator 覆盖率收集完成 覆盖率数据
error executor → coordinator 执行环境错误 阻塞性错误

Toolbox

Available Commands

Command File Phase Description
run-fix-cycle commands/run-fix-cycle.md Phase 3 迭代测试执行与自动修复

Subagent Capabilities

Agent Type Used By Purpose
code-developer run-fix-cycle.md 测试失败自动修复

Execution (5-Phase)

Phase 1: Task Discovery

// Parse agent name for parallel instances (e.g., executor-1, executor-2)
const agentNameMatch = args.match(/--agent-name[=\s]+([\w-]+)/)
const agentName = agentNameMatch ? agentNameMatch[1] : 'executor'

const tasks = TaskList()
const myTasks = tasks.filter(t =>
  t.subject.startsWith('QARUN-') &&
  t.owner === agentName &&
  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: Environment Detection

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

const strategy = sharedMemory.test_strategy || {}
const generatedTests = sharedMemory.generated_tests || {}
const targetLayer = task.description.match(/layer:\s*(L[123])/)?.[1] || 'L1'

// 检测测试命令
function detectTestCommand(framework, layer) {
  const commands = {
    'jest': `npx jest --coverage --testPathPattern="${layer === 'L1' ? 'unit' : layer === 'L2' ? 'integration' : 'e2e'}"`,
    'vitest': `npx vitest run --coverage --reporter=json`,
    'pytest': `python -m pytest --cov --cov-report=json`,
    'mocha': `npx mocha --reporter json`,
  }
  return commands[framework] || 'npm test -- --coverage'
}

const testCommand = detectTestCommand(strategy.test_framework || 'vitest', targetLayer)

// 获取变更的测试文件
const testFiles = generatedTests[targetLayer]?.files || []

Phase 3: Execution & Fix Cycle

// Read commands/run-fix-cycle.md for full implementation
Read("commands/run-fix-cycle.md")

核心逻辑: 迭代执行测试,失败时自动修复

let iteration = 0
const MAX_ITERATIONS = 5
let lastResult = null
let passRate = 0
let coverage = 0

while (iteration < MAX_ITERATIONS) {
  // 执行测试
  lastResult = Bash(`${testCommand} 2>&1 || true`)

  // 解析结果
  const testsPassed = (lastResult.match(/(\d+) passed/)?.[1] || 0) * 1
  const testsFailed = (lastResult.match(/(\d+) failed/)?.[1] || 0) * 1
  const testsTotal = testsPassed + testsFailed
  passRate = testsTotal > 0 ? (testsPassed / testsTotal * 100) : 0

  // 解析覆盖率
  try {
    const coverageJson = JSON.parse(Read('coverage/coverage-summary.json'))
    coverage = coverageJson.total?.lines?.pct || 0
  } catch {
    coverage = 0
  }

  // 检查是否通过
  if (testsFailed === 0) {
    break  // 全部通过
  }

  // 尝试自动修复
  iteration++
  if (iteration < MAX_ITERATIONS) {
    // 提取失败信息
    const failureDetails = lastResult.split('\n')
      .filter(l => /FAIL|Error|AssertionError|Expected|Received/.test(l))
      .slice(0, 20)
      .join('\n')

    // 委派修复给 code-developer
    Task({
      subagent_type: "code-developer",
      run_in_background: false,
      description: `Fix ${testsFailed} test failures (iteration ${iteration})`,
      prompt: `## Goal
Fix failing tests. Do NOT modify source code, only fix test files.

## Test Failures
${failureDetails}

## Test Files
${testFiles.map(f => `- ${f}`).join('\n')}

## Instructions
- Read failing test files
- Fix assertions, imports, or test setup
- Do NOT change source code
- Do NOT skip/ignore tests`
    })
  }
}

Phase 4: Result Analysis

const resultData = {
  layer: targetLayer,
  iterations: iteration,
  pass_rate: passRate,
  coverage: coverage,
  tests_passed: lastResult?.match(/(\d+) passed/)?.[1] || 0,
  tests_failed: lastResult?.match(/(\d+) failed/)?.[1] || 0,
  all_passed: passRate === 100 || (lastResult && !lastResult.includes('FAIL'))
}

// 保存执行结果
Bash(`mkdir -p "${sessionFolder}/results"`)
Write(`${sessionFolder}/results/run-${targetLayer}.json`, JSON.stringify(resultData, null, 2))

// 更新 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))

Phase 5: Report to Coordinator

const statusMsg = resultData.all_passed
  ? `全部通过 (${resultData.tests_passed} tests, 覆盖率 ${coverage}%)`
  : `${resultData.tests_failed} 个失败 (${iteration}次修复尝试, 覆盖率 ${coverage}%)`

const msgType = resultData.all_passed ? 'tests_passed' : 'tests_failed'

mcp__ccw-tools__team_msg({
  operation: "log",
  team: teamName,
  from: "executor",
  to: "coordinator",
  type: msgType,
  summary: `[executor] ${targetLayer}: ${statusMsg}`,
  ref: `${sessionFolder}/results/run-${targetLayer}.json`,
  data: { pass_rate: passRate, coverage, iterations: iteration }
})

SendMessage({
  type: "message",
  recipient: "coordinator",
  content: `## [executor] Test Execution Results

**Task**: ${task.subject}
**Layer**: ${targetLayer}
**Status**: ${resultData.all_passed ? 'PASS' : 'FAIL'}
**Pass Rate**: ${passRate}%
**Coverage**: ${coverage}%
**Iterations**: ${iteration}/${MAX_ITERATIONS}

### Details
- Tests passed: ${resultData.tests_passed}
- Tests failed: ${resultData.tests_failed}`,
  summary: `[executor] QARUN ${targetLayer}: ${resultData.all_passed ? 'PASS' : 'FAIL'} ${passRate}%`
})

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

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

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