fix(core): skip orphaned beta sessions
This commit is contained in:
@@ -269,7 +269,8 @@ Before transforming V1 rows, look for `opencode-next.db` in the data directory.
|
||||
builds. Open it read-only with Bun SQLite and copy its `project`, `session`, and `session_message` rows directly into the
|
||||
current `project`, `session_v2`, and `session_message` tables. Existing current projects and Sessions win ID collisions.
|
||||
Do not copy its durable events or runtime caches; initialize each imported Session's `event_sequence` watermark from its
|
||||
maximum message sequence. Commit each imported Session independently and leave the source database untouched.
|
||||
maximum message sequence. Commit each imported Session independently and leave the source database untouched. Skip and
|
||||
warn for beta Sessions whose referenced project row is missing rather than blocking the remaining migration.
|
||||
|
||||
The previous V2 import is part of this migration and uses the same completion marker. It needs no source-specific cursor:
|
||||
the destination Session row is the per-Session idempotency boundary, so a retry skips transactions that already committed.
|
||||
|
||||
@@ -618,10 +618,15 @@ function importNextDatabase(
|
||||
nextCompleted = 0
|
||||
for (const session of sessions) {
|
||||
const project = projects.get(session.project_id)
|
||||
if (!project)
|
||||
return yield* Effect.die(
|
||||
new Error(`Previous V2 session ${session.id} references missing project ${session.project_id}`),
|
||||
)
|
||||
if (!project) {
|
||||
yield* Effect.logWarning("Skipped previous V2 session with missing project", {
|
||||
sessionID: session.id,
|
||||
projectID: session.project_id,
|
||||
})
|
||||
nextCompleted++
|
||||
yield* Effect.yieldNow
|
||||
continue
|
||||
}
|
||||
const messages = source
|
||||
.query<NextMessage, [string]>(
|
||||
"SELECT id, session_id, type, seq, time_created, time_updated, data FROM session_message WHERE session_id = ? ORDER BY seq",
|
||||
|
||||
@@ -825,10 +825,12 @@ describe("V1Migration database workflow", () => {
|
||||
) VALUES
|
||||
('ses_next', 'next-project', 'next', 'C:/Users/sewer', 'Imported', '2', 'build',
|
||||
'{"id":"model","providerID":"provider"}', 10, 20),
|
||||
('ses_existing', 'next-project', 'source-existing', '/tmp/next', 'Source existing', '2', NULL, NULL, 11, 21);
|
||||
('ses_existing', 'next-project', 'source-existing', '/tmp/next', 'Source existing', '2', NULL, NULL, 11, 21),
|
||||
('ses_orphan', 'missing-project', 'orphan', '/tmp/orphan', 'Orphan', '2', NULL, NULL, 12, 22);
|
||||
INSERT INTO session_message VALUES
|
||||
('msg_next', 'ses_next', 'user', 4, 12, 13, '{"text":"from next","time":{"created":12}}'),
|
||||
('msg_source_existing', 'ses_existing', 'user', 2, 12, 13, '{"text":"source","time":{"created":12}}');
|
||||
('msg_source_existing', 'ses_existing', 'user', 2, 12, 13, '{"text":"source","time":{"created":12}}'),
|
||||
('msg_orphan', 'ses_orphan', 'user', 0, 12, 13, '{"text":"orphan","time":{"created":12}}');
|
||||
`)
|
||||
source.close()
|
||||
|
||||
@@ -851,13 +853,13 @@ describe("V1Migration database workflow", () => {
|
||||
expect(yield* V1Migration.status({ nextDatabasePath: filename })).toEqual({
|
||||
status: "required",
|
||||
completed: 0,
|
||||
total: 2,
|
||||
total: 3,
|
||||
})
|
||||
expect(yield* V1Migration.run({ nextDatabasePath: filename })).toEqual({ status: "completed" })
|
||||
expect(yield* V1Migration.status({ nextDatabasePath: filename })).toEqual({
|
||||
status: "completed",
|
||||
completed: 2,
|
||||
total: 2,
|
||||
completed: 3,
|
||||
total: 3,
|
||||
})
|
||||
expect(yield* db.get(sql`SELECT title, agent, model FROM session_v2 WHERE id = 'ses_next'`)).toEqual({
|
||||
title: "Imported",
|
||||
@@ -888,6 +890,7 @@ describe("V1Migration database workflow", () => {
|
||||
expect(yield* db.all(sql`SELECT id FROM session_message WHERE session_id = 'ses_existing'`)).toEqual([
|
||||
{ id: "msg_current_existing" },
|
||||
])
|
||||
expect(yield* db.get(sql`SELECT id FROM session_v2 WHERE id = 'ses_orphan'`)).toBeUndefined()
|
||||
expect(yield* db.get(sql`SELECT name, worktree FROM project WHERE id = 'next-project'`)).toEqual({
|
||||
name: "Current project",
|
||||
worktree: "/tmp/current",
|
||||
|
||||
Reference in New Issue
Block a user