(text: string)
| 96 | |
| 97 | /** Parse the critic's response into a verdict. Tolerant of fenced/loose JSON. */ |
| 98 | export function parseCriticVerdict(text: string): CriticVerdict { |
| 99 | const parsed = tryParseJson(text) as any; |
| 100 | if (parsed && typeof parsed === 'object' && Array.isArray(parsed.findings)) { |
| 101 | const findings: CriticFinding[] = parsed.findings |
| 102 | .filter((f: any) => f && typeof f.issue === 'string') |
| 103 | .map((f: any) => ({ |
| 104 | severity: f.severity === 'blocker' ? 'blocker' : 'warning', |
| 105 | location: typeof f.location === 'string' ? f.location : undefined, |
| 106 | issue: f.issue, |
| 107 | })); |
| 108 | const hasBlocker = findings.some(f => f.severity === 'blocker'); |
| 109 | // Honor an explicit pass:false even if the model forgot to mark a blocker. |
| 110 | const pass = parsed.pass === false ? false : !hasBlocker; |
| 111 | return { pass, findings }; |
| 112 | } |
| 113 | // Couldn't parse — fail OPEN (don't block shipping on a critic we can't read), |
| 114 | // but surface the raw text so the loop can log it. |
| 115 | return { pass: true, findings: [], raw: (text ?? '').slice(0, 500) }; |
| 116 | } |
| 117 | |
| 118 | /** Build the corrective message sent back to the worker when the critic blocks. */ |
| 119 | export function buildCriticRepairMessage(verdict: CriticVerdict): string { |
no test coverage detected