Commit Graph

925 Commits

Author SHA1 Message Date
Steve Yegge
2c86404d65 fix: resolve P2 sync noise and cleanup issues
- bd-6pni: Auto-filter tombstoned issues with mismatched prefixes during
  import instead of failing. Tombstones from contributor PRs with different
  test prefixes are pollution and safe to ignore.

- bd-ffr9: Stop recreating deletions.jsonl after tombstone migration.
  Added IsTombstoneMigrationComplete() check to all code paths that write
  to the legacy deletions manifest.

- bd-admx: Fix perpetual "JSONL file hash mismatch" warning. Now clears
  both export_hashes AND jsonl_file_hash when mismatch detected, so the
  warning doesn't repeat.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 00:56:06 -08:00
Steve Yegge
8c45069228 fix(doctor,sync): clean up deletions manifest and reduce sync noise
- bd-8v5o: When doctor --fix hydrates issues from git history, also
  remove them from the deletions manifest to prevent perpetual skip
  warnings during sync

- bd-wsqt: Remove verbose per-issue "Skipping bd-xxx" messages during
  sync. Caller already shows summary of skipped issues.

Added RemoveDeletions() function to deletions package with tests.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 00:42:36 -08:00
Steve Yegge
171f7de250 Merge pull request #584 from rsnodgrass/repair-hashes
fix(rename-prefix): use hash IDs instead of sequential in --repair mode
2025-12-16 00:42:32 -08:00
Steve Yegge
4657ec7640 Merge pull request #569 from crcatala/feature/daemon-status-config
feat(daemon): show configuration in `bd daemon --status` output
2025-12-16 00:35:07 -08:00
Steve Yegge
07fc9bf094 Merge pull request #564 from deblasis/fix-daemon-stale-cache
fix(daemon): detect external db file replacement
2025-12-16 00:34:57 -08:00
Ryan Snodgrass
421d41dfa0 docs: add detailed comments explaining ID formats and merge deletion logic
Document the intent and nuances of recent fixes:

internal/importer/utils.go:
- RenameImportedIssuePrefixes: explain the three ID formats (sequential,
  hash-based, hierarchical) and how prefix renaming preserves identity
- isValidIDSuffix: document why dots are allowed (hierarchical parent-child
  relationships) and what characters are rejected

cmd/bd/deletion_tracking.go:
- isIssueNotFoundError: explain why "not found" is success during merge
  (issue may be tombstoned, never existed locally, or manually deleted)
- Deletion loop: document what "accepted deletions" means and why we
  tolerate missing issues during the pruning phase
2025-12-16 00:17:40 -08:00
Ryan Snodgrass
e3d8119f8e fix(sync): tolerate "issue not found" during 3-way merge deletion
During sync, the 3-way merge logic tries to delete issues that were
removed remotely. If an issue is already gone (tombstoned or never
existed locally), that shouldn't be an error - the goal is just to
ensure the issue is deleted.

