Add bd mol current: Shows current position in molecule workflow
- Displays all steps with status indicators (done/current/ready/blocked/pending)
- Infers molecule from in_progress issues when no ID given
- Supports --for flag to check another agent's molecules
Add bd close --continue: Auto-advances to next molecule step
- After closing, finds parent molecule and next ready step
- Auto-claims next step by default (--no-auto to skip)
- Shows molecule completion message when all steps closed
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The daemon.log was showing duplicate log messages:
- 'File change detected' appeared 4x for a single file change
- 'Git ref change detected' appeared 2x for a single ref update
Root cause:
- fsnotify generates multiple events (Write, Create, Chmod) for single file ops
- Both parent directory and file watchers can trigger for the same change
- Multiple git ref files may be updated simultaneously
Fix:
- Add log deduplication with 500ms window (matching debouncer window)
- Track last log time for file changes and git ref changes separately
- Only log if enough time has passed since last log of same type
- Still trigger debouncer for every event (functionality unchanged)
This reduces log noise while maintaining full functionality.
The error message 'path exists but is not a valid git worktree' was appearing
in daemon.log when the daemon attempted to use an existing worktree that was
in the git worktree list but had other issues (broken sparse checkout, etc.).
Root cause:
- CreateBeadsWorktree only checked isValidWorktree (is it in git worktree list)
- CheckWorktreeHealth was called separately and checked additional things
- If the worktree passed isValidWorktree but failed health check, an error
was logged and repair was attempted
Fix:
- CreateBeadsWorktree now performs a full health check when it finds an
existing worktree that's in the git worktree list
- If the health check fails, it automatically removes and recreates the
worktree
- Removed redundant CheckWorktreeHealth calls in daemon_sync_branch.go and
syncbranch/worktree.go since CreateBeadsWorktree now handles this internally
This eliminates the confusing error message and ensures worktrees are always
in a healthy state after CreateBeadsWorktree returns successfully.
Add --auto-pull flag to control whether the daemon periodically pulls from
remote to check for updates from other clones.
Configuration precedence:
1. --auto-pull CLI flag (highest)
2. BEADS_AUTO_PULL environment variable
3. daemon.auto_pull in database config
4. Default: true when sync.branch is configured
When auto_pull is enabled, the daemon creates a remoteSyncTicker that
periodically calls doAutoImport() to pull remote changes. When disabled,
users must manually run 'git pull' to sync remote changes.
Changes:
- cmd/bd/daemon.go: Add --auto-pull flag and config reading logic
- cmd/bd/daemon_event_loop.go: Gate remoteSyncTicker on autoPull parameter
- cmd/bd/daemon_lifecycle.go: Add auto-pull to status output and spawn args
- internal/rpc/protocol.go: Add AutoPull field to StatusResponse
- internal/rpc/server_core.go: Add autoPull to Server struct and SetConfig
- internal/rpc/server_routing_validation_diagnostics.go: Include in status
- Tests updated to pass autoPull parameter
Closes #TBD
Modernize sorting code to use Go 1.21+ slices package:
- Replace sort.Slice with slices.SortFunc across 16 files
- Use cmp.Compare for orderable types (strings, ints)
- Use time.Time.Compare for time comparisons
- Use cmp.Or for multi-field sorting
- Use slices.SortStableFunc where stability matters
Benefits: cleaner 3-way comparison, slightly better performance,
modern idiomatic Go.
Part of GH#692 refactoring epic.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(daemon): include tombstones in exportToJSONLWithStore for sync propagation
The daemon's exportToJSONLWithStore() function was using an empty
IssueFilter which defaults to IncludeTombstones: false. This caused
deleted issues (tombstones) to be excluded from JSONL exports during
daemon sync cycles.
Bug scenario:
1. User runs `bd delete <issue>` with daemon active
2. Database correctly marks issue as tombstone
3. Main .beads/issues.jsonl correctly shows status:"tombstone"
4. But sync branch worktree JSONL still showed status:"open"
5. Other clones would not see the deletion
The fix adds IncludeTombstones: true to match the behavior of
exportToJSONL() in sync.go, ensuring tombstones propagate to other
clones and prevent resurrection of deleted issues.
Adds regression test TestExportToJSONLWithStore_IncludesTombstones
that verifies tombstones are included in daemon JSONL exports.
* fix: resolve all golangci-lint errors (cherry-pick from fix/linting-errors)
Cherry-picked linting fixes to ensure CI passes.
---------
Co-authored-by: Charles P. Cross <cpdata@users.noreply.github.com>
* fix(daemon): handle diverged sync branch with fetch-rebase-retry on push
When pushing to the sync branch, if the remote has newer commits that
the local worktree doesn't have, the push would fail with "fetch first"
error and the daemon would log the failure without recovery.
Bug scenario:
1. Clone A pushes commit X to sync branch
2. Clone B has local commit Y (not based on X)
3. Clone B's push fails with "fetch first" error
4. Without this fix: daemon logs failure and stops
5. With this fix: daemon fetches, rebases Y on X, and retries push
This fix adds fetch-rebase-retry logic to gitPushFromWorktree():
1. Detect push rejection due to remote having newer commits
2. Fetch the latest remote sync branch
3. Rebase local commits on top of remote
4. Retry the push
If rebase fails (e.g., due to conflicts), the rebase is aborted and
an error is returned with helpful context.
This allows multiple clones to push to the same sync branch without
manual intervention, as long as the changes don't conflict.
Adds integration test TestGitPushFromWorktree_FetchRebaseRetry that
verifies the fetch-rebase-retry behavior with diverged branches.
* fix: resolve all golangci-lint errors (cherry-pick from fix/linting-errors)
Cherry-picked linting fixes to ensure CI passes.
---------
Co-authored-by: Charles P. Cross <cpdata@users.noreply.github.com>
* fix(daemon): add periodic remote sync to event-driven mode
The event-driven daemon mode only triggered imports when the local JSONL
file changed (via file watcher) or when the fallback ticker fired (only
if watcher failed). This meant the daemon wouldn't see updates pushed
by other clones until something triggered a local file change.
Bug scenario:
1. Clone A creates an issue and daemon pushes to sync branch
2. Clone B's daemon only watched local file changes
3. Clone B would not see the new issue until something triggered local change
4. With this fix: Clone B's daemon periodically calls doAutoImport
This fix adds a 30-second periodic remote sync ticker that calls
doAutoImport(), which includes syncBranchPull() to fetch and import
updates from the remote sync branch.
This is essential for multi-clone workflows where:
- Clone A creates an issue and daemon pushes to sync branch
- Clone B's daemon needs to periodically pull to see the new issue
- Without periodic sync, Clone B would only see updates if its local
JSONL file happened to change
The 30-second interval balances responsiveness with network overhead.
Adds integration test TestEventDrivenLoop_PeriodicRemoteSync that
verifies the event-driven loop starts with periodic sync support.
* feat(daemon): add configurable interval for periodic remote sync
- Add BEADS_REMOTE_SYNC_INTERVAL environment variable to configure
the interval for periodic remote sync (default: 30s)
- Add getRemoteSyncInterval() function to parse the env var
- Minimum interval is 5s to prevent excessive load
- Setting to 0 disables periodic sync (not recommended)
- Add comprehensive integration tests for the configuration
Valid duration formats:
- "30s" (30 seconds)
- "1m" (1 minute)
- "5m" (5 minutes)
Tests added:
- TestEventDrivenLoop_HasRemoteSyncTicker
- TestGetRemoteSyncInterval_Default
- TestGetRemoteSyncInterval_CustomValue
- TestGetRemoteSyncInterval_MinimumEnforced
- TestGetRemoteSyncInterval_InvalidValue
- TestGetRemoteSyncInterval_Zero
- TestSyncBranchPull_FetchesRemoteUpdates
* fix: resolve all golangci-lint errors (cherry-pick from fix/linting-errors)
Cherry-picked linting fixes to ensure CI passes.
* feat(daemon): add config.yaml support for remote-sync-interval
- Add remote-sync-interval to .beads/config.yaml as alternative to
BEADS_REMOTE_SYNC_INTERVAL environment variable
- Environment variable takes precedence over config.yaml (follows
existing pattern for flush-debounce)
- Add config binding in internal/config/config.go
- Update getRemoteSyncInterval() to use config.GetDuration()
- Add doctor validation for remote-sync-interval in config.yaml
Configuration sources (in order of precedence):
1. BEADS_REMOTE_SYNC_INTERVAL environment variable
2. remote-sync-interval in .beads/config.yaml
3. DefaultRemoteSyncInterval (30s)
Example config.yaml:
remote-sync-interval: "1m"
---------
Co-authored-by: Charles P. Cross <cpdata@users.noreply.github.com>
The interactions.jsonl file is an append-only audit log that should be
tracked in git (synced with bd sync), but it was missing from the
negation patterns in .beads/.gitignore.
This adds !interactions.jsonl alongside !issues.jsonl and !metadata.json
for consistency and clarity that this file should be committed.
Co-authored-by: Christian Catalan <crcatala@gmail.com>
- bd repo add/remove now writes to .beads/config.yaml instead of database
- bd repo remove deletes hydrated issues from the removed repo
- Added internal/config/repos.go for YAML config manipulation
- Added DeleteIssuesBySourceRepo for cleanup on remove
Fixes config disconnect where bd repo add wrote to DB but hydration read from YAML.
Breaking change: bd repo add no longer accepts optional alias argument.
Co-authored-by: Dylan Conlin <dylan.conlin@gmail.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(doctor): UX improvements for diagnostics and daemon
- Add Repo Fingerprint check to detect when database belongs to a
different repository (copied .beads dir or git remote URL change)
- Add interactive fix for repo fingerprint with options: update repo ID,
reinitialize database, or skip
- Add visible warning when daemon takes >5s to start, recommending
'bd doctor' for diagnosis
- Detect install method (Homebrew vs script) and show only relevant
upgrade command
- Improve WARNINGS section:
- Add icons (⚠ or ✖) next to each item
- Color numbers by severity (yellow for warnings, red for errors)
- Render entire error lines in red
- Sort by severity (errors first)
- Fix alignment with checkmarks above
- Use heavier fail icon (✖) for better visibility
- Add integration and validation tests for doctor fixes
* fix(lint): address errcheck and gosec warnings
- mol_bond.go: explicitly ignore ephStore.Close() error
- beads.go: add nosec for .gitignore file permissions (0644 is standard)
Implements wisp management commands for ephemeral molecules:
- bd wisp list: List wisps with stale detection
- bd wisp gc: Garbage collect orphaned wisps
- bd mol burn: Delete wisp without creating digest
Wisps are ephemeral molecules stored in .beads-wisp/ for patrol cycles
and operational loops that shouldn't accumulate in permanent storage.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add wisp detection in mol squash: checks wisp storage if not in main
- squashWispToPermanent: creates digest in permanent, deletes from wisp
- Fix directory naming: .beads-wisps → .beads-wisp (singular, matches doc)
- Add comprehensive tests for wisp squash scenarios
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Standardize terminology for ephemeral molecule storage:
- .beads-ephemeral/ → .beads-wisps/
- --ephemeral flag → --wisp flag
- All Ephemeral* functions → Wisp*
Wisps are the "steam" in Gas Town's engine metaphor - ephemeral
molecules that evaporate after squash.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When --ephemeral is set:
- Spawned molecules go to .beads-ephemeral/ instead of .beads/
- Ephemeral directory is automatically gitignored
- No auto-flush (ephemeral does not sync)
- Proto+proto bonding ignores flag (templates stay in permanent storage)
Updated help text to document wisp workflow (bond then squash/burn).
Generated with Claude Code
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- bd ship <capability> finds issue with export:<capability> label
- Validates issue is closed (or --force to override)
- Adds provides:<capability> label to mark capability as shipped
- Protects provides:* namespace in bd label add
- Supports --dry-run for preview
External projects depend on capabilities via:
bd dep add <issue> external:<project>:<capability>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Config (bd-66w1):
- Add external_projects config for mapping project names to paths
- Add GetExternalProjects() and ResolveExternalProjectPath() functions
- Add config documentation and tests
External deps (bd-om4a):
- bd dep add accepts external:project:capability syntax
- External refs stored as-is in dependencies table
- GetBlockedIssues includes external deps in blocked_by list
- blocked_issues_cache includes external dependencies
- Add validation and parsing helpers for external refs
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The code assumed rootID was at least 8 chars, but issue IDs
like 'gt-i4lo' are only 7 chars (prefix-hash format).
Simply use the full ID instead of truncating - IDs are already
short enough for display.
- Add CheckOrphanedIssues to detect issues referenced in commits but still open
- Pattern matches (prefix-xxx) in git log against open issues in database
- Reports warning with issue IDs and commit hashes
- Add 8 comprehensive tests for the new check
Also:
- Add tests for mol spawn --attach functionality (bd-f7p1)
- Document commit message convention in AGENT_INSTRUCTIONS.md
- Fix CheckpointWAL to use wrapDBError for consistency
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The --as flag description correctly says "Custom title" but the variable
was named customID and dry-run output said "Custom ID". Fixed for consistency:
- Renamed variable customID -> customTitle
- Changed dry-run output from "Custom ID" to "Custom title"
Closes bd-e7ou
GetConfig() returns ("", nil) for missing/empty config keys.
getRepoConfig() then tried json.Unmarshal([]byte(""), ...) which
fails with "unexpected end of JSON input".
Add empty value check before JSON parsing - return empty map when
config value is empty string.
Closes GH#680
Co-Authored-By: dylan-conlin <dylan-conlin@users.noreply.github.com>
Fixes:
- P0 priority preserved - omitempty removed from Priority field (GH#671)
- nil pointer check in markdown parsing (GH#674)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove cleanupSnapshots() and validateSnapshotConsistency() from
deletion_tracking.go - both were marked deprecated and never called.
The other two deprecated functions (getSnapshotStats, initializeSnapshotsIfNeeded)
are still used by tests and production code respectively.
Filed bd-xsl9 to track removal of legacy autoflush dual-path code.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove omitempty from Priority field so P0 issues preserve priority:0
- Add nil check for store in createIssuesFromMarkdown
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
gosec G204 warning - the cmdName comes from user configuration
(mail_delegate setting), not untrusted input.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Wisp = ephemeral vapor produced by the Steam Engine (Gas Town).
This aligns with the metaphor:
- Claude = Fire
- Claude Code = Steam
- Gas Town = Steam Engine
- Wisps = ephemeral vapor it produces
Changes:
- types.Issue.Ephemeral → types.Issue.Wisp
- types.IssueFilter.Ephemeral → types.IssueFilter.Wisp
- JSON field: "ephemeral" → "wisp"
- CLI flag: --ephemeral → --wisp (bd cleanup)
- All tests updated
Note: SQLite column remains "ephemeral" (no migration needed).
This is a breaking change for JSON consumers using 0.33.0.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Ephemeral molecules release:
- bd mol spawn creates ephemeral issues by default
- Ephemeral issues never export to JSONL
- bd mol squash with --summary for agent summaries
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Ephemeral issues should never be exported to issues.jsonl. They exist only
in SQLite and are shared via .beads/redirect pointers. This prevents
"zombie" issues from resurrecting after mol squash deletes them.
Changes:
- Filter ephemeral issues in autoflush, export, and multirepo_export
- Add --summary flag to bd mol squash for agent-provided summaries
- Fix DeleteIssue to also remove comments (missing cascade)
- Add tests for ephemeral filtering and comment deletion
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements Tier 2 of the ephemeral molecule cleanup workflow. The squash
command compresses a molecule's ephemeral children into a single digest issue.
Features:
- Collects all ephemeral child issues of a molecule
- Generates a structured digest with execution summary
- Creates a non-ephemeral digest issue linked to the root
- Optionally deletes ephemeral children (default behavior)
- Supports --dry-run and --keep-children flags
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Molecule spawning now creates ephemeral issues that can be bulk-deleted
when closed using `bd cleanup --ephemeral`. This supports the ephemeral
molecule workflow pattern where execution traces are cleaned up while
outcomes persist.
Changes:
- Add `ephemeral` parameter to cloneSubgraph() and spawnMolecule()
- mol spawn: ephemeral=true by default, add --persistent flag to opt out
- mol run: ephemeral=true (molecule execution)
- mol bond: ephemeral=true (bonded protos)
- template instantiate: ephemeral=false (deprecated, backwards compat)
This is Tier 1 of the ephemeral molecule workflow epic (bd-2vh3).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add done channel to runGitCmdWithTimeoutMsg and runCmdWithTimeoutMessage
so goroutines exit immediately when command completes, rather than
waiting for the full timeout duration.
Follow-up to PR #678.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Code review found that bondProtoProto was setting Labels on the compound
issue, but CreateIssue doesn't persist labels (they're stored separately.
Fixed by calling AddLabel after creating the compound.
Also added comprehensive tests for:
- isProto() - template label detection
- operandType() - proto vs molecule string
- minPriority() - priority comparison
- bondProtoProto() - compound proto creation with deps and labels
- bondProtoMol() - spawn proto and attach to molecule
- bondMolMol() - join two molecules with sequential/parallel bonds
Tests verify dependency types (blocks vs parent-child) based on bond type.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
EOF
)
The timerChan and timeoutChan variables were created but never read from,
causing lint errors. Removed them since we don't need channel synchronization -
we only want the timeout to trigger a message, not block anything.
Agents naturally use spawn-style (variable=value) for both spawn and
distill commands. Now distill accepts both:
- --var branch=feature-auth (spawn-style)
- --var feature-auth=branch (substitution-style)
Smart detection checks which side appears in the epic text:
- Right side found → spawn-style (left is varname)
- Left side found → substitution-style (left is value)
- Both found → prefers spawn-style (more common guess)
Embodies the Beads philosophy: watch what agents do, make their guess correct.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When git push or other git operations hang waiting for credential/browser
auth, show a helpful message to the user after 5 seconds of inactivity
instead of appearing frozen.
Added:
- runCmdWithTimeoutMessage() in internal/syncbranch/worktree.go
- runGitCmdWithTimeoutMsg() in cmd/bd/sync.go
- Both functions print a message after timeout delay with advice
to check for browser auth prompts
Fixes issue #647 (bd sync frozen waiting for browser auth)