New commands and features:
- bd worktree for parallel development
- bd slot commands for agent bead slot management
- bd agent state for ZFC-compliant state reporting
- bd doctor --deep for full graph integrity validation
- Agent bead support (type=agent, type=role, migration 030)
- Computed .parent field in JSON output
- Auto-bypass daemon for wisp operations
- Pour warning for vapor-phase formulas
Performance:
- O(2^n) → O(V+E) cycle detection (GH#775)
Fixes:
- Import hash mismatch warnings
- Test updates for version tracking thresholds
- Migration test updated for new agent schema fields
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Strip (bd-xxx), (gt-xxx) suffixes from code comments and changelog
entries. The descriptions remain meaningful without the ephemeral
issue IDs.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace the recursive SQL CTE in DetectCycles with Go-layer DFS using
shared visited set. The previous implementation enumerated all paths
through the dependency graph, causing exponential blowup with diamond
patterns (multiple issues depending on the same target).
Changes:
- Add loadDependencyGraph() to load deps as adjacency list in one query
- Implement DFS cycle detection with recStack for back-edge detection
- Add normalizeCycle() for consistent cycle deduplication
- Add DetectCycles-specific benchmarks (Linear, Dense, Tree graphs)
- Use direct SQL INSERT in benchmarks to bypass AddDependency overhead
Performance improvement on dense graph (500 nodes, 2500 edges):
- Before: >120s timeout
- After: 1.6ms
Benchmarks:
- DetectCycles_Linear_1000: 0.84ms (1000 nodes, 999 edges)
- DetectCycles_Dense_500: 1.59ms (500 nodes, ~2500 edges)
- DetectCycles_Tree_1000: 0.85ms (1000 nodes, 999 edges)
Add slot management commands:
- bd slot set <agent> <slot> <bead> - set slot (error if occupied)
- bd slot clear <agent> <slot> - clear slot
- bd slot show <agent> - show all slots
These enforce cardinality constraints for agent bead slots.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add nolint:unparam for readOnly param in sqlite_open.go
- Handle cmd.Help() error in wisp.go
- Handle rows.Close() error in migration 028
- Handle targetStore.Close() error in create.go
- Update Nix vendorHash for current dependencies
Cherry-picked from PR #769
Analysis found these commands are dead code:
- gt never calls `bd pin` - uses `bd update --status=pinned` instead
- Beads.Pin() wrapper exists but is never called
- bd hook functionality duplicated by gt mol status
- Code comment says "pinned field is cosmetic for bd hook visibility"
Removed:
- cmd/bd/pin.go
- cmd/bd/unpin.go
- cmd/bd/hook.go
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The migration was using SELECT * which fails when migrating databases
that predate the created_by column (34 columns vs 35). Now explicitly
lists columns and provides empty default for created_by if missing.
Also fixes missed Wisp→Ephemeral rename in multirepo_test.go.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Migration 028 lacked an idempotency check, causing it to fail on databases
where the migration had already been applied. The migration would attempt
to copy data from issues to issues_new, but both tables had the same CHECK
constraint, causing the insert to fail.
Added check for "status = 'tombstone'" in the table schema to detect if
the migration has already been applied and skip if so.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Wisps are ephemeral issues used for patrol/operational cycles and should
not appear in `bd ready` output as trackable work items. Added ephemeral
exclusion filter to both the GetReadyWork() function and ready_issues VIEW.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Migration 028 (tombstone_closed_at) recreates the issues table but was
missing the created_by column that exists in schema.go. This caused
`SELECT * FROM issues` to fail with column count mismatch on fresh DBs.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add a created_by field to track who created each issue, similar to how
comments have an author field.
- Add CreatedBy string field to Issue struct
- Add migration 029 to add created_by column to issues table
- Update all SELECT/INSERT/Scan statements across storage layer
- Populate created_by in bd create from actor chain
- Display created_by in bd show output
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Previously, tombstones could not have a closed_at timestamp due to:
1. Go validation: `if status != closed && closed_at != nil` failed
2. SQL CHECK constraint: `(status = 'closed') = (closed_at IS NOT NULL)`
This caused import failures for tombstones that were closed before being
deleted - a valid scenario where we want to preserve the historical
closed_at timestamp for audit purposes.
Changes:
- internal/types/types.go: Updated validation to allow tombstones with
closed_at (line 253)
- internal/storage/sqlite/schema.go: Updated CHECK constraint to allow
closed AND tombstone statuses to have closed_at
- internal/storage/sqlite/migrations/028_tombstone_closed_at.go: Migration
to update existing databases with the new constraint
- .beads/issues.jsonl: Fixed bd-6s61 status from 'closed' to 'tombstone'
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Expands the inline comment to serve as a reference for when to use the
COALESCE(NULLIF(?, zero), column) defensive idiom in JSONL imports.
Pattern protects clone-local state (pinned, gate fields) from being
overwritten when importing JSONL where omitempty causes zero values to
represent "field absent" rather than "field is zero".
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Apply COALESCE(NULLIF(...)) pattern to await_type, await_id, timeout_ns,
and waiters fields in upsertIssueInTx. This prevents gate await fields
from being cleared when importing issues from JSONL that don't have
these fields (since gates are wisps and aren't exported to JSONL.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
EOF
)
* feat(ready,blocked): Add --parent flag for scoping by epic/bead descendants
Add --parent flag to `bd ready` and `bd blocked` CLI commands and MCP tools
to filter results to all descendants of a specific epic or bead.
## Backward Compatibility
- CLI: New optional --parent flag; existing usage unchanged
- RPC: New `blocked` operation added (was missing); existing operations unchanged
- MCP: New optional `parent` parameter; existing calls work as before
- Storage interface: GetBlockedIssues signature changed to accept WorkFilter
- All callers updated to pass empty filter for existing behavior
- Empty WorkFilter{} returns identical results to previous implementation
## Implementation Details
SQLite uses recursive CTE to traverse parent-child hierarchy:
WITH RECURSIVE descendants AS (
SELECT issue_id FROM dependencies
WHERE type = 'parent-child' AND depends_on_id = ?
UNION ALL
SELECT d.issue_id FROM dependencies d
JOIN descendants dt ON d.depends_on_id = dt.issue_id
WHERE d.type = 'parent-child'
)
SELECT issue_id FROM descendants
MemoryStorage implements equivalent recursive traversal with visited-set
cycle protection via collectDescendants helper.
Parent filter composes with existing filters (priority, labels, assignee, etc.)
as an additional WHERE clause - all filters are AND'd together.
## RPC Blocked Support
MCP beads_blocked() existed but daemon client raised NotImplementedError.
Added OpBlocked and handleBlocked to enable daemon RPC path, which was
previously broken. Now both CLI and daemon clients work for blocked queries.
## Changes
- internal/types/types.go: Add ParentID *string to WorkFilter
- internal/storage/sqlite/ready.go: Add recursive CTE for parent filtering
- internal/storage/memory/memory.go: Add getAllDescendants/collectDescendants
- internal/storage/storage.go: Update GetBlockedIssues interface signature
- cmd/bd/ready.go: Add --parent flag to ready and blocked commands
- internal/rpc/protocol.go: Add OpBlocked constant and BlockedArgs type
- internal/rpc/server_issues_epics.go: Add handleBlocked RPC handler
- internal/rpc/client.go: Add Blocked client method
- integrations/beads-mcp/: Add BlockedParams model and parent parameter
## Usage
bd ready --parent bd-abc # All ready descendants
bd ready --parent bd-abc --priority 1 # Combined with other filters
bd blocked --parent bd-abc # All blocked descendants
## Testing
Added 4 test cases for parent filtering:
- TestParentIDFilterDescendants: Verifies recursive traversal (grandchildren)
- TestParentIDWithOtherFilters: Verifies composition with priority filter
- TestParentIDWithBlockedDescendants: Verifies blocked issues excluded from ready
- TestParentIDEmptyParent: Verifies empty result for childless parent
* fix: Correct blockedCmd indentation and suppress gosec false positive
- Fix syntax error from incorrect indentation in blockedCmd Run function
- Add nolint:gosec comment for GetBlockedIssues SQL formatting (G201)
The filterSQL variable contains only parameterized WHERE clauses with
? placeholders, not user input
When closing an issue, the new --suggest-next flag returns a list of
issues that became unblocked (ready to work on) as a result of the close.
This helps agents and users quickly identify what work is now available
after completing a blocker.
Example:
$ bd close bd-5 --suggest-next
✓ Closed bd-5: Completed
Newly unblocked:
• bd-7 "Implement feature X" (P1)
• bd-8 "Write tests for X" (P2)
Implementation:
- Added GetNewlyUnblockedByClose to storage interface
- Implemented efficient single-query for SQLite using blocked_issues_cache
- Added SuggestNext field to CloseArgs in RPC protocol
- Added CloseResult type for structured response
- CLI handles both daemon and direct modes
Thanks to @kraitsura for the detailed feature request and design.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Changed prefix logic to combine the database prefix with the type prefix:
- bd-wisp-xxx (was: wisp-xxx)
- bd-mol-xxx (was: mol-xxx)
This ensures IDs like hq-wisp-xxx at town level and gt-mol-xxx for gastown,
maintaining database namespace while adding type recognition.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Added type-specific ID prefixes for better visual recognition:
- `bd pour` now generates IDs with "mol-" prefix
- `bd wisp create` now generates IDs with "wisp-" prefix
- Regular issues continue using the configured prefix
Implementation:
- Added IDPrefix field to types.Issue (internal, not exported to JSONL)
- Added Prefix field to CloneOptions for spawning operations
- Added IDPrefix to RPC CreateArgs for daemon communication
- Updated storage layer to use issue.IDPrefix when generating IDs
- Updated pour.go and wisp.go to pass appropriate prefixes
This enables instant visual recognition of entity types and prevents
accidental modification of templates.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
extractParentChain was using strings.Split(id, ".") which incorrectly
parsed prefixes containing dots (like "alicealexandra.com"). This caused
--parent to fail with "parent does not exist" even when the parent was
present in the database.
The fix uses IsHierarchicalID to walk up the hierarchy correctly, only
splitting on dots followed by numeric suffixes (the actual hierarchy
delimiter).
Example:
- "test.example-abc.1" now correctly returns ["test.example-abc"]
- Previously it incorrectly returned ["test", "test.example-abc"]
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When creating issues with explicit hierarchical IDs (e.g., bd-test.1, bd-test.2
via --id flag or import), the child_counters table was not being updated.
This caused GetNextChildID to return colliding IDs when later called with
--parent.
Changes:
- Add ensureChildCounterUpdatedWithConn() to update counter on explicit child creation
- Add ParseHierarchicalID() to extract parent and child number from IDs
- Update CreateIssue to call counter update after hierarchical ID validation
- Update EnsureIDs to call counter update when parent exists
- Add post-insert phase in batch operations to update counters after FK
constraint can be satisfied
- Update tests to reflect new behavior where counter is properly initialized
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Comment timestamps were being overwritten with CURRENT_TIMESTAMP during
import, causing infinite sync loops between hosts as each import would
update timestamps.
Added ImportIssueComment() method that accepts and preserves the original
timestamp from JSONL, and updated importComments() to use it.
Closes#735
New RPC endpoint to get detailed progress for a molecule (parent issue
with child steps). Returns moleculeID, title, assignee, and list of
steps with their status (done/current/ready/blocked) and timestamps.
Used when user expands a worker in the activity feed TUI.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
merge-request, gate, molecule, and message types are internal workflow
items processed by Refinery/Deacon, not work for polecats to claim.
These are now excluded from bd ready unless explicitly requested via
--type flag.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixed two code paths where pinned=false from JSONL would overwrite
existing pinned=true in database:
- importer.go: Only update pinned if explicitly true in JSONL
- multirepo.go: Use COALESCE to preserve existing pinned value
Added tests for pinned field preservation.
Note: Bug may have additional code path - investigation ongoing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
External dependencies (external:<project>:<capability>) reference issues in
other projects and are expected to not exist in the local issues table.
The orphan check now excludes these with: AND d.depends_on_id NOT LIKE 'external:%'
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add async gates - coordination primitives for agents to wait on external events
like CI completion, PR merges, timers, or human approval.
Changes:
- Add 'gate' issue type to types.go with gate-specific fields:
- AwaitType: condition type (gh:run, gh:pr, timer, human, mail)
- AwaitID: condition identifier
- Timeout: max wait duration
- Waiters: mail addresses to notify when gate clears
- Add SQLite migration 027_gate_columns for new fields
- Update all SQLite storage queries to handle gate fields
- Add bd gate commands: create, show, list, close, wait
- All commands support --json output and --no-daemon mode
Closes: bd-2v0f, bd-lz49, bd-u66e
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add _ = prefix for ignored Close/Remove return values
- Fix unused cmd parameter in runMoleculeReady
- Add nolint:misspell for intentional British "cancelled" spelling
- Update comments to use US spelling where not intentional
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds 'waits-for' dependency type for dynamic molecule bonding:
- DepWaitsFor blocks an issue until spawner's children are closed
- Two gate types: all-children (wait for all) or any-children (first)
- Updated blocked_cache.go CTE to handle waits-for dependencies
- Added --waits-for and --waits-for-gate flags to bd create command
- Added WaitsForMeta struct for gate metadata storage
- Full test coverage for all gate types and dynamic child scenarios
This enables patrol molecules to wait for dynamically-bonded arms to
complete before proceeding (Christmas Ornament pattern).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Filters issues by parent issue ID using parent-child dependencies.
Example: bd list --parent=bd-xyz --status=open
Changes:
- Add ParentID field to IssueFilter type
- Add --parent flag to list command
- Forward parent filter through RPC
- Implement filtering in SQLite and memory storage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Conditional bonds now work as documented: "B runs only if A fails".
Implementation:
- Add DepConditionalBlocks dependency type to types.go
- Add IsFailureClose() helper to detect failure keywords in close_reason
- Update blocked cache to handle conditional-blocks:
- B is blocked while A is open
- B stays blocked if A closes with success
- B becomes unblocked if A closes with failure
Failure keywords: failed, rejected, wontfix, cancelled, abandoned,
blocked, error, timeout, aborted (case-insensitive)
Updated bondProtoProto, bondProtoMol, bondMolMol to use
DepConditionalBlocks for conditional bond type.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add testEnv struct with common test helper methods to test_helpers.go
- Methods include CreateIssue, CreateEpic, AddDep, AddParentChild, Close,
GetReadyWork, AssertReady, AssertBlocked
- Migrate 12+ tests in ready_test.go to use new helpers (-252 lines)
- Migrate 3 tests in dependencies_test.go to use new helpers (-34 lines)
- Total reduction: ~286 lines from test files
The testEnv pattern makes tests more readable and maintainable by:
- Eliminating boilerplate setup (store, cleanup, ctx)
- Providing semantic helper methods (AddDep vs manual AddDependency)
- Using t.Cleanup for automatic resource management
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When multiple bd commands are run in parallel, they can race during database
migrations, causing "duplicate column name" errors. This happens because:
1. Process A checks if column exists → false
2. Process B checks if column exists → false
3. Process A adds column → succeeds
4. Process B adds column → FAILS (duplicate column)
Changes:
- Wrap RunMigrations in BEGIN EXCLUSIVE transaction to serialize migrations
- Disable foreign keys BEFORE the transaction (PRAGMA must be called outside tx)
- Convert nested BEGIN/COMMIT in migrations 010, 022, 025 to use SAVEPOINTs
(SQLite does not support nested transactions)
- Remove redundant PRAGMA foreign_keys calls from individual migrations
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Migration 026_additional_indexes adds 5 indexes identified during schema review (bd-h0we):
- idx_issues_updated_at: For GetStaleIssues date filtering
- idx_issues_status_priority: For common list query patterns
- idx_labels_label_issue: Covering index for label lookups
- idx_dependencies_issue_type: For blocked issues queries
- idx_events_issue_type: For close reason queries
These indexes improve query performance for common operations,
particularly at scale (10K+ issues).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
External dependencies (external:project:capability) are now visible in
the dependency tree output. Previously they were invisible because the
recursive CTE only JOINed against the issues table.
Changes:
- GetDependencyTree now fetches external deps and adds them as synthetic
leaf nodes with resolution status (satisfied/blocked)
- formatTreeNode displays external deps with special formatting
- Added helper parseExternalRefParts for parsing external refs
Test coverage added for:
- External deps appearing in dependency tree
- Cycle detection ignoring external refs
- CheckExternalDep when target has no .beads directory
- Various invalid external ref format variations
Closes: bd-vks2, bd-mv6h, bd-d9mu
GetBlockedIssues was showing external deps as blocking even when they
were satisfied (had a closed issue with provides:capability label).
Added filterBlockedByExternalDeps() which:
- Collects all external refs from blocked issues
- Checks each with CheckExternalDeps() in batch
- Filters satisfied refs from BlockedBy lists
- Updates BlockedByCount accordingly
- Removes issues with no remaining blockers (unless status=blocked/deferred)
This matches the behavior of GetReadyWork which already filters by
external deps correctly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add EntityRef type for structured entity references with URI support
- Add Creator field to Issue for tracking who created work
- Add Validation type and Validations field for proof-of-stake approvals
- Fix RemoveDependency FK violation on external deps (bd-a3sj)
- Include all new fields in content hash computation
- Full test coverage for all new types
🤝 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.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>