Skip to content

Split Claude Code's running total by the growth each session made - #22

Merged
lr00rl merged 2 commits into
mainfrom
fix/20261022_running-total
Oct 23, 2026
Merged

lr00rl merged 2 commits into
mainfrom
fix/20261022_running-total

Conversation

@lr00rl

@lr00rl lr00rl commented Oct 23, 2026

Copy link
Copy Markdown
Owner

Summary

After ccli copy 1087b979 --from talcum --to default --apply, $258 of unlogged calls moved from default to talcum (default $466 → $208, talcum → $278), though that work never happened in talcum.

Claude Code's cost-state line is one running total per session (same startTime), restored and carried on across resumes, into another account after a copy, into continued sessions and into both branches of a fork. ccli kept only each file's newest line and gave the whole total to one owner file; the sync put talcum's newest line into default as an imported one and reset default's creation time, so talcum became the owner.

  • Every cost-state line is kept in a new snapshots table with its offset and import flag. A session's share is the growth between consecutive snapshots in its own transcript, counted at its own snapshots; a copied-in snapshot moves the base. A snapshot several files hold as their own counts once, in the file created first; one only copies hold counts in the earliest copy. A continued or forked session's first snapshot, with nothing before it in its file, only sets the base. Each share is compared with the session's own calls in the same time windows.
  • Opening an older index re-reads the transcripts that have cost-state lines once (177 files, 3.1 GB, 20–50 s here); deleted transcripts keep their last snapshot. ReconcileVersion 5 recomputes every adjustment.
  • A fast-forward copy appends the new bytes to the target in place (staged, bounded, re-checked, cut back only when safe) instead of rewriting the whole transcript, so the target keeps its identity and creation time.

On a copy of the real index: 1087b979 is back to $466 in default ($27.82 in talcum, its own work). Unlogged cost index-wide rises by $154, from sessions whose old figure subtracted another session's calls from their total.

Test plan

  • go vet ./..., gofmt -l ., go test ./..., -race on store, index, transfer
  • New tests: a running total split between accounts across a copy, continued work and a sync back by rename (fails on the old code: default charged with work's growth); a continuation's first snapshot only setting the base; a dipping total; snapshots of a copy whose original is not indexed; the migration of on-disk and deleted transcripts; fast-forward appending in place; an append onto a changed target refused.
  • Migration on a fresh copy of the real index, compared session by session with the old one.

…e itself

After `ccli copy 1087b979 --from talcum --to default --apply`, $258 of
unlogged calls moved from default to talcum: default fell from $466 to
$208 and talcum rose to $278, though none of that work had happened in
talcum.

Claude Code's cost-state line is not one process's total. It is one
running total per session, named by startTime, that Claude Code restores
and carries on when the session is resumed, and that carries on in
another account after a copy, in a continued session and in both
branches of a fork. ccli kept only each file's newest line of a total
and gave the whole total to one owner file: a file holding it as its own,
the earliest created. Talcum's newest line (default's total plus
talcum's growth) was copied back into default, where it replaced
default's own line as an imported one, and the sync's rename made
default's file the newer one, so talcum became the owner of everything.

Every cost-state line is now kept, in a new snapshots table, with its
offset and import flag. A session's share of a total is the growth
between consecutive snapshots in its own transcript, counted at each
snapshot that is its own; a copied-in snapshot only moves the base. A
snapshot that several files hold as their own (a transcript copied by
hand, the history a continuation or a native fork repeats) counts once,
in the file created first. Each share is compared with the session's
own calls in the same time windows.

Opening an older index re-reads the files that have cost-state lines
(177 transcripts, 3.1 GB, 50 s on this machine) once and empties the old
processes table; ReconcileVersion 5 recomputes every adjustment. On a
copy of the real index, 1087b979 goes back to $466 in default and $24.30
in talcum. Totals across the index rise by $374: the old rule dropped
the growth of all but one branch of a forked or continued conversation,
and subtracted one session's calls from another session's total.

A fast-forward copy now appends the missing bytes to the target in place
instead of rewriting the whole transcript through a temporary file, so
the target keeps its identity and creation time and a 200 MB sync writes
only what is new.
- A continued or forked session starts its transcript with the running
  total as it stood plus its own work, with no earlier line to measure
  from. Counting that first snapshot from zero counted the parent's part
  again (three of the user's sessions gained $189 that way). In a file
  created after the total began elsewhere, such a first snapshot now only
  sets the base. The total began in the earliest file holding a snapshot
  of it as its own.
- A snapshot that only copies hold, because the original is not indexed,
  counts in the earliest copy instead of nowhere.
- The migration dropped the processes rows of transcripts already gone
  from disk, which cannot be read again, and with them their sessions'
  share. Those rows now move over as one snapshot each.
- The in-place append ran its check on the target before staging up to
  the whole tail, so a Claude Code process resuming the target meanwhile
  could interleave with it, and a failed append was cut back blindly.
  The tail is now staged next to the target first, bounded to the bytes
  the ledger recorded, the target is checked just before the short
  append, a failed append is cut back only if nothing else wrote, and if
  bytes may be left the ledger record stays.
- A total that dips no longer lowers the base, the adjustment takes the
  latest counted snapshot's time, windows are merged before the query,
  and finding the sessions that share a total no longer joins the
  snapshots table with itself row by row.

Tested: go test ./..., -race on store, index and transfer; on a fresh
copy of the real index, 1087b979 is $466 in default and $27.82 in
talcum, and the index-wide adjustment total moves by $154, all of it in
sessions whose old figure subtracted another session's calls.
@lr00rl
lr00rl merged commit e79a0a3 into main Oct 23, 2026
2 checks passed
@lr00rl
lr00rl deleted the fix/20261022_running-total branch October 23, 2026 09:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant