Add new bd doctor check that detects when sync-branch is configured
but the pre-push hook is too old (< 0.29.0) to support it. This causes
circular "bd sync" failures where the hook recommends running bd sync
but the user is already running bd sync.
The check:
- Returns error when hook version < 0.29.0 with sync-branch configured
- Returns warning for custom (non-bd) hooks that can't be verified
- Returns OK when hook is compatible or sync-branch not configured
Also adds checkSyncBranchHookQuick() for --check-health mode.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add JSONL-only mode detection in ensureStoreActive() with context-aware
error messages that suggest correct actions based on project state
- Improve error messages in main.go to detect JSONL presence and suggest
appropriate solutions (bd init, --no-db flag, or config.yaml setting)
- Update documentation to use issues.jsonl as canonical filename:
- AGENT_INSTRUCTIONS.md, README.md, resolve-beads-conflict.md
- docs/GIT_INTEGRATION.md
- Update hook template comments to clarify issues.jsonl is canonical
while maintaining backward compatibility for beads.jsonl
Fixes#534🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds comprehensive Git worktree support for beads issue tracking:
Core changes:
- New internal/git/gitdir.go package for worktree detection
- GetGitDir() returns proper .git location (main repo, not worktree)
- Updated all hooks to use git.GetGitDir() instead of local helper
- BeadsDir() now prioritizes main repository's .beads directory
Features:
- Hooks auto-install in main repo when run from worktree
- Shared .beads directory across all worktrees
- Config option no-install-hooks to disable auto-install
- New bd worktree subcommand for diagnostics
Documentation:
- New docs/WORKTREES.md with setup instructions
- Updated CHANGELOG.md and AGENT_INSTRUCTIONS.md
Testing:
- Updated tests to use exported git.GetGitDir()
- Added worktree detection tests
Co-authored-by: Claude <noreply@anthropic.com>
Closes: #478
Stealth mode was adding generic `.beads/` pattern to global gitignore,
which ignored ALL .beads/ folders across all repositories. Users who
want stealth mode in one project but open beads usage in others were
blocked.
Now uses absolute project paths instead:
- `/path/to/project/.beads/`
- `/path/to/project/.claude/settings.local.json`
This allows multiple stealth projects while other repos can use beads
openly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Windows CI was timing out at 30 minutes due to fundamentally slow I/O
(NTFS, Defender scans, slow Git). RAM disk experiments didn't help enough.
Replace two parallel full test jobs with single smoke test job that:
- Builds bd.exe
- Runs version check
- Tests init, create, list, show, update, close
Should complete in <2 minutes vs 30+ minute timeouts.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fix daemon auto-sync delete mutation not reflected in sync branch
When deleting an issue with `bd delete <id> --force`, the daemon auto-sync now properly removes the deleted issue from the sync branch.
**Problem:** The merge logic saw fewer local issues (due to deletion) and would re-add the deleted issue.
**Solution:** Add `ForceOverwrite` option to bypass merge logic when mutations occur. Mutation-triggered exports are authoritative and should overwrite, not merge.
Reviewed-by: stevey
* docs: fix Go version typo in CONTRIBUTING.md (1.25 -> 1.24)
* docs: update Go version requirement to 1.24+ across docs
* docs: fix broken links to docs/ in npm-package/README.md
* docs: fix QUICKSTART.md path reference in AGENTS.md
When BEADS_DIR environment variable points to a separate git repository,
bd sync previously failed with "fatal: 'main' is already used by worktree"
because it computed repoRoot from cwd instead of the beads directory.
This fix detects when beads dir is in a different git repo than cwd and
uses direct git operations (add/commit/push/pull) instead of worktree-based
sync, bypassing the problematic worktree creation entirely.
Cherry-picked from PR #533 (cleaned up unrelated changes).
Co-Authored-By: dand-oss <dand-oss@users.noreply.github.com>
The sanitizeJSONLWithDeletions function was incorrectly removing ALL issues
whose ID appeared in deletions.jsonl, including tombstones. This caused:
1. Second sync after delete: tombstone removed from JSONL by sanitize
2. Import sees ID in deletions.jsonl but no tombstone in JSONL
3. Import creates new tombstone via convertDeletionToTombstone
4. UNIQUE constraint error: tombstone already exists in DB
The fix checks the issue status and only removes non-tombstone issues.
Tombstones are the proper representation of deletions and must be preserved.
Added test: TestSanitizeJSONLWithDeletions_PreservesTombstones
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The importer was not seeing tombstones when building the dbByID map,
causing it to treat tombstone IDs as "new" issues. This led to UNIQUE
constraint violations during INSERT.
- Include tombstones in SearchIssues call (IncludeTombstones: true)
- Skip tombstones when matching by ID instead of trying to update them
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add two new doctor checks for tombstone health:
1. Updated Deletions Manifest check:
- Warns when legacy deletions.jsonl has entries (suggests migration)
- Shows "Migrated to tombstones" when .migrated file exists
- Shows "Using inline tombstones" for new repos
2. New Tombstones check:
- Reports total tombstone count
- Warns about expired tombstones (older than 30 days)
- Shows tombstones expiring within 7 days
- Suggests 'bd compact' to prune expired tombstones
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When CreateTombstone was called on a closed issue, the CHECK constraint
(status = closed) = (closed_at IS NOT NULL) was violated because
closed_at was not cleared. Now setting closed_at = NULL in the UPDATE.
Added regression test for creating tombstone from closed issue.
Fixes: bd-fi05
Generated with Claude Code
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
RAM disk approach proved insufficient for Windows test speedup:
- t.Chdir() panics on ImDisk drives
- Temp-only RAM disk provides marginal improvement (~50%)
- Still times out at 30 minutes
Reverted to simple Windows tests with continue-on-error.
Created bd-bmev to replace with smoke tests instead.
Closed bd-5we as RAM disk approach didn't work.
Reorganize the claude-code-skill into a publishable plugin that can be
installed through the beads-marketplace. Users can now reference the
'beads' skill right after installing the marketplace.
Co-authored-by: Steve Yegge <steve.yegge@gmail.com>
The deleted_at column was defined as TEXT in the schema but code was
trying to scan into sql.NullTime. The ncruces/go-sqlite3 driver only
auto-converts TEXT to time.Time for columns declared as DATETIME/DATE/
TIME/TIMESTAMP. For TEXT columns, it returns raw strings which
sql.NullTime.Scan() cannot handle.
Added parseNullableTimeString() helper that manually parses time strings
and changed all deletedAt variables from sql.NullTime to sql.NullString.
Fixes import failure: "sql: Scan error on column index 22, name
deleted_at: unsupported Scan, storing driver.Value type string into
type *time.Time"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The previous fix didn't handle the multi-repo case - it used bare
'jsonl_content_hash' key but daemon uses 'jsonl_content_hash:<repoKey>'.
Now properly computes repoKey for multi-repo support.
* fix(daemon): check for stale startlock before waiting 5 seconds
When a previous daemon startup left behind a bd.sock.startlock file
(e.g., from a crashed process), the code was waiting 5 seconds before
checking if the lock was stale. This caused unnecessary delays on
every bd command when the daemon wasn't running.
Now checks if the PID in the startlock file is alive BEFORE waiting.
If the PID is dead or unreadable, the stale lock is cleaned up
immediately and lock acquisition is retried.
Fixes ~5s delay when startlock file exists from crashed process.
* perf: add benchmarks for large descriptions, bulk operations, and sync merge
Added three new performance benchmarks to identify bottlenecks in common operations:
1. BenchmarkLargeDescription - Tests handling of 100KB+ issue descriptions
- Measures string allocation/parsing overhead
- Result: 3.3ms/op, 874KB/op allocation
2. BenchmarkBulkCloseIssues - Tests closing 100 issues sequentially
- Measures batch write performance
- Result: 1.9s total, shows write amplification
3. BenchmarkSyncMerge - Tests JSONL merge cycle with creates/updates
- Simulates real sync operations (10 creates + 10 updates per iteration)
- Result: 29ms/op, identifies sync bottlenecks
Added BENCHMARKS.md documentation describing:
- How to run benchmarks with various options
- All available benchmark categories
- Performance targets on M2 Pro hardware
- Dataset caching strategy
- CPU profiling integration
- Optimization workflow
This completes performance testing coverage for previously unmeasured scenarios.
* docs: clarify daemon lock acquisition logic in comments
Improve comments to clarify that acquireStartLock does both:
1. Immediately check for stale locks from crashed processes (avoids 5s delay)
2. If PID is alive, properly wait for legitimate daemon startup (5s timeout)
No code changes - only clarified comment documentation for maintainability.
---------
Co-authored-by: Steve Yegge <steve.yegge@gmail.com>
After bd sync completes with sync.branch mode, the daemon or next CLI
command could see a hash mismatch between the restored JSONL file and
the DB metadata, triggering auto-import which then schedules re-export,
dirtying the working directory.
Two fixes:
1. sync.go: Update jsonl_content_hash after restoreBeadsDirFromBranch
to match the restored file hash
2. daemon_sync.go: Update jsonl_content_hash after performAutoImport
succeeds (was missing, unlike CLI import path)
Fixes: bd-lw0x, bd-hxou
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Can't checkout directly to R: drive. Instead:
1. Checkout normally
2. Use copy-workspace: true to copy to RAM disk
3. Run build/tests from R: working directory
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move everything to RAM disk for maximum I/O speedup:
- Setup RAM disk BEFORE checkout
- Checkout source code directly to R:\work
- Set TEMP/TMP/GOCACHE/GOMODCACHE at job level
- Run build and tests from RAM disk working directory
This ensures ALL file operations happen on RAM, not just temp files.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(config): add no-install-hooks config to disable git hook installation
Add `no-install-hooks` boolean config that prevents git hook installation
during `bd init`. This can be set via:
- Environment variable: BD_NO_INSTALL_HOOKS=1
- Global config: ~/.config/bd/config.yaml with `no-install-hooks: true`
- Local config: .beads/config.yaml with `no-install-hooks: true`
The existing `--skip-hooks` flag continues to work and takes precedence.
Default behavior unchanged: hooks install by default.
* docs: add no-install-hooks to configuration documentation
- Add no-install-hooks to Supported Settings table in CONFIG.md
- Add example in config file section
- Add "Disabling Hook Installation" section to GIT_INTEGRATION.md
with examples for flag, env var, and config file methods
Use chad-golden/setup-ramdisk@v1 to create a 4GB RAM disk (R:) on
Windows runners. Benchmarks show ~1750x faster IOPS compared to
the C: drive (247k vs 140 IOPS).
Redirect all test temp files, GOCACHE, and GOMODCACHE to the RAM
disk. This should dramatically speed up SQLite tests and other
disk-intensive operations that were causing 30+ minute timeouts.
Closes bd-5we
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The .beads/.gitignore now ignores everything by default and explicitly
whitelists tracked files. This fixes confusion about which files to
commit when using protected branches workflow.
Changes:
- Use `*` to ignore all by default, then `!file` to whitelist
- Fix config.json -> config.yaml (wrong filename in negation)
- Update doctor check to validate new patterns
- Update PROTECTED_BRANCHES.md documentation
- Simplify git add instructions to just `git add .beads/`
Fixes#473🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
These tests fail on Windows because:
- os.Symlink requires elevated privileges
- Unix-style permissions (0700, 0600) don't apply
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update Makefile install target to extract and pass git commit/branch via ldflags
- Add -X main.Commit and -X main.Branch to all build configurations in .goreleaser.yml
- Create scripts/install.sh helper for explicit version control during installation
- Add comprehensive tests for commit/branch resolution and output formatting
Fixes github #503: 'bd version' now reports as-built commit hash and branch
information regardless of installation method (make install, go install, or
released binaries from goreleaser).