# PR 1 — Durable execution authority

> Implement this PR in `juspay/odu`. This is an implementation handoff, not a request for another plan. Deliver complete native CLI behavior, packaging, migration, documentation, and tests in one self-contained PR. PR size is not a splitting criterion. Read repository instructions and current code first; preserve unrelated work. Do not merge or deploy without authorization.

Plan: https://odu-agent-spec.pages.dev/#composition  
Next release: https://odu-agent-spec.pages.dev/phases/2.md  
Design baseline: 2026-09-06; Odu `db12c8fa42f4a52debf18a6d783e2dda203973b0`. Paths below describe that baseline; locate their equivalents on your branch. This is an audit reference, not a required implementation checkout.

## Outcome and release boundary

Existing CLI users can address any run and attempt, react to a failure before other nodes finish, read evidence after teardown, and retry the correct recorded snapshot. This release must remain useful and complete if PR 2 never happens.

```text
native CLI → composition root → independently owned coordinator → workers
                 ↓                         ↓ single writer
           RunHistory reader         durable run catalog + evidence
```

PR 2 owns `odu web`, the browser, service Surface, generated service CLI, and MCP migration. Do not introduce public web placeholders in this PR. Preserve the existing MCP interface and existing native commands while moving their dependencies; do not remove a working client in anticipation of PR 2.

The motivating session review found prompt diagnosis once red actually reached agents, but frequent polling, buffered `tail` pipelines, waiting for all CI to settle, incomplete evidence, dead retry sockets, and masked shell exits. Do not assume agents are simply slow: fix the delivery and recovery contracts. Existing log barriers/reconnect fixes may already address some historical symptoms; preserve and reuse them.

## Start with the real code

Read `packages/run-client/README.md` and its closure tests; `src/coordinator/run.ts`, coordinator lifecycle/log/retry/shard tests; `src/common/runRecord.ts`; `src/cli`, `src/mcp`, `src/runner`, and `src/just`. Inspect `default.nix`, `nix/`, `shell.nix`, `scripts/hydrate-kolu-packages.sh`, `package.json`, `bunfig.toml`, `justfile`, `ci/mod.just`, and `tests/e2e/README.md` before changing packaging.

Inspect the Kolu source selected by Odu's actual npins dependency. The design inspected `f3ba6394b4ac667906190f2e5cda3b38ea871e47`; APIs may have moved. Current inspected Surface uses Effect RPC; stale guide references to oRPC are not authoritative.

Useful source references:

- https://github.com/juspay/kolu/tree/f3ba6394b4ac667906190f2e5cda3b38ea871e47/packages/surface
- https://github.com/juspay/kolu/tree/f3ba6394b4ac667906190f2e5cda3b38ea871e47/packages/surface-daemon-supervisor
- https://github.com/juspay/odu

## Packages and composition

```text
packages/
  run-client/   existing lean coordinator wire + dial
  run-history/  schema + store + attention replay
  execution/    coordinator, worker, DAG, leases, shards, verdict/posting
  cli/          native CLI/TUI and existing MCP presentation
src/
  main.ts         executable composition root
  runner-main.ts  worker composition root

execution → run-history → run-client
execution ──────────────→ run-client
cli → run-history + run-client
root → cli + execution; root injects execution entry/launch ports
```

- `run-client` remains a lean downstream package. Preserve exports and browser-safe schema entrypoints. No history, engine, UI, or MCP implementation dependencies.
- `run-history/schema` owns versioned run/attempt/cursor identities and record schemas. `run-history/store` owns durable storage operations, readers, and a shared attention reducer/query. Separate portable types from platform I/O.
- `execution` owns strict snapshots, just DAG, lane Surface, scheduling, partitions, leases, log-finalization barrier, verdict, and GitHub status posting. Remove imports of CLI rendering; inject a presentation/event sink. Domain classifications belong here or in shared schemas, not in CLI helpers.
- `cli` owns argument parsing, display, TUI, and observation. It never becomes a durable-store writer or second scheduler. Root-injected ports support native launch and finalized recovery without importing the engine into CLI.
- Root constructs and closes runtimes; worker root uses only the execution worker closure. Avoid a generic dumping-ground `shared` package.
- All workspace packages ship together. Update manifests, exports, TypeScript, lock/generated Nix data, binary entrypoints, and hydration. Keep one Effect instance and the existing isolated-linker convention. Do not add independent publishing/version machinery or new flake inputs.

