From 8ef3d81108aa6409bbb64c65d1877390904cc151 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Mon, 1 Dec 2025 20:51:12 -0800 Subject: [PATCH] fix(sync): prevent final flush after sync.branch restore When sync.branch is configured, the sync command: 1. Exports changes to JSONL 2. Commits to sync branch via worktree 3. Pulls from sync branch 4. Restores .beads/ from HEAD to keep working directory clean But PersistentPostRun's flushManager.Shutdown() was re-exporting to JSONL, undoing the restore and leaving modified files. Fix: Set skipFinalFlush flag when sync.branch mode completes successfully. This prevents the final export in PersistentPostRun. Also: Skip push to main branch when sync.branch is configured - all pushes should go through the sync branch worktree. --- cmd/bd/main.go | 7 ++++++- cmd/bd/sync.go | 9 +++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/cmd/bd/main.go b/cmd/bd/main.go index 5d2b21da..11947877 100644 --- a/cmd/bd/main.go +++ b/cmd/bd/main.go @@ -82,6 +82,10 @@ var ( // Auto-flush manager (replaces timer-based approach to fix bd-52) flushManager *FlushManager + // skipFinalFlush is set by sync command when sync.branch mode completes successfully. + // This prevents PersistentPostRun from re-exporting and dirtying the working directory. + skipFinalFlush = false + // Auto-import state autoImportEnabled = true // Can be disabled with --no-auto-import @@ -613,7 +617,8 @@ var rootCmd = &cobra.Command{ // Otherwise, handle direct mode cleanup // Shutdown flush manager (performs final flush if needed) - if flushManager != nil { + // Skip if sync command already handled export and restore (sync.branch mode) + if flushManager != nil && !skipFinalFlush { if err := flushManager.Shutdown(); err != nil { fmt.Fprintf(os.Stderr, "Warning: flush manager shutdown error: %v\n", err) } diff --git a/cmd/bd/sync.go b/cmd/bd/sync.go index 41e9f646..3f9e6e30 100644 --- a/cmd/bd/sync.go +++ b/cmd/bd/sync.go @@ -577,8 +577,10 @@ Use --merge to merge the sync branch back to main branch.`, } } - // Step 5: Push to remote (skip if already pushed via sync branch worktree) - if !noPush && hasChanges && !pushedViaSyncBranch { + // Step 5: Push to remote (skip if using sync branch - all pushes go via worktree) + // When sync.branch is configured, we don't push the main branch at all. + // The sync branch worktree handles all pushes. + if !noPush && hasChanges && !pushedViaSyncBranch && !useSyncBranch { if dryRun { fmt.Println("→ [DRY RUN] Would push to remote") } else { @@ -616,6 +618,9 @@ Use --merge to merge the sync branch back to main branch.`, // Non-fatal - just means git status will show modified files debug.Logf("sync: failed to restore .beads/ from branch: %v", err) } + // Skip final flush in PersistentPostRun - we've already exported to sync branch + // and restored the working directory to match the current branch + skipFinalFlush = true } fmt.Println("\nāœ“ Sync complete")