fix: address review findings — buffer exception safety, integrity check ordering

- Move integrity check before migrations/seeds to avoid writing to a
  corrupted database (makes corruption worse)
- Fix buffer swap-before-commit hazard: entries are now only removed from
  the buffer after the transaction succeeds
- Add try-catch to timer-triggered flush to prevent uncaught exceptions
  from crashing the process
- Log warning in close() catch instead of silently swallowing errors

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Timothy Laurent
2026-05-03 08:46:44 -07:00
parent af2bc0e341
commit 2fce9b13c9

View File

@@ -4704,7 +4704,14 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
this.flushAgentLogBuffer();
} else if (!this.agentLogFlushTimer) {
this.agentLogFlushTimer = setTimeout(
() => this.flushAgentLogBuffer(),
() => {
try {
this.flushAgentLogBuffer();
} catch (err) {
// Timer-triggered flush failed — log but don't crash the process.
console.error("[fusion] Timer-triggered agent log flush failed:", err);
}
},
TaskStore.AGENT_LOG_FLUSH_MS,
);
this.agentLogFlushTimer.unref();
@@ -4722,8 +4729,11 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
}
if (this.agentLogBuffer.length === 0) return;
const batch = this.agentLogBuffer;
this.agentLogBuffer = [];
// Snapshot the entries to flush. New entries appended during the
// synchronous transaction will appear past batch.length in
// this.agentLogBuffer, so we splice only the flushed count.
const batch = this.agentLogBuffer.slice();
const flushCount = batch.length;
this.db.transaction(() => {
const stmt = this.db.prepare(`
@@ -4734,6 +4744,11 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
stmt.run(entry.taskId, entry.timestamp, entry.text, entry.type, entry.detail, entry.agent);
}
});
// Remove only the flushed entries. If appendAgentLog added entries
// during the transaction (can't happen in single-threaded Node, but
// defensive), they remain in the buffer.
this.agentLogBuffer.splice(0, flushCount);
this.db.bumpLastModified();
}
@@ -6107,8 +6122,10 @@ ${stepsSection}`;
if (this.agentLogBuffer.length > 0) {
try {
this.flushAgentLogBuffer();
} catch {
} catch (err) {
// Best-effort flush — entries for deleted tasks will fail FK check.
// Log the error instead of silently swallowing it.
console.warn("[fusion] Could not flush remaining agent log entries on close:", err);
}
}
if (this._db) {