Round2 主线:
- A: 事件落库地基 (RuntimeApp EventStore 单例 + 14 repo wiring)
- B: 执行体对齐 (read-before-edit, verification-before-completion)
- C: 界面对齐 (@opentui/solid, 删除 runtime 依赖)
- D: 经验闭环 (ExperienceMiner, DebuggerRole, CompactorRole)
Round2 补充修复:
- fail-on-missing 反作弊门禁
- projection-store-apply.test.ts 补写
- 3个空壳测试转行为 (evidence-store, recovery-impl, knowledge-store)
- ask 项目根支持 AIRCODING_PROJECT_ROOT
- Worker 事件契约修复 (task.attempt.started → checkpoint)
Round3-F: cpp 工具切换
- 删除 BuiltInToolRegistrar cpp.* 闭包
- 接入 toolchain-cpp 真实 CppToolRegistrar
- canonical envelope {status/output/metadata}
- ExecutorRole system prompt 对齐新工具名
Round3-H: Doctor 5 类报告
- toolchain (cmake/ninja/cppcheck/clangd/g++)
- display (X11/Wayland + ImageMagick)
- network (internet connectivity)
- provider (api_key/base_url/model/connectivity)
Secret 脱敏:
- 状态交接.md: sk- → \${OPENAI_API_KEY}
- .gitignore: 添加 .air/ .claude/
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
116 lines
4.0 KiB
TypeScript
Executable File
116 lines
4.0 KiB
TypeScript
Executable File
/**
|
|
* ReviewerRole - Code review worker
|
|
* Read-only, reviews code changes for correctness and compliance.
|
|
* DD §8.4.
|
|
*
|
|
* @module packages/workers/src/roles/ReviewerRole
|
|
*/
|
|
|
|
import { WorkerRuntime } from '../WorkerRuntime.js'
|
|
|
|
export interface ReviewerResult {
|
|
status: 'pass' | 'fail' | 'needs_work' | 'blocked'
|
|
findings: Array<{
|
|
severity: 'info' | 'warning' | 'error' | 'fatal'
|
|
file?: string
|
|
line?: number
|
|
message: string
|
|
suggestion?: string
|
|
}>
|
|
summary: string
|
|
}
|
|
|
|
export class ReviewerRole {
|
|
private runtime: WorkerRuntime
|
|
|
|
constructor(runtime: WorkerRuntime) {
|
|
this.runtime = runtime
|
|
}
|
|
|
|
async run(review_spec: { task_id: string; change_files: string[] }): Promise<ReviewerResult> {
|
|
const result: ReviewerResult = { status: 'pass', findings: [], summary: '' }
|
|
|
|
try {
|
|
this.runtime.checkpoint('review_started', { task_id: review_spec.task_id })
|
|
|
|
for (const file of review_spec.change_files) {
|
|
try {
|
|
// Read each changed file
|
|
const read_result = await this.runtime.call_tool('fs.read', { path: file })
|
|
if (read_result.type === 'error') {
|
|
result.findings.push({ severity: 'warning', file, message: `Cannot read file: ${file}` })
|
|
continue
|
|
}
|
|
|
|
// Get git diff
|
|
const diff_result = await this.runtime.call_tool('git.diff', { path: file, staged: false })
|
|
|
|
// Check for common issues
|
|
const content = typeof read_result.content === 'string' ? read_result.content :
|
|
(read_result.content as any)?.content || JSON.stringify(read_result.content)
|
|
|
|
// Check for execSync usage (security audit)
|
|
if (content.includes('execSync')) {
|
|
result.findings.push({
|
|
severity: 'error',
|
|
file,
|
|
message: 'Found execSync usage. Use execFileSync with args array for command injection prevention.',
|
|
suggestion: 'Replace with execFileSync(cmd, args, opts)'
|
|
})
|
|
}
|
|
|
|
// Check for hardcoded credentials
|
|
if (/api_key|password|secret|token\s*[:=]\s*['"][^'"]+['"]/i.test(content)) {
|
|
result.findings.push({
|
|
severity: 'error',
|
|
file,
|
|
message: 'Possible hardcoded credential detected',
|
|
suggestion: 'Use environment variables or config files for credentials'
|
|
})
|
|
}
|
|
|
|
// Check for direct import violations (INV-4)
|
|
if (file.includes('tui/src/') && content.includes("from '@aircoding/runtime'")) {
|
|
result.findings.push({
|
|
severity: 'fatal',
|
|
file,
|
|
message: 'INV-4 violation: TUI must not import from runtime',
|
|
suggestion: 'Use contracts package or duplicate the ProjectionClient contract locally'
|
|
})
|
|
}
|
|
|
|
// Successful inspection with no issues
|
|
if (result.findings.filter(f => f.file === file).length === 0) {
|
|
result.findings.push({
|
|
severity: 'info',
|
|
file,
|
|
message: 'File reviewed — no issues found'
|
|
})
|
|
}
|
|
} catch (e: any) {
|
|
result.findings.push({
|
|
severity: 'warning',
|
|
file,
|
|
message: `Review error on ${file}: ${e.message}`
|
|
})
|
|
}
|
|
}
|
|
|
|
// Determine overall status
|
|
const has_fatal = result.findings.some(f => f.severity === 'fatal')
|
|
const has_error = result.findings.some(f => f.severity === 'error')
|
|
if (has_fatal) result.status = 'fail'
|
|
else if (has_error) result.status = 'needs_work'
|
|
|
|
result.summary = `Reviewed ${review_spec.change_files.length} files: ${result.findings.length} findings`
|
|
|
|
this.runtime.checkpoint('review_completed', { task_id: review_spec.task_id })
|
|
return result
|
|
|
|
} catch (error) {
|
|
result.status = 'blocked'
|
|
result.findings.push({ severity: 'fatal', message: error instanceof Error ? error.message : String(error) })
|
|
return result
|
|
}
|
|
}
|
|
} |