From df9317ef4bbf3708bdc389a415f7f315c3ad1350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Sat, 23 May 2026 14:37:25 -0400 Subject: [PATCH] fix(new): ledger every schema migration Amp-Thread-ID: https://ampcode.com/threads/T-019e55df-9bcd-73e6-9935-c1e6238cd273 Co-authored-by: Amp --- AgentCompany-New/src/main/database/schema.ts | 53 +++++++++++++++---- .../tests/unit/domain-services.test.ts | 8 +++ 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/AgentCompany-New/src/main/database/schema.ts b/AgentCompany-New/src/main/database/schema.ts index 805982f5..d2f4241d 100644 --- a/AgentCompany-New/src/main/database/schema.ts +++ b/AgentCompany-New/src/main/database/schema.ts @@ -9,16 +9,22 @@ import { skillHardeningSchema } from "./migrations/007-skill-hardening"; import { providerHealthSchema } from "./migrations/008-provider-health"; import { socialGovernanceSchema } from "./migrations/009-social-governance"; -const MIGRATIONS: Array<{ version: number; sql: string }> = [ - { version: 1, sql: initialEnterpriseSchema }, - { version: 2, sql: collaborationProviderPolicySchema }, - { version: 3, sql: onboardingExecutionSchema }, - { version: 4, sql: filesPinnedAndScheduledPosts }, - { version: 5, sql: chatReadState }, - { version: 6, sql: appStateSchema }, - { version: 7, sql: skillHardeningSchema }, - { version: 8, sql: providerHealthSchema }, - { version: 9, sql: socialGovernanceSchema }, +interface Migration { + version: number; + name: string; + sql: string; +} + +const MIGRATIONS: Migration[] = [ + { version: 1, name: "initial_enterprise_schema", sql: initialEnterpriseSchema }, + { version: 2, name: "collaboration_provider_policy_schema", sql: collaborationProviderPolicySchema }, + { version: 3, name: "onboarding_execution_schema", sql: onboardingExecutionSchema }, + { version: 4, name: "files_pinned_and_scheduled_posts", sql: filesPinnedAndScheduledPosts }, + { version: 5, name: "chat_read_state", sql: chatReadState }, + { version: 6, name: "app_state", sql: appStateSchema }, + { version: 7, name: "skill_hardening", sql: skillHardeningSchema }, + { version: 8, name: "provider_health", sql: providerHealthSchema }, + { version: 9, name: "social_governance", sql: socialGovernanceSchema }, ]; const SCHEMA_VERSION = MIGRATIONS[MIGRATIONS.length - 1].version; @@ -37,10 +43,35 @@ export function installSchema(db: DatabaseSync): void { if (currentVersion > SCHEMA_VERSION) { throw new Error(`Database schema ${currentVersion} is newer than supported schema ${SCHEMA_VERSION}.`); } + backfillMigrationLedger(db, currentVersion); for (const migration of MIGRATIONS) { if (currentVersion >= migration.version) continue; - db.exec(migration.sql); + applyMigration(db, migration); currentVersion = migration.version; } } + +function applyMigration(db: DatabaseSync, migration: Migration): void { + db.exec("begin immediate"); + try { + db.exec(migration.sql); + recordMigration(db, migration); + db.exec("commit"); + } catch (error) { + db.exec("rollback"); + throw error; + } +} + +function backfillMigrationLedger(db: DatabaseSync, currentVersion: number): void { + for (const migration of MIGRATIONS) { + if (migration.version > currentVersion) return; + recordMigration(db, migration); + } +} + +function recordMigration(db: DatabaseSync, migration: Migration): void { + db.prepare("insert or ignore into schema_migrations(version, name, applied_at) values (?, ?, datetime('now'))") + .run(migration.version, migration.name); +} diff --git a/AgentCompany-New/tests/unit/domain-services.test.ts b/AgentCompany-New/tests/unit/domain-services.test.ts index 0e7a6e2c..dac7f107 100644 --- a/AgentCompany-New/tests/unit/domain-services.test.ts +++ b/AgentCompany-New/tests/unit/domain-services.test.ts @@ -19,6 +19,14 @@ function createConnection(): DatabaseConnection { } describe("domain services", () => { + test("records every applied schema migration version", () => { + const connection = createConnection(); + + const rows = connection.db.prepare("select version from schema_migrations order by version asc").all() as Array<{ version: number }>; + expect(rows.map((row) => row.version)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]); + connection.close(); + }); + test("creates an organization with providers, chat spaces, employee, and audit", () => { const connection = createConnection(); const organization = new OrganizationService(connection.db);