Skip to content

feat(savings-ledger): record lean-ctx version per event (v4) - #710

Merged
yvgude merged 1 commit into
yvgude:mainfrom
getappz:worktree-savings-ledger-version
Jul 5, 2026
Merged

feat(savings-ledger): record lean-ctx version per event (v4)#710
yvgude merged 1 commit into
yvgude:mainfrom
getappz:worktree-savings-ledger-version

Conversation

@getappz

@getappz getappz commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Summary

SavingsEvent had no way to say which lean-ctx version recorded a given saving. stats.json (the display cache lean-ctx gain --daily reads) tracks a per-day version tag separately, but if that cache is ever lost or corrupted, rebuilding it from the append-only savings ledger (the real source of truth) can't recover the version column at all — there's nothing in the ledger to recover it from.

What

  • version: String added to SavingsEvent, populated with CARGO_PKG_VERSION at append time (savings_ledger/mod.rs::new_event).
  • New v4 canonical-content hash commits it; canonical_content_v3 (the pre-version format) kept intact so existing ledger entries keep verifying unchanged.
  • hash_matches now checks v4, v3, v2, and the legacy v1 format, same as before plus one more tier.
  • Pre-v4 entries deserialize with version defaulting to an empty string via serde, not a guessed current version — same convention already used by DayStats::version in core/stats/model.rs for the same "unknown, don't fabricate" reason.

This follows the exact pattern this file already used for v2→v3 (the mechanism field, enterprise#19): additive field, versioned canonical-content format, old canonical method kept around, backward-compat regression test mirroring the existing one for that transition.

Why

Hit this directly: stats.json got corrupted (unrelated incident, a Windows-encoding bug in a different tool that truncated it mid-write) and had to be rebuilt by replaying savings/ledger.jsonl. Everything reconstructed cleanly except the per-day version tag, because the ledger never recorded it. This closes that gap for next time.

Test Plan

cargo fmt --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features

All pass locally.

Test What it proves
v3_hash_still_verifies_and_v4_commits_version pre-v4 entries (no version key in JSON) still verify via canonical_content_v3; a v4 entry's hash breaks if version is rewritten
existing hash_is_deterministic, hash_changes_when_content_changes, hash_depends_on_prev unchanged, now implicitly exercising the v4 canonical form
existing legacy_v1_hash_still_verifies, v2_hash_still_verifies_and_v3_commits_mechanism unchanged — proves the new tier didn't disturb older verification paths

Backward Compatibility

No schema break. Existing ledger files verify unchanged (v1/v2/v3 canonical forms all still checked by hash_matches). New appends use v4. Not a CLI/MCP tool surface change — no registry/schema/docs updates needed.

Related Issue

No prior issue — small, mechanical, additive change following an established in-file pattern; happy to open one retroactively if you'd rather have that paper trail before merging.

SavingsEvent had no way to say which lean-ctx version recorded a
given saving. stats.json (the display cache) tracked a per-day
version tag separately, but if that cache is ever lost or corrupted,
rebuilding it from the append-only savings ledger (the actual source
of truth) couldn't recover the version column at all.

Adds `version: String` (CARGO_PKG_VERSION at append time), following
the same additive, backward-compatible pattern already used for the
v2->v3 `mechanism` field: serde default for pre-v4 entries (empty,
not a guessed version), a new v4 canonical-content hash that commits
it, and the v3 canonical kept intact so old ledger entries keep
verifying unchanged.
@yvgude
yvgude merged commit 3157ca4 into yvgude:main Jul 5, 2026
26 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 5, 2026
@getappz
getappz deleted the worktree-savings-ledger-version branch July 6, 2026 02:56
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants