From e8e5b850a04a31f0b6c9237cea34aebbc9d7fc02 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Thu, 19 Mar 2026 11:24:18 -0700 Subject: [PATCH] Fix race conditions in state persistence by reading fresh session before writing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Batch-clearing stale flags in list and blindly writing back the session in review could clobbер concurrent changes. Use per-session get/set instead of whole-state load/save, and re-read before clearing in_review. --- src/commands/list.ts | 12 ++++++------ src/commands/review.ts | 10 +++++++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/commands/list.ts b/src/commands/list.ts index eb8c839..71413ce 100644 --- a/src/commands/list.ts +++ b/src/commands/list.ts @@ -61,13 +61,13 @@ export async function action(opts: { json?: boolean }) { ) const statuses = Object.fromEntries(statusEntries) - // Batch-clear stale in_review flags in a single write - if (staleReviewBranches.length > 0) { - const freshState = await state.load(root) - for (const branch of staleReviewBranches) { - if (freshState.sessions[branch]) freshState.sessions[branch].in_review = false + // Clear stale in_review flags via setSession to avoid clobbering concurrent writes + for (const branch of staleReviewBranches) { + const fresh = await state.getSession(root, branch) + if (fresh) { + fresh.in_review = false + await state.setSession(root, fresh).catch(() => {}) } - await state.save(root, freshState).catch(() => {}) } if (opts.json) { diff --git a/src/commands/review.ts b/src/commands/review.ts index 56ae0f2..faf6560 100644 --- a/src/commands/review.ts +++ b/src/commands/review.ts @@ -69,7 +69,7 @@ Your thoughts, in brief. if (extra) prompt += "\n\n" + extra session.in_review = true - await state.setSession(root, session).catch(() => {}) + await state.setSession(root, session) try { if (opts.print) { @@ -82,8 +82,12 @@ Your thoughts, in brief. } } finally { spin.stop() - session.in_review = false - await state.setSession(root, session).catch(() => {}) + // Load fresh session to avoid clobbering changes made during the review + const fresh = await state.getSession(root, session.branch) + if (fresh) { + fresh.in_review = false + await state.setSession(root, fresh).catch(() => {}) + } if (!opts.print) await saveChanges(session.worktree, session.branch).catch(() => {}) } }