From 6d9b7f91b23be2c5f944e556e3825935ca1beb46 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Tue, 7 Apr 2026 13:46:37 -0500 Subject: [PATCH] fix(gsd): detect concurrent event log growth during reconcile Adds a pre-write guard in reconcileWorktreeLogs: re-reads the event log before overwriting and retries if it grew since the initial read. Prevents appendEvent calls between read and rewrite from being silently dropped by the atomic overwrite. --- src/resources/extensions/gsd/workflow-reconcile.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/resources/extensions/gsd/workflow-reconcile.ts b/src/resources/extensions/gsd/workflow-reconcile.ts index 035b4222f..2a27eb108 100644 --- a/src/resources/extensions/gsd/workflow-reconcile.ts +++ b/src/resources/extensions/gsd/workflow-reconcile.ts @@ -433,6 +433,14 @@ function _reconcileWorktreeLogsInner( const merged = indexed.map(({ e }) => e); // Step 7: Write merged event log FIRST (so crash recovery can re-derive DB state) + // Guard: detect concurrent appendEvent calls between our read (step 1) and + // this rewrite. If the log grew, re-read and retry to avoid dropping events. + const preWriteEvents = readEvents(mainLogPath); + if (preWriteEvents.length > mainEvents.length) { + logWarning("reconcile", `Event log grew during reconcile (${mainEvents.length} → ${preWriteEvents.length}), retrying with fresh read`); + return _reconcileWorktreeLogsInner(mainBasePath, worktreeBasePath); + } + const baseEvents = mainEvents.slice(0, forkPoint + 1); const mergedLog = baseEvents.concat(merged); const logContent = mergedLog.map((e) => JSON.stringify(e)).join("\n") + (mergedLog.length > 0 ? "\n" : "");