# Autonomy P2a — State 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.

**Goal:** Journal v2 (SQLite-backed events + migration, per amended spec 2026-07-09), Node single-writer state (V6), runner modularization (V7), intent log (I3), land exactly-once journal side (I4) — the trustworthy-state substrate the resolver and daemon stand on.

**Architecture:** New `src/state/` modules per `spec/JOURNAL-V2.md` (canonical — implement per spec, this doc only sequences). Bash writers become exec shims. Then a pure mechanical split of `src/runner.js` into `src/engine/` modules, then the engine switches to the v2 writer.

**Tech Stack:** Node, bash shims.

**Sequencing:** After `autonomy-p1-foundations` lands. Handoff plan P2 first half ("starts with V6+V7 — everything later builds on these"). Read `spec/IMPLEMENTER-NOTES.md` + `spec/JOURNAL-V2.md` END TO END first.

---

## Wave Plan

| Wave | Tasks | Files touched | Safe to parallelize? |
|------|-------|---------------|----------------------|
| 1 | Task 1, Task 2 | src/state/journal.js · src/state/lock.js, src/state/liveness.js | ✅ no overlap |
| 2 | Task 3, Task 4 | src/state/cli.js, lib/journal.sh, lib/liveness · src/state/intents.js | ✅ no overlap |
| 3 | Task 5 | src/runner.js, src/engine/scheduler.js, src/engine/lease.js | single task |
| 4 | Task 6 | src/runner.js, src/engine/gates.js, src/engine/repair.js, src/engine/ship.js | single task |
| 5 | Task 7 | src/runner.js, src/engine/ship.js | single task |
| 6 | Task 8 | test/chaos/scenarios/ | single task |

---

### Task 1: `src/state/journal.js` — v2 core

**Wave:** 1 · **Blocks:** Task 3, Task 4, Task 7 · **Blocked by:** —

**Files:**
- Create: `src/state/journal.js` + colocated `src/state/journal.test.js`.

**Contract:** `spec/JOURNAL-V2.md` is canonical and exhaustive: SQLite schema (`node:sqlite`, WAL, append-only triggers, `user_version=2`), API surface (append/appendGroup/readAll/readSince/lastSeq/export), open checks (version-skew, `integrity_check` fail-closed), migration v1→v2 (kind-less → `task.state`, legacy kind kept, jsonl → `.v1.bak` rename as commit point). Tests 1–5 of the spec's test list live here.

**Acceptance:**
- Run: `node --test src/state/journal.test.js`
- Expected: PASS — round-trip + trigger rejection, crash-kill fuzz (≥50 SIGKILL iterations, committed-prefix invariant), corrupt-db fail-closed, version-skew refusal, migration fixture byte-equal payloads

- [ ] Tests FROM spec test list first · [ ] Implement · [ ] Acceptance · [ ] Commit

### Task 2: `src/state/lock.js` + `src/state/liveness.js`

**Wave:** 1 · **Blocks:** Task 3 · **Blocked by:** —

**Files:**
- Create: `src/state/lock.js`, `src/state/liveness.js` + colocated tests.

**Contract:** Per `spec/JOURNAL-V2.md` § Module layout: lock = existing hardlink semantics + lock content `{pid, pidStartTime, host, formatVersion}`, staleness = pid AND start-time (`/proc/<pid>/stat` field 22). Liveness = port of `lib/liveness` set/get/reap semantics, same sidecar path + VALID_STATES + flock file, plus `pgid` + `pidStartTime` fields (I5).

**Acceptance:**
- Run: `node --test src/state/lock.test.js src/state/liveness.test.js`
- Expected: PASS — pid-reuse simulation never steals a live lock; reap semantics match bash behavior fixtures

- [ ] Tests · [ ] Implement · [ ] Acceptance · [ ] Commit

### Task 3: Shims — `src/state/cli.js` + bash reduction

**Wave:** 2 · **Blocks:** Task 7 · **Blocked by:** Task 1, Task 2

**Files:**
- Create: `src/state/cli.js`; Modify: `lib/journal.sh`, `lib/liveness` → ≤10-line exec shims (write paths only; bash read paths may stay).

**Contract:** `spec/JOURNAL-V2.md` § Module layout + shims. Acceptance gate is the spec's: `lib/test-journal.sh` green with its file-content assertions rewritten to assert via `cli.js journal-export` (write-path call sites unmodified); liveness tests pass UNMODIFIED (liveness storage unchanged).

