feat(FN-3898): add checkout/schema self-heal and import compatibility updates
- Add self-healing for legacy tasks schema/columns and checkout-lease recovery with regression coverage - Extend companies.sh import parsing and dashboard import routes for repo subpath handling - Fix verification command spawning to disable Corepack download prompts in engine tooling - Update dependency-graph plugin packaging/exports and wire CLI dashboard interop types - Add and include follow-up changesets for FN-3856, FN-3883, FN-3896, and FN-3898 Fusion-Task-Id: FN-3898
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Wire the checkout-lease column self-heal as an unconditional startup compatibility backfill (`ensureTasksSchemaCompatibility`) so legacy or mesh-synced task databases no longer fail with `no such column: checkoutNodeId` when schemaVersion is already past migration 20.
|
||||
@@ -1191,6 +1191,52 @@ describe("schema migrations", () => {
|
||||
db.close();
|
||||
});
|
||||
|
||||
it("backfills missing checkout lease columns when schemaVersion is already current", () => {
|
||||
tmpDir = makeTmpDir();
|
||||
const fusionDir = join(tmpDir, ".fusion");
|
||||
const legacyDb = new Database(fusionDir);
|
||||
|
||||
legacyDb.exec(`
|
||||
CREATE TABLE IF NOT EXISTS __meta (key TEXT PRIMARY KEY, value TEXT);
|
||||
CREATE TABLE IF NOT EXISTS config (
|
||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||
nextId INTEGER DEFAULT 1,
|
||||
nextWorkflowStepId INTEGER DEFAULT 1,
|
||||
settings TEXT DEFAULT '{}',
|
||||
workflowSteps TEXT DEFAULT '[]',
|
||||
updatedAt TEXT
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS tasks (
|
||||
id TEXT PRIMARY KEY,
|
||||
description TEXT NOT NULL,
|
||||
"column" TEXT NOT NULL,
|
||||
createdAt TEXT NOT NULL,
|
||||
updatedAt TEXT NOT NULL
|
||||
);
|
||||
`);
|
||||
legacyDb.exec("INSERT INTO __meta (key, value) VALUES ('schemaVersion', '70')");
|
||||
legacyDb.exec("INSERT INTO __meta (key, value) VALUES ('lastModified', '1000')");
|
||||
legacyDb.exec(`INSERT INTO tasks (id, description, "column", createdAt, updatedAt) VALUES ('FN-lease', 'legacy', 'triage', '2026-01-01', '2026-01-01')`);
|
||||
legacyDb.close();
|
||||
|
||||
const db = new Database(fusionDir);
|
||||
db.init();
|
||||
|
||||
const columns = db.prepare("PRAGMA table_info(tasks)").all() as Array<{ name: string }>;
|
||||
const columnNames = columns.map((column) => column.name);
|
||||
expect(columnNames).toContain("checkedOutBy");
|
||||
expect(columnNames).toContain("checkedOutAt");
|
||||
expect(columnNames).toContain("checkoutNodeId");
|
||||
expect(columnNames).toContain("checkoutRunId");
|
||||
expect(columnNames).toContain("checkoutLeaseRenewedAt");
|
||||
expect(columnNames).toContain("checkoutLeaseEpoch");
|
||||
|
||||
const task = db.prepare("SELECT checkoutLeaseEpoch FROM tasks WHERE id = 'FN-lease'").get() as { checkoutLeaseEpoch: number | null };
|
||||
expect(task.checkoutLeaseEpoch).toBe(0);
|
||||
|
||||
db.close();
|
||||
});
|
||||
|
||||
it("backfills legacy routines table missing agentId with safe defaults", () => {
|
||||
tmpDir = makeTmpDir();
|
||||
const fusionDir = join(tmpDir, ".fusion");
|
||||
|
||||
@@ -1064,6 +1064,7 @@ export class Database {
|
||||
this.migrate();
|
||||
|
||||
// Compatibility backfills that must run even when schemaVersion is current.
|
||||
this.ensureTasksSchemaCompatibility();
|
||||
this.ensureRoutinesSchemaCompatibility();
|
||||
this.ensureInsightRunsSchemaCompatibility();
|
||||
this.ensureEvalTaskResultsSchemaCompatibility();
|
||||
@@ -1086,6 +1087,29 @@ export class Database {
|
||||
* Column additions use `hasColumn()` so they are idempotent — safe to
|
||||
* re-run even if a previous migration partially applied.
|
||||
*/
|
||||
/**
|
||||
* Applies idempotent compatibility fixes for legacy tasks checkout lease columns.
|
||||
*
|
||||
* FN-3879 documented a self-heal for missing checkout lease columns, but the
|
||||
* original column adds lived only in the `version < 20` migration block. Some
|
||||
* legacy/mesh-synced databases report `schemaVersion >= 20` despite never
|
||||
* receiving those columns, so task listing queries can fail with `no such
|
||||
* column: checkoutNodeId`. Running this unconditionally on init guarantees the
|
||||
* canonical lease columns exist.
|
||||
*/
|
||||
private ensureTasksSchemaCompatibility(): void {
|
||||
if (!this.hasTable("tasks")) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.addColumnIfMissing("tasks", "checkedOutBy", "TEXT");
|
||||
this.addColumnIfMissing("tasks", "checkedOutAt", "TEXT");
|
||||
this.addColumnIfMissing("tasks", "checkoutNodeId", "TEXT");
|
||||
this.addColumnIfMissing("tasks", "checkoutRunId", "TEXT");
|
||||
this.addColumnIfMissing("tasks", "checkoutLeaseRenewedAt", "TEXT");
|
||||
this.addColumnIfMissing("tasks", "checkoutLeaseEpoch", "INTEGER DEFAULT 0");
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies idempotent compatibility fixes for legacy routines table shapes.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user