When sync-branch is configured, check that branch's upstream instead of
current HEAD's upstream. This fixes --auto-push with jj/jujutsu which
always operates in detached HEAD mode.
Adds gitBranchHasUpstream(branch) to check specific branch's upstream
tracking, independent of current HEAD state.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The --ready flag filters to status=open, excluding hooked, in_progress,
blocked, and deferred issues. This makes it easy to see work that can
actually be picked up.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
GetIssue returns (nil, nil) when an issue is not found, but the code
assumed a non-nil issue when err was nil. Added nil checks before
accessing issue fields.
Fixes crash when running `bd mol wisp create` with nonexistent proto.
Closes: gt-2uzn2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(create): Use prefix from routes.jsonl when creating issues with --rig
When using `bd create --rig <name>`, the prefix from routes.jsonl was
being discarded. This caused issues to be created with the target
database's default prefix instead of the route's prefix.
This is particularly problematic when using the redirect mechanism to
share a single database across multiple rigs - the redirect correctly
routes to the shared database, but the prefix was not being applied.
The fix:
1. Capture the prefix from routing.ResolveBeadsDirForRig()
2. Temporarily override the target database's issue_prefix config
3. Restore the original prefix after issue creation
Example scenario that now works:
- routes.jsonl: {"prefix": "aops-", "path": "src/academicOps"}
- src/academicOps/.beads/redirect points to ~/writing/.beads
- `bd create --rig aops "Test"` now creates aops-xxx instead of ns-xxx
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(create): pass prefix via struct field instead of mutating config
The previous approach temporarily mutated the database's issue_prefix
config during cross-rig issue creation, then restored it afterward.
This was fragile in multi-user scenarios where concurrent operations
could see the wrong prefix.
New approach:
- Add PrefixOverride field to types.Issue
- CreateIssue checks PrefixOverride first, uses it if set
- createInRig sets issue.PrefixOverride instead of mutating config
This passes state as a parameter rather than mutating shared state,
making it safe for concurrent multi-user access.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
The duplicate merge target selection now considers structural relationships:
1. Dependent count (children, blocked-by) - highest priority
2. Text reference count - secondary
3. Lexicographically smallest ID - tiebreaker
This fixes the bug where `bd duplicates --auto-merge` would suggest closing
an epic with 17 children instead of the empty shell duplicate.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
sync_base.jsonl is an internal sync mechanism file used for 3-way merge,
not a competing issue database. The doctor check now correctly ignores it.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Apply ui.RenderMarkdown() to comment text in both bd show and
bd comments commands. This enables syntax highlighting, code
blocks, and other Markdown formatting in comments.
Add check to prevent 'bd init' from running inside a git worktree.
Worktrees should share the .beads database from the main repository,
not create their own.
The error message guides users to:
1. Run 'bd init' from the main repository
2. Use 'bd worktree create' to create worktrees with proper
redirects
This prevents the confusing behavior where init would create files
in unexpected locations or fail with "pathspec '.beads/.gitignore' did
not match any files" errors.
According to docs/WORKTREES.md, the proper workflow is:
- Initialize beads once in the main repository
- Use 'bd worktree create' to create worktrees with redirect files
- All worktrees share the single .beads/ database via redirects
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Two code paths bypassed syncbranch.ValidateSyncBranchName(), allowing
main/master to be set as sync.branch and causing worktree conflicts.
Now both use syncbranch.Set() for proper validation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- multirepo.go: discoverChildTypes now returns []string instead of
([]string, error) since error was always nil
- socket_path.go: tmpDir changed from function to const since it
always returned "/tmp" regardless of platform
Fixes CI lint failures caused by unparam linter detecting unused
error returns and constant function results.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Executed-By: beads/crew/dave
Rig: beads
Role: crew
fix(daemon): socket path shortening for long workspace paths
Fixes GH#1001 where long workspace paths (e.g., pytest temp directories) caused
socket path mismatches. The daemon now uses rpc.ShortSocketPath() consistently
with the client.
Changes:
- daemon.go: Use rpc.ShortSocketPath() + EnsureSocketDir() for daemon socket
- daemon_config.go: Update getSocketPathForPID() to use rpc.ShortSocketPath()
- Added tests for long path handling and client-daemon socket path agreement
Co-Authored-By: Eugene Sukhodolin <sukhodolin@users.noreply.github.com>
Tests were failing because beads.FindDatabasePath() follows the
project's .beads/redirect file, causing tests to find unexpected
databases. Fixed by:
- Setting BEADS_DIR in tests that need isolation from git repo detection
- Clearing BEADS_DIR in TestMain to prevent global contamination
- Updating migration test schema to include owner column
This ensures tests work correctly in crew directories that have
redirect files pointing to shared .beads directories.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Executed-By: beads/crew/dave
Rig: beads
Role: crew
When a repo has a .beads/redirect file pointing to a shared beads
directory, restoreBeadsDirFromBranch would fail because it runs
git checkout from the local repo but with a path outside that repo.
Skip the restore when the beads directory is redirected since the
beads dir is managed by a different repo anyway.
Fixes: bd-lmqhe
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add tests for gitHasBeadsChanges and gitHasUncommittedBeadsChanges
functions when .beads directory is redirected to another repo:
- Test non-redirected case (baseline functionality)
- Test redirected case with no changes (clean target repo)
- Test redirected case with changes (modified file in target)
These tests cover the redirect detection logic added in a61cbde6
that uses `git -C <targetRepoDir>` when beadsDir is redirected.
Closes: bd-hxcf
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add three new bd doctor checks for redirect configuration health:
1. CheckRedirectTargetValid - Verifies redirect target exists and
contains a valid beads database
2. CheckRedirectTargetSyncWorktree - Verifies redirect target has
beads-sync worktree when using sync-branch mode
3. CheckNoVestigialSyncWorktrees - Detects unused .beads-sync
worktrees in redirected repos that waste space
These checks help diagnose redirect configuration issues in
multi-clone setups like Gas Town polecats and crew workspaces.
Closes bd-b88x3
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add 'owner' field to Issue struct for tracking the human responsible
for the issue, distinct from 'created_by' which tracks the executor.
Owner is populated from git author email (GIT_AUTHOR_EMAIL or git
config user.email), per Decision 008 for CV accumulation.
Changes:
- Add Owner field to types.Issue with omitempty JSON tag
- Include Owner in content hash computation
- Add owner column migration (036_owner_column.go)
- Update all SQL queries to include owner field
- Add getOwner() helper using git author email fallback chain
- Populate owner in bd create command
- Add owner to RPC CreateArgs protocol
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Executed-By: beads/crew/dave
Rig: beads
Role: crew
Follow-up fixes to PR #1006:
- Silence deprecation warnings when --json flag is set (agent ergonomics)
- Remove unused outputStatusJSON() function
- Remove unused encoding/json import from daemon_status.go
Agents parsing JSON output dont need deprecation warnings cluttering stderr.
The subcommand syntax (bd daemon start) remains the documented path forward.
See bd-ntl3b for deduplication work, bd-kpa7c for type consolidation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Merging with plan to address in follow-up commits:
- Deduplicate daemon_start.go validation logic (call startDaemon())
- Silence deprecation warnings in --json mode for agent ergonomics
- Consolidate DaemonStatusReport with DaemonHealthReport
- Remove unused outputStatusJSON()
- Add tests for new subcommands
Same fix as PR #991 for FindBeadsDir() - the loop condition
dir != "/" && dir != "." doesn't handle Windows drive roots.
On Windows, filepath.Dir("C:\\") returns "C:\\", not "/" or ".".
Changed both functions to check parent == dir to detect filesystem root,
which works correctly on both Unix and Windows.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Insert git config user.name in fallback chain before final $USER default
- Consolidate duplicate actor resolution logic into single function
- Add BEADS_ACTOR as env var alias for MCP compatibility
- Add tests for actor resolution priority
- Update CONFIG.md with Actor Identity Resolution section
The new fallback order is:
--actor flag > BD_ACTOR > BEADS_ACTOR > git user.name > $USER > "unknown"
Co-authored-by: Ohffs <ohffsnotnow@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
On Unix systems, socket paths are limited to 104 chars (macOS) or 108 chars
(Linux). Deep workspace paths like /Volumes/External Drive/Dropbox/...
would exceed this limit and cause daemon startup failures.
This fix:
- Adds ShortSocketPath() which computes /tmp/beads-{hash}/bd.sock for
paths that would exceed the limit
- Keeps backward compatibility: short paths still use .beads/bd.sock
- Updates daemon discovery to check both locations
- Uses SHA256 hash of canonical workspace path for unique directories
Closes GH#1001
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
warnIfCyclesExist was called with nil storage when using daemon mode.
Add nil check to skip cycle warning in daemon mode (daemon handles it).
Fixes#998
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds ability to burn multiple molecules in a single command:
bd mol burn bd-a1 bd-b2 bd-c3
Changes:
- Accept multiple molecule IDs as arguments
- Add BatchBurnResult struct for aggregated results
- Implement burnMultipleMolecules for batch efficiency
- Wisps deleted in single batch call
- Maintains backward compatibility for single ID
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When removing all hooks from an event, the key was being set to null
instead of being deleted. Claude Code expects arrays, not null values,
causing startup failures with 'Expected array, but received null'.
Changes:
- removeHookCommand now deletes the event key when no hooks remain
- installClaude cleans up any existing null values from buggy removal
- Added tests for null value prevention and cleanup
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The sync command was closing the daemon connection without initializing
the direct store, leaving store=nil. This caused errors in post-checkout
hook when running bd sync --import-only.
Fixed by using fallbackToDirectMode() which properly closes daemon and
initializes the store.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added IsBlocked method to Storage interface that checks if an issue is
in the blocked_issues_cache and returns the blocking issue IDs.
The close command now checks for blockers before allowing an issue to
be closed:
- If an issue has open blockers, closing is blocked with an error message
- The --force flag overrides this check
- Works in both daemon mode (RPC) and direct storage mode
- Also handles cross-rig routed IDs
This addresses the bug where agents could close a bead even when it
depends on an open bug/issue.
Closes#962
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add support for lefthook v1.10.0+ jobs array syntax in addition to
the older commands map syntax. Both syntaxes are now checked for
bd hooks run integration.
Fixes#981
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Executed-By: beads/crew/dave
Rig: beads
Role: crew
Parse $EDITOR value to handle editors that need flags like
"zeditor --wait" or "code --wait". Previously the entire string
was treated as the executable name.
Fixes#987
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Executed-By: beads/crew/dave
Rig: beads
Role: crew
Adds a diagnostic check that discovers and reports custom types
used by child repos in multi-repo setups. This is the 'discover'
part of the 'trust + discover' pattern for federation.
The check:
- Lists custom types found in each child repo's config/DB
- Warns about hydrated issues using types not found anywhere
- Is informational only (doesn't fail overall doctor check)
Part of epic: bd-9ji4z
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When database is stale and a read-only command runs in --no-daemon mode,
auto-import from JSONL instead of returning an error. This fixes `gt sling`
failing with 'not a valid bead' after git pull.
Root cause: `ensureDatabaseFresh` would return an error when DB was stale,
but read-only commands like `bd show` should be able to auto-import and
proceed rather than blocking the user.
Changes:
- Modify ensureDatabaseFresh to call autoImportIfNewer() when stale
- Only error if --no-auto-import flag is explicitly set
- Add comprehensive tests for all staleness scenarios
Fixes: bd-9dao
Co-authored-by: Claude <noreply@anthropic.com>
After devcontainer restart (cold-start), `bd --no-daemon show` failed to
find beads because:
1. Read-only commands skipped auto-import
2. Newly created DB had no issue_prefix set, causing import to fail
This fix enables seamless cold-start recovery by:
- Allowing read-only commands (show, list, etc.) to auto-bootstrap when
JSONL exists but DB doesn't
- Setting needsBootstrap flag when falling back from read-only to
read-write mode for missing DB
- Auto-detecting and setting issue_prefix from JSONL during auto-import
when DB is uninitialized
Fixes: gt-b09
Co-authored-by: Claude <noreply@anthropic.com>
* fix(doctor): add .sync.lock and sync_base.jsonl to gitignore
Problem:
- .sync.lock and sync_base.jsonl were missing from GitignoreTemplate
- Files introduced in PR #918 (pull-first sync) appeared as untracked
Solution:
- Add patterns to GitignoreTemplate with explanatory comment
- Add patterns to requiredPatterns for bd doctor validation
Impact:
- Existing users get warning via bd doctor, fix via bd doctor --fix
- New repos get correct gitignore on bd init
Fixes: GH#974
* docs(sync): fix sync_base.jsonl tracking status and add tests
Problem:
- SYNC.md incorrectly documented sync_base.jsonl as "git-tracked"
- No tests validated sync state file gitignore patterns
Solution:
- Update Files Reference table: sync_base.jsonl is "not tracked, per-machine"
- Add TestGitignoreTemplate_ContainsSyncStateFiles
- Add TestRequiredPatterns_ContainsSyncStatePatterns
Relates to: GH#974
Two issues caused `bd migrate sync` to fail when run from a git worktree:
1. Used GetGitDir() instead of GetGitCommonDir() for worktree path
- GetGitDir() returns the worktree-specific path (.bare/worktrees/main)
- GetGitCommonDir() returns the shared git dir (.bare) where new
worktrees can actually be created
2. Used strings.Index instead of LastIndex in GetRepoRoot()
- When user paths contain "worktrees" (e.g., ~/Development/worktrees/),
Index finds the first occurrence and incorrectly strips the path
- LastIndex finds git's internal /worktrees/ directory
Added GetGitCommonDir() to internal/git/gitdir.go for reuse.
Fixes GH#639 (remaining unfixed callsite in migrate_sync.go)
When running `bd sync --import-only` while the daemon is connected, the command
fails with "no database store available for inline import".
Root cause:
1. PersistentPreRun connects to daemon and returns early without initializing
the store global
2. sync command closes the daemon connection (for consistency)
3. sync --import-only calls importFromJSONLInline which requires store != nil
4. Without ensureStoreActive(), the store is never initialized after daemon disconnect
Fix: Call ensureStoreActive() after closing the daemon connection in sync.go.
This ensures the local SQLite store is initialized for all sync operations
that need direct database access.
- Add ensureStoreActive() call after daemon disconnect in sync.go
- Add test documenting the bug and verifying the fix
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add AllowStale field to ListArgs struct to support resilient hook detection.
When set, callers signal they accept potentially stale data rather than
failing on staleness check errors.
This enables gastown checkSlungWork() to fall back gracefully when the
beads database is out of sync with JSONL (common after concurrent agent syncs).
- Add AllowStale bool to ListArgs in internal/rpc/protocol.go
- Pass --allow-stale flag through to RPC in cmd/bd/list.go
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Executed-By: beads/crew/dave
Rig: beads
Role: crew
Add new command to find molecules where a gate has closed and the workflow
is ready to resume:
- `bd ready --gated` - Flag on existing ready command
- `bd mol ready` - Subcommand for discoverability
The command finds molecules where:
1. A gate bead (type=gate) has been closed
2. The step blocked by that gate is now ready
3. The molecule is not currently hooked by any agent
This enables the Deacon patrol to discover and dispatch gate-ready
molecules without explicit waiter tracking, supporting async molecule
resume workflows.
Includes 5 tests verifying:
- No false positives when no gates exist
- Detection of molecules with closed gates
- Filtering out molecules with open gates
- Filtering out already-hooked molecules
- Handling multiple gate-ready molecules
Part of epic bd-ka761 (Gate-based async molecule resume).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Executed-By: beads/crew/dave
Rig: beads
Role: crew
bd list --tree:
- Use actual parent-child dependencies instead of dotted ID hierarchy
- Treat epic dependencies as parent-child relationships
- Sort children by priority (P0 first)
- Fix tree display in daemon mode with read-only store access
bd graph:
- Add --all flag to show dependency graph of all open issues
- Add --compact flag for tree-style rendering (reduces 44+ lines to 13)
- Fix "needs:N" cognitive noise by using semantic colors
- Add blocks:N indicator with semantic red coloring
bd show:
- Tufte-aligned header with status icon, priority, and type badges
- Add glamour markdown rendering with auto light/dark mode detection
- Cap markdown line width at 100 chars for readability
- Mute entire row for closed dependencies (work done, no attention needed)
Design system:
- Add shared status icons (○ ◐ ● ✓ ❄) with semantic colors
- Implement priority colors: P0 red, P1 orange, P2 muted gold, P3-P4 neutral
- Add TrueColor profile for distinct hex color rendering
- Type badges for epic (purple) and bug (red)
Design principles:
- Semantic colors only for actionable items
- Closed items fade (muted gray)
- Icons > text labels for better scanability
Co-Authored-By: SageOx <ox@sageox.ai>
- resolve_conflicts.go: Mark unused `num` parameter with `_`
- .golangci.yml: Add resolve_conflicts.go to G306 exclusion (JSONL files use 0644)
- .golangci.yml: Add doctor/git.go to G304 exclusion (safe path construction)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Problem:
- When dbPath is set to relative fallback (".beads/beads.db"),
findJSONLPath() returns a relative path
- daemon_sync_branch.go calls filepath.Rel(absolutePath, relativePath)
which fails with: "Rel: can't make .beads/issues.jsonl relative to ..."
Solution:
- Canonicalize dbPath at assignment in main.go:471 (source fix)
- Add defensive guard in findJSONLPath() (defense-in-depth)
- Use utils.CanonicalizePath() for OS-agnostic handling
(symlinks, case normalization on macOS)
Testing:
- Add TestFindJSONLPath_RelativeDbPath (tracer bullet for bug)
- Add edge case tests for BEADS_JSONL and empty dbPath
- All sync mode tests pass including daemon E2E
Reviewed by beads/crew/wolf. Fixes daemon mode silently ignoring --due and --defer flags. Adds comprehensive tests including TestDualPathParity for regression prevention.
Adds runBDInProcessAllowError helper and dry-run test coverage.
Interrupted work - committing to preserve progress.
Co-Authored-By: Claude <noreply@anthropic.com>
Implements a new command to resolve git merge conflict markers in JSONL files.
Features:
- Mechanical mode (default): deterministic merge using updated_at timestamps
- Closed status wins over open
- Higher priority (lower number) wins
- Notes are concatenated when different
- Dependencies are unioned
- Dry-run mode to preview changes
- JSON output for agent integration
- Automatic backup creation before changes
The command defaults to resolving .beads/beads.jsonl but accepts any file path.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When an external hook manager config file (like .pre-commit-config.yaml) exists
but the actual hooks are bd shims, doctor now correctly reports OK instead of
warning that the manager doesn't call bd.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(sync): implement pull-first synchronization strategy
- Add --pull-first flag and logic to sync command
- Introduce 3-way merge stub for issue synchronization
- Add concurrent edit tests for the pull-first flow
Ensures local changes are reconciled with remote updates before pushing to prevent data loss.
* feat(sync): implement 3-way merge and state tracking
- Implement 3-way merge algorithm for issue synchronization
- Add base state storage to track changes between syncs
- Add comprehensive tests for merge logic and persistence
Ensures data consistency and prevents data loss during concurrent
issue updates.
* feat(sync): implement field-level conflict merging
- Implement field-level merge logic for issue conflicts
- Add unit tests for field-level merge strategies
Reduces manual intervention by automatically resolving overlapping updates at the field level.
* refactor(sync): simplify sync flow by removing ZFC checks
The previous sync implementation relied on Zero-False-Convergence (ZFC)
staleness checks which are redundant following the transition to
structural 3-way merging. This legacy logic added complexity and
maintenance overhead without providing additional safety.
This commit introduces a streamlined sync pipeline:
- Remove ZFC staleness validation from primary sync flow
- Update safety documentation to reflect current merge strategy
- Eliminate deprecated unit tests associated with ZFC logic
These changes reduce codebase complexity while maintaining data
integrity through the robust structural 3-way merge implementation.
* feat(sync): default to pull-first sync workflow
- Set pull-first as the primary synchronization workflow
- Refactor core sync logic for better maintainability
- Update concurrent edit tests to validate 3-way merge logic
Reduces merge conflicts by ensuring local state is current before pushing changes.
* refactor(sync): clean up lint issues in merge code
- Remove unused error return from MergeIssues (never returned error)
- Use _ prefix for unused _base parameter in mergeFieldLevel
- Update callers to not expect error from MergeIssues
- Keep nolint:gosec for trusted internal file path
* test(sync): add mode compatibility and upgrade safety tests
Add tests addressing Steve's PR #918 review concerns:
- TestSyncBranchModeWithPullFirst: Verifies sync-branch config
storage and git branch creation work with pull-first
- TestExternalBeadsDirWithPullFirst: Verifies external BEADS_DIR
detection and pullFromExternalBeadsRepo
- TestUpgradeFromOldSync: Validates upgrade safety when
sync_base.jsonl doesn't exist (first sync after upgrade)
- TestMergeIssuesWithBaseState: Comprehensive 3-way merge cases
- TestLabelUnionMerge: Verifies labels use union (no data loss)
Key upgrade behavior validated:
- base=nil (no sync_base.jsonl) safely handles all cases
- Local-only issues kept (StrategyLocal)
- Remote-only issues kept (StrategyRemote)
- Overlapping issues merged (LWW scalars, union labels)
* fix(sync): report line numbers for malformed JSON
Problem:
- JSON decoding errors when loading sync base state lacked line numbers
- Difficult to identify location of syntax errors in large state files
Solution:
- Include line number reporting in JSON decoder errors during state loading
- Add regression tests for malformed sync base file scenarios
Impact:
- Users receive actionable feedback for corrupted state files
- Faster troubleshooting of manual configuration errors
* fix(sync): warn on large clock skew during sync
Problem:
- Unsynchronized clocks between systems could lead to silent merge errors
- No mechanism existed to alert users of significant timestamp drift
Solution:
- Implement clock skew detection during sync merge
- Log a warning when large timestamp differences are found
- Add comprehensive unit tests for skew reporting
Impact:
- Users are alerted to potential synchronization risks
- Easier debugging of time-related merge issues
* fix(sync): defer state update until remote push succeeds
Problem:
- Base state updated before confirming remote push completion
- Failed pushes resulted in inconsistent local state tracking
Solution:
- Defer base state update until after the remote push succeeds
Impact:
- Ensures local state accurately reflects remote repository status
- Prevents state desynchronization during network or push failures
* fix(sync): prevent concurrent sync operations
Problem:
- Multiple sync processes could run simultaneously
- Overlapping operations risk data corruption and race conditions
Solution:
- Implement file-based locking using gofrs/flock
- Add integration tests to verify locking behavior
Impact:
- Guarantees execution of a single sync process at a time
- Eliminates potential for data inconsistency during sync
* docs: document sync architecture and merge model
- Detail the 3-way merge model logic
- Describe the core synchronization architecture principles
* fix(lint): explicitly ignore lock.Unlock return value
errcheck linter flagged bare defer lock.Unlock() calls. Wrap in
anonymous function with explicit _ assignment to acknowledge
intentional ignore of unlock errors during cleanup.
* fix(lint): add sync_merge.go to G304 exclusions
The loadBaseState and saveBaseState functions use file paths derived
from trusted internal sources (beadsDir parameter from config). Add
to existing G304 exclusion list for safe JSONL file operations.
* feat(sync): integrate sync-branch into pull-first flow
When sync.branch is configured, doPullFirstSync now:
- Calls PullFromSyncBranch before merge
- Calls CommitToSyncBranch after export
This ensures sync-branch mode uses the correct branch for
pull/push operations.
* test(sync): add E2E tests for sync-branch and external BEADS_DIR
Adds comprehensive end-to-end tests:
- TestSyncBranchE2E: verifies pull→merge→commit flow with remote changes
- TestExternalBeadsDirE2E: verifies sync with separate beads repository
- TestExternalBeadsDirDetection: edge cases for repo detection
- TestCommitToExternalBeadsRepo: commit handling
* refactor(sync): remove unused rollbackJSONLFromGit
Function was defined but never called. Pull-first flow saves base
state after successful push, making this safety net unnecessary.
* test(sync): add export-only mode E2E test
Add TestExportOnlySync to cover --no-pull flag which was the only
untested sync mode. This completes full mode coverage:
- Normal (pull-first): sync_test.go, sync_merge_test.go
- Sync-branch: sync_modes_test.go:TestSyncBranchE2E (PR#918)
- External BEADS_DIR: sync_external_test.go (PR#918)
- From-main: sync_branch_priority_test.go
- Local-only: sync_local_only_test.go
- Export-only: sync_modes_test.go:TestExportOnlySync (this commit)
Refs: #911
* docs(sync): add sync modes reference section
Document all 6 sync modes with triggers, flows, and use cases.
Include mode selection decision tree and test coverage matrix.
Co-authored-by: Claude <noreply@anthropic.com>
* test(sync): upgrade sync-branch E2E tests to bare repo
- Replace mocked repository with real bare repo setup
- Implement multi-machine simulation in sync tests
- Refactor test logic to handle distributed states
Coverage: sync-branch end-to-end scenarios
* test(sync): add daemon sync-branch E2E tests
- Implement E2E tests for daemon sync-branch flow
- Add test cases for force-overwrite scenarios
Coverage: daemon sync-branch workflow in cmd/bd
* docs(sync): document sync-branch paths and E2E architecture
- Describe sync-branch CLI and Daemon execution flow
- Document the end-to-end test architecture
* build(nix): update vendorHash for gofrs/flock dependency
New dependency added for file-based sync locking changes the
Go module checksum.
---------
Co-authored-by: Claude <noreply@anthropic.com>