/** * 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 { 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 } } }