> ## Documentation Index
> Fetch the complete documentation index at: https://www.plungeai.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Session ledger and export

> A long session would grow to a huge, ever-slower context. oceancode avoids that with the session ledger: the model sees a small, fixed window of recent turns, and everything older is kept verbatim in Markdown files beside the session — never summarized away.


A long session would grow to a huge, ever-slower context. `oceancode` avoids that with the
**session ledger**: the model sees a small, fixed window of recent turns, and everything older
is kept verbatim in Markdown files beside the session — never summarized away.

The full concept is in SESSION-LEDGER-CONCEPT.md (`apps/ocean-coding-agent-rust/docs/SESSION-LEDGER-CONCEPT.md` in the repository); this page is
the operator's view.

## What the ledger is

- **A window of the raw conversation.** The model always sees the newest part of the real
  conversation, up to a bounded size — by default **40% of the model's context** (400k on a 1M
  model, 80k on a 200k model). The window is measured in the provider's real tokens after every
  step, and the oldest content leaves it one **block** at a time — inside a long turn
  too (see **The tideline** below). `tokens: 0` instead tracks the model's own ceiling —
  its context window minus the output allowance; 120k only when the route reports no limit. A
  *turn* is your message plus the model's full answer, tool outputs included.
- **Per-turn transcripts on disk.** Every step is written verbatim to its turn's transcript file
  as it ends, and the index files are updated when the turn completes. As the window grows the
  oldest blocks leave it — they are already on disk, so nothing is lost and no model call is made.
  The step's write happens *behind* the step: the next step does not wait for it. `recall` and
  the session meter **settle** the ledger first (they wait for every queued write), so they
  always see the step that just ended. A plain `read` of a transcript file does not wait: each
  append replaces the file atomically, so a read is never torn — at most it is one step behind.
  A pruned tool output is only ever pointed at a transcript line that is already written.
- **`SESSION.md`, one living page.** Goal, decisions, environment facts, errors and their
  fixes, a code map, open questions, and the next step — each line stamped with the turn it came
  from. Facts the database already holds (files touched, commands and exit codes, tasks, the
  log) are written by the engine. The page is *edited*, never rewritten, and by one writer;
  reversed decisions keep the old text struck through rather than deleted.

