instar 1.3.637 → 1.3.638

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.
@@ -0,0 +1,97 @@
1
+ # Side-Effects Review — Bounded Accumulation Increment 2a: token-ledger SQLite retention
2
+
3
+ **Slug:** `bounded-accumulation-increment-2-token-ledger` · **Tier:** 2 (spec-driven; the
4
+ Bounded Accumulation spec converged + operator-approved, merged in #1241)
5
+ **Spec:** `docs/specs/bounded-accumulation-standard.md` §4 (SQLite class) + §6 D1.
6
+
7
+ ## Summary of the change
8
+
9
+ Retrofits the single largest unbounded store — `token-ledger.db` (256MB, the lone SQLite
10
+ store with no retention; ResourceLedger already prunes) — with the standard's bounded
11
+ retention, behind a dark-by-default config flag:
12
+ - `TokenLedger`: `auto_vacuum=INCREMENTAL` pragma (set before table creation, so it takes
13
+ effect on FRESH DBs and is a safe no-op on the existing un-converted file); `pruneOlderThan`
14
+ (BATCHED, bounded-per-call DELETE so a huge backlog never blocks the loop in one statement);
15
+ `incrementalVacuum` (reclaim freed pages); `pruneToRetention` (no-op when disabled).
16
+ - `TokenLedgerPoller`: drives `pruneToRetention` on a 6h SUB-cadence (the scan stays 60s),
17
+ off the scan path; a reported backlog (`more`) drains across subsequent ticks.
18
+ - `InstarConfig.storage.retention.tokenLedger` (`enabled` default false, `maxAgeMs` default 30d).
19
+ - `AgentServer` wires the config to the ledger.
20
+
21
+ ## Decision-point inventory
22
+
23
+ Frozen in spec §6: D1 token-ledger window = 30 days (default), SQLite mechanics =
24
+ `auto_vacuum=INCREMENTAL` + batched delete + incremental_vacuum off the request path on a 6h
25
+ timer (mirrors the real feature-metrics prune). No decision is introduced at the callsite.
26
+
27
+ ## 1. Over-block / data loss
28
+
29
+ The prune deletes token_events older than `maxAgeMs` (default 30d). token-ledger is read-only
30
+ observability, re-derivable from Claude Code transcripts (the §5 re-derivability caveat). Ships
31
+ DARK (`enabled` defaults false) — zero deletion until an operator opts in, so there is no
32
+ surprise data loss. Batched + bounded so it never blocks the event loop.
33
+
34
+ ## 2. Under-block
35
+
36
+ Disabled by default → no enforcement until opted in (intentional dark rollout, spec §6). The
37
+ existing 256MB file's DISK is not reclaimed until the one-time Increment-3 VACUUM converts it to
38
+ auto_vacuum; until then the prune still bounds the ROW COUNT (stops further growth). Stated, not
39
+ hidden.
40
+
41
+ ## 4. Signal vs authority
42
+
43
+ No gate/block. The retention is housekeeping driven by the existing poller cadence; it takes no
44
+ destructive action beyond deleting aged observability rows it owns. Fail-open throughout
45
+ (`@silent-fallback-ok` on every prune/vacuum catch — a failed prune leaves rows for the next
46
+ tick, never throws into the poller).
47
+
48
+ ## 5. Interactions
49
+
50
+ `pruneToRetention` is a no-op when disabled, so the poller's new sub-cadence call is inert on
51
+ every current agent (flag defaults false). The auto_vacuum pragma is a no-op on the existing
52
+ 256MB file (only fresh DBs convert). No existing TokenLedger query/insert path is changed. The
53
+ poller's scan cadence is unchanged (prune rides a separate 6h sub-cadence).
54
+
55
+ ## 6. External surfaces
56
+
57
+ No new route. One new config field (`storage.retention.tokenLedger`), additive + optional. The
58
+ `/tokens/*` routes are unaffected (same data, just aged-out beyond the window when enabled).
59
+
60
+ ## 6b. Operator-surface quality
61
+
62
+ N/A — no operator/dashboard/approval surface. Enabling is a config edit (a future increment may
63
+ add a dashboard toggle).
64
+
65
+ ## Framework generality
66
+
67
+ N/A — TokenLedger is framework-agnostic observability (scans Claude Code + Codex transcripts);
68
+ not part of the session launch/inject abstraction.
69
+
70
+ ## 7. Multi-machine posture
71
+
72
+ token-ledger.db is `derived-cache` / machine-local by design (each machine scans its own
73
+ transcripts). Retention is per-machine; no replicated state. Safe on single- and multi-machine.
74
+
75
+ ## 8. Rollback cost
76
+
77
+ Trivial: the feature is dark (default false), so reverting is removing an unused code path. An
78
+ operator who enabled it sets `enabled:false` to stop pruning (already-deleted rows are
79
+ re-derivable from transcripts within their own retention). The auto_vacuum pragma is harmless
80
+ (no-op on existing files).
81
+
82
+ ## Evidence pointers
83
+
84
+ - `tests/unit/token-ledger-retention.test.ts` (3): prunes-old/keeps-new; no-op-when-disabled;
85
+ batched prune reports `more` + a follow-up call drains the rest. (CI-run: better-sqlite3 ABI
86
+ is rebuilt for CI's Node; these can't execute in a dev worktree on a mismatched Node — the
87
+ existing token-ledger.test.ts has the same constraint.)
88
+ - `tests/unit/token-ledger-poller-retention.test.ts` (3, run locally with a fake ledger): prunes
89
+ on the sub-cadence not every scan; drains a backlog while `more`; a prune error is reported and
90
+ never throws out of the tick (fail-open).
91
+ - Existing `token-ledger-poller-idle.test.ts` (3) still green → no regression to the cadence.
92
+
93
+ ## Conclusion
94
+
95
+ Caps the biggest disk hog (256MB token-ledger) with a dark-by-default, batched, non-blocking,
96
+ fail-open retention prune driven off the existing poller cadence. Zero behavior change until an
97
+ operator opts in. Ship.