* 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>
- 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)
Adds --attach (repeatable) and --attach-type flags to bd mol spawn
for on-the-fly composition of molecules.
Usage:
bd mol spawn mol-feature --attach mol-testing --attach mol-docs
Each attached proto is spawned and bonded to the primary molecule
using the specified bond type (default: sequential).
Also fixes a pre-existing bug where bondProtoMol and bondMolMol tried
to add both parent-child AND blocks dependencies between the same
issues, violating the UNIQUE constraint on (issue_id, depends_on_id).
Now sequential bonds use blocks type, parallel/conditional use
parent-child.
Closes bd-obep
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements bd mol distill to reverse the spawn operation:
- molecule → proto (spawn is proto → molecule)
Features:
- --as flag for custom proto title
- --var value=variable to replace concrete values with placeholders
- --dry-run to preview the distilled structure
- Preserves full subgraph structure and dependencies
- Adds template label to all cloned issues
Also updates AGENTS.md with landing checklist.
Closes: bd-iq19
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Modified fix.DBJSONLSync() to detect which direction to sync:
- If DB > JSONL: run 'bd export' to sync JSONL (DB has newer data)
- If JSONL > DB: run 'bd sync --import-only' to import (JSONL is source of truth)
- If equal but different timestamps: use file mtime to decide direction
- Updated CheckDatabaseJSONLSync() error messages to recommend correct fix direction:
- Shows different guidance based on whether DB or JSONL has more issues
- Added helper functions:
- countDatabaseIssues() to count issues in SQLite
- countJSONLIssues() to count issues in JSONL (local, avoids circular import)
- Added tests for countJSONLIssues() with edge cases
Fixes issue where 'bd doctor --fix' would recommend 'bd sync --import-only'
when DB > JSONL, which would be a no-op since JSONL hasn't changed.
Implement bd mol bond for composing protos and molecules:
- proto + proto → compound proto (reusable template)
- proto + mol → spawn proto, attach to molecule
- mol + proto → spawn proto, attach to molecule
- mol + mol → join into compound molecule
Supports --type (sequential/parallel/conditional) and --dry-run flags.
Closes bd-o91r.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Agents naturally type 'bd mail' when working with beads, but mail
functionality is provided by orchestrators like Gas Town. This command
bridges that gap by delegating to a configured mail provider.
Configuration options (checked in order):
1. BEADS_MAIL_DELEGATE or BD_MAIL_DELEGATE env var
2. mail.delegate config setting
Example usage:
export BEADS_MAIL_DELEGATE="gt mail"
bd mail inbox # Runs: gt mail inbox
bd mail send mayor/... # Runs: gt mail send mayor/...
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
bd mol run = bond + assign + pin:
- Bonds the molecule (creates issues from template)
- Assigns root to the caller
- Sets root status to in_progress
- Pins root issue for session recovery
After a crash or session reset, the pinned root ensures the agent
can resume from where it left off by checking 'bd ready'.
This is the Gas Town integration point that makes molecules immortal.
Closes: bd-icnf
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The simple single-issue instantiation in molecule.go is superseded by
the full DAG-based mol commands. Use 'bd mol bond' for all molecule
instantiation - it handles both single-issue and multi-issue molecules.
Closes: bd-8b0x
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds the mol command group for molecule workflows:
- bd mol catalog - list available molecules
- bd mol show <id> - show molecule structure and variables
- bd mol bond <id> --var k=v - instantiate a molecule
Molecules are templates with workflow semantics - they are the
orchestration primitive for Gas Town. This unifies the template
system under the mol command.
The template commands are deprecated but still work:
- bd template list -> use bd mol catalog
- bd template show -> use bd mol show
- bd template instantiate -> use bd mol bond
Closes: bd-ffjt
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>