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>
Formulas are Gas Town orchestration workflows, not beads primitives.
The release workflow now lives in gastown/mayor/rig/.beads/formulas/
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Mail gates check for messages matching the pattern (await_id):
- Case-insensitive substring match on message subjects
- If waiters specified, only matches messages to those recipients
- Closes gate when matching message found
Note: Full testing blocked by bd-70c4 (await fields being cleared).
The code is correct but gate fields get corrupted by auto-import.
Also adds context and storage imports for mail gate function.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Human gates now have proper workflow:
- Create with: bd gate create --await human:approve-deploy
- Approve with: bd gate approve <gate-id> [--comment "reason"]
- bd gate eval now reports human gates as "awaiting approval"
- Rejects non-human gates with clear error message
Also added mail gate placeholder in eval (awaiting mail: ...).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Align with Gas Town formula format change from JSON to TOML.
Same content, cleaner syntax.
🤖 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
)
The merged field does not exist in gh pr view output. The actual
fields are:
- state: OPEN, CLOSED, or MERGED
- mergedAt: timestamp string (empty if not merged)
Also simplified the switch statement since state already distinguishes
merged vs closed-without-merge PRs.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements evaluation for GitHub-related gate types:
- gh:run: Checks if a GitHub Actions run has completed using
`gh run view <id> --json status,conclusion`
- gh:pr: Checks if a PR has been merged/closed using
`gh pr view <id> --json state,merged`
Both gate types require the gh CLI to be installed and authenticated.
If gh fails (not installed, network issues, invalid IDs), the gate is
skipped rather than erroneously closed.
Refactors the eval loop to use a switch statement for cleaner
gate type dispatch.
(gt-twjr5.3)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- New `bd gate eval` command evaluates all open gates and closes elapsed ones
- Timer gates: closes when elapsed time exceeds timeout duration
- For timer gates, parse duration from await_id if --timeout not explicitly set
- Supports --dry-run and --json output modes
- Idempotent and safe to run repeatedly (e.g., in Deacon patrol)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
In git worktrees, any bd command was slow with a 2-3s pause because:
- git.IsWorktree() was called 4+ times per command
- Each call spawned 2 git processes (git-dir and git-common-dir)
- git.GetRepoRoot() and git.GetMainRepoRoot() also called multiple times
Fix: Cache results using sync.Once since these values do not change during
a single command execution:
- IsWorktree() - caches worktree detection
- GetRepoRoot() - caches repo root path
- GetMainRepoRoot() - caches main repo root for worktrees
Added ResetCaches() for test cleanup between subtests that change directories.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add TOML parsing using BurntSushi/toml
- Update formula loader to try .toml first, fall back to .json
- Add `bd formula convert` command for JSON→TOML migration
- Multi-line string support for readable descriptions
- Cache git worktree/reporoot checks for performance
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When bd init runs in a forked repo (detected via upstream remote), prompt
the user to configure .git/info/exclude to keep beads files local.
- Add --setup-exclude flag for manual trigger
- Add fork detection to init flow (reuses detectForkSetup)
- Add setupForkExclude() to configure .git/info/exclude with:
.beads/, **/RECOVERY*.md, **/SESSION*.md
- Add promptForkExclude() with Y/n prompt (default yes)
- Add containsExactPattern() helper for precise pattern matching
🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* 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
Document the BEADS_DIR workflow for storing beads in a completely
separate git repository from your code. This feature was added in
PR #533 by @dand-oss but wasn't documented.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Steps can now declare their own expansion using the Expand field:
steps:
- id: design
expand: rule-of-five
expand_vars:
iterations: 3
This is more convenient than compose.expand for single-step expansions.
The step is replaced by the expansion template with variables substituted.
Reuses existing expandStep() and mergeVars() from gt-8tmz.34.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add vars parameter to expandStep for {varname} substitution
- Add mergeVars to combine formula defaults with rule overrides
- Update ApplyExpansions to merge and pass vars for expand/map rules
- Apply var substitution to step ID, title, description, assignee,
labels, and dependencies
- Add comprehensive tests for var overrides functionality
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
- Skip daemon mode on Windows (sys.platform == win32)
- Falls back to CLI mode which works on all platforms
- Avoids asyncio.open_unix_connection which does not exist on Windows
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Move write stream creation after redirect handling to avoid orphan
streams that keep file handles open
- Add 100ms delay after file.close() on Windows to ensure handle release
- Fixes "The process cannot access the file" error during extraction
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update help text to clarify mol/wisp/epic support (not just epics)
- Add daemon support (no longer requires --no-daemon)
- Add -o shorthand for --output flag
- Update use cases to match new architecture
Part of bd-1dez.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix YAML → JSON references in formula.go and types.go
- Update MOLECULES.md with ephemeral proto architecture
- Add Distillation section: extract formulas from completed work
- Add Sharing section: Mol Mall formula marketplace
- Update Layer Cake diagram to show ephemeral proto flow
Related: bd-1dez (Mol Mall epic)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add --mode flag (compile/runtime) and --var flag to bd cook command.
Compile-time mode (default):
- Keeps {{variable}} placeholders intact
- Use for: modeling, estimation, contractor handoff
Runtime mode (triggered by --var or --mode=runtime):
- Substitutes variables with provided values
- Validates all required variables have values
- Use for: final validation before pour
Also updates --dry-run output to clearly show which mode is active
and display substituted values in runtime mode.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Test cases for:
- Unary minus in expression: 3*-2 -> -6
- Parenthesized negative: (-5) -> -5
- Unary minus after power: 2^-1 -> 0 (truncated)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add support for for-each expansion over computed ranges:
loop:
range: "1..2^{disks}-1" # Evaluated at cook time
var: move_num
body: [...]
Features:
- Range field in LoopSpec for computed iterations
- Var field to expose iteration value to body steps
- Expression evaluator supporting + - * / ^ and parentheses
- Variable substitution in range expressions using {name} syntax
- Title/description variable substitution in expanded steps
Example use case: Towers of Hanoi formula where step count is 2^n-1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>