From b258a55a6c61ac5d5158a83f9acd11b15700b4d8 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Sun, 31 May 2026 23:50:43 -0500 Subject: [PATCH] fix(core): preserve session metadata migration identity (#30176) --- .../migration.sql | 1 + .../snapshot.json | 0 .../migration.sql | 1 - packages/core/src/database/migration.gen.ts | 2 +- .../20260511173437_session-metadata.ts | 16 ++++++++ .../20260530232709_lovely_romulus.ts | 11 ------ packages/core/test/database-migration.test.ts | 39 +++++++++++++++++++ 7 files changed, 57 insertions(+), 13 deletions(-) create mode 100644 packages/core/migration/20260511173437_session-metadata/migration.sql rename packages/core/migration/{20260530232709_lovely_romulus => 20260511173437_session-metadata}/snapshot.json (100%) delete mode 100644 packages/core/migration/20260530232709_lovely_romulus/migration.sql create mode 100644 packages/core/src/database/migration/20260511173437_session-metadata.ts delete mode 100644 packages/core/src/database/migration/20260530232709_lovely_romulus.ts diff --git a/packages/core/migration/20260511173437_session-metadata/migration.sql b/packages/core/migration/20260511173437_session-metadata/migration.sql new file mode 100644 index 0000000000..1f8fcaf64a --- /dev/null +++ b/packages/core/migration/20260511173437_session-metadata/migration.sql @@ -0,0 +1 @@ +ALTER TABLE `session` ADD `metadata` text; diff --git a/packages/core/migration/20260530232709_lovely_romulus/snapshot.json b/packages/core/migration/20260511173437_session-metadata/snapshot.json similarity index 100% rename from packages/core/migration/20260530232709_lovely_romulus/snapshot.json rename to packages/core/migration/20260511173437_session-metadata/snapshot.json diff --git a/packages/core/migration/20260530232709_lovely_romulus/migration.sql b/packages/core/migration/20260530232709_lovely_romulus/migration.sql deleted file mode 100644 index 0ce73631f0..0000000000 --- a/packages/core/migration/20260530232709_lovely_romulus/migration.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE `session` ADD `metadata` text; \ No newline at end of file diff --git a/packages/core/src/database/migration.gen.ts b/packages/core/src/database/migration.gen.ts index 1a6918b33b..ee76318488 100644 --- a/packages/core/src/database/migration.gen.ts +++ b/packages/core/src/database/migration.gen.ts @@ -22,6 +22,6 @@ export const migrations = ( import("./migration/20260507164347_add_workspace_time"), import("./migration/20260510033149_session_usage"), import("./migration/20260511000411_data_migration_state"), - import("./migration/20260530232709_lovely_romulus"), + import("./migration/20260511173437_session-metadata"), ]) ).map((module) => module.default) satisfies DatabaseMigration.Migration[] diff --git a/packages/core/src/database/migration/20260511173437_session-metadata.ts b/packages/core/src/database/migration/20260511173437_session-metadata.ts new file mode 100644 index 0000000000..413f086671 --- /dev/null +++ b/packages/core/src/database/migration/20260511173437_session-metadata.ts @@ -0,0 +1,16 @@ +import { Effect } from "effect" +import type { DatabaseMigration } from "../migration" + +export default { + id: "20260511173437_session-metadata", + up(tx) { + return Effect.gen(function* () { + // This column briefly shipped again under 20260530232709_lovely_romulus. + if ( + (yield* tx.all<{ name: string }>(`PRAGMA table_info(\`session\`)`)).some((column) => column.name === "metadata") + ) + return + yield* tx.run(`ALTER TABLE \`session\` ADD \`metadata\` text;`) + }) + }, +} satisfies DatabaseMigration.Migration diff --git a/packages/core/src/database/migration/20260530232709_lovely_romulus.ts b/packages/core/src/database/migration/20260530232709_lovely_romulus.ts deleted file mode 100644 index 2fe023543d..0000000000 --- a/packages/core/src/database/migration/20260530232709_lovely_romulus.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Effect } from "effect" -import type { DatabaseMigration } from "../migration" - -export default { - id: "20260530232709_lovely_romulus", - up(tx) { - return Effect.gen(function* () { - yield* tx.run(`ALTER TABLE \`session\` ADD \`metadata\` text;`) - }) - }, -} satisfies DatabaseMigration.Migration diff --git a/packages/core/test/database-migration.test.ts b/packages/core/test/database-migration.test.ts index 5b0b08c962..316974de8c 100644 --- a/packages/core/test/database-migration.test.ts +++ b/packages/core/test/database-migration.test.ts @@ -7,6 +7,7 @@ import { Effect } from "effect" import { sql } from "drizzle-orm" import { DatabaseMigration } from "@opencode-ai/core/database/migration" import sessionUsageMigration from "@opencode-ai/core/database/migration/20260510033149_session_usage" +import sessionMetadataMigration from "@opencode-ai/core/database/migration/20260511173437_session-metadata" import type { SqlClient as SqlClientService } from "effect/unstable/sql/SqlClient" const run = (effect: Effect.Effect) => @@ -89,6 +90,44 @@ describe("DatabaseMigration", () => { ) }) + test("does not replay a migrated session metadata column", async () => { + await run( + Effect.gen(function* () { + const db = yield* makeDb + yield* db.run(sql`CREATE TABLE session (id text PRIMARY KEY, metadata text)`) + yield* db.run( + sql`CREATE TABLE __drizzle_migrations (id INTEGER PRIMARY KEY, hash text NOT NULL, created_at numeric, name text, applied_at TEXT)`, + ) + yield* db.run(sql` + INSERT INTO __drizzle_migrations (hash, created_at, name, applied_at) + VALUES ('hash', 1, '20260511173437_session-metadata', ${new Date().toISOString()}) + `) + + yield* DatabaseMigration.applyOnly(db, [sessionMetadataMigration]) + + expect(yield* db.all(sql`SELECT id FROM migration`)).toEqual([{ id: "20260511173437_session-metadata" }]) + }), + ) + }) + + test("accepts the temporary replacement session metadata migration id", async () => { + await run( + Effect.gen(function* () { + const db = yield* makeDb + yield* db.run(sql`CREATE TABLE session (id text PRIMARY KEY, metadata text)`) + yield* db.run(sql`CREATE TABLE migration (id TEXT PRIMARY KEY, time_completed INTEGER NOT NULL)`) + yield* db.run(sql`INSERT INTO migration (id, time_completed) VALUES ('20260530232709_lovely_romulus', 1)`) + + yield* DatabaseMigration.applyOnly(db, [sessionMetadataMigration]) + + expect(yield* db.all(sql`SELECT id FROM migration ORDER BY id`)).toEqual([ + { id: "20260511173437_session-metadata" }, + { id: "20260530232709_lovely_romulus" }, + ]) + }), + ) + }) + test("skips drizzle import when migration table already has state", async () => { await run( Effect.gen(function* () {