**Acceptance:**
- Run: `bash lib/test-journal.sh && bash run-tests.sh` (liveness section)
- Expected: green; journal test asserts via journal-export, liveness tests unmodified

- [ ] Implement · [ ] Acceptance · [ ] Commit

### Task 4: Intent registry + sweep (I3)

**Wave:** 2 · **Blocks:** Task 7 · **Blocked by:** Task 1

**Files:**
- Create: `src/state/intents.js` + colocated test.

**Contract:** `spec/JOURNAL-V2.md` § Intent records: `intent.start`/`intent.done`/`intent.swept` kinds, deterministic keys, per-op registry declaring `redrive` | `rollback`, unknown op in sweep → fail-closed escalate. Non-git side effects only.

**Acceptance:**
- Run: `node --test src/state/intents.test.js`
- Expected: PASS — orphaned intent per registered op resolves per registry; unknown op fail-closed

- [ ] Tests · [ ] Implement · [ ] Acceptance · [ ] Commit

### Task 5: V7 runner split — part 1 (scheduler, lease)

**Wave:** 3 · **Blocks:** Task 6 · **Blocked by:** —

**Files:**
- Create: `src/engine/scheduler.js`, `src/engine/lease.js`; Modify: `src/runner.js` (moves only).

**Contract (handoff plan PART VI V7):** PURE refactor — moves, zero behavior change. Public exports of `runner.js` preserved via re-exports (`src/control-api.js` imports them). One module per commit. Wave-readiness/dispatch logic → `scheduler.js`; task lease/branch/worktree lifecycle → `lease.js`.

**Acceptance:**
- Run: `bash run-tests.sh`
- Expected: full suite + chaos green with ZERO test-file changes

- [ ] Move scheduler, suite green, commit · [ ] Move lease, suite green, commit

### Task 6: V7 runner split — part 2 (gates, repair, ship)

**Wave:** 4 · **Blocks:** Task 7 · **Blocked by:** Task 5

**Files:**
- Create: `src/engine/gates.js`, `src/engine/repair.js`, `src/engine/ship.js`; Modify: `src/runner.js` → wiring + CLI only.

**Contract:** Same rules as Task 5. Gate execution → `gates.js`; repair ladder/quarantine → `repair.js`; land/ship invocation → `ship.js`.

**Acceptance:**
- Run: `bash run-tests.sh`
- Expected: green, zero test-file changes; `runner.js` contains no engine logic beyond wiring/CLI

- [ ] Move per module, suite green after each, commit each

### Task 7: Engine on v2 single-writer + land intent (I4)

**Wave:** 5 · **Blocks:** Task 8 · **Blocked by:** Task 3, Task 4, Task 6

**Files:**
- Modify: `src/runner.js`, `src/engine/ship.js` — all journal/liveness/lock IO through `src/state/*`; land path wrapped in intent.

**Contract:**
- Engine writes v2 records exclusively via `src/state/journal.js`; v1 journals migrate on open per spec (under run lock, once).
- Terminal run records (`run.done`, `run.paused`-ready kinds) append to the SLUG journal per `spec/JOURNAL-V2.md` § Scope.
- Land exactly-once per spec § land: `land.intent {integrationHead}` before `ship.sh land`; re-entry checks remote reachability → `land.done {verified: "already-landed"}` skip, else re-run. NEVER land without intent record.
- Structured stderr JSON on fatal exit (`spec/RESOLVER.md` § Failure input contract): last stderr line = `{failClass, scope, message, stack?}` on every non-zero fatal exit.

**Acceptance:**
- Run: `bash run-tests.sh` + chaos kill-matrix core
- Expected: green; fixture v1 journal migrates with `.v1.bak`; SIGKILL-between-land-intent-and-done resume converges without double land

- [ ] Tests · [ ] Implement · [ ] Acceptance · [ ] Commit

### Task 8: Ungate P2-state chaos assertions

**Wave:** 6 · **Blocks:** — · **Blocked by:** Task 7

**Files:**
- Modify: `test/chaos/scenarios/` — flip journal-integrity (C9, SQLite form), kill-matrix (core + land-intent), concurrency-suite assertions live per `spec/CHAOS-SUITE.md` phase map.

**Acceptance:**
- Run: `bash run-tests.sh`
- Expected: full suite + all ungated chaos green, zero unexplained warnings

- [ ] Ungate, run, commit
