# Design: Gate Failure Targeted Repair Strategy

**Date:** 2026-03-10
**Status:** Approved
**Scope:** Builder phase fast-gate failure — repair retry loop

---

## Problem

When the fast gate fails during the builder phase, the repair retry loop (`BuildWaveExecutor`) invokes `ReactionsService.buildRepairPrompt()` to generate instructions for the next builder attempt. That prompt currently contains:

- `ctx.logs` — always empty string on a normal gate FAIL (only populated on a thrown exception)
- `ctx.evidenceSummary` — a raw file path string, never read

The builder receives no actionable diagnostic content. It cannot resolve the failure because it cannot see what failed.

---

## Goals

1. Give the builder **specific, actionable failure information** in every repair prompt.
2. Be **platform-agnostic** — no tool-specific parsers; works for any command combination in any gate step.
3. **Refresh context** before each repair attempt so the builder sees its own recent patches.
4. Minimal blast radius — one new file, two modified files.

---

## Design

### Component Map

```
BuildWaveExecutor (modified)
  │
  ├─ after GATES_RUN FAIL ──► re-fetch context
  │                           read step log_path
  │                           call extractGateLog()
  │                                │
  │                                ▼
  │                         GateLogExtraction  ──► GateRepairContext.failureAnalysis
  │
  └─ pass enriched GateRepairContext to ──► ReactionsService.buildRepairPrompt()
                                                (modified, renders structured output)
```

### New: `supervisor/gate-log-extractor.ts`

A pure, stateless function. No file I/O, no external dependencies.

```typescript
export interface FileRef {
  path: string;
  line: number | null;
  col: number | null;
  message: string | null;
}

export interface GateLogExtraction {
  stepName: string;
  cmd: string[];
  exitCode: number;
  affectedFiles: FileRef[]; // unique file:line:col references
  errorLines: string[]; // lines matching error/fail patterns
  summaryLines: string[]; // count/status summary lines
  errorCount: number | null; // parsed from summary lines when present
  logTail: string; // last ~1200 chars as raw fallback
}

export function extractGateLog(input: {
  stepName: string;
  cmd: string[];
  exitCode: number;
  logContent: string;
}): GateLogExtraction;
```

**Universal extraction patterns (no tool knowledge):**

| Signal        | Pattern                                                             |
| ------------- | ------------------------------------------------------------------- |
| File refs     | `\b([\w./\-]+\.\w+):(\d+)(?::(\d+))?(?:[:,]\s*(.+))?`               |
| Error lines   | Lines containing `\berror\b`, `FAILED`, `✗`, `✘` (case-insensitive) |
| Summary lines | `\d+\s+errors?`, `\d+\s+failures?`, `FAILED`, `PASS`                |
| Log tail      | Last 1200 characters                                                |
| Error count   | Parsed from first matching summary line                             |

Deduplication: `affectedFiles` is deduplicated by `path:line:col`. `errorLines` is capped at 30 entries. `summaryLines` is capped at 10 entries.

---

### Modified: `reactions-service.ts`

**`GateRepairContext`** gains one optional field:

```typescript
export interface GateRepairContext {
  // ... existing fields ...
  failureAnalysis?: GateLogExtraction;
}
```

**`buildRepairPrompt`** renders a structured section when `failureAnalysis` is present:

```
Gate `fast` step `test_with_lint_and_typecheck` failed (exit 1).

Affected files (3):
  src/foo.ts:10:5 — error TS2345: Argument of type 'string' is not assignable...
  src/bar.ts:22:1 — Unexpected token
  src/baz.ts:5:3 — 'x' is defined but never used

Errors (5 total):
  src/foo.ts:10:5: error TS2345: ...
  ...

Fix all errors listed above before re-running.
Retry: 1 of 2
Retry delay (ms): 30000

Failure history:
  - attempt=1; gate=fast; exit=1; at=<timestamp>; evidence=<path>

Log tail (for reference):
  <last 800 chars>
```

Falls back to current generic output when `failureAnalysis` is absent.