The ledger ships **enabled** (`ledger.enabled: true`), so every session is laddered from its first turn with no setup; its per-turn transcripts are kept as long as the session is: `retention.days` (default 7 days idle) removes a session and its ledger directory together — see [18 · Operations](/oceancode/operations#retention-and-vacuum). See [02 · Configuration](/oceancode/configuration) to change either. **OceanCode never summarizes a session** — the legacy compaction (summarization) path runs only when `ledger.enabled` is `false`.

**How an open session is stored.** An open session lives in memory: every step reads its history from there, and its rows in `oceancode.db` are written *behind* the step, in batches, by the one database writer — a step never waits on the disk, however long the session grows. Anything that reads the database rather than the open session (an export, opening a session that already left memory, the retention sweep) first **settles** the writer: it waits for every queued write, so it sees the step that just ended. A session idle for 15 minutes leaves memory and is reloaded from the database on its next use. **What a crash loses:** only the writes still queued when the process died — normally the last few milliseconds (`oceancode_writer_lag_ms` on the metrics endpoint, see [18 · Operations](/oceancode/operations#metrics-oc_metrics_addr)). What is on disk is always an in-order prefix of the session — no gap in its event log, no row ahead of the event it came from — so a resume continues from the last committed step.

Find a session's ledger directory with:

```bash
oceancode ledger path <session-id>
```

## The tideline — `/tideline`

You set **one number**: the share of the model's context where the tideline sits
(`ledger.tideline.percent`, default `40`) — the **line**. After every step the ledger reads the
window's size in the provider's real tokens (the **gauge**: input + cache read + cache write of the
step that just ended, plus what that step added — its output and tool results; with `compress` on, plus the
compressor's saved estimate, removed bytes ÷ 4, so the gauge still measures the uncompressed window). Steps are grouped
into **blocks**: a block closes once the gauge has grown by `ledger.tideline.block` since it opened — a
token count, or a share of the line written `"35%"` (unset = 70% of the line, so 70 000 at a
100 000-token line). While the gauge is at or over the line, the oldest closed block leaves the
window, so the window drops to about line − block and grows again. The block still being filled is
never cut, so the window overshoots the line by at most one block. A single turn larger than the
line is cut block by block while it runs; your message in the oldest block leaves with it.

After a cut the window opens with one line that points at the ledger:
`[Earlier content of this session is past the tideline: every row up to seq <n> left this window. The
verbatim record is <ledger>/transcript/ (one file per turn) and SESSION.md carries the merged state. …]`.
A turn whose every row is past the tideline is merged into `SESSION.md`; a turn cut only in part is
not. If the provider still refuses a request as too long, every closed block is cut before the one
retry.

Between cuts every step only appends, so the provider's prompt cache hits; each cut re-writes the
retained window once. The block size sets how often: a 50k block cuts about 20 times per 1M new
tokens past the line, a 20k block 50 times. When the model's context is unknown the fixed `tokens`
(default `300000`) is the line. A line above what the model can hold (its context minus the output
allowance, at most 32k) is clamped to that, with one note in the log.

The status bar's context row measures against the model's own context (`…/1.0M` on
`claude-fable-5-1[1m]`); the `┃` on its gauge sits at the tideline — `tideline @40% (400k): 238k to go`, or `tideline
@300k: 138k to go` under a fixed limit — and it reads `tideline off` when the ledger is disabled.

Config examples (`ledger.shelve` and `ledger.window` are still read as aliases):

```json
{ "ledger": { "tideline": { "percent": 50 } } }
{ "ledger": { "tideline": { "tokens": 500000, "block": 20000 } } }
```

`tokens: 0` means the model's own ceiling. The older keys — the floor (`low`), `reserve`, the
`profile` presets (`wide` · `narrow` · `strict` · `auto`) and `ledger.hard_tokens` — were removed
with the band geometry: an old config still loads, they are ignored, and the load logs one warning
naming them.

Change the tideline for a session at runtime with `/tideline`:

| Command | Effect |
|---|---|
| `/tideline` | Show the current tideline state. |
| `/tideline now` | Move the tide now (a cut). |
| `/tideline 50%` | Set the limit as a percent of the model's context. |
| `/tideline 500k` | Set a fixed limit. |
| `/tideline default` | Restore the configured limit. |

Minimum fixed limit 20k; `k` and `m` suffixes are accepted. The floor and the presets were removed;
a second number or a preset word is refused with the usage line. A change applies from your next
message on, which the confirmation says. Headless, the same set is
`--tideline "<percent>% | <limit> | default"`. A sub-agent (child) session inherits its parent's
`/tideline` override — the percentage, or the limit.

The meter's denominator is the model's context window under `auto` and the effective window
otherwise.

## The session meter

Ocean pager has no cockpit panel; its status bar and `/context` show the session meter:

- the **context meter** — what the current window costs against the session's window size;
- **token counts** — input and output for the session;
- **spend** — `$spent / $cap` on a metered route. On the Claude Pro/Max subscription route the
  bill is a list-price *estimate*, shown as `≈$spent list · subscription`; a `limits` row
  appears when a step or time bound is set.

On the Claude Code route the meter's **context** number is Claude Code's own real context — its
system prompt, `CLAUDE.md` files and MCP schemas, ~100k+ before any turn. When it reaches the limit
OceanCode drops the `--resume` id and cold-starts Claude Code with the ledger page plus the retained
turns' text (tool outputs are not re-sent — the page's links carry them), so the number restarts near
that baseline and climbs again.

## Recovering a step — `/undo` and `/redo`

`/undo` (`/rewind`) reverts the conversation to a previous message and puts back every file that
turn and the later ones changed through the file tools (`edit`, `write`, `apply_patch`, sub-agents'
included); `/redo` re-applies both. It steps a bookmark through the session, so you can back out a
bad turn without losing the history. As in Claude Code, changes a `bash` command made are not
tracked.

## Exporting a session

Write a whole session out as one file:

```bash
oceancode export <session-id> --md   [--out <path>|-]   # one Markdown document
oceancode export <session-id> --atif [--out <path>|-]   # one ATIF-v1.8 trajectory (JSON)
```

Pass exactly one of `--md` or `--atif`. `--out -` (the default) writes to stdout; `--out <path>`
writes a file. The Markdown export carries frontmatter, every turn, and a `## Summary`
table, and is built from the ledger's own transcript files when they exist.

## Rebuilding the ledger

If a ledger directory is lost or a session predates the ledger, rebuild it from the database:

```bash
oceancode ledger rebuild <session-id>            # mechanical: transcripts, indexes, page
oceancode ledger rebuild <session-id> --merge    # also fold the merge backlog (model calls; asks y/N)
oceancode ledger rebuild <session-id> --merge --yes   # skip the confirmation
```

The plain rebuild makes no model calls; `--merge` does, so it is never implied.

## See also

- [02 · Configuration](/oceancode/configuration) — the `ledger` and `database` config sections.
- [08 · Bounding a long run](/oceancode/bounding-a-long-run) — spend and clock bounds.
