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* () {