Context
burn summary today calls queryAll() from @relayburn/ledger, which streams the entire ledger.jsonl from disk and re-folds every stamp in memory on every invocation (packages/cli/src/commands/summary.ts:35, packages/ledger/src/reader.ts). PR #78 landed the derived archive at ~/.relayburn/archive.sqlite with materialized turns rows that already have stamp enrichment columns (workflow_id, agent_id, persona, tier) and indexes on ts, model, activity, project_key, and workflow_id — exactly the dimensions summary filters and aggregates on.
PR #78 explicitly defers this rewire:
Rewiring burn summary / compare / plans / @relayburn/mcp to read from the archive (each command is a self-contained migration that keeps the in-memory fallback intact).
Proposal
Replace the queryAll() call in runSummary with an archive-backed query:
- Call
buildArchive() after ingestAll() so the archive is current before we read.
- Add a small
summarizeFromArchive(query) helper to @relayburn/ledger that issues a single SQL SELECT ... FROM turns WHERE ... GROUP BY model (or the per-row projection summary needs) using the existing indexes.
- Keep the
queryAll() path behind a fallback flag (--no-archive or env RELAYBURN_ARCHIVE=0) so users can validate parity and we have an escape hatch if the archive is missing/corrupt.
- Subagent-tree and by-subagent-type modes still need per-turn rows; for those, either pull from the archive or fall back to
queryAll. The archive already materializes subagent_id / parent_subagent_id / subagent_type so the tree mode should work natively.
Tests:
- A summary fixture that asserts the archive-backed and ledger-backed code paths produce the same output (text +
--json) for a non-trivial mixed-stamp ledger.
- A test that the archive is auto-built if missing before the first
summary invocation.
- A test that
RELAYBURN_ARCHIVE=0 (or the equivalent flag) routes through queryAll unchanged.
Acceptance criteria
Out of scope
- Schema additions to the archive (this is a read-side migration).
- Coverage / fidelity columns (separate follow-up).
- Rewiring
compare, plans, or MCP tools (separate follow-ups).
Refs
Context
burn summarytoday callsqueryAll()from@relayburn/ledger, which streams the entireledger.jsonlfrom disk and re-folds every stamp in memory on every invocation (packages/cli/src/commands/summary.ts:35,packages/ledger/src/reader.ts). PR #78 landed the derived archive at~/.relayburn/archive.sqlitewith materializedturnsrows that already have stamp enrichment columns (workflow_id,agent_id,persona,tier) and indexes onts,model,activity,project_key, andworkflow_id— exactly the dimensionssummaryfilters and aggregates on.PR #78 explicitly defers this rewire:
Proposal
Replace the
queryAll()call inrunSummarywith an archive-backed query:buildArchive()afteringestAll()so the archive is current before we read.summarizeFromArchive(query)helper to@relayburn/ledgerthat issues a single SQLSELECT ... FROM turns WHERE ... GROUP BY model(or the per-row projection summary needs) using the existing indexes.queryAll()path behind a fallback flag (--no-archiveor envRELAYBURN_ARCHIVE=0) so users can validate parity and we have an escape hatch if the archive is missing/corrupt.queryAll. The archive already materializessubagent_id/parent_subagent_id/subagent_typeso the tree mode should work natively.Tests:
--json) for a non-trivial mixed-stamp ledger.summaryinvocation.RELAYBURN_ARCHIVE=0(or the equivalent flag) routes throughqueryAllunchanged.Acceptance criteria
burn summaryno longer scansledger.jsonlline-by-line on the hot path; it issues SQL againstarchive.sqlite.--json).--subagent-tree) and--by-subagent-typemodes work against the archive (or transparently fall back).--no-archiveflag or env) preserves the old behavior.burn summary --since 7dis at least 5x faster than the current ledger walk on the same machine. (Numbers in the PR description.)Out of scope
compare,plans, or MCP tools (separate follow-ups).Refs
burn archive(#40) #78 (Derived analytics archive: materialize the ledger into a local queryable store #40 follow-up)