# Harness v2 Phase 1 Trust Core Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use /ship (recommended) or /executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

audience: AI coding agents first.

**Goal:** Rebuild harness v2 trust core: independent review, bounded gate repair, ordered seat fallback, durable retry stop-loss, rate-limit parking, and flat remaining presets.

**Architecture:** Keep v2 execution kernel process-local and persist every decision in append-only journal events. Add pure projections for retry/fallback/quality state, then wire them into `runPlan`; no daemon or in-memory-only authority. Preserve strict gate and fail closed on malformed verdicts, unknown bindings, exhausted ladders, or unreadable retry state.

**Tech Stack:** Node.js CommonJS, `node:test`, JSON Schema, NDJSON journals, existing wrapper contract and systemd-backed child runner.

---

## Wave Plan

| Wave | Tasks | Files touched | Safe to parallelize? |
|------|-------|---------------|----------------------|
| 1 | Task 1, Task 2, Task 3, Task 4, Task 5, Task 6 | `modules/harness/v2/journal.js`, `modules/harness/spec/events.schema.json`, `modules/harness/v2/test/journal-quality.test.js`; `modules/harness/v2/seats.js`, `modules/harness/spec/presets.schema.json`, `modules/harness/spec/PRESETS.md`, `modules/harness/v2/test/seats-quality.test.js`; `modules/harness/v2/dispatch.js`, `modules/harness/v2/test/dispatch-parking.test.js`; three distinct preset JSON files | ✅ no overlap |
| 2 | Task 7 | `modules/harness/v2/quality.js`, `modules/harness/v2/child.js`, `modules/harness/v2/test/quality.test.js`, `modules/harness/v2/test/child.test.js` | single task |
| 3 | Task 8 | `modules/harness/v2/run.js`, `modules/harness/v2/test/run-quality.test.js` | single task |
| 4 | Task 9 | `modules/harness/v2/test/index.js` | single task |

## File Structure

- `modules/harness/v2/journal.js` — append-only quality events + deterministic retry-ledger projection.
- `modules/harness/v2/seats.js` — ordered binding chains + independent reviewer selection.
- `modules/harness/v2/dispatch.js` — wrapper execution + exit-75 parking loop.
- `modules/harness/v2/quality.js` — review verdict and gate-fix state machine.
- `modules/harness/v2/child.js` — enforce read-only reviewer process boundary.
- `modules/harness/v2/run.js` — integrate trust phases into task lifecycle.
- `modules/harness/spec/events.schema.json` — quality/retry/parking event contracts.
- `modules/harness/spec/presets.schema.json`, `modules/harness/spec/PRESETS.md` — fallback-chain contract.
- `modules/harness/presets/{anthropic-less,canary,grok}.json` — flat seat bindings.
- `modules/harness/v2/test/*quality*.test.js`, `dispatch-parking.test.js` — focused unit/integration coverage.
- `modules/harness/v2/test/index.js` — full v2 suite registration.

### Task 1: Durable Retry Ledger

**Wave:** 1
**Blocks:** Task 7, Task 8, Task 9
**Blocked by:** —

**Files:**
- Modify: `modules/harness/v2/journal.js` — durable quality-event append and retry projection.
- Modify: `modules/harness/spec/events.schema.json` — quality, retry, stop-loss, and parking event shapes.
- Create: `modules/harness/v2/test/journal-quality.test.js` — projection/reopen/fail-closed tests.

**Contract (pin EXACTLY — this is the divergence-prone surface):**
- `retryLedgerKey({ runId, taskId, failureClass, failureFingerprint, baseHead, taskHead }) -> string`
- `projectRetryLedger(events) -> Map<string,{attempts:number,parks:number,lastOutcome:string|null}>`
- `retryDisposition(events, identity, { maxAttempts:3, maxParks:8 }) -> {attempts:number,parks:number,allowAttempt:boolean,allowPark:boolean,stopLoss:boolean}`
- Add schema-valid events for review attempts/verdicts, fix rungs, binding fallback attempts, retry attempts, rate-limit parks/heartbeats/wakes, quarantine, and stop-loss. Every event carries task, phase, binding identity, heads, failure class/fingerprint where applicable.

**Behavior:**
- Reconstruct state from disk only; journal reopen yields same projection.
- Count identical failure identity across coordinator restarts and all review/fix/fallback paths.
- Third identical failed attempt exhausts attempt allowance; eighth park is allowed, ninth is refused.
- Changed fingerprint, base head, or task head produces distinct key.
- Malformed/unknown ledger event fails closed instead of resetting counts.
- Continue existing sequence from last valid event when reopening journal; never restart `seq` at one.

