Add `bd compact --dolt` command to run Dolt garbage collection on
.beads/dolt directory. This helps reclaim disk space when using the
Dolt backend, where auto-commit per mutation causes commit history
to grow over time.
Features:
- Runs `dolt gc` in the .beads/dolt directory
- Shows disk space before/after with bytes freed
- Supports --dry-run to preview without running GC
- Supports --json for machine-readable output
- Helpful error messages for missing dolt directory or command
Closes: hq-ew1mbr.14
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The generic ValidateIDFormat() used isLikelyHash() which treated
3-character suffixes like "nux" as valid hashes, causing agent IDs
like "nx-nexus-polecat-nux" to extract prefix as "nx-nexus-polecat"
instead of the correct "nx".
Fix: For --type=agent, validate agent ID format first and use
ExtractAgentPrefix() which correctly extracts prefix from the
first hyphen for agent IDs.
Fixes#591
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The .claude/skills/ directories are created by Claude Code when agents
add skills (ghi-list, pr-list, handoff). These should not be tracked.
Fixes: gt-q0jy15.2
Fixes#1093: `bd version` was spawning 5-7 git subprocesses before
checking if it even needed database access.
Moved the noDbCommands early-return check to run BEFORE:
- ensureForkProtection() (spawns ~5 git commands)
- signalOrchestratorActivity() (spawns git config)
This eliminates unnecessary process churn for simple commands like
`bd version`, `bd help`, `bd completion`, etc.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add warning to bd prime output that bd edit opens $EDITOR which blocks
autonomous agents. Recommends bd update with --title/--description/etc
flags instead. Fixes#1061.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The "Note: No git repository initialized" message now respects the
--quiet flag, preventing non-essential output in quiet mode.
Fixes#1080
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes#1049 - AGENTS.md referenced CLAUDE.md which doesn't exist on main.
Changes:
- "Key Sections in CLAUDE.md" -> "Key Sections"
- "See CLAUDE.md..." -> "See AGENT_INSTRUCTIONS.md..."
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes#1103: bd sync --merge fails when sync-branch configured due to
skip-worktree/daemon write conflict.
Root cause: When sync-branch is configured, daemon/bd writes to main's
.beads/issues.jsonl which has skip-worktree set. This hides changes from
`git status` but `git merge` still detects them, causing merge failures.
Solution: When sync-branch is configured, redirect all JSONL writes to
the worktree's JSONL only. Main's JSONL is now read-only and only updated
via merges from the sync branch.
Changes:
- autoflush.go: findJSONLPath() now returns worktree JSONL path when
sync-branch configured, with safeguards for test isolation
- sync_branch.go: Remove incomplete skip-worktree manipulation code
that is no longer needed with this architectural fix
Data flow with sync-branch:
1. bd create -> SQLite
2. bd sync --flush-only -> worktree/.beads/issues.jsonl
3. bd sync --merge -> clean merge (main's JSONL unchanged)
4. auto-import -> SQLite updated from merged JSONL
Add nil check at start of ResolvePartialID to return a proper error
instead of panicking when storage interface is nil.
Root cause: When bd refile (or other commands) is called with a nil
storage, calling store.SearchIssues() panics with SIGSEGV. This can
happen when routing fails to initialize storage properly.
Now returns: "cannot resolve issue ID <id>: storage is nil"
Fixes: bd-7ypor
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
When bd init installs hooks with chaining, it renames the user's original
hook to .old and creates a new bd hook that chains to it. If bd doctor
later runs bd hooks install --chain, it would:
1. Not recognize the inline bd hook (from bd init) as a bd hook
2. Rename it to .old, overwriting the user's original hook
3. Install a new shim hook
This fix addresses two root causes:
1. Detection mismatch: areBdShimsInstalled() and getHookVersion() now
recognize inline bd hooks (which have "# bd (beads)" marker) in
addition to shim hooks (which have "# bd-shim" marker)
2. No .old protection: bd hooks install --chain now checks if .old
already exists before renaming, preserving the user's original hook
Fixes#1120
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add "enhancement" to util.issueTypeAliases for consistency
- Make types.IssueType.Normalize() case-insensitive and include all aliases
- Fix update.go to normalize type before validation
- Remove duplicate type validation block in update.go
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add DeleteBranch method to DoltStore for removing branches
- Update hookPostMergeDolt to clean up import branches after merge
- Completes hq-ew1mbr.9 git hook infrastructure
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Support --type enhancement as an alias for --type feature when creating
issues. The normalization happens before validation to ensure consistency
across all code paths.
Closes gt-hzanoe
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add batch query optimization to avoid N+1 queries in scanIssueIDs
- Create GetIssuesByIDs helper to fetch multiple issues in single query
- Add scanIssueRow helper to scan issue data from rows iterator
- Add proper timeout contexts to all Dolt tests using testContext helper
The embedded Dolt driver is slow for repeated queries. Replacing N+1
GetIssue calls with a single IN clause query fixes the 30s+ timeouts
in TestDoltStoreDependencies, TestDoltStoreSearch, and
TestDoltStoreGetReadyWork.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The acquireStartLock function would recursively call itself after
attempting to remove a stale lock file. If os.Remove failed (due to
permissions, race conditions, etc.), the error was silently ignored
with `_`, causing infinite recursion until the 1GB stack limit was
exceeded.
Changes:
- Convert recursive calls to a bounded retry loop (max 3 attempts)
- Check removeFileFn return value before retrying
- Apply same fix to handleStaleLock which had the same issue
- Add test to verify function returns false when remove fails
Fixes the stack overflow crash that occurred when running any bd
command with a stale or problematic lock file.
Co-authored-by: Steven Syrek <steven.syrek@deepl.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Fix infinite recursion bug in computeJSONLHashForHook (was calling
itself instead of computeJSONLHash)
- Update pre-commit, post-merge, post-checkout templates to use
`bd hook <name>` instead of `bd hooks run <name>`, routing to the
Dolt-aware implementations in hook.go
- Update all hook template versions to 0.48.0 for consistency
The hook infrastructure added in 15d74a9a had a critical bug where
the hash computation function recursed infinitely, and the templates
still pointed to the old hook implementations in hooks.go instead
of the new Dolt-aware ones in hook.go.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use exec.LookPath instead of hardcoded path for Dolt detection
- Add test context with timeout to prevent tests from hanging
- Document known issues with embedded Dolt driver async operations
The embedded Dolt driver can hang on complex JOIN queries. This change
ensures tests fail gracefully with timeout rather than hanging.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When an issue is deleted, issues that depend on it were not being marked
dirty. This caused stale dependency references to persist in JSONL after
the target issue was deleted, because the dependent issues were never
re-exported.
This manifests as FK validation failures during multi-repo hydration:
"foreign key violation: issue X depends on non-existent issue Y"
The fix queries for dependent issues before deleting and marks them dirty
so they get re-exported without the stale dependency reference.
Adds test: TestDeleteIssueMarksDependentsDirty
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit implements the git hook infrastructure for Dolt storage backend
as specified in the design document.
Changes:
- Add `bd hook` command (singular) for git hooks to call directly
- Implement per-worktree export state tracking in .beads/export-state/
- Add post-checkout guard to only import if JSONL changed
- Add hook chaining configuration (chain_strategy, chain_timeout_ms)
- Support hooks in .beads/hooks/ directory with git config core.hooksPath
- Implement branch-then-merge import pattern for Dolt storage
- Update bd init to install hooks to .beads/hooks/ for Dolt backend
- Add --beads flag to `bd hooks install` command
The new `bd hook` command supports:
- pre-commit: Export database to JSONL, stage changes
- post-merge: Import JSONL to database after pull/merge
- post-checkout: Import JSONL after branch checkout (with guard)
For Dolt backend, uses branch-then-merge pattern:
1. Create jsonl-import branch
2. Import JSONL data to branch
3. Merge branch to main (cell-level conflict resolution)
4. Delete branch on success
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Document dolthub/dolt, dolthub/go-mysql-server, and dolthub/driver
under Apache License 2.0 for distribution compliance.
Refs: hq-ew1mbr.5
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Git hooks are shared across all worktrees and live in the common git
directory (e.g., /repo/.git/hooks), not the worktree-specific directory
(e.g., /repo/.git/worktrees/feature/hooks).
The core issue was in GetGitHooksDir() which used GetGitDir() instead
of GetGitCommonDir(). This caused hooks to be installed to/read from
the wrong location when running in a worktree.
Additionally, several places in the codebase manually constructed
hooks paths using gitDir + "hooks" instead of calling GetGitHooksDir().
These have been updated to use the proper worktree-aware path.
Affected areas:
- GetGitHooksDir() now uses GetGitCommonDir()
- CheckGitHooks() uses GetGitHooksDir()
- installHooks/uninstallHooks use GetGitHooksDir()
- runChainedHook() uses GetGitHooksDir()
- Doctor checks use git-common-dir for hooks paths
- Reset command uses GetGitCommonDir() for hooks and beads-worktrees
Symptoms that this fixes:
- Chained hooks (pre-commit.old) not running in worktrees
- bd hooks install not finding/installing hooks correctly in worktrees
- bd hooks list showing incorrect status in worktrees
- bd doctor reporting incorrect hooks status in worktrees
Co-authored-by: Zain Rizvi <4468967+ZainRizvi@users.noreply.github.com>
The JSONL file hash mismatch warning was appearing consistently after
git commit operations, even when no actual data inconsistency existed.
Root Cause:
Two code paths export to JSONL but only one updated jsonl_file_hash:
- flushToJSONLWithState() (used by bd update/create): Updated both
jsonl_content_hash AND jsonl_file_hash
- finalizeExport() (used by bd sync --flush-only): Updated only
jsonl_content_hash, NOT jsonl_file_hash
Since validateJSONLIntegrity() checks jsonl_file_hash, any bd command
after a git commit would see a mismatch and trigger the warning.
Steps to Reproduce (before fix):
1. cd /path/to/beads-project
2. bd update -p 3 some-issue-id # Works fine, stores hash H1
3. git add .beads/issues.jsonl && git commit --amend --no-edit
# Pre-commit hook runs bd sync --flush-only
# This updates jsonl_content_hash to H2 but leaves jsonl_file_hash as H1
4. bd update -p 3 some-issue-id # WARNING appears!
# validateJSONLIntegrity() compares file (H2) with jsonl_file_hash (H1)
5. Repeat steps 3-4 indefinitely - warning always appears
The fix adds SetJSONLFileHash() call to finalizeExport(), ensuring both
export paths update the same metadata consistently.
WSL doesn't fully respect Unix file permission semantics - the file
owner can bypass read-only restrictions, similar to macOS. Add isWSL()
helper and skip TestMergeDriverWithLockedConfig_E2E on WSL.
Fixes bd-srv
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The dolt storage backend requires CGO due to its gozstd dependency.
This change makes the dolt backend optional using build tags, allowing
`go install` to work on Windows where CGO is disabled by default.
Changes:
- Add BackendFactory registration pattern to factory package
- Create factory_dolt.go with `//go:build cgo` constraint that
registers the dolt backend only when CGO is available
- Update init.go to use factory instead of direct dolt import
- When dolt backend is requested without CGO, provide helpful error
message directing users to pre-built binaries
The sqlite backend (default) works without CGO and covers the majority
of use cases. Users who need dolt can either:
1. Use pre-built binaries from GitHub releases
2. Enable CGO by installing a C compiler
Fixes#1116
Add "__complete" to noDbCommands list so that Cobra's internal completion
command can run without requiring a beads database to be present.
Previously, running shell completions (e.g., `bd show <TAB>`) in a directory
without a .beads database would fail with "no beads database found" error.
Now completions return empty results gracefully when no database exists,
allowing basic command completion to work everywhere while issue ID
completion still requires a database.
Added integration test that verifies __complete command works without a database.
Remove dead code that was inadvertently orphaned when PR #918 refactored
the sync flow. The function was never called since v0.47.0.
The function caused GH#1100 by running `git checkout HEAD -- .beads/`
which restored the entire .beads/ directory, overwriting uncommitted
config.yaml changes.
Add regression test (TestConfigPreservedDuringSync) to prevent similar
restoration logic from being reintroduced.
Fixes#1100
All time.Now() calls in the dolt storage layer now use time.Now().UTC()
to ensure consistent timezone handling. Previously, timestamps could be
stored with mixed timezone formats (UTC 'Z' vs local '+01:00'), causing
bv validation to fail when updated_at appeared earlier than created_at
in absolute time.
Files modified:
- transaction.go: CreateIssue, UpdateIssue, CloseIssue
- issues.go: CreateIssue, CreateIssues, UpdateIssue, CloseIssue, markDirty, manageClosedAt
- rename.go: UpdateIssueID (2 locations)
- events.go: AddIssueComment (2 locations)
- dirty.go: SetExportHash
- queries.go: Overdue filter, GetStaleIssues
Fixes: bd-84gw9
Co-authored-by: LoomDeBWiles <loomenwiles@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Role and rig beads are reference metadata that should never be closed
or appear as actionable work. They are similar to agent beads which
are already excluded.
- Add 'role' and 'rig' to the issue type exclusion list in GetReadyWork
- Update comments to document the excluded types
Fixes confusion where role/rig beads appeared in bd ready output,
leading agents to try to close them as regular work items.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* fix(release): add Nix package steps to release molecule
Add missing Nix support to the release workflow:
- bump-default-nix: Updates version in default.nix
- update-vendorhash: Prompts to update vendorHash if go.mod changed
- Add default.nix to verify-versions check
This was lost when bump-version.sh was replaced with the molecule
workflow in 39dbe90a. The original Nix support from c802f27f included
vendorHash detection in bump-version.sh, but that logic was not
migrated to the new workflow.
Fixes#1107
* fix(nix): update vendorHash for current go.mod
When .beads is a symlink (e.g., ~/gt/.beads -> ~/gt/olympus/.beads),
findTownRoutes() incorrectly used filepath.Dir() on the resolved
symlink path to determine the town root.
This caused route resolution to fail because the town root would be
~/gt/olympus instead of ~/gt, making routes point to non-existent
directories.
The fix adds findTownRootFromCWD() which walks up from the current
working directory instead of the beads directory path. This finds
the correct town root regardless of symlink resolution.
Changes:
- Add findTownRootFromCWD() function
- Update findTownRoutes() to use CWD-based town root detection
- Add fallback to filepath.Dir() for non-Gas Town repos
- Add debug logging (BD_DEBUG_ROUTING=1)
- Add comprehensive test case
Test: go test ./internal/routing/...
When routing issues to other repos (via contributor routing or --rig flag),
the code was hardcoding sqlite.New instead of using the storage factory.
This meant Dolt-configured repos would fail when receiving routed issues.
Changed two locations:
- Contributor routing (line 357): use factory.NewFromConfig
- createInRig --rig flag (line 789): use factory.NewFromConfig
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The SQL formatting warnings (G201) are safe because:
- Placeholders only contain "?" markers for parameterized queries
- WHERE/SET clauses use validated column names with ? placeholders
- Refs are validated by validateRef() before use in AS OF queries
- LIMIT values are safe integers from filter.Limit
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Changes:
- Save issue-prefix in config.yaml when using --no-db mode
(previously only saved in database which doesn't exist in no-db mode)
- Add config.ResetForTesting() to allow reloading config in tests
- Simplify test to verify config values rather than execute subsequent
commands (cobra's flag caching makes multi-Execute() testing complex)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
setupGitRepo now creates an empty issues.jsonl for RepoContext,
which triggers 'JSONL changed since import' validation in tests
that set up their own .beads/ directory.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Centralizes repository context resolution via RepoContext API, fixing bugs where git commands run in the wrong repo when BEADS_DIR points elsewhere or in worktree scenarios.
The complex bump-version.sh script has been replaced with:
1. A deprecation notice pointing to the release molecule
2. A simpler update-versions.sh for quick local-only version bumps
For releases, use:
bd mol wisp beads-release --var version=X.Y.Z
The molecule provides guided, step-by-step release workflows with
proper handoffs and CI gates.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update all documentation to use the new subcommand syntax:
- `bd daemon --start` → `bd daemon start`
- `bd daemon --stop` → `bd daemon stop`
- `bd daemon --status` → `bd daemon status`
- `bd daemon --health` → `bd daemon status --all`
- `--global=false` → `--local`
The old flag syntax is deprecated but still works with warnings.
Closes: bd-734vd
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>