# Autonomous Gate Self-Repair Implementation Plan

> **For agentic workers:** Execute each task through runplan with preset `codex`. Use TDD, task-scoped commits, strict gates, and serialized landing.

**Goal:** Make runplan detect stalled or misattributed gates, repair harness defects on an isolated branch, synchronize task branches, and resume until every recoverable task lands.

**Architecture:** Gates emit machine-readable semantic progress. Engine classifies failures by reproducing the exact failing check on current clean base, routes base defects through an isolated repair transaction, rebases the task, then resumes. Loaded-engine changes trigger a foreground self-reexec preserving slug, Codex preset, and CLI arguments.

**Tech Stack:** Bash, Node.js CommonJS, SQLite runstate journal, Git worktrees, `ship.sh land`, Node test runner.

---

## Wave Plan

| Wave | Tasks | Files touched | Safe to parallelize? |
|------|-------|---------------|----------------------|
| 1 | Task 1 | `lib/gates.sh`, `lib/test-gates.sh`, `test/gate0-heartbeat.test.sh`, `test/gate0-progress.test.sh` | single task |
| 2 | Task 2 | `src/engine/gates.js`, `src/test/engine-gates.test.js` | single task |
| 3 | Task 3 | `src/engine/repair.js`, `src/engine/repair.test.js`, `src/runner.js`, `src/test/engine-repair.test.js` | single task |
| 4 | Task 4 | `bin/runplan`, `src/runner.js`, `src/test/runner-transcript.test.js`, `test/engine-provenance-runtime.test.sh` | single task |
| 5 | Task 5 | `test/autonomy-e2e.test.sh`, `test/runner-integration.sh` | single task |

## File Structure

- `lib/gates.sh`: gate subprocess lifecycle, semantic progress events, stall enforcement, output artifacts.
- `src/engine/gates.js`: gate evidence parsing, fingerprints, clean-base reproduction, failure classification.
- `src/engine/repair.js`: isolated base-repair transaction, bounded retry ledger, task synchronization.
- `src/runner.js`: orchestration boundary and engine migration request.
- `bin/runplan`: foreground re-exec supervisor preserving Codex invocation.
- Test files: contract, integration, and incident-chain acceptance.

### Task 1: Semantic Gate Progress and Stall Detection

**Wave:** 1
**Blocks:** Task 2
**Blocked by:** —

**Files:**
- Modify: `lib/gates.sh`
- Modify: `lib/test-gates.sh`
- Modify: `test/gate0-heartbeat.test.sh`
- Create: `test/gate0-progress.test.sh`

- [ ] **Step 1: Write failing shell tests**

Assert JSON-line stderr events with fields `event`, `check`, `item`, `pid`, `elapsed_ms`, `output_path`, and `rc`. Cover `check.start`, `item.start`, `item.complete`, `check.complete`, `check.fail`, and `gate.complete`. Run a silent fixture with `RUNPLAN_GATE_STALL_MS=250`; heartbeats must continue but process must terminate nonzero with `FAILCLASS=gate-stalled` and the active item in output.

- [ ] **Step 2: Verify red tests**

Run: `bash test/gate0-progress.test.sh && bash test/gate0-heartbeat.test.sh`

Expected: FAIL because semantic events and semantic-progress timeout do not exist.

- [ ] **Step 3: Implement event protocol**

Add one emitter with stable wire format:

```bash
gate_event() {
  local event="$1" check="$2" item="$3" pid="$4" elapsed_ms="$5" output_path="$6" rc="${7:-}"
  jq -cn --arg event "$event" --arg check "$check" --arg item "$item" \
    --argjson pid "${pid:-0}" --argjson elapsed_ms "${elapsed_ms:-0}" \
    --arg output_path "$output_path" --arg rc "$rc" \
    '{protocol:"runplan.gate/v1",event:$event,check:$check,item:$item,pid:$pid,elapsed_ms:$elapsed_ms,output_path:$output_path}
     + (if $rc=="" then {} else {rc:($rc|tonumber)} end)' >&2
}
```