Changes:
- Add isIssueNotFoundError helper to detect missing issue errors
- Skip "issue not found" errors during merge deletion (count as success)
- Update stats output to show already-gone count when relevant
2025-12-16 00:15:35 -08:00
Brennon Bortz
68135806c6 feat(show): Display status for all dependent issues (#583)
Update bd show command to include status information for all dependency
types (children, blocks, related, discovered). Issues now display with
format [P1 - closed] instead of just [P1].

This provides better visibility into the state of dependent issues
without needing to run bd show on each individual issue.

Changes:
- Updated daemon mode formatting for all dependency types (lines 208, 214, 220, 226)
- Updated direct mode formatting for all dependency types (lines 400, 406, 412, 418)
- Changed format from "[P%d]" to "[P%d - %s]" with dash separator

Example output:
Children (9):
  ↳ pma-an8: Create config.py module [P1 - closed]
  ↳ pma-38g: Create utils.py module [P1 - open]

Blocks (1):
  ← pma-uzm: Adapt validate_outputs.py [P1 - blocked]

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

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-16 00:13:51 -08:00
Charles P. Cross
a69e94a958 Auto-disable daemon in git worktrees for safety (#567)
* feat: auto-disable daemon in git worktrees for safety

Implement worktree daemon compatibility as proposed in the analysis.
The daemon is now automatically disabled when running in a git worktree
unless sync-branch is configured.

Git worktrees share the same .beads directory, and the daemon commits
to whatever branch its working directory has checked out. This causes
commits to go to the wrong branch when using daemon in worktrees.

- Add shouldDisableDaemonForWorktree() helper that checks:
  1. If current directory is a git worktree (via git rev-parse)
  2. If sync-branch is configured (env var or config.yaml)
- Modify shouldAutoStartDaemon() to call the helper
- Modify daemon connection logic in main.go to skip connection
- Add FallbackWorktreeSafety constant for daemon status reporting
- Update warnWorktreeDaemon() to skip warning when sync-branch configured

- In worktree WITHOUT sync-branch: daemon auto-disabled, direct mode used
- In worktree WITH sync-branch: daemon enabled (commits go to dedicated branch)
- In regular repo: no change (daemon works as before)

- Added comprehensive unit tests for shouldDisableDaemonForWorktree()
- Added integration tests for shouldAutoStartDaemon() in worktree contexts
- Manual E2E testing verified correct behavior

- Updated WORKTREES.md with new automatic safety behavior
- Updated DAEMON.md with Git Worktrees section

* feat: check database config for sync-branch in worktree safety logic

Previously, the worktree daemon safety check only looked at:
- BEADS_SYNC_BRANCH environment variable
- sync-branch in config.yaml

This meant users who configured sync-branch via `bd config set sync-branch`
(which stores in the database) would still have daemon disabled in worktrees.

Now the check also reads sync.branch from the database config table,
making daemon work in worktrees when sync-branch is configured via any method.

Changes:
- Add IsConfiguredWithDB() function that checks env, config.yaml, AND database
- Add findBeadsDB() to locate database (worktree-aware via git-common-dir)
- Add getMainRepoRoot() helper using git rev-parse
- Add getConfigFromDB() for lightweight database reads
- Update shouldDisableDaemonForWorktree() to use IsConfiguredWithDB()
- Update warnWorktreeDaemon() to use IsConfiguredWithDB()
- Add test case for database config path

* refactor: use existing beads.FindDatabasePath() instead of duplicating code

Remove duplicate getMainRepoRoot() and findBeadsDB() functions from
syncbranch.go and use the existing beads.FindDatabasePath() which is
already worktree-aware.

Changes:
- Replace custom findBeadsDB() with beads.FindDatabasePath()
- Remove duplicate getMainRepoRoot() (git.GetMainRepoRoot() exists)
- Remove unused imports (exec, strings, filepath)
- Clean up debug logging in tests

---------

Co-authored-by: Charles P. Cross <cpdata@users.noreply.github.com>
2025-12-16 00:06:19 -08:00
Ryan Snodgrass
fa566a9700 fix(rename-prefix): use hash IDs instead of sequential in --repair mode
The --repair flag was generating sequential IDs (sageox-9895, sageox-9896)
instead of hash-based IDs (sageox-jwnv, sageox-urtm). This fix uses the
proper GenerateIssueID function from sqlite package to generate consistent
hash-based IDs during prefix repair operations.

Changes:
- Import sqlite package for hash ID generation
- Add generateRepairHashID helper that uses sqlite.GenerateIssueID
- Track used IDs within batch to avoid collisions
- Update test to verify hash IDs instead of sequential
2025-12-15 23:59:21 -08:00
Alessandro De Blasis
78c248a17a fix(daemon): detect external db file replacement
When git merge replaces the .beads/beads.db file, the daemon's
SQLite connection becomes stale (still reading deleted inode).
This adds FreshnessChecker that detects file replacement via
inode/mtime comparison and triggers automatic reconnection.

Implementation:
- freshness.go: monitors db file for replacement
- store.go: adds EnableFreshnessChecking() and reconnect()
- queries.go: calls checkFreshness() on GetIssue/SearchIssues
- daemon.go: enables freshness checking at startup
- freshness_test.go: comprehensive tests including merge scenario

Code quality (per review):
- Extract configureConnectionPool() helper to reduce duplication
- Handle Close() error in reconnect() (log but continue)
- Use t.Cleanup() pattern in tests per project conventions
- Rename setupFreshnessTest() per naming conventions

Overhead: ~2.6μs per read op (~0.8% of total query time)

Signed-off-by: Alessandro De Blasis <alex@deblasis.net>

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 06:29:59 +01:00
Steve Yegge
0745bd69c8 fix(doctor): make --fix automatically migrate tombstones
When bd doctor detects legacy deletions.jsonl, --fix now runs the
tombstone migration automatically instead of requiring users to
manually run bd migrate-tombstones.

This makes the migration smoother for multi-clone scenarios where
only one clone needs to do the actual migration, but other clones
may still have local deletions.jsonl files that need cleanup.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 21:05:45 -08:00
cc-vps
4e87ae18e5 feat: show daemon config in 'bd daemon --status' output
Add auto-commit, auto-push, local mode, sync interval, and daemon mode
to the status output when querying a running daemon.

This helps users understand the current daemon configuration without
having to check logs or remember what flags were used at startup.

Changes:
- Add config fields to StatusResponse in protocol.go
- Add SetConfig() method to Server for daemon to set its config
- Update handleStatus() to include config in response
- Update showDaemonStatus() to query and display config via RPC
- Add comprehensive test coverage for new functionality

Co-authored-by: Christian Catalan <crcatala@gmail.com>
2025-12-15 09:03:20 -08:00
Steve Yegge
bc3e8f6359 chore: Bump version to 0.30.0
Updated all component versions:
- bd CLI: 0.29.0 → 0.30.0
- Plugin: 0.29.0 → 0.30.0
- MCP server: 0.29.0 → 0.30.0
- npm package: 0.29.0 → 0.30.0
- Documentation: 0.29.0 → 0.30.0

Generated by scripts/bump-version.sh
2025-12-15 00:10:54 -08:00
Steve Yegge
3a186a69d6 docs: prepare v0.30.0 changelog and whats-new entry
- Add comprehensive CHANGELOG entry for v0.30.0 (tombstone architecture)
- Add v0.30.0 to versionChanges in info.go for `bd info --whats-new`
- Acknowledge 13 community contributors

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 00:10:40 -08:00
Steve Yegge
8e58c306a7 fix(sync,blocked): add safety guards for issue deletion and include status=blocked in bd blocked
GH#464: Add safety guards to prevent deletion of open/in_progress issues during sync:
- Safety guard in git-history-backfill (importer.go)
- Safety guard in deletions manifest processing
- Warning when uncommitted changes detected before pull (daemon_sync.go)
- Enhanced repo ID mismatch error message

GH#545: Fix bd blocked to show status=blocked issues (sqlite/ready.go):
- Changed from INNER JOIN to LEFT JOIN to include issues without dependencies
- Added WHERE clause to include both status=blocked AND dependency-blocked issues

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 23:07:54 -08:00
Steve Yegge
db3e22fef9 Merge pull request #561 from steveyegge/bd-6xfz-furiosa
fix(prime): document priority format to prevent Claude using 'medium'
2025-12-14 23:00:11 -08:00
Steve Yegge
7535dc88d2 fix(lint): resolve all golangci-lint errors
- Mark unused ctx parameter with underscore in getRepoRootForWorktree
- Replace exec.Command("test", "-d") with os.Stat for directory check
- Handle file.Close() errors properly in compact.go and migrate_tombstones.go
- Explicitly ignore cleanup errors with _ assignment

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 22:36:51 -08:00
Steve Yegge
1e20d702f2 fix(doctor): include tombstones in getCurrentJSONLIDs to prevent corruption (#552)
The previous bd-in7q fix had backwards logic - by EXCLUDING tombstones
from currentIDs, they appeared missing when compared to historicalIDs,
causing HydrateDeletionsManifest to erroneously add them to deletions.jsonl.

This corruption manifested when:
1. Issues were migrated to tombstones via migrate-tombstones
2. Doctor hydration ran (directly or via sync)
3. Tombstones were seen as deleted and re-added to deletions.jsonl
4. Next import skipped thousands of issues with in deletions manifest

Fix: Include ALL issues (including tombstones) in currentIDs. Tombstones
represent migrated deletions that ARE accounted for - they should not
trigger new deletion records.

Generated with Claude Code

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 17:33:07 -08:00
Steve Yegge
76568e8802 fix(prime): document priority format to prevent Claude using 'medium'
GH#517: Claude uses 'medium' instead of P2/2 for priority, causing
infinite error loops. The bd prime output didn't show the --priority
flag or explain the valid format.

Fix: Add --priority=2 to the create example and add a clear note
explaining that priority must be 0-4 or P0-P4, NOT 'high/medium/low'.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 17:19:06 -08:00
Steve Yegge
a60972cd6a Merge remote-tracking branch 'origin/main' into performance-fix 2025-12-14 14:53:30 -08:00
cbro
2651620a4c fix(storage): persist close_reason to issues table on close (#551)
CloseIssue was storing the reason only in the events table, not in the
issues.close_reason column. This caused `bd show --json` to return an
empty close_reason even when one was provided.

- Update CloseIssue in queries.go and transaction.go to set close_reason
- Clear close_reason when reopening issues (in manageClosedAt)
- Add tests for close_reason in storage and CLI JSON output
- Document the dual-storage of close_reason (issues + events tables)
2025-12-14 14:18:01 -08:00
Steve Yegge
3a4da4e08d fix(lint): address errcheck and De Morgan's law violations in doctor/fix
- Handle file.Close() errors in deletions.go and deletions_test.go
- Simplify boolean logic to apply De Morgan's law in common.go
- All golangci-lint checks now pass
2025-12-14 14:12:13 -08:00
matt wilkie
a22d949cbd Fix bd-in7q: prevent migrate-tombstones from corrupting deletions manifest (#554)
Root cause: bd doctor hydrate was re-adding migrated tombstones to the
deletions manifest because getCurrentJSONLIDs() included all issues,
including tombstones. When compared against git history, tombstones
appeared as 'deleted' and were incorrectly added to the manifest as new
deletions, corrupting the database on next sync.

Fix: Skip tombstone-status issues in getCurrentJSONLIDs() so they don't
participate in deletion detection. Tombstones represent already-recorded
deletions/migrations and shouldn't be treated as active issues.

Changes:
- cmd/bd/doctor/fix/deletions.go: Skip tombstones in getCurrentJSONLIDs()
- cmd/bd/doctor/fix/deletions_test.go: New tests for tombstone skipping
- cmd/bd/migrate_tombstones_test.go: Test that tombstones are valid

This fixes the bug where 'bd migrate-tombstones' followed by 'bd sync'
would add thousands of deletion records with author 'bd-doctor-hydrate'
2025-12-14 14:11:27 -08:00
Ryan Snodgrass
f88a0d015b feat(cli): add 'bd thanks' command to thank contributors
Adds a new command that displays a thank you page listing all human
contributors to the beads project. Features:

- Static list of contributors (compiled into binary)
- Top 20 featured contributors displayed in columns
- Additional contributors in wrapped list
- Styled output using lipgloss (colored box, sections)
- Dynamic width based on content
- JSON output support (--json flag)
- Excludes bots and AI agents by email pattern
2025-12-14 12:40:32 -08:00
Steve Yegge
3a9749279a fix(sync): protect locally exported issues from sanitization (bd-3ee1)
The sync sanitize process was incorrectly removing newly created issues
when they happened to have IDs matching entries in the deletions manifest.
This could occur with hash-based IDs when content is similar to previously
deleted issues.

The fix adds protection for issues that were in the left snapshot (local
export before pull). These represent local work and should not be removed
by sanitize, even if they match entries in the deletions manifest.

Changes:
- Load left snapshot in sanitizeJSONLWithDeletions() to build protection set
- Add protection check before removing issues from JSONL
- Add ProtectedCount/ProtectedIDs to SanitizeResult for tracking
- Log protected issues during sync for visibility
- Add comprehensive test coverage for the fix

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 00:55:55 -08:00
Steve Yegge
a61ca252ae fix(cleanup): resolve CHECK constraint failure and add tombstone pruning
- Fix bd-tnsq: executeDelete now sets closed_at=NULL when creating
  tombstones, satisfying the CHECK constraint that requires
  closed_at IS NULL when status != 'closed'

- Fix bd-08ea: cleanup command now also prunes expired tombstones
  (older than 30 days) after converting closed issues to tombstones

- Add regression test for batch deletion of closed issues

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 00:37:54 -08:00
Steve Yegge
9db756f8b6 fix(memory): implement GetReadyWork/GetBlockedIssues + child counters
Fixes #543, #544, #545, #546 (no-db mode regressions)

Memory backend fixes:
- GetReadyWork now properly excludes issues with open blocks dependencies
- GetBlockedIssues now includes issues with status=blocked (even with 0 blockers)
- LoadFromIssues initializes hierarchical child counters from existing IDs
  so repeated --parent creates bd-xxx.1, bd-xxx.2, etc.

JSONL path discovery:
- findJSONLPath works in no-db mode when dbPath is empty
- Honors BEADS_JSONL environment variable override
- Falls back to locating .beads directory

Based on PR #547 by @joelklabo - cherry-picked core fixes.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-13 23:22:10 -08:00
Steve Yegge
6c84d36592 fix: bd onboard hangs on Windows (GH #531)
Added onboard and hooks commands to the noDbCommands list so they
skip database and daemon initialization. These commands don't need
database access - they just output documentation or manage git hooks.

On Windows, if no .beads directory exists, the PersistentPreRun
would attempt database discovery (including git worktree detection)
which could hang due to git command issues.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 18:09:34 -08:00
Steve Yegge
fc23dca7fb feat(cli): add --lock-timeout flag for SQLite busy_timeout control (#536)
Implements single-shot mode improvements for Windows and Docker scenarios:

- Add --lock-timeout global flag (default 30s, 0 = fail immediately)
- Add config file support: lock-timeout: 100ms
- Parameterize SQLite busy_timeout via NewWithTimeout() function
- In --sandbox mode: default lock-timeout to 100ms
- In --sandbox mode: skip FlushManager creation (no background goroutines)

This addresses bd.exe hanging on Windows and locking conflicts when
using beads across host + Docker containers.

Closes: bd-59er, bd-r4od, bd-dh8a

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 18:07:49 -08:00
Steve Yegge
45328d6bfe fix(doctor): detect sync-branch hook incompatibility (#532)
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>
2025-12-13 18:05:49 -08:00
Steve Yegge
5d71ca6356 fix: improve JSONL-only mode detection and error messages (GH #534)
- 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>
2025-12-13 12:50:33 -08:00
matt wilkie
e01b7412d9 feat: add Git worktree compatibility (PR #478)
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
2025-12-13 12:50:33 -08:00
Steve Yegge
de7b511765 fix(stealth): use project-specific paths in global gitignore (#538)
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>
2025-12-13 10:58:23 -08:00
Charles P. Cross
eb988fcb21 Fix daemon auto-sync delete mutation not reflected in sync branch (#537)
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
2025-12-13 10:53:09 -08:00
Steve Yegge
947e7188aa fix(sync): support external BEADS_DIR in separate git repository
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>
2025-12-13 10:44:58 -08:00
Steve Yegge
e5068df3aa fix(sync): preserve tombstones in sanitizeJSONLWithDeletions (bd-kzxd)
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>
2025-12-13 10:21:54 -08:00
Steve Yegge
a0f4a9cacd feat(doctor): add tombstone health checks (bd-s3v)
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>
2025-12-13 10:16:45 -08:00
Steve Yegge
59e32e3529 fix(sync): add multi-repo support to hash update after restore
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.
2025-12-13 06:57:20 -08:00
Ryan
335887e000 perf: fix stale startlock delay and add comprehensive benchmarks (#484)
* 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>
2025-12-13 06:57:11 -08:00
Steve Yegge
97500a8de0 fix(sync): prevent daemon race condition from dirtying working directory
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>
2025-12-13 06:54:46 -08:00
Ryan Stortz
4254c3f2f5 feat(config): add BD_NO_INSTALL_HOOKS environment variable to disable git hook installation (#500)
* 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
2025-12-13 06:38:26 -08:00
Steve Yegge
e72fe12bbc feat(reset): add integration tests and doctor reset suggestion
- Add 9 integration tests for bd reset command (reset_test.go)
- Tests cover: dry-run, force, skip-init, backup, confirmation,
  cancellation, no-beads-dir, multiple issues, verbose mode
- Enhance bd doctor to suggest reset when >= 3 unfixable errors found
- Addresses bd-aydr.7, bd-aydr.5, bd-aydr.8

Closes: #479 (via GitHub comment with solution)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 10:20:31 +11:00
Steve Yegge
9057aeba17 fix(gitignore): switch to whitelist approach for .beads/.gitignore (#473)
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>
2025-12-13 10:14:05 +11:00
Steve Yegge
df84e3f531 fix: skip symlink/permissions tests on Windows
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>
2025-12-13 10:08:34 +11:00
Steve Yegge
2fd1d1fb87 fix: resolve golangci-lint warnings
- Handle ignored errors with explicit _ assignment (errcheck)
- Add #nosec comments for false positive G304/G204 warnings (gosec)
- Fix misspelling: cancelled -> canceled

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 10:07:36 +11:00
matt wilkie
2a3a53531c docs: Add comprehensive documentation for version reporting changes 2025-12-13 10:04:01 +11:00
matt wilkie
0423fc689f fix: Add commit and branch to bd version output (github #503)
- 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).
2025-12-13 10:04:01 +11:00
Steve Yegge
88153f224f feat(reset): implement bd reset CLI command with unit tests
Implements the bd reset command for GitHub issue #479:
- CLI command with flags: --hard, --force, --backup, --dry-run, --skip-init, --verbose
- Impact summary showing issues/tombstones to be deleted
- Confirmation prompt (skippable with --force)
- Colored output for better UX
- Unit tests for reset.go and git.go
- Fix: use --force flag in git rm to handle staged files

Part of epic bd-aydr.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 10:00:47 +11:00
Steve Yegge
ca9d306ef0 feat(reset): implement core reset package for bd reset command
Phase 1 implementation of bd reset (GitHub #479):

- internal/reset/reset.go: Core reset logic with ResetOptions, ResetResult,
  ImpactSummary structs. Handles daemon killing, backup, file removal,
  git operations, and re-initialization.

- internal/reset/backup.go: CreateBackup() for timestamped .beads/ backups
  with permission preservation.

- internal/reset/git.go: Git state detection and operations for --hard mode.
  CheckGitState(), GitRemoveBeads(), GitCommitReset(), GitAddAndCommit().

- cmd/bd/doctor/gitignore.go: Add .beads-backup-*/ to gitignore template.

Code review fixes applied:
- Git rm now runs BEFORE file deletion (was backwards)
- Removed stderr output from core package (CLI-agnostic)
- IsDirty now checks only .beads/ changes, not entire repo
- GitCommitReset handles nothing to commit gracefully
2025-12-13 09:47:26 +11:00