Add ability to save doctor diagnostics to a JSON file for historical
analysis and bug reporting. The export includes timestamp and platform
info (OS, Go version, SQLite version) for tracking intermittent issues.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When bd ready found no ready work, it always showed 'all issues have
blocking dependencies' even when there were no open issues at all.
Now it checks if any open/in_progress issues exist:
- If no open issues: shows 'No open issues' (green)
- If open issues but all blocked: shows 'all issues have blocking
dependencies' (yellow)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add a new Config Values check to bd doctor that validates:
- flush-debounce: must be a valid duration (e.g., 30s, 1m)
- issue-prefix: must start with letter, alphanumeric with dashes/underscores
- routing.mode: must be auto, maintainer, or contributor
- sync-branch: must be a valid git branch name
- routing paths: warns if configured paths do not exist
- metadata.json database: should be filename (not path), with db extension
- metadata.json jsonl_export: should have .jsonl extension
- deletions_retention_days: must be non-negative if set
This catches misconfigurations before they cause runtime errors.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Run PRAGMA integrity_check to detect database corruption.
Reports any issues found and suggests recovery options.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add --dry-run flag to preview fixes without applying changes
- Handle corrupted/empty/null-byte registry files gracefully
- Treat corrupted registry as empty instead of failing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add 'Sync Branch Health' check that detects:
1. Local sync branch diverged from remote (after force-push reset)
2. Sync branch significantly behind main on source files (20+ commits, 50+ files)
Add --fix support that:
- Handles worktree case (resets within worktree)
- Handles regular branch case (deletes and recreates from main)
- Pushes the reset branch to remote
This helps contributors whose local beads-sync becomes orphaned after
someone else resets the branch.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add a --readonly flag that blocks all write operations, allowing workers
to read beads state without modifying it. Workers can use:
- bd show, bd list, bd ready (read operations)
Workers cannot use:
- bd create, bd update, bd close, bd sync, etc. (write operations)
The flag can be set via:
- --readonly flag on command line
- BD_READONLY=true environment variable
- readonly: true in config file
This enables swarm workers to see their assigned work from a static
snapshot of the beads database without accidentally modifying it.
Commands protected by readonly mode:
- create, update, close, delete, edit
- sync, import, reopen
- comment add, dep add/remove, label add/remove
- repair-deps, compact, migrate, migrate-hash-ids, migrate-issues
- rename-prefix, validate --fix-all, duplicates --auto-merge
- epic close-eligible, jira sync
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Root cause: metadata.json is tracked in git and contains last_bd_version.
When git operations (pull, checkout, merge) reset metadata.json to the
committed version, the upgrade notification would fire repeatedly.
Fix: Store the last used bd version in .beads/.local_version which is
gitignored, so git operations don't affect version tracking.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Also updated CONFIG.md to clarify mass delete threshold requires >5 issues (bd-in6).
The string(rune('0'+i)) pattern produces incorrect characters when i >= 10.
Changed to strconv.Itoa(i) for reliable conversion.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- bd-dtm: Changed stderr printing to use SafetyWarnings in worktree.go
- bd-ciu: Fixed non-deterministic output order in formatVanishedIssues
- bd-dmd: Removed duplicate safety check message in sync.go
- bd-k2n: PushSyncBranch now recreates worktree if cleaned up
- bd-c5m: Fixed string(rune()) in tests to use strconv.Itoa
- bd-8uk: Added test for SafetyWarnings population
- bd-1kf: Fixed mergePriority to handle negative priorities
- bd-xo9: Documented sync.require_confirmation_on_mass_delete config
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add auto-push functionality to PullFromSyncBranch for true one-command sync:
- After successful content merge, auto-push to remote by default
- Safety check: warn (but dont block) if >50% issues vanished AND >5 existed
- Vanished = removed from JSONL entirely, NOT status=closed
Changes:
- Add push parameter to PullFromSyncBranch function
- Add Pushed field to PullResult struct
- Add countIssuesInContent helper for safety check
- Add test for countIssuesInContent function
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When multiple clones commit to beads-sync branch and histories diverge,
git merge would fail. This replaces git's commit-level merge with a
content-based merge that extracts JSONL from base/local/remote and
merges at the semantic level.
Key changes:
- Add divergence detection using git rev-list --left-right
- Extract JSONL content from specific commits for 3-way merge
- Reset to remote's history then commit merged content on top
- Pre-emptive fetch before commit to reduce divergence likelihood
- Deletions.jsonl merged by union (keeps all deletions)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The test was picking up global git config when checking that --skip-merge-driver
didn't set the merge.beads.driver config locally.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add migration 017_close_reason_column.go to create the column
- Update all INSERT statements to include close_reason
- Update all SELECT statements to include close_reason
- Update doctor.go to check for close_reason in schema validation
- Remove workaround code that batch-loaded close reasons from events table
- Fix migrations_test.go to include close_reason in test table schema
This fixes sync loops where close_reason values were silently dropped
because the DB lacked the column despite the struct having the field.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
In multi-repo mode, non-primary repos incorrectly wrote ALL issues to their
local issues.jsonl, including foreign issues from other repos. This caused
prefix mismatch errors on subsequent imports.
The fix adds prefix filtering in flushToJSONLWithState() when:
1. Multi-repo mode is configured (repos.primary set)
2. Current repo is not the primary repo
3. The repo has a configured issue_prefix
Issues not matching the local prefix are filtered out before writing to JSONL.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Extract shared performSync implementation with skipGit parameter:
- createSyncFunc and createLocalSyncFunc now delegate to performSync
- Follows same pattern as performExport and performAutoImport
- Reduces ~80 lines of duplicated code
Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Adds --foreground flag to 'bd daemon --start' that runs the daemon in
foreground instead of forking to background. This enables management by
process supervisors like systemd, supervisord, or similar tools.
Usage: bd daemon --start --foreground
Closes#438🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Consolidate createSyncFunc and createLocalSyncFunc into a shared
performSync function, following the same pattern used for:
- performExport (used by createExportFunc and createLocalExportFunc)
- performAutoImport (used by createAutoImportFunc and createLocalAutoImportFunc)
The new performSync takes a skipGit bool parameter that skips all git
operations (commits, pulls, pushes) and 3-way merge when true.
This reduces ~60 lines of duplicated code and provides a single
implementation to maintain.
Closes: bd-73u
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Fix DEPENDENCIES.md: correct parent-child syntax (child depends on parent)
- Update bd show: display Children instead of Blocks for parent-child deps
- Group dependents by type with distinct labels
Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
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.
When sync.branch is configured, the main branch's .beads/ directory was
showing as modified after every sync, even though the data was correctly
synced to the sync branch worktree.
This happened because:
1. Export writes to main's .beads/
2. Files are copied to worktree and committed there
3. But main's .beads/ now differs from what's committed on main
Fix: After sync completes, restore .beads/ from HEAD to keep the working
directory clean. The actual beads data lives on the sync branch; the main
branch's .beads/ is just a snapshot that should match what's committed.
Add detection for when the current branch is the configured sync branch.
This is a misconfiguration that causes bd sync to fail with:
fatal: 'beads-sync' is already used by worktree
The doctor now reports this as an error with a clear fix:
Switch to your main working branch: git checkout main
Also shows current branch info in the OK case for better visibility.
Add a new command that encapsulates all the work needed to migrate a clone
to use the sync.branch workflow for multi-clone setups like Gas Town:
- Validates current state (not on sync branch, not already configured)
- Creates sync branch if it doesn't exist (from remote or locally)
- Sets up git worktree for the sync branch
- Syncs current beads data to worktree
- Commits initial state to sync branch
- Sets sync.branch configuration
- Pushes sync branch to remote
Usage:
bd migrate-sync beads-sync # Basic migration
bd migrate-sync beads-sync --dry-run # Preview changes
bd migrate-sync beads-sync --force # Reconfigure even if set
Extract performExport() and performAutoImport() shared implementations:
- createExportFunc() and createLocalExportFunc() now use performExport()
with skipGit parameter
- createAutoImportFunc() and createLocalAutoImportFunc() now use performAutoImport()
with skipGit parameter
- createLocalSyncFunc() kept as-is (different flow from createSyncFunc)
Reduces daemon_sync.go by 89 lines (1003 -> 914) while maintaining
identical behavior and test coverage.
All existing tests pass. New shared functions use conditional logic
to skip git operations when skipGit=true, eliminating the need for
separate implementations.
* feat(daemon): add --local flag for git-free operation
Add --local mode to the daemon that allows it to run without a git
repository. This decouples the daemon's core functionality (auto-flush
to JSONL, auto-import from JSONL) from git synchronization.
Changes:
- Add --local flag to daemon command
- Skip git repo check when --local is set
- Add validation that --auto-commit and --auto-push cannot be used with --local
- Create local-only sync functions that skip git operations:
- createLocalSyncFunc: export-only for polling mode
- createLocalExportFunc: export without git commit/push
- createLocalAutoImportFunc: import without git pull
- Update startup message to indicate LOCAL mode
- Update event loop to use local functions when in local mode
This enables use cases like:
- Single-machine issue tracking without git
- Auto-flush to JSONL for backup purposes
- Running daemon in environments without git access
Multi-machine sync still requires git (as expected).
* fix(daemon): skip fingerprint validation in local mode
validateDatabaseFingerprint() calls beads.ComputeRepoID() which
executes git commands. This fails in non-git directories even
with --local flag.
Skip fingerprint validation entirely when running in local mode
since there's no git repository to validate against.
* test(daemon): add comprehensive test coverage for --local mode
Add tests for:
- Flag validation (--local incompatible with --auto-commit/--auto-push)
- Git check skip logic in local mode
- createLocalSyncFunc, createLocalExportFunc, createLocalAutoImportFunc
- Fingerprint validation skip in local mode
- Full integration test in non-git directory
- Export/import round-trip test
---------
Co-authored-by: Claude <noreply@anthropic.com>
When importing JSONL that contains issues in the deletions manifest,
import now:
- Filters out deleted issues before import
- Prints per-issue warning with deletion details (date, actor)
- Shows count of skipped issues in summary
- Suggests --ignore-deletions flag to force import
The new --ignore-deletions flag allows importing issues that are in the
deletions manifest, useful for recovering accidentally deleted issues.
Fixes bd-4zy
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Previously, bd init blocked when JSONL existed with issues but no database,
telling users to run 'bd doctor --fix'. But doctor --fix just ran bd migrate
which requires an existing database - creating a circular dependency.
Now:
- bd init allows fresh clones (JSONL exists, no database) to proceed
- bd init creates the database and imports from JSONL automatically
- bd doctor --fix runs bd init (not migrate) when there's no database
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Test isJiraExternalRef helper with various URL patterns
- Test stats struct initialization
- Test result struct fields
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add ensureStoreActive() check in jiraSyncCmd
- Improve external_ref validation with isJiraExternalRef helper
- Add error logging for UpdateIssue failures
- Make stub conflict resolution functions more honest about limitations
- Fix external_ref counting to not count non-Jira refs as pending
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements Jira synchronization with the following features:
- bd jira sync --pull - Import issues from Jira
- bd jira sync --push - Export issues to Jira
- bd jira sync - Bidirectional sync (pull then push)
- bd jira status - Show sync status and configuration
Conflict resolution options:
- --prefer-local - Always prefer local beads version
- --prefer-jira - Always prefer Jira version
- Default: newer timestamp wins
Additional options:
- --dry-run - Preview sync without making changes
- --create-only - Only create new issues, don't update
- --update-refs - Update external_ref after creating Jira issues
- --state - Filter by issue state (open, closed, all)
Uses Python scripts in examples/jira-import/ for API calls.
Stores jira.last_sync timestamp in config.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
FindBeadsDir() now checks for actual beads project files before returning
a .beads directory. This prevents false positives when ~/.beads/ exists
only for daemon registry (registry.json).
Changes:
- Add hasBeadsProjectFiles() helper that checks for:
- metadata.json or config.yaml (project config)
- *.db files (excluding backups and vc.db)
- *.jsonl files (JSONL-only mode)
- Update FindBeadsDir() to validate directories during tree search
- Add comprehensive tests for project file detection
- Update version_tracking_test.go to create project files
Fixes#420🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
- Change comments from sync.branch to sync-branch for consistency
- Add head -1 to handle malformed YAML with duplicate keys
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Previously sync.branch was stored in the database via bd config set.
Now it is in config.yaml (version controlled, shared across clones):
sync-branch: "beads-sync"
Changes:
- Add sync-branch to .beads/config.yaml
- Update syncbranch.Get() to check config.yaml before database
- Add syncbranch.GetFromYAML() and IsConfigured() for fast checks
- Update hooks to read sync-branch from config.yaml directly
- Update bd doctor to check config.yaml instead of database
- Remove auto-fix (config.yaml changes should be committed)
Precedence: BEADS_SYNC_BRANCH env > config.yaml > database (legacy)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
sync.branch config was lost on clone because it was only stored in
the database (which is gitignored). Now syncbranch.Get() checks:
1. BEADS_SYNC_BRANCH env var (highest)
2. config.yaml sync-branch (tracked, persists across clones)
3. Database config (local override)
4. Empty (use current branch)
Changes:
- Update syncbranch.Get() to check config.yaml
- Update config.yaml template with sync-branch option
- Set sync-branch: beads-sync in this repo config.yaml
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update the embedded template hooks (used by bd init) to also check
for sync.branch configuration. This complements the earlier update
to examples/git-hooks/.
Changes:
- templates/hooks/pre-push: Skip uncommitted check when sync.branch set
- templates/hooks/pre-commit: Skip flush/staging when sync.branch set
- Bump version to 0.27.1 for all hooks
- Add sync.branch hooks change to --whats-new
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When sync.branch is configured, bd sync now commits beads changes
to that branch via git worktree, keeping the user's current branch
(e.g., main) clean of beads sync commits.
Changes:
- Add internal/syncbranch/worktree.go with CommitToSyncBranch and
PullFromSyncBranch functions for worktree-based operations
- Modify sync.go to check sync.branch config and use worktree
functions when configured
- Skip pre-commit hooks in worktree commits (--no-verify) since
bd's pre-commit hook would fail in worktree context
- Re-export after import also uses worktree when sync.branch set
This enables the orchestrator workflow where multiple workers stay
on main but all beads commits flow to a dedicated beads-sync branch.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add -u/--unassigned flag to bd ready command to show only issues
with no assignee. This supports the SCAVENGE protocol where polecats
query the 'Salvage Yard' for unassigned ready work.
Changes:
- Add NoAssignee field to WorkFilter struct
- Update SQLite GetReadyWork to filter by empty/null assignee
- Add --unassigned/-u flag to ready command
- Update RPC protocol and daemon handler
- Add test for NoAssignee filter functionality
Fixes: gt-3rp
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds the ability to filter ready work for issues with no assignee,
which is useful for the SCAVENGE protocol in Gas Town where polecats
need to query the "Salvage Yard" for unclaimed work.
Changes:
- Add Unassigned bool field to types.WorkFilter
- Add --unassigned/-u flag to bd ready command
- Update SQL query in GetReadyWork to filter for NULL/empty assignee
- Add Unassigned field to RPC ReadyArgs for daemon support
- Add tests for the new functionality
Closes: gt-3rp
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When bd init --contributor detects a fork setup (upstream remote exists),
it now configures sync.remote = upstream. This ensures bd sync pulls
beads from the source repo (upstream/main) rather than the fork's
potentially outdated origin/main.
Changes:
- Add sync.remote config in contributor wizard when fork detected
- Modify doSyncFromMain() to use configured sync.remote
- Add getDefaultBranchForRemote() to support any remote name
- Verify configured remote exists before fetching
Fixes bd-bx9
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Use `git rev-parse --git-dir` instead of hardcoded `.git` path to find
the actual git directory. In worktrees, `.git` is a file containing a
gitdir pointer, not a directory.
Changes:
- Add getGitDir() helper in hooks.go
- Update installHooks(), uninstallHooks(), CheckGitHooks() to use it
- Update hooksInstalled(), detectExistingHooks(), installGitHooks() in init.go
- Update checkHooksQuick() in doctor.go
- Update GitHooks() in doctor/fix/hooks.go
- Update tests to use real git repos via `git init`
Fixes bd-63l
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds worktree detection to post-checkout hooks. When entering a git
worktree, displays a warning explaining that daemon mode has limitations
with worktrees and recommends using BEADS_NO_DAEMON=1.
Detection uses `git rev-parse --git-dir` vs `--git-common-dir` comparison
to identify worktree checkouts.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: maphew <maphew@users.noreply.github.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes#417: When using --from-main mode (either explicitly or auto-detected),
git history backfill now defaults to disabled. This prevents creating
incorrect deletion records for locally-created beads that don't exist in
main's git history.
Changes:
- Add resolveNoGitHistoryForFromMain() helper function
- Apply noGitHistory=true for both explicit and auto-detected from-main mode
- Add comprehensive unit tests
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: GraemeF <graeme@graemef.com>
Co-Authored-By: Claude <noreply@anthropic.com>