fix(opencode2): accept v1 stores migrated to OpenCode 2 - #477
Merged
ualtinok merged 1 commit intoSep 20, 2026
Merged
Conversation
When OpenCode 2 migrates an existing 1.18.x store, it keeps the v1 `message` and `part` tables beside its own schema. detectOpenCodeStoreGeneration() (rightly, for 1.18.x stores that also ship session_message) calls any store with message+part v1, which breaks OpenCode 2 hosts on every migrated install: - assertOpenCodeStoreGeneration(db, "v2") in V2StoreReader throws "OpenCode store generation mismatch ... expected v2, found v1", the v2 hook refuses, and every turn in every session is interrupted. - doctor's dangling-boundary check reads the `message` table, which is frozen at the migration point, so boundaries written since the upgrade are reported as dangling. `session_v2` is written only by an OpenCode 2 host: the 1.18.31 binary never references it, while 2.0.7 creates it in both fresh and migrated stores. The v2 assertion now also passes when the store has session_message and session_v2. Detection itself is unchanged, so a 1.18.x store is still v1 and still refused for v2 readers, and v1 assertions behave exactly as before. A store can carry both generations in either direction (migrated by 2.x, or used by 1.x again after an upgrade), so table presence cannot say which one is live. The doctor boundary check now takes the host generation doctor already resolved from the active install, and falls back to detection without it, including for Desktop installs, which report no version. (On a real migrated store, 6,529 of its 6,885 v1 message ids are present in session_message under the same ids.) Tests pin the table lists observed on a migrated 2.0.7 store and on a fresh 2.0.7 data directory, and add migrated and downgraded stores to the doctor boundary check. The migrated cases fail without the fix; the downgraded case fails if the table is chosen from session_v2 instead of the host; and dropping the session_v2 requirement fails the existing 1.18.30 fixture.
| const dangling = listDanglingCompartmentBoundaries( | ||
| contextDb, | ||
| sessionDb, | ||
| /\d/.test(activeInstallation.version) ? hostGeneration : undefined, |
There was a problem hiding this comment.
Host generation selects stale tables
When the v2 CLI is installed but has not migrated an existing v1 store yet, this override selects session_message instead of the live v1 message table, so doctor can report valid compartment boundaries as dangling. The reverse happens for Desktop installations: their version is "unknown", so a migrated mixed-generation v2 store falls back to schema detection, is classified as v1, and reads the frozen message table. Table selection needs to account for both the host generation and the store's migration state.
This was referenced Sep 20, 2026
This was referenced Sep 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On an OpenCode 2 host whose store was migrated from 1.18.x, 0.42.6 refuses the store:
After that, every turn in every session is interrupted.
When OpenCode 2 migrates a 1.18.x store, it keeps the v1
messageandparttables beside its own schema.detectOpenCodeStoreGeneration()calls any store withmessage+partv1. That's correct for real 1.18.x stores, which also shipsession_message, as the existing fixture pins. SoV2StoreReader'sassertOpenCodeStoreGeneration(db, "v2")throws on every migrated install.doctor's dangling-boundary check has the same blind spot. On a migrated store it reads
message, which is frozen at the migration point, and reports every boundary written since the upgrade as dangling.Evidence for the marker
session_v2is written only by an OpenCode 2 host:session_v2,session_inboxorsession_pending.Table lists as observed:
session_message,session_v2,session_inbox,session_pending, … and nomessage/part;message,part,session,todo, …;session_v2.On a real migrated store, 6,529 of its 6,885 v1 message ids are also in
session_messageunder the same ids. Sosession_messageis the right table on a migrated host.Fix
The v2 assertion: it now also passes when the store has
session_messageandsession_v2.detectOpenCodeStoreGeneration()is unchanged, so a 1.18.x store is still v1 and still refused for v2 readers, and v1 assertions behave exactly as before.The doctor boundary check: it now takes the host generation that doctor already resolves from the active install. It falls back to detection when there is none, including for Desktop installs, which report no version.
Table presence can't settle this alone, because a store can hold both generations either way: migrated by 2.x, or used by 1.x again after an upgrade (
session_v2stays behind while 1.x writes tomessage).Tests
opencode-db-path.test.ts:doctor-compartment-boundaries.test.tsadds two stores:v2, whose legacymessagetable holds only one id;v1, whosesession_messageholds only one id.What each test guards, checked by mutation:
session_v2instead of from the host fails the downgraded case.session_v2requirement fails the 1.18.30 fixture.Results:
bun test src/commands/(cli): 164 pass.bun test --parallel: 4947 pass, 1 fail. The failure iscreateCtxSearchTools > explains why commit search is unavailable for a non-repository project, which fails the same way onmasterin my environment.Out of scope, but worth knowing
doctor migrate(packages/cli/src/commands/migrate.ts) asserts v1 and readsmessage/part. On a migrated OpenCode 2 store that passes, and it exports only the history from before the upgrade. This PR doesn't change it (the v1 assertion is untouched). It probably wants to either readsession_messagefor OpenCode 2 sources or refuse them explicitly.Related: #476 (dropped tool parts leak into OpenCode 2 requests) and #475 (migration 85 relabels real OpenCode 2 rows).
Summary by cubic
Fixes OpenCode 2 rejecting stores migrated from 1.18.x, which previously interrupted every turn on those installs. The v2 reader now accepts migrated stores, and doctor's dangling-boundary check reads the live host's tables instead of the frozen v1 ones.
session_messageandsession_v2, even if it also keeps the v1message/parttables;session_v2is created only by OpenCode 2 hosts.doctor migratestill reads the v1message/parttables on migrated stores, so it exports only pre-migration history; changing that is left for a follow-up.Written for commit 16da14c. Summary will update on new commits.
The PR is not yet safe to merge because doctor can emit materially incorrect dangling-boundary diagnostics for freshly upgraded v1 stores and migrated Desktop stores.
Findings
Summary
This PR broadens OpenCode v2 store acceptance for databases migrated from v1 and makes doctor select the boundary table using the detected host generation.
session_messageplussession_v2as evidence that a mixed-generation store is readable by v2 readers.Diagram
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Active OpenCode installation] --> B{Version contains a digit?} B -->|Yes| C[Use generation parsed from installed binary] B -->|No / Desktop| D[Detect generation from store tables] C --> E{Store state} E -->|Already migrated or downgraded mixed store| F[Use host generation to choose live table] E -->|Old v1 store before first v2 launch| G[Incorrectly query session_message] D --> H{Mixed migrated v2 store?} H -->|Yes| I[Detected as v1; incorrectly query frozen message] H -->|No| J[Query detected generation table]Reviews (1) · Last reviewed commit: "fix(opencode2): accept v1 stores migrate..."