fix(opencode-usage): merge a migrated session's two rows per column (#22550)

A session that lived through OpenCode 2's V1 import has a row in both
`session` and `session_v2`, and neither is complete. #22391 resolved the
pair by ranking whole rows on one number — total token count, ties to
`session_v2` — which let that number decide everything else on the row.

Three consequences, each reproduced against that PR's own fixtures:

- A recorded cost could be zeroed. `session_v2` wins on tokens while
  carrying `cost = 0`, and row parsing maps a zero cost to `null`, so a
  legacy row's $12.50 disappeared. Cost is re-derived by the same lossy
  reduce as the tokens, but only the tokens were guarded.
- The token comparison decided metadata. A legacy row with more tokens
  supplied a stale pre-migration directory, and a legacy row with a NULL
  model erased the model `session_v2` had — 23 of 234 shared ids on a
  real migrated database have a model only on the v2 side.
- Winner-takes-all is per row, so a legacy row holding the input tokens
  and a v2 row holding the cache reads reported one of them as zero.

Metadata now comes from the generation OpenCode still writes, with older
generations filling only its NULLs; usage columns take a per-column MAX.
Both rows aggregate the same assistant messages, and the import can only
drop messages, never invent them, so each column's MAX is a tighter lower
bound on the truth than either row and can never exceed it.

The relation stays exactly one row per id — the highest-priority
generation holding it — every column stays `columnExists`-guarded with a
SQL fallback, and a database with a single session table builds the same
SQL it did before.

Cache schema version 4 -> 5 so existing caches rescan.
This commit is contained in:
Neil
2026-09-23 17:39:26 -07:00
committed by GitHub
parent 800d33e5c9
commit d4386763d5
4 changed files with 279 additions and 41 deletions
@@ -7,7 +7,9 @@ import type {
} from './types'
// Why: v4 reads OpenCode 2's `session_v2` table; v3 caches miss every v2 session.
export const OPENCODE_USAGE_SCHEMA_VERSION = 4
// v5 merges a migrated session's two rows per column instead of picking one, so
// v4 caches hold zeroed costs and pre-migration metadata.
export const OPENCODE_USAGE_SCHEMA_VERSION = 5
export const openCodeUsageProvider = {
id: 'opencode',