**Acceptance (one executable check):**
- Run: `node --test modules/harness/v2/test/journal-quality.test.js`
- Expected: PASS — reopen persistence, monotonic sequence, identity separation, attempt/park caps, malformed-event stop.

- [ ] Write tests covering behavior above.
- [ ] Implement contract + acceptance.
- [ ] Run acceptance check → expected output above.
- [ ] Commit: `git add modules/harness/v2/journal.js modules/harness/spec/events.schema.json modules/harness/v2/test/journal-quality.test.js && git commit -m "feat: add durable quality retry ledger"`

### Task 2: Ordered Seat Fallback and Independent Reviewer

**Wave:** 1
**Blocks:** Task 7, Task 8, Task 9
**Blocked by:** —

**Files:**
- Modify: `modules/harness/v2/seats.js` — binding-chain resolution and reviewer independence.
- Modify: `modules/harness/spec/presets.schema.json` — fallback reference/inline/ordered-chain validation.
- Create: `modules/harness/spec/PRESETS.md` — fail-closed fallback and reviewer rules.
- Create: `modules/harness/v2/test/seats-quality.test.js` — chain and independence tests.

**Contract (pin EXACTLY — this is the divergence-prone surface):**
- `resolveBindingChain({ preset, seatName, tier, failureKind }) -> Binding[]`
- `resolveIndependentReviewer({ preset, coderBinding, healthyBindings }) -> Binding`
- `bindingIdentity(binding) -> {wrapper:string,model:string|null,provider:string}`
- `failureKind` eligible enum: `engine-down|timeout-repeated`; all other values reject fallback.
- Resolve `fallback` as seat-ref string, inline binding, or ordered array. Maximum four total hops; repeated binding identity is a cycle.

**Behavior:**
- Preserve configured order; validate entire chain before first dispatch.
- Reject unknown reference, invalid binding, cycle, and fifth hop.
- Never fall back on task, review, code, scope, or gate failure.
- Select reviewer with different provider/model when healthy; refuse self-review if any independent healthy binding exists.
- Return immutable normalized bindings; never mutate loaded preset.

**Acceptance (one executable check):**
- Run: `node --test modules/harness/v2/test/seats-quality.test.js`
- Expected: PASS — ordered fallback, eligibility, missing target, cycle, hop cap, reviewer independence.

- [ ] Write tests covering behavior above.
- [ ] Implement contract + acceptance.
- [ ] Run acceptance check → expected output above.
- [ ] Commit: `git add modules/harness/v2/seats.js modules/harness/spec/presets.schema.json modules/harness/spec/PRESETS.md modules/harness/v2/test/seats-quality.test.js && git commit -m "feat: add ordered seat fallback"`

### Task 3: Rate-Limit Parking

**Wave:** 1
**Blocks:** Task 8, Task 9
**Blocked by:** —

**Files:**
- Modify: `modules/harness/v2/dispatch.js` — bounded exit-75 park/wake loop.
- Create: `modules/harness/v2/test/dispatch-parking.test.js` — fake-clock/reset/wake tests.

**Contract (pin EXACTLY — this is the divergence-prone surface):**
- Extend `runDispatch({ ..., initialParkCount=0, maxParks=8, wakeFile, now, sleep, onParkEvent }) -> Promise<ChildResult & {parkCount:number}>`.
- Exit `75` result MUST carry provider reset timestamp in wrapper result metadata; absent/invalid reset is a fail-closed dispatch failure.
- `onParkEvent({kind:'parked'|'heartbeat'|'woken',provider,resetAt,parkCount})`.
- Wake-file content is a nonce; consume by atomic rename before retry.

**Behavior:**
- Park until valid reset time, consumed wake nonce, or bounded provider probe signal.
- Emit heartbeat during wait; preserve worktree and task attempt.
- Do not consume normal retry attempt.
- Refuse ninth park.
- Accept injected clock/sleep for deterministic tests; production defaults use real clock.
- Never block alternate healthy fallback dispatch admission.

**Acceptance (one executable check):**
- Run: `node --test modules/harness/v2/test/dispatch-parking.test.js`
- Expected: PASS — reset wake, external wake, heartbeat, malformed reset, eight-park cap, nonblocking fallback.

