feat(FN-5115): complete Step 2 — add done pause backfills

Fusion-Task-Id: FN-5115
Fusion-Task-Lineage: 5c3d9d42-9dcd-4ebb-ba10-8a4b14d541dd
This commit is contained in:
Fusion (runfusion.ai)
2026-05-19 05:28:33 -07:00
committed by gsxdsm
parent 35b00d2869
commit f36172f564
2 changed files with 44 additions and 1 deletions

View File

@@ -120,7 +120,7 @@ export function probeFts5(db: DatabaseSync): boolean {
// ── Schema Definition ────────────────────────────────────────────────
const SCHEMA_VERSION = 87;
const SCHEMA_VERSION = 88;
function normalizeTaskComments(
steeringComments: SteeringComment[] | undefined,
@@ -3433,6 +3433,28 @@ export class Database {
});
}
if (version < 88) {
this.applyMigration(88, () => {
try {
const result = this.db
.prepare(`UPDATE tasks
SET paused = 0,
userPaused = 0,
pausedByAgentId = NULL,
pausedReason = NULL
WHERE column = 'done'
AND (paused = 1
OR userPaused = 1
OR pausedByAgentId IS NOT NULL
OR pausedReason IS NOT NULL)`)
.run();
console.log(`[done-paused-backfill] db.ts migration repaired ${result.changes} done task rows`);
} catch (error) {
console.warn("[done-paused-backfill] db.ts migration failed", error);
}
});
}
}
/**

View File

@@ -882,6 +882,8 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
private lastKnownModified: number = 0;
/** ISO timestamp of last poll — used to filter changed tasks */
private lastPollTime: string | null = null;
/** One-shot startup sweep flag for clearing stale pause fields on done tasks. */
private donePauseBackfillDone = false;
/** Short-lived startup memo for repeated slim listTasks reads before steady-state watch/polling. */
private startupSlimListMemo = new Map<string, { expiresAt: number; promise: Promise<Task[]> }>();
private static readonly STARTUP_SLIM_LIST_MEMO_TTL_MS = 2_500;
@@ -6513,6 +6515,25 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
this.taskCache.set(task.id, { ...task });
}
if (!this.donePauseBackfillDone) {
const repairedTaskIds: string[] = [];
for (const [taskId, cachedTask] of this.taskCache.entries()) {
if (cachedTask.column !== "done") continue;
if (!this.clearDoneTransientFields(cachedTask)) continue;
await this.atomicWriteTaskJson(this.taskDir(taskId), cachedTask);
this.taskCache.set(taskId, { ...cachedTask });
repairedTaskIds.push(taskId);
}
this.donePauseBackfillDone = true;
storeLog.info("done-task pause metadata backfill completed", {
phase: "watch:done-pause-backfill",
repairedCount: repairedTaskIds.length,
repairedTaskIds: repairedTaskIds.slice(0, 20),
});
}
// Store current lastModified
this.lastKnownModified = this.db.getLastModified();
// Initialize lastPollTime so the first checkForChanges() cycle filters by