session-chunks.md
docs/session-chunks.md
Daily session chunks — .tesserae/session_chunks.db
<!-- translations:start -->
한국어 · 中文 · 日本語 · Русский · Español · Français · Deutsch
<!-- translations:end --> Windowed session queries — tesserae summary, tesserae decisions, and the ask planner's activity actions — used to re-parse every in-window Claude Code / Codex transcript on every call. The daily chunk store persists each normalised turn once, bucketed by KST day label, so a fully covered past day is served from SQLite instead of a raw rescan. Measured on a real multi-thousand-session corpus this makes windowed summaries ~20x faster.
The store is one SQLite file, .tesserae/session_chunks.db (WAL, short-lived connection per operation): a turns table indexed by day, a day_coverage table recording which (day, harness) pairs are complete, and a meta table with the schema version.
What writes it
- Live — the engine tailer. While
tesserae engineruns, the session tailer appends turns to the store as it tails them, per poll, and upserts coverage for the affected days (source: "tailer"). The write path is append-only, idempotent against re-delivered turns, and never raises into the daemon loop. There is deliberately no SessionEnd hook writer — backgrounded SessionEnd writers pile up (a recorded failure mode). - Backfill. Two entry points walk existing transcripts and fill history (
source: "backfill"): tesserae refreshruns a backfill automatically as part of its sessions-import step, so the first refresh after upgrading populates the store with no extra action.tesserae sessions chunk-backfill [--since YYYY-MM-DD]runs it explicitly;--sincebounds how far back to walk (default: full history).
Backfill takes a non-blocking flock on .tesserae/session_chunks.lock with skip-if-held semantics — a concurrent backfill (or an engine already holding it) makes the second caller skip cleanly instead of queueing. Backfill upserts are keyed on (session_path, ts, role, hash(text)), so tailer rows and backfill rows never duplicate each other. A one-day overlap on incremental backfills heals turns that landed after a day's coverage was first claimed.
What reads it
The fast path lives at the single scan choke point (activity_summary.iter_project_transcripts / scan_messages), so everything downstream inherits it transparently:
tesserae summary(including its embedded decisions gathering)tesserae decisionstesserae ask— the planner'sactivity_summary/decisionsactions- MCP
activity_summaryandquery_decisions - the live-sessions view
Coverage rule: today is always raw-scanned
A window is served from chunks only when all of the following hold:
- it is an exact KST-aligned single day;
- that day is strictly before today — today is still being written, so it always takes the raw transcript scan;
- a
day_coveragerow exists for every requested harness on that day.
Anything else falls back to the raw scan for that window.
The raw-scan fallback guarantee
The chunk store is an accelerator, never a source of truth:
- Any DB error, a missing/corrupt file, or a
schema_versionmismatch yields nothing from the chunk path — the caller's raw transcript scan proceeds exactly as before. A schema mismatch drops and rebuilds the store empty; coverage vanishes with it, so the fallback stays correct. - Days without coverage (for example, the engine wasn't running and no backfill has happened) silently take the slow path. Correct, but the speedup disappears —
tesserae doctorreports coverage gaps in the recent window and points attesserae sessions chunk-backfill(see doctor.md). - Parity invariant: for a fully covered day, chunk-served turns are equal to what the raw scan would have produced (same timestamp, role, name, text, session key, and harness).
Operational notes
- Keep
tesserae enginerunning and past days stay covered live; otherwise an occasionaltesserae refresh(or explicitchunk-backfill) closes the gaps. - The store is per project, lives under
.tesserae/, and can always be deleted safely — the next backfill rebuilds it, and readers fall back to raw scans in the meantime.