github mksglu/context-mode v1.0.148

latest releases: v1.0.151, v1.0.150, v1.0.149...
4 hours ago

context-mode v1.0.148 — Critical stats accuracy fix

TL;DR

The per-conversation % kept out of context display in ctx_stats was
under-reporting by a wide margin due to a cascade of seven distinct
bugs. v1.0.148 lands all of them in a single release. On the reporter's
machine, the displayed Section 1 ratio corrected from 6% → 95%;
on yours it will likely correct similarly. Your lifetime headline
(14.7 MB kept out)
and capture counts are unchanged — this
release fixes the per-conversation display only.

Trigger: PR #683 (comprehensive ctx_* tool description audit) shipped
v1.0.147 and a user opened ctx_stats to verify the release was
healthy. The display showed Without 158 KB / With 158 KB / 0% kept out — a degenerate identity bar. What looked like one display
glitch turned into a seven-bug archaeological dig.

Bug summary (in discovery order)

Bug Layer Symptom Fix
A Schema Historical session DBs (pre-v1.0.130) on disk lacked the bytes_avoided / bytes_returned / project_dir columns. Migration ran only when SessionDB constructor opened a DB. 131 of 197 DBs on the reporter's machine were stuck on legacy schema. New ensureSessionEventsSchema(dbPath) helper, called from the analytics aggregator before each readonly open. Self-healing: every ctx_stats call migrates any legacy DBs it scans.
C Aggregator When the SUM query referenced a missing column, the prepare() threw, the catch swallowed it, and the WHOLE DB was skipped — even the LENGTH(data) signal was lost. Same lazy migration fixes both A and C in one path.
D Design The aggregator's "skip on corrupt DB" path was hiding actively-correctable legacy DBs rather than fixing them. Lazy migration converts the skip into a one-time backfill.
E Scope The per-conversation aggregator filtered by a single session_id. A Claude Code conversation routinely spans 80+ session_ids (resume cycles, /compact rebirths, PID sub-process sessions spawned by ctx_execute). The single-session_id scope missed every sandbox burst's bytes_avoided. New projectDir option on getRealBytesStats uses a META subquery: WHERE session_id IN (SELECT session_id FROM session_meta WHERE project_dir = ?).
F Attribution Sandbox-burst PID-session EVENTS write project_dir = '' even when their META row carries the parent cwd. An event-level project_dir filter would still miss them. The META subquery (Bug E fix) catches PID-burst sessions by their META row regardless of event-level project_dir.
G Display formula The Section 1 Without / With ratio (analytics.ts:1991-1993) folded eventDataBytes (hook payload metadata, never enters the model context window) into both sides. This was an incidental fix from v1.0.134 SLICE B (commit ce62275) designed to dodge a different degenerate-100% bar bug. After bugs A+C+D+E+F let real signal flow, SLICE B started reporting 56% on conversations the user knew should be 95%+. Strict-compression formula: Without = bytesAvoided + bytesReturned, With = max(1, bytesReturned). eventDataBytes is rendered in Section 2 (captures count), not in Section 1. Empty-state branch handles the SLICE B degenerate case honestly with a hint line. See ADR-0004.

Before / after on the reporter's data

Metric v1.0.147 (broken) v1.0.148
Without context-mode 158 KB 3,038 KB
With context-mode 158 KB 140 KB
% kept out 0% (identity bar) 95.4%
Runtime multiplier 22×
Lifetime headline (14.7 MB) ✓ unchanged ✓ unchanged
Capture counts (19,949 lifetime) ✓ unchanged ✓ unchanged

The 95% headline is not a metric inflation — it is the literal
compression ratio your data has always supported. The previous
display was measuring infrastructure size, not context savings.

Upgrade

npx context-mode@latest
# or, if you have the plugin installed:
/context-mode:ctx-upgrade

After upgrade, run ctx_stats once. The legacy-schema migration runs
transparently on first call — no manual command. Your Section 1
display will then reflect the strict-compression ratio.

What does NOT change

  • Lifetime "X MB kept out across N projects" headline — same formula
  • Capture counts (Section 2) — same data, same totals
  • Cost claims ($ saved) — unaffected by the per-conversation display fix
  • Schema — no new columns, no breaking migrations, ADR-0001
    multi-writer invariant preserved

Test coverage

  • 464 tests pass. 3 pre-existing PR #617 ctx_doctor / ctx_index
    storage e2e tests remain in the same pre-existing failure state —
    out of scope for this hotfix.
  • New behavioural tests in tests/session/real-bytes-stats.test.ts
    pin schema-migration recovery + META-based projectDir scope +
    idempotency. RED→GREEN proven (formula change disabled → tests
    fail; re-enabled → tests pass).
  • Three new behavioural tests in tests/analytics/format-report.test.ts
    pin the strict-compression formula: empty-state branch, mixed
    case 60% (verifies eventDataBytes is excluded), only-avoided
    honest 100%.

Acknowledgements

This release ships because of the diagnostic discipline of the
seven-agent EM ops audit: Git Archaeologist, DB Architect, Math
Engineer, QA Engineer, PO/UX Engineer, Edge Case Engineer, and the
safe-harbour Architect. Their parallel verdicts converged on the
strict-compression formula after the empirical data falsified
three of the four initially-proposed fixes (raw status quo, content
store inclusion, dedup-by-hash).

  • @kerneltoast (PR #654) — original Opus 4.6 reproduction that
    triggered the whole audit chain.
  • @NgoQuocViet2001 (PR #666) — custom TTL on ctx_fetch_and_index,
    shipped in v1.0.147 and surfaced in tool descriptions during the
    PR #683 review.

The full archaeology is in ADR-0004.

Don't miss a new context-mode release

NewReleases is sending notifications on new releases.