# mega-plan-harness — Plan A (build)

audience: AI coding agents first. Contract-level: task seams + sequence, NOT code bodies.
slug: `mega-plan-harness` · date: 2026-06-27 · spec: `docs/specs/2026-06-27-mega-plan-harness-design.md`

## Scope

Build Plan A: the shared shell core + standalone anthropic-less Node runner + DynWF driver + presets +
tests. `~/.claude/run-plan.js` cutover AND its sonnet→haiku declassify are **Plan B** (separate run,
separate repoRoot, self-edit hazard) — OUT of scope here. Read the spec + `spec/{FORK,PRESETS,WRAPPER-CONTRACT}.md`
first; those are the contracts, never duplicated below.

## Execution Strategy Selection

`dag-parallel`. Each wave holds independent units on disjoint files (the `files` hint per task proves
disjointness). Waves are barriers: control planes (wave 2) need the core (wave 1); integration tests
(wave 3) need the planes. Within a wave, tasks run concurrently.

## Bootstrap note (how THIS build is coded)

Per spec Bootstrap: the EXISTING `~/.claude` run-plan tooling builds the new harness (chicken-and-egg —
the new harness does not run itself until its wave-3 integration test passes). Coder seat = cursor/composer
(anthropic-less coding); review = codex primary, opus floor. Orchestration transcription is the "Anthropic
plans" allowance. The operator picks the launch machinery in the next session.

## Waves

| wave | task | seat·tier | files | deps |
|------|------|-----------|-------|------|
| 1 | wrappers (na/ca/codex + probe) | coder·medium | `wrappers/*` | — |
| 1 | resolve-seat.sh + tests | coder·high | `lib/resolve-seat.sh`, `lib/test-resolve-seat.sh` | — |
| 1 | journal.sh + tests | coder·medium | `lib/journal.sh`, `lib/test-journal.sh` | — |
| 1 | gates.sh (vendor gate0 lib + risk) | coder·medium | `lib/gates.sh`, `lib/risk-router.sh`, `lib/test-gates.sh` | — |
| 2 | presets + _validate.mjs | coder·medium | `presets/*` | wrappers |
| 2 | src/runner.js (standalone, zero-anthropic) | coder·high | `src/runner.js` | resolve-seat, wrappers, gates, journal |
| 2 | src/dynwf-driver.js (haiku dispatch glue) | coder·high | `src/dynwf-driver.js` | resolve-seat, wrappers, gates, journal |
| 3 | runner integration test | coder·medium | `test/runner-integration.sh` | runner, presets |
| 3 | dynwf drift-guard dry-run | coder·medium | `test/dynwf-drift-guard.sh` | dynwf-driver, runner |

## Task contracts (seams — implementer writes bodies from the spec + repo)

**w1 wrappers** — vendor `na.sh` (verbatim; already contract-clean), `ca.sh` (adapt rc → 0/124/2/3
distinct; it currently `set -e`s), `codex.sh` (vendor+adapt from `~/.claude/workflows/lib/cx.sh`). Add
`wrappers/_contract-probe.sh <wrapper>` asserting the fixed flag set + exit-code map against a mock engine.
Each MUST satisfy `WRAPPER-CONTRACT.md` (foreground `timeout -k 5`, stdin `</dev/null`, env scoped to
subprocess, `--model` pinned, raw log to file).

**w1 resolve-seat.sh** — `resolve-seat.sh --plan <jsonl> --runconfig <json> --node <id>` → stdout binding
JSON `{seat,tier,wrapper,model,timeout?,flags?,fallback?}` exit 0; resolution ladder PRESETS.md rungs 1–3
(override → tiered → flat), rung 4 unresolvable → exit 2 + stderr `{ok:false,detail}` (fail-closed). MUST
REJECT deterministic gates (`gate0`,`risk`) as non-seats (exit 2). Pure function of (plan,runconfig,node);
fallback resolution is the runner's concern, not here. Tests assert every rung + the gate-rejection + a
cyclic/unknown fail-closed (both branches), `run-plan-lib.sh` test pattern.

**w1 journal.sh** — `append <jsonl> <record>` (one line, never pretty), `state <jsonl> <task>` → last
state, `reconcile <jsonl> <repoRoot> <branch>` → re-derive done-ness from git, downgrade phantom
`COMMITTED`. `runstate/v1` schema per FORK.md. Tests both branches.

**w1 gates.sh** — wrap the EXISTING tested gate0 (vendor the `gate0_*` functions from
`~/.claude/workflows/lib/run-plan-lib.sh`; both modes `strict` + `baseline-ratchet`, default strict) and
vendor `risk-router.sh`. Deterministic, NEVER a seat. Repo-agnostic check discovery, fail-closed if none.
Reuse the existing lib's tests as the vendoring fidelity check.

**w2 presets** — `presets/anthropic-less.json` + `presets/codex.json` (`preset/v1`). Wrapper paths
repo-relative `wrappers/<x>.sh` (canonical copies live here). `presets/_validate.mjs` (zero-dep) asserts
every preset + example runconfig against `spec/{presets,runconfig}.schema.json` AND that every bound
wrapper path resolves to an existing executable (no vapor — `opus-review.sh` does not exist; `anthropic`
preset reviewer is illustrative-only, not shipped here).

**w2 src/runner.js** — `harness run --plan <jsonl> --runconfig <json> [--concurrency N] [--resume]`.
Control flow ONLY (FORK.md iron rule): parse → wave groups → bounded Promise pool (default min(cores-2,8));
per task lease → resolve-seat → `child_process.spawn(wrapper)` passing `binding.model` → gate0 (bounded
retry-fix) → risk → if reviewer seat bound dispatch review → commit; journal each transition; computes
base/head shas itself. Fail-closed: wrapper exit 2/3, gate-not-green-after-N, resolver error → halt + record
blocker. `fallback` on exit 3 / repeated 124 (bounded 4 hops, cycle-detected). `--resume` = journal.reconcile
then advance each task to next APPLICABLE state. **NO model anywhere.** Imports lib/*.sh via child_process,
never a JS port.

**w2 src/dynwf-driver.js** — the `Workflow`-script form of the SAME loop. Allowed drift only (FORK.md):
plan via `args` injection (no FS); dispatch = `agent(prompt,{model:'haiku',schema})` whose prompt shells out
to `resolve-seat.sh` + the wrapper and transcribes rc (pure dispatch+transcribe, NEVER judgment);
concurrency/resume via Workflow `parallel`/runId; journals the SAME `runstate/v1` at the SAME per-task
granularity. Re-derive in-flight state from git+JSONL on compaction.

**w3 runner integration test** — fixture plan + stub wrapper (echoes a diff) → assert journal transitions
`leased→…→committed` AND a fail-closed halt on stub exit 3.

**w3 dynwf drift-guard** — dry-run asserting the DynWF driver shells the SAME `resolve-seat.sh` + wrapper
paths as the runner (the two planes MUST call identical core CLIs — the FORK delete-and-rebuild seam).

## Optional pre-flight (anthropic-less, no orchestrator)

Harvest the real engine exit-code vocabulary by invoking `na.sh` directly in a disposable repo (zero model,
pure wrapper) to verify WRAPPER-CONTRACT's `0/124/2/3` + "any other nonzero → non-completion" coverage is
complete BEFORE the runner that consumes those codes exists. Closes a contract-completeness assumption with
primary evidence. Not a build dependency.

## Testing

Per shared-core unit: a shell test asserting both branches (success + fail-closed). `_contract-probe.sh`
per wrapper. `_validate.mjs` for presets. Runner integration test (above). DynWF drift-guard (above).
