Commit Graph

15 Commits

Author SHA1 Message Date
slit
f36c8f6556 fix(sync): Handle redirected beads directories in gitCommitBeadsDir
When beads directory is redirected to a different repository (via bd-arjb),
gitCommitBeadsDir() was running git add from the current working directory's
repo root instead of the beads directory's repo root, causing exit status 128.

This fix adds the same redirect handling already present in gitHasBeadsChanges()
and gitHasUncommittedBeadsChanges() - checking GetRedirectInfo() and using
filepath.Dir(beadsDir) as the repo root when redirected.

Fixes: git add failed: exit status 128 errors during bd sync
2026-01-13 11:51:20 -08:00
Phredrick Phool
465f39b366 Merge remote-tracking branch 'origin/main' into fix-jj-detached-head-upstream-check
# Conflicts:
#	cmd/bd/sync_git_test.go
2026-01-12 06:49:51 -06:00
Phredrick Phool
78fec4bc45 fix(daemon): check sync-branch upstream for jj/jujutsu compatibility
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>
2026-01-12 06:44:31 -06:00
Steve Yegge
8942261a12 fix: skip beads restore when directory is redirected (bd-lmqhe)
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>
2026-01-10 22:09:13 -08:00
Peter Chanthamynavong
1561374c04 feat(sync): pull-first sync with 3-way merge (#918)
* 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>
2026-01-07 21:27:20 -08:00
beads/crew/wolf
a61cbde692 fix(sync): handle .beads/redirect in git status checks (bd-arjb)
When running from a crew directory with .beads/redirect pointing to a shared
beads directory in another repo, git status commands would fail with:
  fatal: /path/to/mayor/rig/.beads: is outside repository

The fix detects when beadsDir is redirected and runs git commands from the
directory containing the actual .beads/ using git -C <targetRepoDir>.

Updated functions:
- gitHasBeadsChanges(): checks redirect and runs git from target repo
- gitHasUncommittedBeadsChanges(): same fix for pre-flight check

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 19:57:19 -08:00
emma
9b84ef73dd fix(sync): atomic export and force-push detection (bd-3bhl, bd-4hh5)
bd-3bhl: Add sync rollback on git commit failure
- Use exportToJSONLDeferred() instead of exportToJSONL() for atomic sync
- Call finalizeExport() only after git commit succeeds
- Rollback JSONL from git HEAD on commit failure
- Add rollbackJSONLFromGit() helper function
- Coverage: regular commit, sync branch, external beads repo paths

bd-4hh5: Fix false-positive force-push detection
- Use explicit refspec in CheckForcePush fetch
- +refs/heads/beads-sync:refs/remotes/origin/beads-sync
- Ensures tracking ref is always created/updated
- Fixes stale ref comparison causing false positives

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 23:14:11 -08:00
ruby
32aea46ca6 test(sync): add unit tests for gitHasUncommittedBeadsChanges (bd-p7i2)
Extract parseGitStatusForBeadsChanges helper function from
gitHasUncommittedBeadsChanges to enable unit testing without git setup.

Add 14 test cases covering:
- Empty/no changes
- Modified files (staged, unstaged, both)
- Added files (staged, with modifications)
- Untracked files (should return false)
- Deleted files (should return false)
- Renamed/copied files
- Edge cases (short status line)

Part of GH#885 follow-up.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 16:22:20 -08:00
beads/crew/fang
44a5c3a0ec feat: detect uncommitted JSONL changes before sync (GH#885, bd-vd8e)
Add pre-flight safety check to detect when a previous sync exported
but failed before commit, leaving JSONL in an inconsistent state.

- Add gitHasUncommittedBeadsChanges() helper in sync_git.go
- Call in sync pre-flight checks after merge/rebase check
- If uncommitted changes detected, force re-export to reconcile state

This catches the failure mode early before it compounds across worktrees.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 15:10:47 -08:00
dave
6c4b67f65d fix: respect sync.remote config in bd sync (GH#872, bd-ypvj)
The sync.remote config was being set via `bd config set sync.remote <name>`
but `bd sync` was still using 'origin' for git pull/push operations.

Changes:
- Updated gitPull() and gitPush() in sync_git.go to accept a configuredRemote
  parameter that takes precedence over git's branch tracking config
- Updated sync.go to read sync.remote config and pass it to gitPull/gitPush
- Updated daemon_sync.go to read sync.remote config for all daemon sync ops
- Added user-facing messages to show which remote is being used

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Executed-By: beads/crew/dave
Rig: beads
Role: crew
2026-01-03 13:24:28 -08:00
beads/crew/emma
363c5a3819 fix: bd sync fails with exit 128 in bare repo worktrees (GH#827)
Two fixes for bare repo worktree setups:

1. fork_protection.go: Use git.GetGitDir() instead of hardcoding .git
   In worktrees, .git is a file containing gitdir path, not a directory.
   Using GetGitDir() handles this correctly.

2. sync_git.go: Simplify gitHasBeadsChanges to use absolute paths
   The previous code used git -C main-repo-root status, but in bare
   repo worktrees GetMainRepoRoot() returns the parent of the bare repo
   which is not a valid working tree. Using absolute paths without -C
   lets git find the repo from cwd, which always works.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 11:05:05 -08:00
Steve Yegge
d3b6855aa9 fix: resolve lint errors blocking CI
- Add explicit error checking for fmt.Fprintf/Fprintln in claude.go
- Add gosec nolint for safe exec.CommandContext calls in sync_git.go
- Remove unused error return from findTownRoutes in routes.go

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 15:58:24 -08:00
Steve Yegge
71e2f22849 fix: Add nolint comments for gosec/errcheck/unparam warnings
Fixes CI lint failures by adding appropriate nolint directives for:
- G204 (subprocess with variable) - git commands with trusted inputs
- G304 (file inclusion via variable) - paths from internal helpers
- G302/G306 (file permissions) - .gitignore needs 0644
- errcheck (unchecked return values) - fmt.Fprint* to stdout/stderr
- unparam (unused parameters) - reserved for future use
2025-12-29 14:39:43 -08:00
Steve Yegge
7ff5481966 fix: resolve golangci-lint errors (errcheck, gosec, unparam)
- orphans.go: check fmt.Scanln return value
- edit.go: #nosec G204 for trusted $EDITOR execution
- sync_git.go: #nosec G204 for git commands with internal branch names
- worktree_cmd.go: #nosec G304/G306 for .gitignore file operations
- migrations: handle rows.Close() error
- doctor_pollution.go: mark unused path parameter

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 20:50:54 -08:00
Steve Yegge
6c14fd2225 refactor: Split large cmd/bd files to meet 800-line limit (bd-xtf5)
Split 6 files exceeding 800 lines by extracting cohesive function groups:

- show.go (1592→578): extracted show_thread.go, close.go, edit.go, update.go
- doctor.go (1295→690): extracted doctor_fix.go, doctor_health.go, doctor_pollution.go
- sync.go (1201→749): extracted sync_git.go
- compact.go (1199→775): extracted compact_tombstone.go, compact_rpc.go
- linear.go (1190→641): extracted linear_sync.go, linear_conflict.go
- main.go (1148→800): extracted main_help.go, main_errors.go, main_daemon.go

All files now under 800-line acceptance criteria.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 18:43:09 -08:00