Create output path before process start. Update semantic-progress timestamp only for start/complete/output change, never heartbeat. On timeout terminate process group, emit `check.fail`, and return `gate-stalled` without starting another check.

- [ ] **Step 4: Verify gate contracts**

Run: `bash test/gate0-progress.test.sh && bash test/gate0-heartbeat.test.sh && bash lib/test-gates.sh`

Expected: all TAP assertions pass.

- [ ] **Step 5: Commit**

```bash
git add lib/gates.sh lib/test-gates.sh test/gate0-heartbeat.test.sh test/gate0-progress.test.sh
git commit -m "feat(gates): enforce semantic progress"
```

### Task 2: Evidence-Based Failure Attribution

**Wave:** 2
**Blocks:** Task 3
**Blocked by:** Task 1

**Files:**
- Modify: `src/engine/gates.js`
- Modify: `src/test/engine-gates.test.js`

- [ ] **Step 1: Write failing unit tests**

Cover deterministic fingerprints, structured event parsing, `base-drift`, `gate-stalled`, `base-defect`, `task-defect`, `flake`, and `unclassified`. Base-defect requires same check and fingerprint on clean current base; a different clean-base failure is unclassified.

- [ ] **Step 2: Verify red tests**

Run: `node --test src/test/engine-gates.test.js`

Expected: FAIL because attribution API is absent.

- [ ] **Step 3: Implement attribution API**

Export:

```js
async function attributeGateFailure(context, task, failure, deps = {}) {
  // Return { failureClass, fingerprint, check, item, outputTail,
  //          taskBase, currentBase, taskHead, cleanBaseEvidence }.
}
```

Fingerprint canonical fields `{check,item,rc,normalizedExcerpt}` with SHA-256. Compare task base to fetched integration base first. Reproduce only exact failing check in a clean detached worktree. Record every classification and evidence path in run journal.

- [ ] **Step 4: Verify engine gate tests**

Run: `node --test src/test/engine-gates.test.js`

Expected: PASS.

- [ ] **Step 5: Commit**

```bash
git add src/engine/gates.js src/test/engine-gates.test.js
git commit -m "feat(engine): attribute gate failures"
```

### Task 3: Isolated Base Repair and Task Synchronization

**Wave:** 3
**Blocks:** Task 4
**Blocked by:** Task 2

**Files:**
- Modify: `src/engine/repair.js`
- Modify: `src/engine/repair.test.js`
- Modify: `src/runner.js`
- Modify: `src/test/engine-repair.test.js`

- [ ] **Step 1: Write failing repair tests**

Simulate task failure reproducing on clean base. Assert dispatch freezes, branch name is `repair/<run-id>/<fingerprint>`, fixer uses active `codex` preset, exact check then full gate pass, land command is `.claude/scripts/ship.sh land`, task rebases onto landed base, declared task delta survives, repair branch is removed, and task resumes. Assert mixed base/task diffs fail closed.

- [ ] **Step 2: Verify red tests**

Run: `node --test src/engine/repair.test.js src/test/engine-repair.test.js`

Expected: FAIL because base repair transaction is absent.

- [ ] **Step 3: Implement bounded repair transaction**

Add:

```js
async function repairBaseFailure(context, task, evidence, retryTask, deps = {}) {}
async function synchronizeTaskBase(context, task, landedBase, deps = {}) {}
function retryLedgerKey({ runId, taskId, failureClass, fingerprint, baseHead, taskHead }) {}
```

Journal `base-repair.started`, `base-repair.fixed`, `base-repair.gated`, `base-repair.landed`, `task-base.synchronized`, or terminal failure. Permit one base repair per ledger key and one base-sync retry; unchanged evidence never retries. Fixer prompt contains failing check, excerpt, clean-base evidence, and allowed paths discovered from failure evidence. Use current preset unchanged; reject any preset mutation.