- [ ] Write tests covering behavior above.
- [ ] Implement contract + acceptance.
- [ ] Run acceptance check → expected output above.
- [ ] Commit: `git add modules/harness/v2/dispatch.js modules/harness/v2/test/dispatch-parking.test.js && git commit -m "feat: park rate-limited dispatches"`

### Task 4: Flatten Anthropic-Less Preset

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

**Files:**
- Modify: `modules/harness/presets/anthropic-less.json` — flat coder binding.

**Contract (pin EXACTLY — this is the divergence-prone surface):**
- Replace tiered `seats.coder` with `{wrapper:"wrappers/ca.sh",model:"composer-2.5"}` from current `medium`.
- Preserve reviewer, fixer, resolver bindings byte-for-value.
- Remove tier-only `coder.high.fallback`.

**Behavior:**
- Preset validates under published schema.
- v2 flat resolver finds non-empty coder wrapper without task tier.

**Acceptance (one executable check):**
- Run: `node modules/harness/presets/_validate.mjs`
- Expected: PASS — validator reports all presets valid; `anthropic-less.coder.wrapper` is flat.

- [ ] Update preset to exact contract.
- [ ] Run acceptance check → expected output above.
- [ ] Commit: `git add modules/harness/presets/anthropic-less.json && git commit -m "fix: flatten anthropic-less preset"`

### Task 5: Flatten Canary Preset

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

**Files:**
- Modify: `modules/harness/presets/canary.json` — flat coder and reviewer bindings.

**Contract (pin EXACTLY — this is the divergence-prone surface):**
- Replace tiered coder with `{wrapper:"wrappers/canary-stub.sh",model:"canary-coder",timeout:120}`.
- Replace tiered reviewer with `{wrapper:"wrappers/canary-stub.sh",model:"canary-reviewer",timeout:120}`.
- Preserve fixer and resolver bindings byte-for-value.

**Behavior:**
- Remove all duplicate tier maps.
- Preserve deterministic offline canary behavior.

**Acceptance (one executable check):**
- Run: `node modules/harness/presets/_validate.mjs`
- Expected: PASS — validator reports all presets valid; canary coder/reviewer are flat.

- [ ] Update preset to exact contract.
- [ ] Run acceptance check → expected output above.
- [ ] Commit: `git add modules/harness/presets/canary.json && git commit -m "fix: flatten canary preset"`

### Task 6: Flatten Grok Preset

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

**Files:**
- Modify: `modules/harness/presets/grok.json` — flat coder binding.

**Contract (pin EXACTLY — this is the divergence-prone surface):**
- Replace tiered coder with `{wrapper:"wrappers/grok.sh",model:"grok-composer-2.5-fast",timeout:1200}` from current `medium`.
- Preserve reviewer, fixer, resolver bindings byte-for-value.

**Behavior:**
- Remove duplicate tier map.
- Preserve Grok-only coder execution.

**Acceptance (one executable check):**
- Run: `node modules/harness/presets/_validate.mjs`
- Expected: PASS — validator reports all presets valid; `grok.coder.wrapper` is flat.

- [ ] Update preset to exact contract.
- [ ] Run acceptance check → expected output above.
- [ ] Commit: `git add modules/harness/presets/grok.json && git commit -m "fix: flatten grok preset"`

### Task 7: Review and Gate-Fix State Machine

**Wave:** 2
**Blocks:** Task 8, Task 9
**Blocked by:** Task 1, Task 2

**Files:**
- Create: `modules/harness/v2/quality.js` — pure review/gate-fix orchestration.
- Modify: `modules/harness/v2/child.js` — read-only reviewer execution boundary.
- Create: `modules/harness/v2/test/quality.test.js` — verdict and rung tests.
- Modify: `modules/harness/v2/test/child.test.js` — read-only process tests.

**Contract (pin EXACTLY — this is the divergence-prone surface):**
- `parseReviewVerdict(output) -> {verdict:'PASS'|'FAIL',findings:Array<{severity:string,path:string|null,message:string}>}`; reject extra/malformed output.
- `reviewRequired(risk) -> boolean` where `high|unknown` require review.
- `nextGateFixRung(events, identity) -> 'dependency-repair'|'fixer'|'stronger-fixer'|'exhausted'`.
- `runQualityPhase({ task, risk, coderBinding, preset, worktree, failedCheck, journal, dispatch, gate }) -> Promise<{status:'green'|'quarantined',review?,rungs:[]}>`.
- Extend child launch options with `readOnlyWorkspace:true`; reviewer process MUST receive read-only workspace at OS boundary.

