Repository navigation
Conversation
OpenCode <= 1.0.65 wrote messages without the assistant's `agent` and `parentID` and without the user's `agent` and `model`. The V1->V2 migrator validates every legacy row against the current SessionV1.Info, so those rows failed and were dropped one by one, leaving the session in session_v2 with no session_message rows at all - the whole conversation disappeared, reported as 4867 dropped rows and 64 empty sessions. The fields are metadata rather than conversation content, so recover them before the decode instead of losing the history: `agent` from the row, its `mode` (the pre-rename name of the field), the reply that handled the turn, or the session row; the user `model` from the same reply or the session; the assistant `parentID` from the nearest earlier user message. Rows that still cannot be resolved keep being dropped, and their warning now names the fields no source could supply.
|
Thanks for your contribution! This PR doesn't have a linked issue. All PRs must reference an existing issue. Please:
See CONTRIBUTING.md for details. |
|
The following comment was made by an LLM, it may be inaccurate: |
|
Noting the automated |
|
I'm still reviewing this pull request; I'll answer here when that's done. |
There was a problem hiding this comment.
Looks good. On the base, the PR's new tests fail and the old-format rows are dropped as invalid-message (the #53971 behaviour). With the PR, test/v1-migration.test.ts passes (28/28) and packages/core typechecks clean. I also ran a throwaway multi-turn session with one unanswered prompt: every row migrated in order, agent was filled in from mode, and the session model came from the latest reply. A filled-in parentID that points at a dropped user row only means subtask and compaction-summary pairing finds no parent, which is harmless. The "build" fallback matches Agent.defaultID. No blocking concerns.
|
Thanks for explaining. My review didn't consider that check, and I didn't change it. The body links #53971, and the review was against |
|
Cross-posting the point @opencode-agent asked me to bring here (from #55991, which is being closed as a duplicate of #53971). Independent confirmation from a second database of the row shape this PR targets: 9,330 assistant rows across 127 sessions have The gap this PR leaves open, and the reason I'm posting here: the migration returns early whenever Would it be feasible to also cover already-migrated installs — for example, re-run when the marker is I can test a build against this database (318 V1-origin sessions, 9,330 affected rows, re-run currently lossless here: 257 V2-only rows in V1-origin sessions, none of them real conversation content) and report the before/after numbers back here. |
|
Thanks for the cross-post, and for the numbers. The independent count is useful, and the On the gap you found: confirmed, and it cannot be closed by a re-runThe migration short-circuits on the completion marker:
The obvious trigger you suggest — re-run when the marker is
So a re-run deletes every already-projected message for the session, including anything V2 appended after the first run, and puts back the same partial set: the V1 source rows have not changed, so the same rows drop again — which is what #53971 already reports. That is the mechanism behind the warning in #52226, and it is why I am not making the marker conditional in this PR. The failure mode is data loss and the blast radius is every affected install on the next boot. What a safe repair would have to look likeIt cannot reuse the migration's per-session replace. It has to be additive per message: transform the session as today, then insert only the projected messages whose ids are absent from What I will not doAdd "delete What would help this PR right nowYour offer to test a build is the most useful thing available. This PR resolves a missing A before/after count from a scratch copy of that database would be worth more than anything I can run here, and it would pin both halves at once:
Both runs also answer the question in your last paragraph: whether the repair would need to be scoped per session, or whether re-projecting every legacy session on first start after the upgrade is cheap enough at your size (336 sessions / 9,330 rows). |
|
Closing this to stay within GitHub's 3-open-pull-request limit for contributors without write access, which currently blocks every new pull request here. The branch remains on my fork, so I can reopen it on request. |
|
Independent before/after run on a real affected database (Windows, opencode 2.1.2, State before
Method (the manual path, because the PR alone cannot re-run)
State after
Sample session What the destructive re-run cost, measured
So for already-migrated installs the gap you described is the whole problem: without the reset nothing runs, and with the reset the per-session I kept the pre-fix backup and can run a build against it if that PR appears. Acceptance numbers on that copy: Happy to see this reopened if the PR limit allows. |
Issue for this PR
Closes #53971
Type of change
What does this PR do?
The V1 to V2 migration validates every legacy row against the current
SessionV1.Info, so messages written beforeagent, the usermodeland the assistantparentIDbecame required (OpenCode <= 1.0.65) fail decoding and are dropped one by one. When a session's rows all fail it lands insession_v2with nosession_messagerows at all, so the history disappears even though the V1 rows are intact. #53971 measured 4867 dropped rows and 64 empty sessions, and #52226 is the same shape.Those fields are metadata rather than conversation content, so this recovers them in the migrator, immediately before the decode, instead of losing the history:
agent: the row's own value, thenmode(the pre-rename name of the field, which rows of that era carry), then the reply that handled the turn, then the session row, then the built-in default.model: the reply that handled the turn, then the session row.parentID: the nearest earlier user message, using the ordering the projection already relies on.packages/schemais untouched;SessionV1stays the strict contract for current rows, so nothing else that decodes V1 data changes behaviour.Rows that still cannot be resolved keep being dropped, but their
invalid-messagewarning now names the fields no source could supply, so an operator can find the sessions that need a manual value instead of only a message id. Field-level detail for other undecodable shapes is #42671's scope, not duplicated here.How did you verify your code works?
packages/core/test/v1-migration.test.tsgains two cases built from the <= 1.0.65 row shapes: one where the metadata is derivable (both rows now migrate, in the right order, and the session picks up the agent and model) and one where it is not (the warning namesmodelandparentID).Red baseline: with the source reverted and only the new test in place, the derivable case fails with both rows reported as
invalid-messagefor a session that should have migrated completely.tsgo --noEmitonpackages/core,oxlintand the ast-grep effect rules are clean. Run on Bun 1.4.2, the versionv2pins.Screenshots / recordings
Not a UI change.
Checklist