Skip to content

fix(opencode): merge legacy file sessions and SQLite DB instead of short-circuiting#622

Merged
ozymandiashh merged 1 commit into
getagentseal:mainfrom
morpheus9393:fix/opencode-merge-sqlite-and-legacy-sessions
Jul 5, 2026
Merged

fix(opencode): merge legacy file sessions and SQLite DB instead of short-circuiting#622
ozymandiashh merged 1 commit into
getagentseal:mainfrom
morpheus9393:fix/opencode-merge-sqlite-and-legacy-sessions

Conversation

@morpheus9393

Copy link
Copy Markdown
Contributor

Summary

Fixes #621

The OpenCode provider's discoverSessions() short-circuited on legacy file-based JSON sessions (storage/session/*.json) and never fell back to the SQLite DB (opencode.db) when any legacy file existed. This caused migrated installs — which keep stale legacy JSON files from before the OpenCode SQLite migration alongside the now-current SQLite DB — to report only a tiny fraction of actual usage, with the SQLite DB silently ignored.

Root cause

async discoverSessions(): Promise<SessionSource[]> {
  const fileSessions = await discoverOpenCodeFileSessions(resolvedDataDir, 'opencode')
  if (fileSessions.length > 0) return fileSessions   // ← short-circuits
  return discoverSqliteSessions(sqliteConfig)
}

The comment said "Prefer file-based when present, otherwise fall back to the DB so pre-migration installs keep reporting." But the real-world migration direction is the opposite: OpenCode migrated from file-based JSON to SQLite. After an in-place upgrade, the legacy storage/session/ directory is left behind with stale files, and all new data flows into opencode.db. The short-circuit fires on the stale legacy files and the SQLite DB is never read.

Fix

Merge both sources instead of either/or:

async discoverSessions(): Promise<SessionSource[]> {
  const fileSessions = await discoverOpenCodeFileSessions(resolvedDataDir, 'opencode')
  const sqliteSessions = await discoverSqliteSessions(sqliteConfig)
  return [...fileSessions, ...sqliteSessions]
}

Per-message dedup is already handled in createSessionParser via seenKeys (keyed by ${provider}:${sessionId}:${messageId}), so sessions that exist in both stores are not double-counted.

Verification (Windows 11, Node v24.13.0)

Before fix (v0.9.15, codeburn report --provider opencode -p all):

  • $3.31 cost, 549 calls, 12 sessions
  • Last data point: 2026-02-13 (stale legacy JSON only)
  • Top model: missing (GPT-5.5 not visible)

After fix (patched dist, same command):

  • $3,182.91 cost, 40,338 calls, 470 sessions
  • Last data point: 2026-07-05 (today)
  • Top model: GPT-5.5 at $999.99 (matches tokscale's independent reading of the same SQLite DB)

Cross-checked against tokscale reading the same opencode.db: $2,616.89 across 71 models — consistent with the patched CodeBurn output (the small delta is explained by CodeBurn counting a few legacy JSON sessions that tokscale deduplicates differently).

Why this also helps Mac users

The bug is not Windows-specific. Any OpenCode install that was upgraded in place (on any OS) and retains the storage/session/ directory with even one legacy JSON file will hit the same short-circuit. The issue title mentions Windows because that's where it was reported, but the fix applies to all platforms.

Checklist

…ort-circuiting

The OpenCode provider's discoverSessions() short-circuited on legacy
file-based JSON sessions (storage/session/*.json) and never fell back to
the SQLite DB (opencode.db) when any legacy file existed. This caused
migrated installs (which keep stale legacy JSON files from before the
OpenCode SQLite migration) to report only a tiny fraction of actual
usage — the SQLite DB was silently ignored.

Replace the either/or short-circuit with a merge of both sources. Per-
message dedup via seenKeys (keyed by provider:sessionId:messageId)
prevents double-counting sessions that exist in both stores.

Fixes getagentseal#621
@ozymandiashh

Copy link
Copy Markdown
Collaborator

Nice catch, and the diagnosis is right. I traced the parser path to sanity-check the merge approach and it holds up:

  • discoverSqliteSessions already returns [] gracefully when opencode.db is absent (the isSqliteAvailable guard plus the readdir catch and the dbPaths.length === 0 early return), so calling it unconditionally does not regress pure file-based / pre-migration installs.
  • parserDedup is created once per provider run in parser.ts and shared across every source, so the file pass populates the seen keys first and the SQLite pass skips the duplicates. The dedup you rely on works as described, and the updated header comment matches the real migration direction now.

Two things worth tightening before merge:

  1. Regression test for the both-stores case. The existing discovery tests in opencode.test.ts are all SQLite-only, so [...[], ...sqlite] reduces to the old behavior and none of them actually exercise the bug this fixes. A fixture with legacy JSON and a SQLite DB present that asserts no double-count would lock this in; otherwise a future revert to the short-circuit stays green.

  2. ID stability across the migration. The per-message dedup only holds if OpenCode preserves sessionId/messageId when it migrates JSON to SQLite. If the migration re-keys them, sessions present in both stores double-count. Your numbers suggest the practical impact is tiny (the time windows barely overlap), but if you can confirm the IDs are stable it'd be worth a one-line comment noting why the dedup is safe; if they aren't, the guarantee needs a second look.

Out of scope for this PR, but the header comment in opencode-file-parser.ts still describes file-based JSON as the newer 1.1+ format, which now contradicts the migration direction documented here. Might be worth reconciling in a follow-up.

@ozymandiashh ozymandiashh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving on correctness. I traced the parser path (graceful [] when opencode.db is absent, parserDedup shared across sources so the file pass dedups the SQLite pass) and CI is green. The both-stores regression test and the ID-stability note above remain worth a follow-up, but neither blocks the fix.

@ozymandiashh ozymandiashh merged commit cc6d84a into getagentseal:main Jul 5, 2026
3 checks passed
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.

OpenCode provider ignores SQLite DB when legacy JSON files exist (Windows, v0.9.15)

2 participants