- [ ] **Step 4: Verify repair tests**

Run: `node --test src/engine/repair.test.js src/test/engine-repair.test.js`

Expected: PASS.

- [ ] **Step 5: Commit**

```bash
git add src/engine/repair.js src/engine/repair.test.js src/runner.js src/test/engine-repair.test.js
git commit -m "feat(engine): repair base failures autonomously"
```

### Task 4: Foreground Engine Migration Supervisor

**Wave:** 4
**Blocks:** Task 5
**Blocked by:** Task 3

**Files:**
- Modify: `bin/runplan`
- Modify: `src/runner.js`
- Modify: `src/test/runner-transcript.test.js`
- Modify: `test/engine-provenance-runtime.test.sh`

- [ ] **Step 1: Write failing migration tests**

Assert a landed repair touching loaded engine exits with reserved migration code, launcher re-execs in same foreground terminal, preserves slug plus `--preset codex --foreground --engine-migrate`, consumes child stdout/stderr, and returns only final child status. Assert Cursor preset strings are rejected for this plan.

- [ ] **Step 2: Verify red tests**

Run: `node --test src/test/runner-transcript.test.js && bash test/engine-provenance-runtime.test.sh`

Expected: FAIL because migration records provenance but does not supervise re-exec.

- [ ] **Step 3: Implement migration request and launcher loop**

Use exit code `75` exclusively for `engine.migration-requested`. Before exit, checkpoint journal and release locks. `bin/runplan` must synchronously invoke updated launcher with original arguments, force `--engine-migrate`, and reject a changed preset. No daemon, detached child, polling, or output redirection.

- [ ] **Step 4: Verify migration tests**

Run: `node --test src/test/runner-transcript.test.js && bash test/engine-provenance-runtime.test.sh`

Expected: PASS.

- [ ] **Step 5: Commit**

```bash
git add bin/runplan src/runner.js src/test/runner-transcript.test.js test/engine-provenance-runtime.test.sh
git commit -m "feat(runplan): reexec repaired engine in foreground"
```

### Task 5: Incident-Chain Runtime Acceptance

**Wave:** 5
**Blocks:** —
**Blocked by:** Task 4

**Files:**
- Modify: `test/autonomy-e2e.test.sh`
- Modify: `test/runner-integration.sh`

- [ ] **Step 1: Add failing end-to-end fixtures**

Fixture chain: t1 lands; t2 gate stalls; clean base reproduces harness defect; Codex base repair lands; engine re-execs foreground; t2 rebases and lands; t3-t5 execute and land. Assert one `run.done`, zero quarantined/skipped tasks, no Cursor binding, no mixed repair diff, and semantic event identifies active test at stall.

- [ ] **Step 2: Verify acceptance red then green**

Run: `bash test/autonomy-e2e.test.sh && bash test/runner-integration.sh`

Expected before fixture support: FAIL. Expected after fixture support: PASS.

- [ ] **Step 3: Run complete quality gate**

Run: `make test && npm run lint --if-present && npm run typecheck --if-present`

Expected: exit 0.

- [ ] **Step 4: Build and probe launcher artifact**

Run: `cargo build --release --locked --manifest-path tui/Cargo.toml && bin/runplan preflight --plan docs/plans/2026-07-22-autonomous-gate-self-repair.jsonl --preset codex --foreground`

Expected: release build succeeds; preflight exits 0 and reports Codex preset.

- [ ] **Step 5: Commit**

```bash
git add test/autonomy-e2e.test.sh test/runner-integration.sh
git commit -m "test(runplan): cover autonomous base repair chain"
```

## Runtime Acceptance

Resume original user plan only through blocking foreground execution:

```bash
bin/runplan autonomous-run-completion --preset codex --foreground --engine-migrate
```

Success requires terminal exit 0, `run.done`, t1-t5 landed, zero quarantined/skipped tasks, and updated launcher artifact resolved by `bin/runplan`.