The final PR 2 graph adds `service-client`, `service`, and `web-ui`; do not create empty packages for them now.

## Durable ownership and storage

Proposed logical layout (choose platform-appropriate per-user state roots and document them):

```text
user-state/odu/runs/RUN_ID/
  manifest.json                 run, snapshot, build, owner/endpoint identity
  events                        durable ordered journal
  attempts/ENCODED_NODE_KEY/N/log
checkout/.ci/...                 existing compatibility paths/aliases
```

| Fact | Authority |
| --- | --- |
| Snapshot, run identity, attempts, logs, events, verdict, posting debt | One coordinator per run |
| Native historical reads and attention | RunHistory reader/query |
| Scheduling, reset/dependency closure, recovery execution | Execution |
| Delivered cursor | Each caller separately |
| Interrupted unfinished record | Guarded recovery only after ownership is proven lost |

Register every coordinator in the catalog before executing, including native launches when no web daemon exists. PR 2 must discover runs from this catalog without scanning arbitrary checkouts. Use atomic publication, versioned records, encoded keys, and ownership fencing; PID/socket disappearance alone must not allow concurrent writers or fabricate a terminal result.

Make launch/retry request IDs and accepted receipts durable at their authority. Repeated IDs with identical input return the same accepted result; conflicting input refuses. PR 2's service receipt will reconcile against these identities. Test crashes around acceptance/spawn, not just concurrent calls in one process.

Old checkout records remain readable through a documented explicit import/migration path. New evidence survives checkout removal. Retain active records; default finalized retention is 30 days with run-addressed pruning and typed expiry. Missing snapshot inputs may prevent retry while logs remain readable. Do not silently erase legacy history or pretend missing artifacts exist.

## Complete native behavior

These are target UX examples; adapt grammar to established commands, document the final grammar, and test the real binary. Preserve existing command meanings and native exit behavior.

```sh
odu logs --run R --attempt 1 NODE
odu rerun --run R NODE
odu wait --run R --after C
```

### 1. Resumable attention

- Implement one durable attention query consumed by native CLI now and service later. Include run/SHA, scope, reason, settled/passed, cursor, included events, unresolved failures, and reporting debt.
- Default bounded observation deadline: 30 seconds. Return actionable red as soon as the log barrier completes; unrelated nodes keep running. Deadline means `still_running`, not pass or cancellation.
- Persist ordered event identity across disconnect/restart. Deliver failures that occur while the agent edits. Acknowledging a cursor only suppresses repeat delivery; it does not clear unresolved failures.
- Each concurrent caller owns its cursor. Advance only through included events; paginate with `has_more` and counts. A settled run always returns its terminal verdict. Wrong-run/expired cursors refuse with a resync route.
- Native machine-readable output must work without a TTY or line-buffering shell workaround. Define and test native wait exits explicitly; PR 2's generated Surface CLI has its own fixed framework exit discipline.

### 2. Durable diagnostics

- Address evidence by run, node/shard, and attempt. Old attempts are immutable after finalization; retries cannot overwrite them.
- Failure attention includes exit/signal, attempt and placement, log key, completeness, and a useful excerpt. Prefer reporter evidence; label tail fallback. Do not infer flakiness from missing evidence.
- Reuse the current log barrier. Publish complete diagnostics or explicitly record truncation when a producer is lost. Test late final bytes and noisy logs.
- Default attention domain payload budget: 16 KiB encoded, excerpts up to 4 KiB each. Bound and paginate, including multibyte text. Transport envelopes are outside this budget. Durable log reading uses byte offsets/limits and explicit EOF/completeness.

### 3. Correct recovery

| Request | Result |
| --- | --- |
| Live run, same snapshot | New selected attempt; reset affected dependants, preserve independent siblings |
| Finalized run, same snapshot | New linked selection run from recorded inputs; reacquire capacity |
| New commit | New explicit full run with expected SHA; supersede is explicit |

Choose live versus finalized retry atomically against settlement. Refuse stale expected attempts. Return an addressed receipt with effective run, attempts, roots, reset dependants, scope, SHA, and cursor. Reconcile lost replies by request ID rather than blindly repeating mutations.

