# Context ledger — JIT plan (spt's durable context for one OMP session)

Requirement: `REQ-CONTEXT-LEDGER` (traceable-reqs.toml). Operator design, 2026-09-06.
Release: omp-spt 0.9.0.

## Why

OMP's `context` hook is `transformContext`, applied inside `prepareProviderCall` to build ONE
provider request; its output is never written back to the session store. The
`before_agent_start` system-prompt override is discarded when the turn ends
(`agent-session.ts`: "The per-turn before_agent_start override lives only for this turn";
`clearTurnSystemPromptOverride()` in the `finally`). So everything omp-spt added to context
that way — the startup brief, the durable mind on resume, every now-signal (turn-start and
boundary), every delivery polled while busy — was seen for one turn or one request and then
gone. claude-spt's hook `additionalContext` lands in the transcript and persists; omp-spt's
did not, and `docs/PARITY.md` did not say so. Hazard #10 (delivered bodies vanishing after the
first request) was the first symptom of the same fact; the ledger generalises its fix.

## Design (operator ruling: no curation among now-signal kinds — order and timing ARE the memory)

- **One block, last message of every provider request** of an activated session, in-turn and
  off-turn: hidden custom message `spt-context-ledger`, re-spliced by the `context` hook
  (idempotent; a stale copy is dropped first). Tail placement keeps the transcript prefix
  cache-stable; the system prompt would invalidate the provider cache for the whole session on
  every change.
- **Head** (persists across resets): the durable mind (resume pull, prepended when it resolves),
  then the startup brief. Seeded at activation.
- **Log**: ordered, UTC-timestamped entries for everything spt adds: `now-signal` (every kind,
  verbatim, turn-start and boundary alike), `delivery` (arrival time + sender; the body is
  durable via the stub — hazard #10), `delivery (polled while busy)` (the envelopes verbatim —
  they had no durable home), `reset`.
- **Bound**: 32 KB by default (`contextLedgerBytes`), oldest evicted first with a visible
  count; a single entry is clipped at 16 KB.
- **Reset**: `session_compact` clears the log (head stays) and records the reset as the first
  entry. A new session gets a new ledger at activation.
- The per-request `spt-now-signal` and `spt-event` messages are gone: the ledger carries them.
- The system-prompt override is no longer used at all.

## Tasks

- [x] impl: ledger helpers (`createContextLedger`, `recordContext`, `resetContextLedger`,
      `renderContextLedger`, `withContextLedger`) + wiring, tag `[impl->REQ-CONTEXT-LEDGER]`.
- [x] unit: persists across turns; head order (mind, brief); eviction with count; compaction
      reset keeps the head; polled deliveries and both now-signal paths logged; idempotent
      re-splice. Tag `[unit->REQ-CONTEXT-LEDGER]`. Existing brief/now-signal/resume tests
      rewritten to read the ledger instead of the system prompt.
- [x] doc: `docs/PARITY.md` section (durability gap and its closure, token budget), hazard
      note in `docs/KNOWN-HAZARDS.md` (#18: per-request injections are ephemeral).
- [x] gates green; 0.9.0 bump; build; publish; field check on hertz (ledger visible in a probe
      turn, entries across two turns, reset after a checkpoint).

## Gate

Build + `traceable-reqs check` green; field check recorded in PARITY.md.
