From aba3ccd275c05c60e67304bb3690c6ebf968c547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Fri, 17 Apr 2026 01:36:56 -0400 Subject: [PATCH] fix(schema): guard migration 28 indexes behind tableExists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migration 28 (add_snapshot_hot_path_indexes) referenced task_comments, documents, meetings, sprints, agent_messages, activity — all tables created by ensureOptionalColumns() which runs AFTER runMigrations() on a fresh DB. Converted from sql-string migration to fn-based so each index is gated by tableExists(); safe on both fresh and migrated DBs. Also updated tests/unit/selective-ipc.test.ts exhaustive switch to include the 7 new DesktopEvent variants added in ef90a58. --- src/main/database/schema.ts | 33 +++++++++++++++++++++----------- tests/unit/selective-ipc.test.ts | 14 ++++++++++++++ 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/main/database/schema.ts b/src/main/database/schema.ts index 013e4f72..56de77f0 100644 --- a/src/main/database/schema.ts +++ b/src/main/database/schema.ts @@ -409,17 +409,28 @@ export function runMigrations( }, { version: 28, name: "add_snapshot_hot_path_indexes", - sql: `CREATE INDEX IF NOT EXISTS idx_tasks_company_created_at ON tasks(company_id, created_at DESC); - CREATE INDEX IF NOT EXISTS idx_tasks_company_updated_at ON tasks(company_id, updated_at DESC); - CREATE INDEX IF NOT EXISTS idx_approvals_company_created_at ON approvals(company_id, created_at DESC); - CREATE INDEX IF NOT EXISTS idx_task_comments_company_created_at ON task_comments(company_id, created_at); - CREATE INDEX IF NOT EXISTS idx_documents_company_updated_at ON documents(company_id, updated_at DESC); - CREATE INDEX IF NOT EXISTS idx_meetings_company_scheduled_at ON meetings(company_id, scheduled_at DESC); - CREATE INDEX IF NOT EXISTS idx_sprints_company_start_date ON sprints(company_id, start_date DESC); - CREATE INDEX IF NOT EXISTS idx_runs_agent_created_at ON runs(agent_id, created_at DESC); - CREATE INDEX IF NOT EXISTS idx_cost_entries_agent_created_at ON cost_entries(agent_id, created_at DESC); - CREATE INDEX IF NOT EXISTS idx_activity_company_actor_action ON activity(company_id, actor, action); - CREATE INDEX IF NOT EXISTS idx_tasks_company_assignee_status ON tasks(company_id, assignee_agent_id, status)`, + fn: () => { + // Some tables (task_comments / documents / meetings / sprints / agent_messages) + // are created by ensureOptionalColumns, which runs AFTER runMigrations on + // a fresh DB. Guard each CREATE INDEX with a tableExists check so the + // migration is safe on both fresh and migrated databases. + const indexPlan: Array<{ table: string; sql: string }> = [ + { table: "tasks", sql: "CREATE INDEX IF NOT EXISTS idx_tasks_company_created_at ON tasks(company_id, created_at DESC)" }, + { table: "tasks", sql: "CREATE INDEX IF NOT EXISTS idx_tasks_company_updated_at ON tasks(company_id, updated_at DESC)" }, + { table: "approvals", sql: "CREATE INDEX IF NOT EXISTS idx_approvals_company_created_at ON approvals(company_id, created_at DESC)" }, + { table: "task_comments", sql: "CREATE INDEX IF NOT EXISTS idx_task_comments_company_created_at ON task_comments(company_id, created_at)" }, + { table: "documents", sql: "CREATE INDEX IF NOT EXISTS idx_documents_company_updated_at ON documents(company_id, updated_at DESC)" }, + { table: "meetings", sql: "CREATE INDEX IF NOT EXISTS idx_meetings_company_scheduled_at ON meetings(company_id, scheduled_at DESC)" }, + { table: "sprints", sql: "CREATE INDEX IF NOT EXISTS idx_sprints_company_start_date ON sprints(company_id, start_date DESC)" }, + { table: "runs", sql: "CREATE INDEX IF NOT EXISTS idx_runs_agent_created_at ON runs(agent_id, created_at DESC)" }, + { table: "cost_entries", sql: "CREATE INDEX IF NOT EXISTS idx_cost_entries_agent_created_at ON cost_entries(agent_id, created_at DESC)" }, + { table: "activity", sql: "CREATE INDEX IF NOT EXISTS idx_activity_company_actor_action ON activity(company_id, actor, action)" }, + { table: "tasks", sql: "CREATE INDEX IF NOT EXISTS idx_tasks_company_assignee_status ON tasks(company_id, assignee_agent_id, status)" }, + ]; + for (const { table, sql } of indexPlan) { + if (tableExists(db, table)) db.exec(sql); + } + }, }, { version: 29, name: "add_run_events_seq_unique_index", diff --git a/tests/unit/selective-ipc.test.ts b/tests/unit/selective-ipc.test.ts index c442858d..a045e650 100644 --- a/tests/unit/selective-ipc.test.ts +++ b/tests/unit/selective-ipc.test.ts @@ -110,6 +110,20 @@ describe("selective IPC event types", () => { return "thread-wait-state-updated"; case "recovery-needed": return "recovery-needed"; + case "oauth:status": + return "oauth:status"; + case "oauth:auth-url": + return "oauth:auth-url"; + case "oauth:progress": + return "oauth:progress"; + case "oauth:prompt-request": + return "oauth:prompt-request"; + case "oauth:manual-code-request": + return "oauth:manual-code-request"; + case "credentials-changed": + return "credentials-changed"; + case "agent-event": + return "agent-event"; default: { const _exhaustive: never = event; return _exhaustive;