Finalized replay uses recorded partitions and required dependency closure. Missing inputs require an explicit broader recovery choice; never substitute current HEAD. A passing selection does not rewrite its parent or imply full pipeline success. Keep GitHub posting debt distinct from test outcome. Release leases at completion; do not retain idle fleets just to keep `rerun` alive.

Share this policy through execution/launch ports; neither CLI nor the future service duplicates it.

### 4. Actionable startup

Validate checkout, expected SHA, host pool, and scoped configuration before accepting work. Reserve/register identity before slow provisioning. Return existing run ID/SHA for checkout conflicts; expose accepted queue/provisioning state. Recovery suggestions are structured data/argv, never strings to eval. A host-pool refusal identifies config source and valid scoped alternatives; do not modify global configuration as recovery.

### 5. Adoption and compatibility

Document the native start → bounded wait → diagnose → retry/new run → resume loop. Update relevant source documentation/skill examples in this PR so users can actually use its behavior. Preserve native foreground Ctrl-C/cancel semantics, existing logs/status/attach and MCP compatibility, strict snapshot rules, and remote runner packaging. Do not advertise future service commands.

## Process lifetime

Expose a `RunLauncher` port taking an immutable launch request and returning an addressed receipt. Root binds it to the packaged coordinator. Use Kolu's survivable spawn mechanism for independently owned coordinators; exercise it through the native path in this PR. A detached subprocess inside a service cgroup is not independently owned.

The inspected driver uses `systemd-run --user` when under a systemd service and detached spawn otherwise. Inspect the real current implementation, configure absolute executables/environment, and test applicable branches. Preserve native foreground cancel by explicit forwarding; observation disconnect and explicit cancellation must be distinguishable. Never kill guessed process names. Finalized records, real owner death, and transient connection loss remain distinct states.

## Build order inside this PR

1. Establish schema/ownership seams and package boundaries while keeping existing clients working.
2. Complete catalog, immutable attempts, journal, and native reads/waits.
3. Complete live/finalized recovery and independent lifecycle through shared ports.
4. Finish compatibility/migration, packaging, docs, and acceptance tests before exposing the release.

These are internal work steps, not separate partial releases.

## Acceptance gates

| Fixture | Required observable proof |
| --- | --- |
| Unit fails at ~8s; sibling ends ~90s | Native wait returns red with diagnostics before sibling settles |
| Second failure during disconnect; two callers | Each caller replays unseen events; unresolved red is retained |
| Deadline; settled red/green; wrong cursor/run | Correct typed state and documented native exits; no endless terminal wait |
| Noisy producer/late bytes; repeated retries | Complete or explicitly truncated evidence; immutable old logs |
| Live retry races settlement; duplicate request; lost reply | One accepted effective action and stable receipt |
| Finalized retry; shards and dependencies | Correct recorded inputs/closure and honest selection scope |
| Checkout deleted; artifacts expired; legacy records | Logs remain readable when retained; precise refusals/import behavior |
| Coordinator interruption/restart and spawn acceptance crash | No double owner/duplicate run; no fabricated pass |
| Native Ctrl-C, attach/status/logs, existing MCP | Compatibility preserved and lifecycle documented |
| Package closure and shipped binaries | No engine → CLI dependency; lean client; real packaged runner works |

Use actual event ordering assertions instead of flaky exact latency thresholds. Extend the existing black-box localhost fixtures and use real process boundaries for lifecycle/receipt tests. Test host-specific spawn branches where available and identify any unavailable platform evidence honestly.

Baseline verification entrypoints (inspect current recipes before running):

```sh
just typecheck
just test
just e2e
nix build .#odu .#odu-runner
# If lockfile changed:
just regenerate-bun-nix
# If Nix changed:
just fmt-check
```

Run applicable repository CI checks too. Nix e2e builds real binaries; do not substitute only in-process mocks. Avoid recursively launching expensive duplicate builds. Tests should target invariants and failures rather than mirror code structure.

## Completion handoff

Produce the self-contained implementation and a PR-ready description stating behavior, architecture, validation, migration, and any actual limitations. Include final exported ports/schema versions, storage/ownership rules, native command examples, and the exact merged/base commit PR 2 should consume. No unfinished user-facing path may depend on PR 2. Do not claim checks you could not run. If a material design assumption is false, document it and resolve the architecture coherently rather than adding a second engine or a stub.