**Behavior:**
- Review occurs after coder diff, before gate/commit; normal-risk work may skip with journal reason.
- Reviewer cannot write. Any mutation attempt fails review and leaves coder diff unchanged.
- Reviewer binding satisfies Task 2 independence.
- Gate-fix order is deterministic dependency repair → fixer → stronger fixer; stop at first green.
- After each rung, run exact failed check then full strict gate.
- Restore unauthorized fixer edits before one constrained retry; repeated escape quarantines.
- Invalid verdict, missing reviewer, rung failure, or exhausted retry ledger quarantines.

**Acceptance (one executable check):**
- Run: `node --test modules/harness/v2/test/quality.test.js modules/harness/v2/test/child.test.js`
- Expected: PASS — review schema/read-only boundary and all gate-fix transitions hold.

- [ ] Write tests covering behavior above.
- [ ] Implement contract + acceptance.
- [ ] Run acceptance check → expected output above.
- [ ] Commit: `git add modules/harness/v2/quality.js modules/harness/v2/child.js modules/harness/v2/test/quality.test.js modules/harness/v2/test/child.test.js && git commit -m "feat: add trust quality ladder"`

### Task 8: Integrate Trust Core into Run Lifecycle

**Wave:** 3
**Blocks:** Task 9
**Blocked by:** Task 1, Task 2, Task 3, Task 7

**Files:**
- Modify: `modules/harness/v2/run.js` — task lifecycle integration.
- Create: `modules/harness/v2/test/run-quality.test.js` — end-to-end trust-core tests.

**Contract (pin EXACTLY — this is the divergence-prone surface):**
- Task lifecycle: admission → coder dispatch/fallback/parking → diff validation → review when required → strict gate/fix ladder → checkpoint.
- Before every dispatch/fallback/fix/review, compute ledger identity from current integration/task heads and call `retryDisposition`.
- Journal every phase transition before side effect and terminal receipt after side effect.
- Completion commit/push is unreachable unless review requirement passed and full strict gate is green.

**Behavior:**
- Engine-down or repeated timeout advances Task 2 chain; other failures never do.
- Exit `75` invokes Task 3 parking and Task 1 park accounting.
- Identical third failure quarantines; unchanged quarantined identity never redispatches after restart.
- Changed head/fingerprint permits distinct attempt.
- Review/fix processes use named seat bindings and preserve task file claims.
- Existing concurrency, wave barriers, checkpoint retry, resume trailers, and landing behavior remain unchanged.

**Acceptance (one executable check):**
- Run: `node --test modules/harness/v2/test/run-quality.test.js`
- Expected: PASS — full ladder, fallback, parking, restart ledger, stop-loss, and checkpoint invariant.

- [ ] Write tests covering behavior above.
- [ ] Implement contract + acceptance.
- [ ] Run acceptance check → expected output above.
- [ ] Commit: `git add modules/harness/v2/run.js modules/harness/v2/test/run-quality.test.js && git commit -m "feat: integrate v2 trust core"`

### Task 9: Register and Run Full v2 Regression Suite

**Wave:** 4
**Blocks:** —
**Blocked by:** Task 1, Task 2, Task 3, Task 4, Task 5, Task 6, Task 7, Task 8

**Files:**
- Modify: `modules/harness/v2/test/index.js` — register all new Phase 1 test files.

**Contract (pin EXACTLY — this is the divergence-prone surface):**
- Register `journal-quality.test.js`, `seats-quality.test.js`, `dispatch-parking.test.js`, `quality.test.js`, and `run-quality.test.js` exactly once.
- Preserve every existing test registration.

**Behavior:**
- Full v2 suite and preset validation run from repository root.
- Syntax-check every changed/new runtime module.
- No skipped or focused tests; no warnings.

**Acceptance (one executable check):**
- Run: `node --check modules/harness/v2/journal.js && node --check modules/harness/v2/seats.js && node --check modules/harness/v2/dispatch.js && node --check modules/harness/v2/quality.js && node --check modules/harness/v2/run.js && node modules/harness/presets/_validate.mjs && node modules/harness/v2/test/index.js`
- Expected: exit `0`; all existing and Phase 1 tests pass with no warnings.

- [ ] Register new test files.
- [ ] Run acceptance check → expected output above.
- [ ] Commit: `git add modules/harness/v2/test/index.js && git commit -m "test: register v2 trust core coverage"`
