Skip to content

fix(stats): count forked sessions' copied messages once (backport #306) - #313

Merged
slkiser merged 2 commits into
release/4.xfrom
fix/306-fork-copies-4.x
Oct 3, 2026
Merged

slkiser merged 2 commits into
release/4.xfrom
fix/306-fork-copies-4.x

Conversation

@slkiser

@slkiser slkiser commented Oct 3, 2026

Copy link
Copy Markdown
Owner

Summary

Backport of the #306 fix to the 4.x (OpenCode 1) line.

  • OpenCode 1's Session.fork clones each parent message into the fork with a new id but the same times, model, tokens and cost. The all-history, time-window and multi-session readers counted each copy again, so forking a $10 session without using the fork showed $20 in token/cost totals.
  • dropForkCopies in src/lib/opencode-storage.ts counts each finished fingerprint (created/completed time, provider, model, tokens, cost) as many times as the session that has it most often, and drops the extra copies. It also runs on the no-json_extract fallback of the completed-message reader. Unfinished rows are never merged.
  • A single session's own read is unchanged.
  • Known limits: two unrelated sessions with exactly the same created/completed millisecond, model, tokens and cost are counted once; a copied reply with usage but no completion time (only after a crash mid-reply) still counts twice.

Linked Issue

Fixes #306

OpenCode Validation

  • Current production released OpenCode version tested: not tested live on OpenCode 1; covered by unit tests. The same fix was tested live on OpenCode 2.0.20 (v5 PR).
  • Why this version is relevant to the fix: the reporter uses OpenCode 1.18.34, whose fork clones message info unchanged (verified in its Session.fork source).

Before-and-after evidence

Not applicable for screenshots on OpenCode 1 (not run live). The new tests fail on the old 4.x code (5 fork-copy tests) and pass with this change.

Surface checks:

  • /tokens_* output: covered by unit tests only.
  • TUI sidebar, toast, compact line: not applicable; they show provider quota, not local token history.

Quality Checklist

  • I ran pnpm verify
  • This change is focused and avoids unrelated behavior changes
  • I updated or added tests when behavior changed
  • I updated docs when user-facing workflow, command, or config behavior changed (no docs describe how totals are counted)
  • For provider changes, I followed Provider Changes, or this does not apply

OpenCode 1's Session.fork copies the parent's messages into the fork
session with new ids but identical times, model, tokens, and cost, so
all-history, completed, and multi-session reads counted each copy again.

Drop a finished assistant row when an earlier row with the same
fingerprint (created/completed time, provider, model, tokens, cost) is in
a different session. Unfinished rows and same-session duplicates are
kept, and a fork's own single-session read still shows its copied history.

Fixes #306
…ckport #306)

Remembering only the first session per fingerprint undercounted: when a
parent had two identical finished messages, an early fork copied one and
a later fork copied both, reading the two forks (or all history after the
parent was deleted) returned one row instead of two.

Count each finished fingerprint as many times as the session that has it
most often. Unfinished rows are still always kept.

Refs #306
@slkiser
slkiser merged commit ffdc82e into release/4.x Oct 3, 2026
4 checks passed
@slkiser
slkiser deleted the fix/306-fork-copies-4.x branch October 3, 2026 13:00
@slkiser slkiser mentioned this pull request Oct 3, 2026
2 tasks done
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