* feat: comprehensive NixOS support improvements
This commit adds full NixOS support and automates Nix package maintenance:
## Static Linux Binaries for NixOS
Changes .goreleaser.yml to create static Linux binaries:
- Set CGO_ENABLED=0 for Linux amd64 and arm64 builds
- Remove cross-compiler dependencies (aarch64-linux-gnu-gcc)
- Simplifies build process while fixing NixOS compatibility
Static binaries work on all Linux distributions including NixOS, Alpine,
and musl-based distros without dynamic linker dependencies.
## Automated default.nix Version Management
Adds default.nix to the version bump workflow:
- Updates default.nix version field in bump-version.sh (new step 9)
- Adds default.nix to version verification checks
- Prevents version drift (was 5 releases behind: 0.42.0 vs 0.47.0)
- Updates README.md to remove glibc 2.32+ requirement
## Automated vendorHash Management
Creates scripts/update-nix-vendorhash.sh to automate vendorHash updates:
- Automatically detects correct hash by triggering Nix build error
- Extracts hash from error message and updates default.nix
- Verifies update with clean build
- Eliminates error-prone manual copy-paste workflow
- Works with local Nix OR Docker (uses nixos/nix image automatically)
Integrates vendorHash check into bump-version.sh:
- Detects when go.mod or go.sum have changed
- Prompts to run vendorHash update script interactively
- Catches synchronization issues at release time
## Documentation
Updates AGENTS.md with Nix package maintenance guide:
- Documents when and how to update vendorHash
- Recommends automated script as primary method
- Provides manual and alternative methods as fallback
- Notes Docker fallback for maintainers without Nix
## Impact
- NixOS users can now install via standard methods
- Nix package version stays synchronized automatically
- vendorHash updates work without Nix installed (via Docker)
- vendorHash updates are caught during release workflow
- All Linux users benefit from more portable binaries
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: address PR review feedback for NixOS support
Fixes three issues identified in PR review:
1. Replace undefined log_* functions in bump-version.sh with existing
echo pattern (log_warning, log_info, log_success, log_error were
called but not defined)
2. Update default.nix version from 0.42.0 to 0.47.0 to fix version
drift with cmd/bd/version.go
3. Remove Nix Package Maintenance section from AGENTS.md per beads
architecture (use bd prime for dynamic context, keep AGENTS.md
minimal)
* fix: update versions to 0.47.1 after merge with main
- Update claude-plugin plugin.json to 0.47.1
- Update default.nix to 0.47.1
- Fixes version check failures after merging latest main branch
---------
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
When using short socket paths (workspaces with paths >103 chars),
filepath.Dir(socketPath) returns /tmp/beads-XXXXX/ instead of the
actual .beads/ directory. This caused TryDaemonLock to look in the
wrong location, always return false, clean up the startlock, and
recurse infinitely causing stack overflow.
Changed 4 occurrences in acquireStartLock, handleStaleLock,
handleExistingSocket, and getPIDFileForSocket to use
filepath.Dir(dbPath) which correctly points to .beads/.
Fixes: gt-qlt
Co-authored-by: furiosa <mayor@gastown.local>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The function always returns nil for error since it is designed to
return HEAD on detached HEAD state. Simplify signature to just
return string.
Fixes golangci-lint unparam warning.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add bd doctor check that detects legacy gastown merge queue JSON files
in .beads/mq/. These files are local-only remnants from the old mrqueue
implementation and can safely be deleted since gt done already creates
merge-request wisps in beads.
- CheckStaleMQFiles() detects .beads/mq/*.json files
- FixStaleMQFiles() removes the entire mq directory
- Comprehensive tests for check and fix
This is the first step toward removing the mrqueue side-channel from
gastown. The follow-up convoy will update Refinery/Witness to use
beads exclusively.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add insertIssuesStrict function that uses plain INSERT instead of
INSERT OR IGNORE. Update bulkInsertIssues and transactional CreateIssues
to use the strict variant.
This fixes a race condition where INSERT OR IGNORE could silently skip
duplicate insertions, but the code would still attempt to record events
for those "inserted" issues, causing FOREIGN KEY constraint failures.
The strict INSERT will now fail explicitly if a duplicate is encountered,
which should never happen since checkForExistingIDs runs first within
the same transaction.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add bd doctor check that warns if .beads/last-touched is tracked by git.
This file is local runtime state that should never be committed, as it
causes spurious diffs in other clones.
- CheckLastTouchedNotTracked() detects if file is git-tracked
- FixLastTouchedTracking() untracks with git rm --cached
- Comprehensive tests for all scenarios
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The plugin.json was left at 0.47.0 while all other version files
were bumped to 0.47.1, causing CI failures on all PRs.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
chore(deps): bump fastmcp from 2.14.1 to 2.14.3 in /integrations/beads-mcp
Bug fixes in this release:
- Fix HTTP transport timeout defaulting to 5 seconds
- OAuth token storage TTL fix
- Redis key prefix for ACL isolation
- ContextVar propagation for ASGI-mounted servers
Add missing reconnectMu.RLock() protection to storage methods that were
vulnerable to the same race condition fixed in GH#607. The FreshnessChecker
can trigger reconnect() which closes s.db while queries are in flight,
causing "database is closed" errors during daemon export operations.
Protected methods:
- labels.go: GetLabelsForIssues (GetLabels intentionally unprotected - called from GetIssue which holds lock)
- comments.go: GetIssueComments, GetCommentsForIssues
- dependencies.go: GetDependencyCounts, GetDependencyRecords, GetAllDependencyRecords, GetDependencyTree, loadDependencyGraph
- config.go: SetConfig, GetConfig, GetAllConfig, DeleteConfig, SetMetadata, GetMetadata
- dirty.go: MarkIssueDirty, GetDirtyIssues, GetDirtyIssueHash, GetDirtyIssueCount
- events.go: GetEvents, GetStatistics, GetMoleculeProgress
- hash.go: All hash methods
- hash_ids.go: GetNextChildID, ensureChildCounterUpdated (getNextChildNumber unprotected - called internally)
Internal helpers called from already-locked contexts intentionally omit
RLock to avoid deadlock (Go's RWMutex doesn't support recursive locking).
Fixes: bd-vx7fp
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Executed-By: beads/crew/dave
Rig: beads
Role: crew
prek (https://prek.j178.dev) is a faster Rust-based alternative to
pre-commit that uses the same .pre-commit-config.yaml config files.
Changes:
- Add prek detection pattern in hookManagerPatterns (before pre-commit
to ensure correct detection since prek hooks may contain 'pre-commit')
- Handle prek in checkManagerBdIntegration using same config parser
- Update init_git_hooks.go to recognize prek-installed hooks
- Rename isPreCommit field to isPreCommitFramework for clarity
- Use regex pattern matching all pre-commit/prek signatures consistently
- Add test cases for prek run and prek hook-impl signatures
Co-authored-by: Ismar Iljazovic <ismar@gmail.com>
macOS allows file owners to write to their own read-only (0444) files,
so TestSetupGlobalGitIgnore_ReadOnly cannot exercise the "Unable to write"
code path on this platform.
Skip both test cases on darwin with an explanatory message.
The test was failing because it called 'git config --global core.excludesfile'
which returned the real user's gitignore path instead of using the test's
temp directory.
Fix: Set GIT_CONFIG_GLOBAL env var to an empty temp config file, ensuring
the test uses the temp directory's .config/git/ignore path as intended.
Also extracted the isolation logic into a reusable setupIsolatedGitConfig helper.
Co-authored-by: Ismar Iljazovic <ismar@gmail.com>
Fixes#1041
The pre-push hook was not passing arguments to chained hooks,
causing them to fail. This change:
1. Changes runPrePushHook() to accept args []string parameter
2. Passes args to runChainedHook() instead of nil
3. Updates call site to pass hookArgs
4. Renames local 'args' to 'statusArgs' to avoid variable shadowing
Co-authored-by: Ismar Iljazovic <ismar@gmail.com>
The export command was incorrectly listed in readOnlyCommands, causing the SQLite database to be opened in read-only mode. This prevented export from clearing dirty issues and updating the JSONL file hash.
Problem:
When a JSONL file hash mismatch was detected (e.g., after git operations
that modify the JSONL without updating export_hashes), the autoflush
system would trigger a full re-export. During this re-export, all issue
comments were silently dropped from the exported JSONL file.
Steps to reproduce:
1. Have a beads database with issues containing comments
2. Create a situation where JSONL hash doesn't match stored hash
(e.g., clone a repo, or manual JSONL edits)
3. Run any bd command that triggers autoflush (e.g., `bd create foo`)
4. Observe warning: "JSONL file hash mismatch detected"
5. Check .beads/issues.jsonl - all comments are now missing
Root cause:
Two different export code paths existed:
- exportToJSONLWithStore (daemon_sync.go) - correctly populated comments
- fetchAndMergeIssues (autoflush.go) - only fetched dependencies, NOT comments
When hash mismatch triggered a full re-export via the autoflush path,
fetchAndMergeIssues was called but it never populated issue.Comments,
resulting in all comments being lost.
Fix:
Add GetIssueComments call in fetchAndMergeIssues to populate comments
for each issue before export, matching the behavior of exportToJSONLWithStore.
Note: Labels were not affected because GetIssue() already populates them
internally. Comments are stored in a separate table and require explicit
fetching via GetIssueComments().
- Remove standalone beads-merge binary section (no longer maintained)
- Update Jujutsu config to use `bd merge` instead of `beads-merge`
- Fix config path to standard `~/.config/jj/config.toml`
- Add note that tool only works for .beads/issues.jsonl
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add getCurrentBranchOrHEAD() which returns "HEAD" when in detached HEAD
state instead of failing. This fixes bd sync --status for jj/jujutsu users.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
The --ready flag filters to status=open, excluding hooked, in_progress,
blocked, and deferred issues. This makes it easy to see work that can
actually be picked up.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
GetIssue returns (nil, nil) when an issue is not found, but the code
assumed a non-nil issue when err was nil. Added nil checks before
accessing issue fields.
Fixes crash when running `bd mol wisp create` with nonexistent proto.
Closes: gt-2uzn2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(create): Use prefix from routes.jsonl when creating issues with --rig
When using `bd create --rig <name>`, the prefix from routes.jsonl was
being discarded. This caused issues to be created with the target
database's default prefix instead of the route's prefix.
This is particularly problematic when using the redirect mechanism to
share a single database across multiple rigs - the redirect correctly
routes to the shared database, but the prefix was not being applied.
The fix:
1. Capture the prefix from routing.ResolveBeadsDirForRig()
2. Temporarily override the target database's issue_prefix config
3. Restore the original prefix after issue creation
Example scenario that now works:
- routes.jsonl: {"prefix": "aops-", "path": "src/academicOps"}
- src/academicOps/.beads/redirect points to ~/writing/.beads
- `bd create --rig aops "Test"` now creates aops-xxx instead of ns-xxx
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(create): pass prefix via struct field instead of mutating config
The previous approach temporarily mutated the database's issue_prefix
config during cross-rig issue creation, then restored it afterward.
This was fragile in multi-user scenarios where concurrent operations
could see the wrong prefix.
New approach:
- Add PrefixOverride field to types.Issue
- CreateIssue checks PrefixOverride first, uses it if set
- createInRig sets issue.PrefixOverride instead of mutating config
This passes state as a parameter rather than mutating shared state,
making it safe for concurrent multi-user access.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
The duplicate merge target selection now considers structural relationships:
1. Dependent count (children, blocked-by) - highest priority
2. Text reference count - secondary
3. Lexicographically smallest ID - tiebreaker
This fixes the bug where `bd duplicates --auto-merge` would suggest closing
an epic with 17 children instead of the empty shell duplicate.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The enabledPlugins entry referenced beads@beads-marketplace which
does not exist, causing claude doctor to report a plugin error.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add Reset section to CLI_REFERENCE.md documenting bd admin reset
- Add troubleshooting section for "old data returns after reset"
- Clarify that --hard and --skip-init flags were never implemented
- Document workarounds for complete clean slate including remote data
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
RenameDependencyPrefix updates issue IDs in the dependencies table but
was not rebuilding the blocked_issues_cache, leaving stale IDs in the
cache that no longer exist in the issues table.
Add invalidateBlockedCache() call at the end of RenameDependencyPrefix
to rebuild the cache with the new issue IDs.
Fixes: GH#1016
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
sync_base.jsonl is an internal sync mechanism file used for 3-way merge,
not a competing issue database. The doctor check now correctly ignores it.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add QualityScore *float32 field to internal/merge/merge.go to match
internal/types/types.go. Also add last-touched to .beads/.gitignore.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Apply ui.RenderMarkdown() to comment text in both bd show and
bd comments commands. This enables syntax highlighting, code
blocks, and other Markdown formatting in comments.
Change IssueType and IssueStatus from Literal to str to support
custom types configured via:
bd config set types.custom "agent,molecule,event"
bd config set status.custom "awaiting_review,awaiting_testing"
The CLI handles validation of these values against the configured
options. The MCP layer is just a transport and shouldn't re-validate
what the CLI already validates.
This fixes Pydantic validation errors when listing issues that have
custom types like 'event', 'molecule', or 'agent'.
Built-in types: bug, feature, task, epic, chore
Built-in statuses: open, in_progress, blocked, deferred, closed
* test(validation): add failing tests for hyphenated prefix parsing
Reproduces bug where `bd create --parent` fails for projects with
hyphenated prefixes like "bead-me-up" or "web-app".
Root cause: ValidateIDFormat splits on first hyphen, so:
"bead-me-up-3e9.1" → prefix "bead" (wrong, should be "bead-me-up")
The bug flow in create.go:
1. User runs: bd create "Child" --parent bead-me-up-3e9
2. GetNextChildID generates: bead-me-up-3e9.1
3. ValidateIDFormat extracts: "bead" (splits at first hyphen)
4. ValidatePrefix compares: "bead" vs "bead-me-up" → MISMATCH
Tests added:
- TestValidateIDFormat: 6 cases for hyphenated prefix IDs
- TestValidateIDFormat_ParentChildFlow: simulates exact --parent flow,
showing simple prefixes pass while hyphenated prefixes fail
Workaround: use --force flag to bypass prefix validation.
* fix(validation): support hyphenated prefixes in ValidateIDFormat
Use utils.ExtractIssuePrefix instead of naive first-hyphen splitting.
This fixes bd create --parent failing for projects with hyphenated
prefixes like "bead-me-up" or "web-app".
Before: "bead-me-up-3e9" → prefix "bead" (wrong)
After: "bead-me-up-3e9" → prefix "bead-me-up" (correct)
ExtractIssuePrefix uses smart heuristics: split on last hyphen,
check if suffix is hash-like (3-8 chars, alphanumeric, digits for 4+).
* Update internal/validation/bead_test.go
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Add check to prevent 'bd init' from running inside a git worktree.
Worktrees should share the .beads database from the main repository,
not create their own.
The error message guides users to:
1. Run 'bd init' from the main repository
2. Use 'bd worktree create' to create worktrees with proper
redirects
This prevents the confusing behavior where init would create files
in unexpected locations or fail with "pathspec '.beads/.gitignore' did
not match any files" errors.
According to docs/WORKTREES.md, the proper workflow is:
- Initialize beads once in the main repository
- Use 'bd worktree create' to create worktrees with redirect files
- All worktrees share the single .beads/ database via redirects
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Remove duplicate crystallizes column from schema.go
- Add crystallizes to SELECT in transaction.go SearchIssues
- Add crystallizes to SELECT in ready.go GetReadyWork and GetNewlyUnblockedByClose
- Add crystallizes to SELECT in labels.go GetIssuesByLabel
- Add missing placeholder in issues.go INSERT VALUES
- Update migrations_test.go schema to include crystallizes column
Fixes test failures caused by schema/query column count mismatches.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add TypeRig IssueType constant for rig identity beads
- Add TypeRig to IsValid() switch statement
- Add IsBuiltIn() method for multi-repo hydration trust checks
- Add Crystallizes field to Issue struct and ComputeContentHash
Fixes validation rejecting documented issue types like 'rig' and 'agent'.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>