tokenol 0.3.2__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tokenol-0.3.2 → tokenol-0.4.0}/.gitignore +1 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/CHANGELOG.md +55 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/PKG-INFO +31 -5
- {tokenol-0.3.2 → tokenol-0.4.0}/README.md +28 -3
- tokenol-0.4.0/docs/superpowers/plans/2026-05-02-persistent-history-pr1.md +3270 -0
- tokenol-0.4.0/docs/superpowers/plans/2026-05-03-cold-start-bench.md +822 -0
- tokenol-0.4.0/docs/superpowers/plans/2026-05-03-opt-in-persistence.md +1050 -0
- tokenol-0.4.0/docs/superpowers/specs/2026-05-02-persistent-history-design.md +273 -0
- tokenol-0.4.0/docs/superpowers/specs/2026-05-03-cold-start-bench-design.md +107 -0
- tokenol-0.4.0/docs/superpowers/specs/2026-05-03-opt-in-persistence-design.md +261 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/pyproject.toml +4 -2
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/__init__.py +1 -1
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/cli.py +22 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/ingest/discovery.py +31 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/model/events.py +1 -0
- tokenol-0.4.0/src/tokenol/persistence/__init__.py +1 -0
- tokenol-0.4.0/src/tokenol/persistence/flusher.py +112 -0
- tokenol-0.4.0/src/tokenol/persistence/forget_handoff.py +114 -0
- tokenol-0.4.0/src/tokenol/persistence/store.py +528 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/app.py +128 -3
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/prefs.py +4 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/session_detail.py +2 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/state.py +223 -19
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/static/breakdown.js +1 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/static/session.html +7 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/static/session.js +17 -7
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/streaming.py +103 -1
- tokenol-0.4.0/tests/test_ingest_discovery.py +52 -0
- tokenol-0.4.0/tests/test_persistence_flusher.py +87 -0
- tokenol-0.4.0/tests/test_persistence_forget_handoff.py +69 -0
- tokenol-0.4.0/tests/test_persistence_store.py +436 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/test_serve_app.py +82 -0
- tokenol-0.4.0/tests/test_serve_app_no_persist.py +96 -0
- tokenol-0.4.0/tests/test_serve_archived_session.py +95 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/test_serve_prefs.py +23 -2
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/test_serve_state.py +142 -1
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/test_serve_streaming.py +166 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/test_session_detail.py +18 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/uv.lock +6 -4
- {tokenol-0.3.2 → tokenol-0.4.0}/.claude/settings.local.json +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/.claudesignore +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/.github/workflows/ci.yml +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/docs/ASSUMPTIONS.md +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/docs/METRICS.md +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/docs/screenshots/breakdown_bottom.jpg +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/docs/screenshots/breakdown_top.jpg +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/docs/screenshots/main.jpg +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/docs/screenshots/project.jpg +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/docs/screenshots/session_bottom.jpg +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/docs/screenshots/session_top.jpg +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/docs/superpowers/plans/2026-04-24-breakdown-pr1.md +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/docs/superpowers/plans/2026-04-24-breakdown-pr2.md +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/docs/superpowers/plans/2026-04-24-breakdown-pr3.md +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/docs/superpowers/specs/2026-04-24-breakdown-tab-design.md +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/assumptions.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/enums.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/ingest/__init__.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/ingest/builder.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/ingest/parser.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/ingest/schema.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/metrics/__init__.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/metrics/context.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/metrics/cost.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/metrics/history.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/metrics/patterns.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/metrics/rollups.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/metrics/thresholds.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/metrics/verdicts.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/metrics/windows.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/model/__init__.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/model/pricing.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/model/registry.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/report/__init__.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/report/text.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/__init__.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/static/app.js +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/static/breakdown.html +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/static/chart.js +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/static/components.js +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/static/day.html +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/static/day.js +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/static/index.html +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/static/model.html +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/static/model.js +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/static/project.html +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/static/project.js +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/static/styles.css +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/static/tool.html +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/src/tokenol/serve/static/tool.js +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/conftest.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/fixtures/basic.jsonl +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/fixtures/dedup.jsonl +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/fixtures/gemini.jsonl +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/fixtures/interrupted.jsonl +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/fixtures/missing_ids.jsonl +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/fixtures/multi.jsonl +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/fixtures/sidechain.jsonl +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/test_cli.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/test_context_metrics.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/test_dedup.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/test_history_and_thresholds.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/test_metrics.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/test_parser.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/test_patterns.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/test_verdicts.py +0 -0
- {tokenol-0.3.2 → tokenol-0.4.0}/tests/test_windows.py +0 -0
|
@@ -4,6 +4,61 @@ All notable changes to tokenol are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow
|
|
5
5
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## 0.4.0 — 2026-05-03
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
- **Persistent history that survives JSONL deletion** (opt-in via
|
|
11
|
+
`--persist`). `tokenol serve --persist` backs the live in-memory
|
|
12
|
+
dashboard with a single-file DuckDB store at `~/.tokenol/history.duckdb`
|
|
13
|
+
(override via `TOKENOL_HISTORY_PATH`). On startup the store seeds the
|
|
14
|
+
in-memory hot tier so cold start is bounded by `hot_window_days`
|
|
15
|
+
(default 90), not by total history length. Each tick parses only JSONLs
|
|
16
|
+
whose `mtime_ns` exceeds the per-session high-water mark — typically
|
|
17
|
+
just today's active files — and appends derived turns to both memory and
|
|
18
|
+
a background batch flush (every 30 s or 100 turns). Deleting a JSONL no
|
|
19
|
+
longer drops its data from the dashboard; the affected sessions are
|
|
20
|
+
marked `archived=True` and continue to render every quantitative panel.
|
|
21
|
+
Only the per-turn modal's content snippets (user prompt, assistant
|
|
22
|
+
preview, tool-call list) become unavailable for archived sessions, in
|
|
23
|
+
line with the privacy intent of the deletion. Default off — `tokenol
|
|
24
|
+
serve` without `--persist` matches the v0.3.2 resource profile
|
|
25
|
+
byte-for-byte (no `import duckdb`, no `~/.tokenol/` directory, no extra
|
|
26
|
+
steady RSS).
|
|
27
|
+
- `Preferences.hot_window_days` (default `90`, accepted range `1..3650`),
|
|
28
|
+
exposed via the existing `/api/prefs` endpoint. Takes effect on next
|
|
29
|
+
startup.
|
|
30
|
+
- `Session.archived: bool` field surfaced through `/api/session/{id}` and
|
|
31
|
+
`/api/session/{id}/turn/{idx}`; the session-detail page renders an
|
|
32
|
+
amber "Archived — text snippets unavailable" badge and hides the
|
|
33
|
+
per-turn snippet block when the flag is set.
|
|
34
|
+
- `tokenol.persistence.forget_handoff` — a pidfile + atomic request-file
|
|
35
|
+
handshake so a future `tokenol forget` CLI (PR 2) can apply deletions
|
|
36
|
+
to a live serve within one tick, without requiring a restart.
|
|
37
|
+
|
|
38
|
+
### Changes
|
|
39
|
+
- `duckdb` moved from a core dependency to the new `[persist]` optional
|
|
40
|
+
extras group. Default `pip install tokenol` no longer pulls the DuckDB
|
|
41
|
+
binary wheel (~30 MB saved). Users who pass `--persist` install with
|
|
42
|
+
`pip install 'tokenol[persist]'`.
|
|
43
|
+
- `build_snapshot_full` now accepts optional `history_store` and
|
|
44
|
+
`flush_queue` arguments. When neither is supplied the legacy whole-corpus
|
|
45
|
+
derivation path is used unchanged, so CLI report commands and any
|
|
46
|
+
existing test that constructs a bare `ParseCache` keep working.
|
|
47
|
+
- Default mode prints a yellow `WARNING` at startup if it finds an existing
|
|
48
|
+
`~/.tokenol/history.duckdb` (or `TOKENOL_HISTORY_PATH`), prompting the
|
|
49
|
+
user to pass `--persist` if they want to use it (rather than silently
|
|
50
|
+
ignoring the file).
|
|
51
|
+
- `select_edge_paths` now tracks per-file `mtime_ns` instead of comparing
|
|
52
|
+
filesystem mtime to turn timestamps — fixes a freshness bug in the
|
|
53
|
+
store-backed snapshot path where backdated turn timestamps could silently
|
|
54
|
+
exclude files from re-parse.
|
|
55
|
+
|
|
56
|
+
### Notes
|
|
57
|
+
- See `docs/superpowers/specs/2026-05-03-opt-in-persistence-design.md` for
|
|
58
|
+
the gating-and-extras design.
|
|
59
|
+
- See `docs/superpowers/specs/2026-05-02-persistent-history-design.md` for
|
|
60
|
+
the underlying store design.
|
|
61
|
+
|
|
7
62
|
## [0.3.2] — 2026-04-28
|
|
8
63
|
|
|
9
64
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tokenol
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Audit Claude Code JSONL session logs: track cost, cache health, context blow-ups, and 5h-window pressure.
|
|
5
5
|
Project-URL: Homepage, https://github.com/farhanferoz/tokenol
|
|
6
6
|
Project-URL: Issues, https://github.com/farhanferoz/tokenol/issues
|
|
@@ -15,7 +15,6 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
16
|
Classifier: Topic :: Utilities
|
|
17
17
|
Requires-Python: >=3.10
|
|
18
|
-
Requires-Dist: duckdb>=0.10
|
|
19
18
|
Requires-Dist: rich>=13
|
|
20
19
|
Requires-Dist: typer>=0.12
|
|
21
20
|
Provides-Extra: dev
|
|
@@ -25,6 +24,8 @@ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
|
25
24
|
Requires-Dist: pytest-cov; extra == 'dev'
|
|
26
25
|
Requires-Dist: pytest>=8; extra == 'dev'
|
|
27
26
|
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
27
|
+
Provides-Extra: persist
|
|
28
|
+
Requires-Dist: duckdb>=0.10; extra == 'persist'
|
|
28
29
|
Provides-Extra: serve
|
|
29
30
|
Requires-Dist: fastapi>=0.111; extra == 'serve'
|
|
30
31
|
Requires-Dist: uvicorn[standard]>=0.29; extra == 'serve'
|
|
@@ -66,9 +67,9 @@ Project page — cache efficiency trend, verdict distribution, top turns:
|
|
|
66
67
|
|
|
67
68
|
## Install
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
pip install tokenol # CLI commands (daily, sessions, projects, ...)
|
|
71
|
+
pip install 'tokenol[serve]' # adds the live dashboard (tokenol serve)
|
|
72
|
+
pip install 'tokenol[serve,persist]' # adds DuckDB-backed history that survives JSONL deletion
|
|
72
73
|
|
|
73
74
|
Requires Python 3.10+. See [tokenol on PyPI](https://pypi.org/project/tokenol/).
|
|
74
75
|
|
|
@@ -152,6 +153,31 @@ The dashboard updates via SSE as Claude Code writes events to disk. The server g
|
|
|
152
153
|
|
|
153
154
|
If SSE delivery silently stalls (browser tab throttling, extension hooks, long-lived `EventSource` quirks), the client self-heals: it polls `/api/snapshot` every 30 s as a backstop, force-reconnects on tab-visibility return, and runs a 90 s staleness watchdog. `/api/snapshot` reuses the broadcaster's cached payload while an SSE group is live, so the backstop costs only a JSON serialize. Hover the live-status dot for a "last update Ns ago" indicator.
|
|
154
155
|
|
|
156
|
+
### Persistent history (opt-in)
|
|
157
|
+
|
|
158
|
+
By default, `tokenol serve` parses your `~/.claude*/projects/**/*.jsonl` files into an in-memory model on each restart — fast, but the dashboard loses any session whose JSONL has been deleted or rotated.
|
|
159
|
+
|
|
160
|
+
Pass `--persist` to enable a DuckDB-backed history store at `~/.tokenol/history.duckdb` (override with `TOKENOL_HISTORY_PATH`). The store contains **no message content** — only token counts, costs, models, timestamps, tool counts, and session metadata, comparable to a billing receipt. With persistence on:
|
|
161
|
+
|
|
162
|
+
- **Deleting a JSONL no longer drops it from the dashboard.** Quantitative panels render as before; only the per-turn modal's verbatim content snippets become unavailable, indicated by an "Archived — text snippets unavailable" badge. Metrics survive; words don't (matching the privacy intent of the deletion).
|
|
163
|
+
- **Restart picks up where you left off.** A background flusher batches writes (every 30 s or 100 turns, whichever first) and force-drains on graceful shutdown. The JSONLs remain the durable substrate — a process crash mid-flush loses nothing because the next start re-derives the missing window from the JSONLs (idempotent on `message.id:requestId`).
|
|
164
|
+
- **Cold start stays bounded.** The hot tier loads only the last `hot_window_days` of turns (default 90, tunable via `/api/prefs`); older history is read on demand from the warm tier.
|
|
165
|
+
|
|
166
|
+
Measured cost on the author's full `~/.claude*` corpus (~1820 files, ~2 GB of JSONLs, page cache cold both runs):
|
|
167
|
+
|
|
168
|
+
| | Default mode | `--persist` first start | `--persist` subsequent starts |
|
|
169
|
+
|---|---|---|---|
|
|
170
|
+
| Time to first paint | ~5 s | ~12 s | ~12 s |
|
|
171
|
+
| Wall to settle | ~5 s | ~4 min (one-time backfill) | <30 s |
|
|
172
|
+
| Steady RSS | ~250 MiB | — | ~750 MiB |
|
|
173
|
+
| Durable disk | 0 | ~40 MB after backfill | grows incrementally |
|
|
174
|
+
|
|
175
|
+
Requires the persist extras (`pip install 'tokenol[serve,persist]'`).
|
|
176
|
+
|
|
177
|
+
See `docs/superpowers/specs/2026-05-03-opt-in-persistence-design.md` for design rationale and `docs/superpowers/specs/2026-05-02-persistent-history-design.md` for the underlying store design.
|
|
178
|
+
|
|
179
|
+
### Main dashboard
|
|
180
|
+
|
|
155
181
|
Main page layout (top to bottom):
|
|
156
182
|
|
|
157
183
|
| Panel | What it shows |
|
|
@@ -33,9 +33,9 @@ Project page — cache efficiency trend, verdict distribution, top turns:
|
|
|
33
33
|
|
|
34
34
|
## Install
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
pip install tokenol # CLI commands (daily, sessions, projects, ...)
|
|
37
|
+
pip install 'tokenol[serve]' # adds the live dashboard (tokenol serve)
|
|
38
|
+
pip install 'tokenol[serve,persist]' # adds DuckDB-backed history that survives JSONL deletion
|
|
39
39
|
|
|
40
40
|
Requires Python 3.10+. See [tokenol on PyPI](https://pypi.org/project/tokenol/).
|
|
41
41
|
|
|
@@ -119,6 +119,31 @@ The dashboard updates via SSE as Claude Code writes events to disk. The server g
|
|
|
119
119
|
|
|
120
120
|
If SSE delivery silently stalls (browser tab throttling, extension hooks, long-lived `EventSource` quirks), the client self-heals: it polls `/api/snapshot` every 30 s as a backstop, force-reconnects on tab-visibility return, and runs a 90 s staleness watchdog. `/api/snapshot` reuses the broadcaster's cached payload while an SSE group is live, so the backstop costs only a JSON serialize. Hover the live-status dot for a "last update Ns ago" indicator.
|
|
121
121
|
|
|
122
|
+
### Persistent history (opt-in)
|
|
123
|
+
|
|
124
|
+
By default, `tokenol serve` parses your `~/.claude*/projects/**/*.jsonl` files into an in-memory model on each restart — fast, but the dashboard loses any session whose JSONL has been deleted or rotated.
|
|
125
|
+
|
|
126
|
+
Pass `--persist` to enable a DuckDB-backed history store at `~/.tokenol/history.duckdb` (override with `TOKENOL_HISTORY_PATH`). The store contains **no message content** — only token counts, costs, models, timestamps, tool counts, and session metadata, comparable to a billing receipt. With persistence on:
|
|
127
|
+
|
|
128
|
+
- **Deleting a JSONL no longer drops it from the dashboard.** Quantitative panels render as before; only the per-turn modal's verbatim content snippets become unavailable, indicated by an "Archived — text snippets unavailable" badge. Metrics survive; words don't (matching the privacy intent of the deletion).
|
|
129
|
+
- **Restart picks up where you left off.** A background flusher batches writes (every 30 s or 100 turns, whichever first) and force-drains on graceful shutdown. The JSONLs remain the durable substrate — a process crash mid-flush loses nothing because the next start re-derives the missing window from the JSONLs (idempotent on `message.id:requestId`).
|
|
130
|
+
- **Cold start stays bounded.** The hot tier loads only the last `hot_window_days` of turns (default 90, tunable via `/api/prefs`); older history is read on demand from the warm tier.
|
|
131
|
+
|
|
132
|
+
Measured cost on the author's full `~/.claude*` corpus (~1820 files, ~2 GB of JSONLs, page cache cold both runs):
|
|
133
|
+
|
|
134
|
+
| | Default mode | `--persist` first start | `--persist` subsequent starts |
|
|
135
|
+
|---|---|---|---|
|
|
136
|
+
| Time to first paint | ~5 s | ~12 s | ~12 s |
|
|
137
|
+
| Wall to settle | ~5 s | ~4 min (one-time backfill) | <30 s |
|
|
138
|
+
| Steady RSS | ~250 MiB | — | ~750 MiB |
|
|
139
|
+
| Durable disk | 0 | ~40 MB after backfill | grows incrementally |
|
|
140
|
+
|
|
141
|
+
Requires the persist extras (`pip install 'tokenol[serve,persist]'`).
|
|
142
|
+
|
|
143
|
+
See `docs/superpowers/specs/2026-05-03-opt-in-persistence-design.md` for design rationale and `docs/superpowers/specs/2026-05-02-persistent-history-design.md` for the underlying store design.
|
|
144
|
+
|
|
145
|
+
### Main dashboard
|
|
146
|
+
|
|
122
147
|
Main page layout (top to bottom):
|
|
123
148
|
|
|
124
149
|
| Panel | What it shows |
|