---

### Modified: `build-wave-executor.ts`

Two changes to the repair retry loop:

**1. Context refresh per iteration**

Currently context is fetched once before the loop and reused stale across all retries. After each builder worker invocation, re-fetch context so the next repair call sees updated `latest_evidence` and reflects the builder's own patches.

**2. Log extraction after each gate FAIL**

After `GATES_RUN` returns `FAIL` in the retry loop:

```
1. Re-fetch context → updated latest_evidence.step_results
2. Find failing step (first step with exit_code !== 0, or step_results[0])
3. Resolve log_path (absolute or relative to repoRoot)
4. Read log file content (fs.readFile)
5. Call extractGateLog({ stepName, cmd, exitCode, logContent })
6. Set failureHistory[N].failureAnalysis = extraction
7. Pass extraction into GateRepairContext.failureAnalysis for next buildRepairPrompt call
```

Same pattern applies to the **initial** gate failure (before entering the retry loop) — the first repair prompt should also carry the extraction, not just subsequent ones.

Error handling: if log file read fails, `failureAnalysis` is omitted and the prompt falls back to current behavior. No hard failure.

---

## Data Flow

```
GATES_RUN (fast) → FAIL
  │
  ├─ re-fetch context
  ├─ read step_results[failing].log_path
  ├─ extractGateLog(stepName, cmd, exitCode, logContent)
  │     └─ returns GateLogExtraction { affectedFiles, errorLines, summaryLines, errorCount, logTail }
  │
  ├─ GateRepairContext { ..., failureAnalysis: extraction }
  │
  └─ buildRepairPrompt(ctx)
        └─ renders structured prompt with file:line errors + log tail
              └─ builder worker receives targeted instructions
                    └─ GATES_RUN (fast) re-run
```

---

## Error Handling

- Log file not found / unreadable → `failureAnalysis` omitted, fallback to generic prompt. No exception propagated.
- `step_results` absent from context → skip extraction, use generic prompt.
- Extraction produces empty `affectedFiles` and `errorLines` → still include `logTail` for raw fallback.

---

## Testing

**New: `test/gate-log-extractor.spec.ts`**

- File ref extraction: `path:line:col: message`, `path:line: message`, path-only
- Error line filtering: `error`, `ERROR`, `FAILED`, `✗`, mixed content
- Summary line parsing: `3 errors`, `FAILED`, `0 failures`
- Error count: extracted from summary
- Log tail: truncated to last 1200 chars
- Deduplication of file refs
- Empty / whitespace-only log
- Malformed / binary-looking log

**Modified: `test/build-wave-executor.spec.ts`**

- Repair loop passes enriched `GateRepairContext` with `failureAnalysis` populated
- Context is re-fetched on each repair iteration
- Log read failure → `failureAnalysis` absent, no crash
- First repair attempt (attempt=1) also has `failureAnalysis`

**Modified: `test/reactions-service.spec.ts` (or new section)**

- `buildRepairPrompt` with `failureAnalysis` present renders file refs, error lines, log tail
- `buildRepairPrompt` without `failureAnalysis` renders current generic output unchanged

---

## Files Changed

| File                                                               | Change                                                                   |
| ------------------------------------------------------------------ | ------------------------------------------------------------------------ |
| `apps/control-plane/src/supervisor/gate-log-extractor.ts`          | **New** — pure extraction function                                       |
| `apps/control-plane/src/application/services/reactions-service.ts` | Add `failureAnalysis` to `GateRepairContext`; update `buildRepairPrompt` |
| `apps/control-plane/src/supervisor/build-wave-executor.ts`         | Read log + call extractor after FAIL; refresh context per iteration      |
| `apps/control-plane/test/gate-log-extractor.spec.ts`               | **New** — unit tests                                                     |
| `apps/control-plane/test/build-wave-executor.spec.ts`              | Tests for enriched repair loop                                           |
| `apps/control-plane/test/reactions-service.spec.ts`                | Tests for structured prompt rendering                                    |
