Expand description
Task 35 — opt-in session journal + periodic atomic snapshots (spec §9.8; decision record: docs/decisions/T35-session-journal-opt-in.md).
The DEFAULT persistence (SessionPersistence::Json) is byte-for-byte the
legacy path: every save atomically rewrites sessions/<id>.json. The
opt-in Journal mode is purely ADDITIVE: the snapshot file keeps the
unchanged legacy Session schema, and an append-only sessions/<id>.journal
(JSONL, schema v1) carries the deltas since the snapshot, so a steady-state
save costs O(delta) instead of O(total history).
Replay is IDEMPOTENT by construction — msg records carry absolute
history indices (i == len appends, i < len skips, i > len stops at
the last consistent prefix) and meta records only apply when their
updated_at is not older than the loaded state. Torn tails (kill during
append), stale journals (kill between snapshot and journal reset), and
manual journal deletion therefore all recover to a consistent session.
Everything here writes through the T4 private_fs helpers: 0700 dirs,
0600 files, symlink-refusing, atomic snapshot replacement, synced appends.
Structs§
- Journal
Meta Tail - Freshest journaled metadata, read from a bounded tail window — lets session listings stay accurate without parsing the full journal.
- Save
Receipt - Machine-readable receipt for benchmarks and delta-proportionality tests.
- Session
DirEntry - One strictly-resolved directory entry for session listings.
Enums§
- Save
Mode - How a save landed on disk.
- Session
Persistence - Which on-disk persistence strategy
Session::saveuses.
Constants§
- JOURNAL_
SCHEMA_ VERSION - Journal record schema version (line-level
"v"field). - JOURNAL_
SNAPSHOT_ MIN_ BYTES - Journal size floor before a periodic snapshot is forced.
- JOURNAL_
SNAPSHOT_ RATIO - A snapshot is due when the journal outgrows
snapshot / RATIO(bounded write amplification: large sessions stretch the threshold proportionally). - MAX_
PERSISTED_ READ_ BYTES - Hard cap on any single persisted-session artifact read (snapshot or journal). Reads are bounded on the OPENED handle; an artifact past the cap is refused rather than slurped.
Functions§
- delete_
session_ files_ in_ dir - Remove a session’s snapshot AND journal (compaction rollback, retention). Idempotent — missing files are not errors.
- journal_
meta_ tail - Freshest
metarecord from a bounded journal tail window, for listing freshness without a full journal read.Nonewhen no journal, no complete supported-version meta record, or a refused (non-confined) artifact — a symlinked journal discloses nothing. - journal_
path sessions/<id>.journal— single-extension name so the session id stays the file stem (chain-head protection and retention pair on the stem).- load_
session_ in_ dir - Load
<id>.jsonand, when a journal exists, replay it idempotently. Old-format sessions (no journal) load exactly as before; a session with a torn or stale journal recovers to its last consistent state. Both reads are confined nofollow bounded handle reads (fix1 I1). - save_
session_ in_ dir - Persist
sessionintodirunder the given persistence mode. - session_
dir_ entries - Enumerate the sessions dir through the SAME strict handle resolution as
every other T35 operation (fix2): handle-relative
readdir+fstatat(AT_SYMLINK_NOFOLLOW)— a symlinked ancestor refuses, a missing dir lists empty, and entry mtimes are of the entries themselves, never symlink targets. - snapshot_
due - Snapshot threshold: journal bytes ≥ max(256 KiB, snapshot / 4).