bd mol install: Install formula from GitHub

Download and cook a formula from a GitHub repository.

## Usage
```bash
# Direct GitHub URL
bd mol install github.com/anthropics/mol-code-review

# With version tag
bd mol install github.com/anthropics/mol-code-review@v1.2.0

# Shorthand (future: via registry lookup)
bd mol install @anthropic/mol-code-review
```

## Implementation
1. Parse URL: extract org, repo, optional version tag
2. Construct raw URL: `https://raw.githubusercontent.com/org/repo/[tag]/formula.json`
3. Fetch formula.json via HTTP
4. Validate formula structure
5. Save to .beads/formulas/
6. Run cook to create local proto
7. Record in .beads/installed.json for update tracking

## installed.json Format
```json
{
  "mol-code-review": {
    "source": "github.com/anthropics/mol-code-review",
    "version": "v1.2.0",
    "installed_at": "2025-12-25T12:00:00Z"
  }
}
```

## Dependencies
- Requires network access
- Uses `gh` CLI or direct HTTP for auth (private repos)


Add GetMoleculeProgress RPC endpoint

New RPC endpoint to get detailed progress for a specific molecule. Returns: moleculeID, title, assignee, and list of steps with their status (done/current/ready/blocked), start/close times. Used when user expands a worker in the activity feed TUI.


Add close_reason field to CLI schema and documentation

PR #551 persists close_reason, but the CLI documentation may not mention this field as part of the issue schema.

## Current State
- close_reason is now persisted in database
- `bd show --json` will return close_reason in JSON output
- Documentation may not reflect this new field

## What's Missing
- CLI reference documentation for close_reason field
- Schema documentation showing close_reason is a top-level issue field
- Example output showing close_reason in bd show --json
- bd close command documentation should mention close_reason parameter is optional

## Suggested Action
1. Update README.md or CLI reference docs to list close_reason as an issue field
2. Add example to bd close documentation
3. Update any type definitions or schema specs
4. Consider adding close_reason to verbose list output (bd list --verbose)


Test agent bead

Testing new agent type


Exclude pinned issues from bd ready

Update bd ready to exclude pinned issues. Pinned issues are context markers, not work items, and should never appear in the ready-to-work list.


Restart running daemons

Kill and restart any running bd daemons to pick up new version: pkill -f 'bd daemon' && bd daemon --start


Implement bd close --continue flag

Auto-advance to next step in molecule when closing an issue. Referenced by gt-um6q, gt-lz13. Needed for molecule navigation workflow.


Agent beads: structured labels for filtering

## Agent Beads Need Queryable Labels

Currently agent beads have role_type/rig in description text, not as labels. This breaks @group resolution in gt mail.

## Current State
```json
{
  "id": "gt-gastown-witness",
  "issue_type": "agent",
  "description": "...\\nrole_type: witness\\nrig: gastown\\n..."
}
```

Cannot query: `bd list --type=agent --label=role_type:witness` returns nothing.

## Required
Agent bead creation should add labels:
- `role_type:<type>` (witness, refinery, crew, polecat, dog, mayor, deacon)
- `rig:<rig>` (gastown, beads, or "town" for town-level)

## Where to Fix
gt polecat/crew/agent creation commands should add labels.

## Acceptance
- New agent beads created with role_type/rig labels
- @group patterns work in gt mail router


Test event


Code smell: ComputeContentHash() is 100+ lines of repetitive code

attached_args: Refactor ComputeContentHash repetitive code

The ComputeContentHash() method in internal/types/types.go (lines 87-190) is over 100 lines of repetitive code:

```go
h.Write([]byte(i.Title))
h.Write([]byte{0}) // separator
h.Write([]byte(i.Description))
h.Write([]byte{0})
h.Write([]byte(i.Design))
// ... repeated 40+ times
```

Consider:
1. Using reflection to iterate over struct fields tagged for hashing
2. Creating a helper function that takes field name and value
3. Using a slice of fields to hash and iterating over it
4. At minimum, extracting the repetitive pattern into a helper

Location: internal/types/types.go:87-190


bd close --continue: auto-advance to next molecule step

Add --continue flag to bd close for seamless molecule step transitions.

## Usage

bd close <step-id> --continue [--no-auto]

## Behavior

1. Closes the specified step
2. Finds next ready step in same molecule (sibling/child)
3. By default, marks it in_progress (--no-auto to skip)
4. Outputs the transition

## Output

[done] Closed gt-abc.3: Implement feature

Next ready in molecule:
  gt-abc.4: Write tests

[arrow] Marked in_progress (use --no-auto to skip)

## If no next step

[done] Closed gt-abc.6: Exit decision

Molecule gt-abc complete! All steps closed.
Consider: bd mol squash gt-abc --summary '...'

## Key behaviors
- Detects parent molecule from closed step
- Finds next unblocked sibling
- Auto-claims by default (propulsion principle)
- Graceful handling when molecule is complete

## Gas Town integration
- gt-lz13: Update templates with nav workflow
- gt-um6q: Update docs with nav workflow


Remove legacy autoflush code paths

## Problem

The autoflush system has dual code paths - an old timer-based approach and a new FlushManager. Both are actively used based on whether flushManager is nil.

## Locations

- main.go:78-81: isDirty, needsFullExport, flushTimer marked 'used by legacy code'
- autoflush.go:291-369: Functions with 'Legacy path for backward compatibility with tests'

## Current Behavior

```go
// In markDirtyAndScheduleFlush():
if flushManager != nil {
    flushManager.MarkDirty(false)
    return
}
// Legacy path for backward compatibility with tests
```

## Proposed Fix

1. Ensure flushManager is always initialized (even in tests)
2. Remove the legacy timer-based code paths
3. Remove isDirty, needsFullExport, flushTimer globals
4. Update tests to use FlushManager

## Risk

Low - the FlushManager is the production path. Legacy code only runs when flushManager is nil (test scenarios).


Ephemeral patrol molecules leaking into beads

## Problem

Ephemeral patrol orchestration molecules (e.g., mol-deacon-patrol, wisp step issues) keep appearing in `bd ready` output in gastown beads.

These are ephemeral/wisp issues that should:
1. Never be synced to the beads-sync branch
2. Live only in .beads-wisp/ (ephemeral storage)
3. Be squashed to digests, not persisted as regular beads

## Examples found

- gt-wisp-6ue: mol-deacon-patrol
- gt-pacdm: mol-deacon-patrol
- gt-wisp-mpm: Check own context limit
- gt-wisp-lkc: Clean dead sessions

## Investigation needed

1. Where are these being created? (gt mol bond? manual bd create?)
2. Why are they using the gt- prefix instead of staying ephemeral?
3. Is the wisp storage (.beads-wisp/) being used correctly?
4. Is bd sync accidentally picking up ephemeral issues?

## Expected behavior

Patrol molecules and their steps should be ephemeral and never appear in `bd ready` or `bd list`.


bd wisp list command

Add bd wisp list command to show ephemeral molecules.

## Usage
```bash
bd wisp list              # List all wisps in current context
bd wisp list --json       # JSON output
bd wisp list --all        # Include orphaned wisps
```

## Output
- Shows in-progress ephemeral molecules
- Columns: ID, Title, Started, Last Update, Status
- Warns about orphaned wisps (old updated_at)

## Implementation
- New 'wisp' command group
- Read from .beads-ephemeral/issues.jsonl
- Filter to ephemeral:true issues


Test daemon RPC delete handler

Add tests for the daemon-side RPC delete handler that processes delete requests from clients.

## What needs testing
- Daemon's Delete RPC handler implementation
- Processing delete requests from RPC clients
- Cascade deletion at daemon level
- Force deletion at daemon level
- Dry-run mode validation
- Error responses to clients
- Dependency validation before deletion
- Tombstone creation via daemon

## Test scenarios
1. Delete single issue via RPC
2. Delete multiple issues via RPC
3. Cascade deletion of dependents
4. Force delete with orphaned dependents
5. Dry-run returns what would be deleted without actual deletion
6. Error: invalid issue IDs
7. Error: insufficient permissions
8. Error: dependency blocks deletion (without force/cascade)

## Related
- Parent epic: bd-kyll
- Original issue: bd-7z4


Release v0.33.2

Version bump workflow for beads release 0.33.2.

## Variables
- `0.33.2` - The new version number (e.g., 0.31.0)
- `2025-12-21` - Release date (YYYY-MM-DD format)

## Workflow Steps
1. Kill running daemons
2. Run tests and linting
3. Bump version in all files (10 files total)
4. Update cmd/bd/info.go with release notes
5. Commit and push version bump
6. Create and push git tag
7. Update Homebrew formula
8. Upgrade local Homebrew installation
9. Verify installation

## Files Updated by bump-version.sh
- cmd/bd/version.go
- .claude-plugin/plugin.json
- .claude-plugin/marketplace.json
- integrations/beads-mcp/pyproject.toml
- integrations/beads-mcp/src/beads_mcp/__init__.py
- README.md
- npm-package/package.json
- cmd/bd/templates/hooks/* (4 files)
- CHANGELOG.md

## Manual Step Required
- cmd/bd/info.go - Add versionChanges entry with release notes


Implement bd mol stale command

Add command to detect complete-but-unclosed molecules.

**Definition of stale:**
A molecule is stale if:
1. All children are closed (Completed >= Total)
2. Root issue is still open
3. Not assigned to anyone
4. Not pinned to any hook
5. AND it's blocking other assigned work (graph pressure)

**Command design:**
```bash
bd mol stale              # List stale mols
bd mol stale --json       # Machine-readable
bd mol stale --blocking   # Only show those blocking other work
bd mol stale --all        # Include orphaned-but-not-blocking
```

**Output:**
```
Stale molecules (complete but unclosed, blocking work):

  bd-xyz  "Version bump v0.36.0"  (blocking: bd-abc, bd-def)
    → Close with: bd close bd-xyz
    → Or squash: bd mol squash bd-xyz

Orphaned molecules (complete but unclosed, not blocking):
  bd-uvw  "Old patrol cycle"
    → Consider: bd mol burn bd-uvw
```

**Implementation:**
1. Query all open issues with children (potential mols)
2. Compute progress for each (Completed vs Total)
3. Filter to complete ones
4. Check assigned_to field
5. Check pinned status (may need gt integration)
6. Check if blocking other assigned work

Does NOT use time-based staleness. Graph pressure only.


Run bump-version.sh

./scripts/bump-version.sh {{version}}


Update CHANGELOG header

Change [Unreleased] to [{{version}}] - {{date}} and add new empty [Unreleased] section above it.


Remove or fix ClearDirtyIssues() - race condition risk (bd-52)

Code health review found internal/storage/sqlite/dirty.go still exposes old ClearDirtyIssues() method (lines 103-108) which clears ALL dirty issues without checking what was actually exported.

Data loss risk: If export fails after some issues written to JSONL but before ClearDirtyIssues called, changes to remaining dirty issues will be lost.

The safer ClearDirtyIssuesByID() (lines 113-132) exists and clears only exported issues.

Fix: Either remove old method or mark it deprecated and ensure no code paths use it.


Code smell: Inconsistent error handling patterns

Commands use a mix of error handling patterns:
- FatalError()
- FatalErrorRespectJSON()
- fmt.Fprintf(os.Stderr, ...) + os.Exit(1)
- Silent continue
- Inconsistent resource cleanup (result.Close())

**Example in show.go lines 89-146:**
Different error paths use different patterns, some close resources, some don't.

**Acceptance Criteria:**
- [ ] Document error handling contract
- [ ] Standardize on consistent pattern
- [ ] Use defer for resource cleanup
- [ ] Create helper for common fetch-and-handle pattern
- [ ] Tests pass


Consider using types.Status in merge package for type safety

The merge package uses string for status comparison (e.g., result.Status == closed, issue.Status == StatusTombstone). The types package defines Status as a type alias with validation. While the merge package needs its own Issue struct for JSONL flexibility, it could import and use types.Status for constants to get compile-time type checking. Current code: if left == closed || right == closed. Could be: if left == string(types.StatusClosed). This is low priority since string comparison works correctly. Files: internal/merge/merge.go:44, 488, 501-521


Update local installation

Run install script or brew upgrade to get new version locally: curl -fsSL .../install.sh | bash


Deacon Patrol

Mayor's daemon patrol loop for handling callbacks, health checks, and cleanup.


bd show --short flag for compact output

## Request
Add a `--short` flag to `bd show` for compact, one-line output per issue.

## Use Case
When checking multiple issues quickly:
```bash
bd show bd-nkqx --short && bd show bd-oxgi --short
```

Currently fails with "unknown flag: --short"

## Expected Output
Something like:
```
bd-nkqx [closed] P1 task: Validate swarm schema design
bd-oxgi [closed] P1 task: Add mol_type schema field to beads
```

## Format Suggestion
`<id> [<status>] <priority> <type>: <title>`

Optional: include blocker count if any, e.g. `(blocked by 2)`


Verify npm package

Confirm npm package published.

```bash
npm show @beads/bd version
```

Should show 0.41.0.

Also check: https://www.npmjs.com/package/@beads/bd


Add tests for mol spawn --attach

Code review (bd-obep) found no tests for the spawn --attach functionality.

**Test cases needed:**
1. Basic attach - spawn proto with one --attach
2. Multiple attachments - spawn with --attach A --attach B
3. Attach types - verify sequential vs parallel bonding
4. Error case: attaching non-proto (missing template label)
5. Variable aggregation - vars from primary + attachments combined
6. Dry-run output includes attachment info

**Implementation notes:**
- Tests should use in-memory storage
- Create test protos, spawn with attachments, verify dependency structure
- Check that sequential uses 'blocks' type, parallel uses 'parent-child'


bd ready resolves external dependencies

Extend bd ready to check external blocked_by references:

1. Parse external:<project>:<capability> from blocked_by
2. Look up project path from external_projects config
3. Check if target project has provides:<capability> label on a closed issue
4. If not satisfied, issue is blocked

Example output:
```bash
bd ready
# gt-xyz: blocked by external:beads:mol-run-assignee (not provided)
# gt-abc: ready
```

Depends on: bd-om4a (external: prefix), bd-66w1 (config)

Part of cross-project dependency system.
See: gastown/docs/cross-project-deps.md


Add daemon RPC endpoints for config and stale check

Two TODOs request daemon RPC endpoints:

1. cmd/bd/create.go:208 - Add RPC method to get config in daemon mode
2. cmd/bd/mol_stale.go:67 - Add RPC endpoint for stale check

These would allow the CLI to query the daemon for configuration and staleness checks instead of reading directly.

Files:
- cmd/bd/create.go:208
- cmd/bd/mol_stale.go:67


Deacon Patrol


bd activity: Real-time molecule state feed

Implement activity feed command for watching molecule state transitions.

## Commands
```bash
bd activity --follow              # Real-time streaming
bd activity --mol <id>            # Activity for specific molecule
bd activity --since 5m            # Last 5 minutes
bd activity --type step           # Only step transitions
```

## Output Format
```
[14:32:01] ✓ patrol-x7k.inbox-check completed
[14:32:03] ✓ patrol-x7k.check-refinery completed
[14:32:08] + patrol-x7k.arm-ace bonded (5 steps)
[14:32:09] → patrol-x7k.arm-ace.capture in_progress
[14:32:10] ✓ patrol-x7k.arm-ace.capture completed
[14:32:14] ✓ patrol-x7k.arm-ace.decide completed (action: nudge-1)
[14:32:17] ✓ patrol-x7k.arm-ace COMPLETE
[14:32:23] ✓ patrol-x7k SQUASHED → digest-x7k
```

## Event Types
- `+` bonded - New molecule/step created
- `→` in_progress - Step started
- `✓` completed - Step/molecule finished
- `✗` failed - Step failed
- `⊘` burned - Wisp discarded
- `◉` squashed - Wisp condensed to digest

## Implementation
- Could use SQLite triggers or polling
- --follow uses OS file watching or polling
- Filter by mol ID, type, time range


BondRef.ProtoID field name is misleading for mol+mol bonds

In bondMolMol, the BondRef.ProtoID field is used to store molecule IDs:

```go
BondedFrom: append(molA.BondedFrom, types.BondRef{
    ProtoID:   molB.ID,  // This is a molecule, not a proto
    ...
})
```

This is semantically confusing since ProtoID suggests it should only hold proto references.

**Options:**
1. Rename ProtoID to SourceID (breaking change, needs migration)
2. Add documentation clarifying ProtoID can hold molecule IDs in bond context
3. Leave as-is, accept the naming is imprecise

Low priority since it's just naming, not functionality.


bd swarm validate command

Add bd swarm validate to check epic structure for swarming.

## Context
Before running a swarm, we need to validate the epic structure:
- Are dependencies correct (not inverted)?
- Are there orphaned issues?
- Is the DAG well-formed?

The Ready Front model (not phases!) means dependencies define execution order.
Common mistake: 'Phase 1 before Phase 2' → inverted deps.
Correct thinking: 'Phase 2 needs Phase 1' → bd dep add phase2 phase1

## Implementation
1. New command: bd swarm validate <epic-id>
2. Walk the dependency DAG from epic children
3. Detect issues:
   - Root issues with no dependents (orphans?)
   - Leaf issues depending on nothing (missing deps?)
   - Cycles (impossible)
   - Disconnected subgraphs
4. Report:
   - Ready fronts (waves of parallel work)
   - Estimated worker-sessions
   - Max parallelism
   - Warnings for potential issues

## Reference
~/gt/docs/swarm-architecture.md - 'The Ready Front Model' section

## Acceptance
- bd swarm validate <epic> runs
- Reports ready fronts accurately
- Warns about structural issues
- Returns success/failure exit code


Merge: topaz-1767106269860

branch: polecat/topaz-1767106269860
target: main
source_issue: topaz-1767106269860
rig: beads


Tier 4: Auto-squash on molecule completion

Automatically squash molecules when they reach terminal state.

## Integration Points

1. Hook into molecule completion handler
2. Detect when all steps are done/failed
3. Trigger squash automatically

## Config

bd config set mol.auto_squash true  # Default: false
bd config set mol.auto_squash_on_success true  # Only on success
bd config set mol.auto_squash_delay '5m'  # Wait before squash

## Implementation Options

### Option A: Post-Completion Hook
In mol completion handler:
- Check if auto_squash enabled
- Call Squash() after terminal state

### Option B: Git Hook
In .beads/hooks/post-commit:
- bd mol squash --auto

### Option C: Daemon Background Task
- Daemon periodically checks for squashable molecules
- Squashes in background

## Acceptance Criteria

- Completed molecules auto-squash without manual intervention
- Configurable delay before squash
- Option to squash only on success vs always
- Works with both daemon and no-daemon modes


Merge: obsidian-1767106243818

branch: polecat/obsidian-1767106243818
target: main
source_issue: obsidian-1767106243818
rig: beads


Merge: bd-l13p

branch: polecat/nux
target: main
source_issue: bd-l13p
rig: beads


Review PR #514: fix plugin install docs

Review and merge PR #514 from aspiers. This PR fixes incorrect docs for installing Claude Code plugin from source in docs/PLUGIN.md. Clarifies shell vs Claude Code commands and fixes the . vs ./beads argument issue. URL: https://github.com/anthropics/beads/pull/514


Molecule bonding: composable workflow templates

Vision: Molecules should be composable like LEGO bricks or Mad Max war rig sections. Bonding lets you attach molecules together to create compound workflows.

THREE BONDING CONTEXTS:
1. Template-time: bd mol bond A B → Create reusable compound proto
2. Spawn-time: bd mol spawn A --attach B → Attach modules when instantiating
3. Runtime: bd mol attach epic B → Add to running workflow

BOND TYPES:
- Sequential: B after A completes (feature → deploy)
- Parallel: B runs alongside A (feature + docs)
- Conditional: B only if A fails (feature → hotfix)

BOND POINTS (Attachment Sites):
- Default: B depends on A root epic completion
- Explicit: --after issue-id for specific attachment
- Future: Named bond points in proto definitions

VARIABLE FLOW:
- Shared namespace between bonded molecules
- Warn on variable name conflicts
- Future: explicit mapping with --map

DATA MODEL: Issues track bonded_from to preserve compound lineage.

SUCCESS CRITERIA:
- Can bond two protos into a compound proto
- Can spawn with --attach for on-the-fly composition
- Can attach molecules to running workflows
- Compound structure visible in bd mol show
- Variables flow correctly between bonded molecules


Create and push git tag v{{version}}

Create the release tag and push it:

```bash
git tag v{{version}}
git push origin v{{version}}
```

This triggers the GoReleaser GitHub Action to build release binaries.


Add pre-migration orphan cleanup to avoid chicken-and-egg fix failure

## Problem

When the database has orphaned foreign key references (dependencies or labels pointing to non-existent issues), the migration invariant check fails, preventing the database from opening. This creates a chicken-and-egg problem:

1. `bd doctor --fix` tries to open the database
2. Opening triggers migrations with invariant checks
3. Invariant check fails due to orphaned refs
4. Fix never runs because database won't open

## Root Cause

Aggressive tombstone deletion (e.g., deleting ~1000 old tombstones) left behind orphaned:
- 218+ dependencies where issue_id not in issues
- 222+ dependencies where depends_on_id not in issues
- 30+ labels where issue_id not in issues

## Current Workaround

Direct SQL to clean up before bd can run:
```sql
DELETE FROM dependencies WHERE NOT EXISTS (SELECT 1 FROM issues WHERE id = issue_id);
DELETE FROM dependencies WHERE NOT EXISTS (SELECT 1 FROM issues WHERE id = depends_on_id) AND depends_on_id NOT LIKE 'external:%';
DELETE FROM labels WHERE NOT EXISTS (SELECT 1 FROM issues WHERE id = issue_id);
```

## Proposed Fix

Add a pre-migration cleanup step that runs BEFORE invariant checks:
1. Option A: Run orphan cleanup as part of migrations (before invariant check)
2. Option B: Add `bd doctor --force-fix` that bypasses invariant checks
3. Option C: Have invariant check auto-clean orphans instead of failing

Option A is cleanest - orphan cleanup should be part of normal migration.


Refactor duplicate JSONL-from-git parsing code

Both readFirstIssueFromGit() in init.go and importFromGit() in autoimport.go have similar code patterns for:
1. Running git show <ref>:<path>
2. Scanning the output with bufio.Scanner
3. Parsing JSON lines

Could be refactored to share a helper like:
- readJSONLFromGit(gitRef, path string) ([]byte, error)
- Or a streaming version: streamJSONLFromGit(gitRef, path string) (io.Reader, error)

Files:
- cmd/bd/autoimport.go:225-256 (importFromGit)
- cmd/bd/init.go:1212-1243 (readFirstIssueFromGit)

Priority is low since code duplication is minimal and both functions work correctly.


Remove all deletions.jsonl code except migration

There's deletions manifest code spread across the entire codebase that should have been removed after tombstone migration:

Files with deletions code (non-migration):
- internal/deletions/ - entire package
- cmd/bd/sync.go - 25+ references, auto-compact, sanitize
- cmd/bd/delete.go - dual-writes to deletions.jsonl
- internal/importer/importer.go - checks deletions manifest
- internal/syncbranch/worktree.go - merges deletions.jsonl
- cmd/bd/doctor/fix/sync.go - cleanupDeletionsManifest
- cmd/bd/doctor/fix/deletions.go - HydrateDeletionsManifest
- cmd/bd/integrity.go - checks deletions for data loss
- cmd/bd/deleted.go - entire command
- cmd/bd/compact.go - pruneDeletionsManifest
- cmd/bd/doctor.go - checkDeletionsManifest
- Plus many more

Action: Aggressively remove all non-migration deletions code. Tombstones are the only deletion mechanism now.


Re: Thread Test 2

Got your message. Testing reply feature.


Add global verbosity flags (--verbose, --quiet)

Add consistent verbosity controls across all commands.

**Current state:**
- bd init has --quiet flag
- No other commands have verbosity controls
- Debug output controlled by BD_VERBOSE env var

**Proposal:**
Add persistent flags:
- --verbose / -v: Enable debug output
- --quiet / -q: Suppress non-essential output

**Implementation:**
- Add to rootCmd.PersistentFlags()
- Replace BD_VERBOSE checks with flag checks
- Standardize output levels:
  * Quiet: Errors only
  * Normal: Errors + success messages
  * Verbose: Errors + success + debug info

**Files to modify:**
- cmd/bd/main.go (add flags)
- internal/debug/debug.go (respect flags)
- Update all commands to respect quiet mode

**Testing:**
- Verify --verbose shows debug output
- Verify --quiet suppresses normal output
- Ensure errors always show regardless of mode


Commit release

Stage and commit all version changes.

```bash
git add -A
git commit -m "chore: Bump version to 0.41.0"
```

Review the commit to ensure all expected files are included.


Investigate charmbracelet-based TUI for beads

Now that we've merged the create-form command (PR #603) which uses charmbracelet/huh, investigate whether beads should have a more comprehensive TUI.

Considerations:
- Should this be in core or a separate binary (bd-tui)?
- What functionality would benefit from a TUI? (list view, issue details, search, bulk operations)
- Plugin/extension architecture vs build tags vs separate binary
- Dependency cost vs user experience tradeoff
- Target audience: humans who want interactive workflows vs CLI/scripting users

Related: PR #603 added charmbracelet/huh dependency for create-form command.

Foundation is in place (lipgloss, huh), but not a priority right now


Exclude pinned issues from bd blocked

Update bd blocked to exclude pinned issues. Pinned issues are context markers and should not appear in the blocked work list.


Review changes since last release

Understand what's being released.

```bash
git log $(git describe --tags --abbrev=0)..HEAD --oneline
```

Categorize changes:
- Features (feat:)
- Fixes (fix:)
- Breaking changes
- Documentation


Update Homebrew formula

./scripts/update-homebrew.sh {{version}}


bd cook: Compile formula to proto

Implement the 'bd cook' command that transforms formulas into protos:

Usage: bd cook <formula-file> [options]
       bd cook .beads/formulas/*.formula.yaml

Process:
1. Parse formula YAML (uses formula parser)
2. Resolve extends (formula inheritance)
3. Expand macros (expansion rules)
4. Apply aspects (cross-cutting concerns)
5. Generate flat proto bead

Output:
- Creates proto bead in .beads/ with mol-prefix
- Proto has all steps pre-expanded
- Proto is ready for pour/wisp instantiation

Example:
  bd cook .beads/formulas/mol-deacon-patrol.formula.yaml
  → Creates mol-deacon-patrol proto bead

Gas Town tracking: gt-8tmz.13


Code smell: Duplicated IssueDetails struct in show.go (4 definitions)

The IssueDetails struct is defined 4 times within show.go at lines 109, 159, 190, and 372 with identical structure but slightly different JSON tag configurations.

**Problem:**
- Violates DRY principle
- Makes maintenance harder - changes must be applied to multiple locations
- Increases risk of inconsistency between definitions
- Takes up ~50 lines of duplicated code

**Acceptance Criteria:**
- [ ] Extract IssueDetails to a single package-level type
- [ ] Update all 4 locations to use the shared type
- [ ] Ensure JSON output remains consistent
- [ ] Tests pass


Split internal/storage/sqlite/queries.go (1586 lines)

Split internal/storage/sqlite/queries.go (1704 lines) into logical modules.

## Current State
queries.go is 1704 lines with mixed responsibilities:
- Issue CRUD operations
- Search/filter operations
- Delete operations (complex cascade logic)
- Helper functions (parsing, formatting)

## Proposed Split

### 1. queries.go (keep ~400 lines) - Core CRUD
```go
// Core issue operations
func (s *SQLiteStorage) CreateIssue(...)
func (s *SQLiteStorage) GetIssue(...)
func (s *SQLiteStorage) UpdateIssue(...)
func (s *SQLiteStorage) CloseIssue(...)
```

### 2. queries_search.go (~300 lines) - Search/Filter
```go
// Search and filtering
func (s *SQLiteStorage) SearchIssues(...)
func (s *SQLiteStorage) GetIssueByExternalRef(...)
func (s *SQLiteStorage) GetCloseReason(...)
func (s *SQLiteStorage) GetCloseReasonsForIssues(...)
```

### 3. queries_delete.go (~400 lines) - Delete Operations
```go
// Delete operations with cascade logic
func (s *SQLiteStorage) CreateTombstone(...)
func (s *SQLiteStorage) DeleteIssue(...)
func (s *SQLiteStorage) DeleteIssues(...)
func (s *SQLiteStorage) resolveDeleteSet(...)
func (s *SQLiteStorage) expandWithDependents(...)
func (s *SQLiteStorage) validateNoDependents(...)
func (s *SQLiteStorage) checkSingleIssueValidation(...)
func (s *SQLiteStorage) trackOrphanedIssues(...)
func (s *SQLiteStorage) collectOrphansForID(...)
func (s *SQLiteStorage) populateDeleteStats(...)
func (s *SQLiteStorage) executeDelete(...)
func (s *SQLiteStorage) findAllDependentsRecursive(...)
```

### 4. queries_helpers.go (~100 lines) - Utilities
```go
// Helper functions (already at top of file)
func parseNullableTimeString(...)
func parseJSONStringArray(...)
func formatJSONStringArray(...)
```

### 5. queries_rename.go (~100 lines) - ID/Prefix Operations
```go
// ID and prefix management
func (s *SQLiteStorage) UpdateIssueID(...)
func (s *SQLiteStorage) RenameDependencyPrefix(...)
func (s *SQLiteStorage) RenameCounterPrefix(...)
func (s *SQLiteStorage) ResetCounter(...)
```

## Implementation Steps

1. **Create new files** with package declaration:
   ```go
   // queries_delete.go
   package sqlite

   import (...)
   ```

2. **Move functions** - cut/paste, maintaining order within each file

3. **Update imports** - each file needs its own imports

4. **Run tests** after each file split:
   ```bash
   go test ./internal/storage/sqlite/...
   ```

5. **Run linter** to catch any issues:
   ```bash
   golangci-lint run ./internal/storage/sqlite/...
   ```

## File Organization
```
internal/storage/sqlite/
├── queries.go          # Core CRUD (~400 lines)
├── queries_search.go   # Search/filter (~300 lines)
├── queries_delete.go   # Delete cascade (~400 lines)
├── queries_helpers.go  # Utilities (~100 lines)
└── queries_rename.go   # ID operations (~100 lines)
```

## Success Criteria
- No file > 500 lines
- All tests pass
- No functionality changes
- Clear separation of concerns


test hook pin fix


Implement subgraph cloning with variable substitution

Core implementation of template instantiation:

1. Add `bd template instantiate <template-id> [--var key=value]...` command
2. Implement subgraph loading:
   - Load template epic
   - Recursively load all children (and their children)
   - Load all dependencies between issues in the subgraph
3. Implement variable substitution:
   - Scan titles and descriptions for `{{name}}` patterns
   - Replace with provided values
   - Error on missing required variables (or prompt interactively)
4. Implement cloning:
   - Generate new IDs for all issues
   - Create cloned issues with substituted text
   - Remap and create dependencies
5. Return the new epic ID

Consider adding `--dry-run` flag to preview what would be created.


Add 0.30.4 to info.go release notes

Update cmd/bd/info.go versionChanges map with release notes for 0.30.4.
Include any workflow-impacting changes for --whats-new output.


Gate eval: Add error visibility for gh CLI failures

Currently evalGHRunGate and evalGHPRGate silently swallow errors when the gh CLI fails (lines 738-741, 780-783 in gate.go).

This makes debugging difficult - if gh isn't installed, auth fails, or network issues occur, gates silently stall forever with no indication of why.

Options:
1. Add debug logging when gh fails
2. Return error info in the skipped list for aggregate reporting
3. Add a --verbose flag to gate eval that shows failures

Low priority since the fail-safe behavior (don't close gate on error) is correct.


Test message

Hello world


Rename 'wisp' back to 'ephemeral' in beads API

The beads API uses 'wisp' terminology (Wisp field, bd wisp command) but the underlying SQLite column is 'ephemeral'.

This creates cognitive overhead since wisp is a Gas Town concept.

Rename to use 'ephemeral' consistently:
- types.Issue.Wisp → types.Issue.Ephemeral
- JSON field: wisp → ephemeral
- CLI: bd wisp → bd ephemeral (or just use flags on existing commands)

The SQLite column already uses 'ephemeral' so no schema migration needed.


bd wisp gc command

Add bd wisp gc command to garbage collect orphaned wisps.

## Usage
```bash
bd wisp gc                # Clean orphaned wisps
bd wisp gc --dry-run      # Show what would be cleaned
bd wisp gc --age 1h       # Custom orphan threshold (default: 1h)
```

## Orphan Detection
A wisp is orphaned if:
- process_id field exists AND process is dead
- OR updated_at older than threshold AND not complete
- AND molecule status is not complete/abandoned

## Behavior
- Delete orphaned wisps (no digest created)
- Report count of cleaned wisps
- --dry-run shows candidates without deleting

## Implementation
- Add 'gc' subcommand to wisp group
- Process detection via os.FindProcess or /proc
- Configurable age threshold


Code smell: Storage interface has 50+ methods - consider interface segregation

The Storage interface in internal/storage/storage.go has 50+ methods covering:

- Issue CRUD (CreateIssue, GetIssue, UpdateIssue, DeleteIssue, SearchIssues)
- Dependencies (AddDependency, RemoveDependency, GetDependencies, GetDependents, etc.)
- Labels (AddLabel, RemoveLabel, GetLabels, etc.)
- Ready work (GetReadyWork, GetBlockedIssues, etc.)
- Events/Comments
- Statistics
- Dirty tracking
- Export hash tracking
- ID generation
- Config/Metadata
- Transactions
- Lifecycle

While this is a valid design for a storage abstraction, consider the Interface Segregation Principle:

1. Split into smaller interfaces: IssueStorage, DependencyStorage, LabelStorage, etc.
2. Compose them: `type Storage interface { IssueStorage; DependencyStorage; ... }`
3. This allows more focused testing and clearer API contracts

Location: internal/storage/storage.go:79-197


Full event test


Add gate issue type to beads

Add 'gate' as a new issue type for async coordination.

## Changes Needed
- Add 'gate' to IssueType enum in internal/types/types.go
- Update validation to accept gate type
- Update CLI help text and completion

## Gate Type Semantics
- Gates are ephemeral (live in wisp storage)
- Managed by Deacon patrol
- Have special fields: await_type, await_id, timeout, waiters[]


RemoveDependency fails on external deps - FK violation in dirty_issues

In dependencies.go:225, RemoveDependency marks BOTH issueID and dependsOnID as dirty. For external refs (e.g., external:project:capability), dependsOnID doesn't exist in the issues table. This causes FK violation since dirty_issues.issue_id has FK constraint to issues.id.

Fix: Check if dependsOnID starts with 'external:' and only mark source issue as dirty, matching the logic in AddDependency (lines 162-170).

Repro: bd dep rm <issue> external:project:capability


Add tests for dependency management

Key dependency functions like mergeBidirectionalTrees, GetDependencyTree, and DetectCycles have low or no coverage. These are essential for maintaining data integrity in the dependency graph.


Add tests for daemon functionality

Critical daemon functions have 0% test coverage including daemon lifecycle, health checks, and RPC server functionality. These are essential for system reliability and need comprehensive test coverage.


bd mol advance: Step through molecule execution

Implement bd mol advance for stepping through molecules.

## Command

```bash
bd mol advance <mol-id>
```

## Behavior

1. Load molecule state from .beads/molecules/<mol-id>.state.yaml
2. Find current step
3. Mark current step completed
4. Find next available step (respecting needs/deps)
5. Update state file
6. Output next step info (or COMPLETE if done)

## Step State Machine

```
pending → in_progress → completed
```

When advancing:
- Current step: in_progress → completed
- Next step: pending → in_progress

## Output

```bash
$ bd mol advance mol-deacon-patrol
✓ Step inbox-check completed
→ Next step: spawn-work

Title: Spawn polecat for ready work
Description: ...
```

Or if complete:
```bash
$ bd mol advance mol-deacon-patrol
✓ Step self-inspect completed
✓ Molecule COMPLETE

Run bd mol reset to loop, or gt handoff to cycle.
```

## Files

- cmd/bd/mol_advance.go
- internal/mol/state.go


Schema: Add 'hooked' as valid status

Add 'hooked' to the valid status enum in the beads schema.

## Files to Update

1. `internal/types/types.go` - Add StatusHooked constant
2. `internal/storage/sqlite/schema.go` - Add 'hooked' to CHECK constraint
3. `internal/storage/sqlite/migrations/` - New migration to alter constraint

## Status Values After Change

- open
- in_progress
- blocked
- deferred
- hooked (NEW)
- closed
- pinned
- tombstone

## Validation

- bd update <id> --status=hooked should work
- bd list --status=hooked should filter correctly


Bug: bd swarm create allows duplicate swarm molecules

Running `bd swarm create <epic>` twice creates two swarm molecules for the same epic.

Should check for existing swarm molecule linked to the epic and either:
1. Error with 'swarm already exists: <id>'
2. Warn and return existing swarm ID
3. Add --force flag to allow creating another

Check by querying for molecules with mol_type=swarm that have a relates-to dependency to the epic.


Merge: bd-401h

branch: polecat/rictus
target: main
source_issue: bd-401h
rig: beads


Implement transaction retry logic for SQLITE_BUSY

BEGIN IMMEDIATE fails immediately on SQLITE_BUSY instead of retrying with exponential backoff.

Location: internal/storage/sqlite/sqlite.go:223-225

Problem:
- Under concurrent write load, BEGIN IMMEDIATE can fail with SQLITE_BUSY
- Current implementation fails immediately instead of retrying
- Results in spurious failures under normal concurrent usage

Solution: Implement exponential backoff retry:
- Retry up to N times (e.g., 5)
- Backoff: 10ms, 20ms, 40ms, 80ms, 160ms
- Check for context cancellation between retries
- Only retry on SQLITE_BUSY/database locked errors

Impact: Spurious failures under concurrent write load

Effort: 3 hours


Install 0.30.4 MCP server locally

Upgrade the MCP server (after PyPI publish):
```bash
pip install --upgrade beads-mcp
# OR if using uv:
uv tool upgrade beads-mcp
```

Verify:
```bash
pip show beads-mcp | grep Version
```

```verify
pip show beads-mcp 2>/dev/null | grep -q 'Version: 0.30.4'
```


Restart daemons

bd daemons killall


bd template commands fail with daemon mode

The `bd template show` and `bd template instantiate` commands fail with 'Error loading template: no database connection' when daemon is running.

**Reproduction:**
```bash
bd daemon --start
bd template show bd-qqc  # Error: no database connection
bd template show bd-qqc --no-daemon  # Works
```

**Expected:** Template commands should work with daemon like other commands.

**Workaround:** Use `--no-daemon` flag.

**Location:** Likely in cmd/bd/template.go - daemon RPC path not implemented for template operations.


Phase 1.3: Basic TOON read/write operations

Add basic TOON read/write operations to bdt executable. Implement create, list, and show commands that use the internal/toon package for encoding/decoding to TOON format.

## Subtasks
1. Implement bdt create command - Create issues and serialize to TOON format
2. Implement bdt list command - Read issues.toon and display all issues
3. Implement bdt show command - Display single issue by ID
4. Add file I/O operations for issues.toon
5. Integrate internal/toon package (EncodeTOON, DecodeJSON)
6. Write tests for create, list, show operations

## Files to Create/Modify
- cmd/bdt/create.go - Create command
- cmd/bdt/list.go - List command
- cmd/bdt/show.go - Show command
- cmd/bdt/storage.go - File I/O helper

## Success Criteria
- bdt create "Issue title" creates and saves to issues.toon
- bdt list displays all issues in human-readable format
- bdt list --json shows JSON output
- bdt show <id> displays single issue
- Issues round-trip correctly: create → list → show
- All tests passing with >80% coverage


Show pin indicator in bd list output

Add a visual indicator (e.g., pin emoji or [P] marker) for pinned issues in bd list output so users can easily identify them.


Merge: bd-thgk

branch: polecat/Compactor
target: main
source_issue: bd-thgk
rig: beads


Upgrade beads-mcp to {{version}}

Upgrade the MCP server via pip:

```bash
pip install --upgrade beads-mcp
pip show beads-mcp | grep Version  # Verify {{version}}
```

Note: Restart Claude Code or MCP session to use new version.


Code smell: 20+ global variables in main.go

main.go lines 63-103 have over 20 global variables managing command-line state, daemon connection, auto-flush, auto-import, and error handling.

**Globals include:**
dbPath, actor, store, jsonOutput, daemonStatus, daemonClient, noDaemon, rootCtx, rootCancel, autoFlushEnabled, flushMutex, storeMutex, storeActive, flushFailureCount, lastFlushError, flushManager, hookRunner, skipFinalFlush, autoImportEnabled, versionUpgradeDetected, previousVersion, upgradeAcknowledged

**Problem:**
- Global state makes testing difficult
- Race conditions possible between goroutines
- Hard to track which functions modify which globals

**Acceptance Criteria:**
- [ ] Create CommandContext struct to hold runtime state
- [ ] Pass context through command execution
- [ ] Reduce global count to essential minimum
- [ ] Tests pass


Restart daemon with 0.33.2

Restart the bd daemon to pick up new version:

```bash
bd daemon --stop
bd daemon --start
bd daemon --health  # Verify Version: 0.33.2
```


Fix JSON errors in label.go (label commands)

Replace ~19 direct stderr writes with FatalErrorRespectJSON() in label.go.

Commands affected:
- labelAddCmd
- labelRemoveCmd
- labelListCmd
- labelListAllCmd


Run bump-version.sh test-squash

Run ./scripts/bump-version.sh test-squash to update version in all files


Fix JSON error output consistency across all commands

## Problem

When `--json` flag is specified, error output should return JSON format, not plain text. Currently only `bd show` uses `FatalErrorRespectJSON` - all other commands use direct `fmt.Fprintf(os.Stderr, ...)`.

## Audit Findings (bd-au0.7)

| File | stderr writes | Commands affected |
|------|---------------|-------------------|
| show.go | 51 | update, close, edit |
| dep.go | 41 | dep add/remove/tree/cycles |
| label.go | 19 | label add/remove/list |
| comments.go | ~10 | comments add/list |
| epic.go | ~5 | epic status/close-eligible |

## Solution

Replace `fmt.Fprintf(os.Stderr, ...) + os.Exit(1)` patterns with `FatalErrorRespectJSON()` in all commands that support `--json` flag.

## Pattern

```go
// Before (inconsistent)
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)

// After (respects --json)
FatalErrorRespectJSON("%v", err)
```

## Success Criteria

- All commands return JSON errors when `--json` flag is set
- Error format: `{"error": "message"}`
- Exit code 1 preserved for errors


Test prefix bd-


Agent bead prefix mismatch: gt- vs bd- for beads polecat agents

When spawning polecats in beads rig, gt sling tries to create agent beads with gt-beads-polecat-* prefix, but beads requires bd- prefix. Warning: 'issue ID gt-beads-polecat-obsidian does not match configured prefix bd'. Agent beads should use the rig's configured prefix.


GH#444: Fix inconsistent status naming in_progress vs in-progress

Documentation uses in-progress (hyphen) but code expects in_progress (underscore). Update all docs to use canonical in_progress. See GitHub issue #444.


Create version-bump template as native Beads

Migrate the version-bump workflow from YAML to a native Beads template:

1. Create epic with template label: Release {{version}}
2. Create child tasks for each step (update version files, changelog, commit, push, publish)
3. Set up dependencies between tasks
4. Add verification commands in task descriptions

This serves as both migration and validation of the new system.


Cross-project dependency support

Enable tracking dependencies across project boundaries.

## Mechanism
- Producer: `bd ship <capability>` adds `provides:<capability>` label
- Consumer: `blocked_by: external:<project>:<capability>`
- Resolution: `bd ready` checks external deps via config

## Design Doc
See: gastown/docs/cross-project-deps.md

## Children
- bd-eijl: bd ship command
- bd-om4a: external: prefix in blocked_by
- bd-66w1: external_projects config
- bd-zmmy: bd ready resolution


bd admin compact --older-than=0 ignores the flag, uses default 30 days

When running bd admin compact --prune --older-than=0, the output says 'No expired tombstones to prune (TTL: 30 days)' - the flag value is ignored.

Root cause: In runCompactPrune() in compact_tombstone.go:180, the check 'if compactOlderThan > 0' means that --older-than=0 is treated the same as not passing the flag, defaulting to 30 days.

Fix: Change the default flag value in compact.go:785 to -1 (sentinel for 'use default'), and treat 0 as 'expire all tombstones' by passing a negative TTL to pruneExpiredTombstones().

Moved from gt-65gwa (gastown) - was filed in wrong rig.


Remove Cache-Related Tests

Delete or update tests that assume multi-repo caching


Protos should have distinct prefix (e.g., proto-)

Currently protos use the same bd- prefix as regular issues, making them visually indistinguishable. When you see bd-7bs4 it looks like work to squash, not a reusable template.

**Problem:**
- Protos look like regular issues
- No visual signal that something is a template vs active work
- Easy to accidentally close/modify protos thinking they're issues

**Proposed Solution:**
- Protos should have their own prefix (e.g., `proto-`, `mol-`, or `tpl-`)
- Could be a separate database/storage for the proto library
- Or a convention where proto IDs are generated differently

**Alternatives:**
1. Separate proto database with `proto-` prefix
2. Special ID format for protos (e.g., `bd:proto:release`)
3. Visual indicator in bd list/show output (already have labels, but not enough)

**Current workaround:** Label with `template` and `molecule`, but IDs still look like regular issues.


gt mail send: support reading message body from stdin

Currently gt mail send -m requires the message as a command-line argument, which causes shell escaping issues with backticks, quotes, and special characters.

Add support for reading message body from stdin:
- gt mail send addr -s 'Subject' --stdin  # Read body from stdin
- echo 'body' | gt mail send addr -s 'Subject' -m -  # Convention: -m - means stdin

This would allow:
cat <<'EOF' | gt mail send addr -s 'Subject' --stdin
Message with `backticks` and 'quotes' safely
EOF

Without this, agents struggle to send handoff messages containing code snippets.


Phase 2.3: TOON deletion tracking

Implement deletion tracking in TOON format.

## Overview
Phase 2.2 switched storage to TOON format. Phase 2.3 adds deletion tracking in TOON format for propagating deletions across clones.

## Required Work

### 2.3.1 Deletion Tracking (TOON Format)
- [ ] Implement deletions.toon file (tracking deleted issue records)
- [ ] Add DeleteTracker struct to record deleted issue IDs and metadata
- [ ] Update bdt delete command to record in deletions.toon
- [ ] Design deletion record format (ID, timestamp, reason, hash)
- [ ] Implement auto-prune of old deletion records (configurable TTL)

### 2.3.2 Sync Propagation
- [ ] Load deletions.toon during import
- [ ] Remove deleted issues from local database when imported from remote
- [ ] Handle edge cases (delete same issue in multiple clones)
- [ ] Deletion ordering and conflict resolution

### 2.3.3 Testing
- [ ] Unit tests for deletion tracking
- [ ] Integration tests for deletion propagation
- [ ] Multi-clone deletion scenarios
- [ ] TTL expiration tests

## Success Criteria
- deletions.toon stores deletion records in TOON format
- Deletions propagate across clones via git sync
- Old records auto-prune after TTL
- All 70+ tests still passing
- bdt delete command works seamlessly


bd mol reset: Reset molecule to initial state for looping

Implement bd mol reset for patrol loop behavior.

## Command

```bash
bd mol reset <mol-id>
```

## Behavior

1. Load molecule state from .beads/molecules/<mol-id>.state.yaml
2. Reset all step states to pending
3. Clear current_step (or set to first step)
4. Preserve: bonded_at, bonded_by, variables
5. Update: reset_count++, last_reset_at

## Use Case: Deacon Patrol Loop

```
inbox-check → spawn-work → self-inspect → mol-reset → (loop)
```

The Deacon runs its patrol, then resets the molecule to start fresh.
No gt handoff needed unless context is high.

## State After Reset

```yaml
id: mol-deacon-patrol
status: running
current_step: inbox-check  # Back to first
reset_count: 5
last_reset_at: 2025-12-24T...
steps:
  inbox-check: pending      # All reset
  spawn-work: pending
  self-inspect: pending
```

## Files

- cmd/bd/mol_reset.go
- internal/mol/state.go (add Reset method)


Code smell: 262 uses of interface{} reduce type safety

Heavy use of interface{} across 92 files (262 occurrences) in:
- RPC update arguments: updatesFromArgs() returns map[string]interface{}
- JSON unmarshaling and formatting
- Storage query helpers

**Example in internal/rpc/server_issues_epics.go:45:**
```go
func updatesFromArgs(a UpdateArgs) map[string]interface{} {
    // Returns map with interface{} values - types must be asserted at use sites
}
```

**Problem:**
- Loss of type safety
- Runtime type assertions required
- Harder to catch errors at compile time

**Acceptance Criteria:**
- [ ] Define typed update structs for specific field groups
- [ ] Use generics where possible (Go 1.18+)
- [ ] Reduce interface{} count by at least 50%
- [ ] Document expected types where interface{} remains necessary
- [ ] Tests pass


Investigate Claude Code crash logging improvements

## Problem

Claude Code doesn't leave useful crash logs when it terminates unexpectedly. Investigation of a crash on 2025-12-26 showed:

- Debug logs in ~/.claude/debug/ just stop mid-stream with no error/exit message
- No signal handlers appear to log SIGTERM/SIGKILL/SIGINT
- No dedicated crash log file exists
- When Node.js crashes hard or gets killed, there's no record of why

## What we found

- Session debug log (02080b1a-...) stopped at 22:58:40 UTC mid-operation
- No 'exit', 'error', 'crash', 'signal' entries at end of file
- macOS DiagnosticReports showed Chrome crashes but no Node crashes
- System logs showed no relevant kill/OOM events

## Desired improvements

1. Exit handlers that log graceful shutdown
2. Signal handlers that log SIGTERM/SIGINT before exiting
3. A dedicated crash log or at least a 'last known state' file
4. Possibly CLI flags to enable verbose crash debugging

## Investigation paths

- Check if Claude Code has --debug or similar flags
- Look at Node.js crash handling best practices
- Consider if we can wrap claude invocations to capture crashes

## Related

This came up while investigating why a crew worker session crashed at end of a code review task.


Sync branch integrity guards

Track sync branch parent commit. If sync branch was force-pushed, warn user and require confirmation before proceeding. Add option to reset to remote if user accepts rebase. Prevents silent corruption from forced pushes.


Test Agent


Add bd doctor --fix flag to automatically repair issues

Add bd doctor --fix flag to automatically repair detected issues.

## Current State
- Doctor checks return issues with "Fix" field containing fix instructions
- No automatic fix execution
- User must manually follow fix instructions

## Implementation

### 1. Add --fix flag to doctor.go
```go
// In cmd/bd/doctor.go init()
doctorCmd.Flags().Bool("fix", false, "Automatically fix detected issues")
doctorCmd.Flags().Bool("yes", false, "Skip confirmation prompts (use with --fix)")
```

### 2. Create fix registry (cmd/bd/doctor/fix/registry.go)
```go
package fix

// Fixer can automatically repair an issue
type Fixer interface {
    // CanFix returns true if this fixer handles the given check name
    CanFix(checkName string) bool
    // Fix attempts to repair the issue, returns error if failed
    Fix(ctx context.Context, issue *CheckResult) error
    // Description returns human-readable description of what will be fixed
    Description() string
}

var registry []Fixer

func Register(f Fixer) {
    registry = append(registry, f)
}

func GetFixer(checkName string) Fixer {
    for _, f := range registry {
        if f.CanFix(checkName) {
            return f
        }
    }
    return nil
}
```

### 3. Implement fixers (one per file)

**cmd/bd/doctor/fix/hooks.go**
```go
type HooksFixer struct{}

func (f *HooksFixer) CanFix(name string) bool {
    return name == "git-hooks" || name == "hooks-outdated"
}

func (f *HooksFixer) Fix(ctx context.Context, issue *CheckResult) error {
    return hooks.Install(".", true) // force reinstall
}

func (f *HooksFixer) Description() string {
    return "Reinstall git hooks"
}

func init() {
    Register(&HooksFixer{})
}
```

**cmd/bd/doctor/fix/sync_branch.go**
```go
type SyncBranchFixer struct{}

func (f *SyncBranchFixer) CanFix(name string) bool {
    return name == "sync-branch-missing" || name == "sync-branch-diverged"
}

func (f *SyncBranchFixer) Fix(ctx context.Context, issue *CheckResult) error {
    // Reset to remote or create branch
    return syncbranch.ResetToRemote(ctx, repoRoot, branch, jsonlPath)
}
```

**cmd/bd/doctor/fix/merge_driver.go**
```go
type MergeDriverFixer struct{}

func (f *MergeDriverFixer) CanFix(name string) bool {
    return name == "merge-driver-missing" || name == "merge-driver-outdated"
}

func (f *MergeDriverFixer) Fix(ctx context.Context, issue *CheckResult) error {
    return setupMergeDriver()
}
```

### 4. Update doctor run logic

```go
func runDoctor(cmd *cobra.Command, args []string) {
    issues := runAllChecks()

    if \!fixFlag {
        // Existing behavior - just display issues
        displayIssues(issues)
        return
    }

    // Collect fixable issues
    var fixable []FixableIssue
    for _, issue := range issues {
        if fixer := fix.GetFixer(issue.CheckName); fixer \!= nil {
            fixable = append(fixable, FixableIssue{issue, fixer})
        }
    }

    if len(fixable) == 0 {
        fmt.Println("No automatically fixable issues found")
        return
    }

    // Show what will be fixed
    fmt.Printf("Found %d fixable issues:\n", len(fixable))
    for i, f := range fixable {
        fmt.Printf("  %d. [%s] %s\n", i+1, f.Issue.CheckName, f.Fixer.Description())
    }

    // Confirm unless --yes
    if \!yesFlag {
        fmt.Print("\nProceed with fixes? [Y/n] ")
        // ... read confirmation
    }

    // Apply fixes
    for _, f := range fixable {
        fmt.Printf("Fixing %s... ", f.Issue.CheckName)
        if err := f.Fixer.Fix(ctx, f.Issue); err \!= nil {
            fmt.Printf("FAILED: %v\n", err)
        } else {
            fmt.Println("OK")
        }
    }
}
```

## Fixable Checks (Initial Set)

| Check | Fixer | Action |
|-------|-------|--------|
| git-hooks | HooksFixer | Reinstall hooks |
| hooks-outdated | HooksFixer | Update hooks |
| merge-driver-missing | MergeDriverFixer | Configure merge driver |
| sync-branch-diverged | SyncBranchFixer | Reset to remote |
| daemon-version-mismatch | DaemonFixer | Restart daemon |

## Testing
```bash
# Test with broken hooks
rm .git/hooks/pre-commit
bd doctor --fix --yes

# Verify fix applied
bd doctor  # Should pass now
```

## Success Criteria
- --fix flag triggers automatic repair
- User prompted for confirmation (unless --yes)
- Clear output showing what was fixed
- Graceful handling of fix failures
- At least 5 common issues auto-fixable


Fix JSON errors in show.go (update/close/edit commands)

Replace ~51 direct stderr writes with FatalErrorRespectJSON() in show.go.

Commands affected:
- updateCmd (~10 error handlers)
- closeCmd (~15 error handlers)
- editCmd (~10 error handlers)
- showCmd (already uses FatalErrorRespectJSON in some places)

Key error paths:
- ID resolution errors (lines 565, 570, 579)
- Validation errors (lines 474, 515, 524, 545)
- RPC/daemon errors (line 639+)
- Store operation errors


Update info.go versionChanges

Add entry to versionChanges in cmd/bd/info.go with agent-actionable changes for {{version}}


Add GetWorkerStatus RPC endpoint

New RPC endpoint to get all workers and their current molecule/step in one call. Returns: assignee, moleculeID, moleculeTitle, currentStep, totalSteps, stepTitle, lastActivity, status. Enables activity feed TUI to show worker state without multiple round trips.


Verify CI is green

Check that CI is passing before starting release:
```bash
gh run list --limit 3
```
All recent runs should show 'success'. If not, fix issues first.


Work on beads-eub: Consolidated context tool for MCP serv...

Work on beads-eub: Consolidated context tool for MCP server (GH#636). Merge set_context, where_am_i, init into single 'context' tool. When done, submit MR (not PR) to integration branch for Refinery.


Run bump-version.sh {{version}}

Run ./scripts/bump-version.sh {{version}} to update version in all files


Code smell: Issue struct is a God Object (50+ fields)

attached_args: Refactor Issue struct God Object

The Issue struct in internal/types/types.go has 50+ fields covering many different concerns:

- Basic issue tracking (ID, Title, Description, Status, Priority)
- Messaging/communication (Sender, Ephemeral, etc.)
- Agent identity (HookBead, RoleBead, AgentState, RoleType, Rig)
- Gate/async coordination (AwaitType, AwaitID, Timeout, Waiters)
- Source tracing (SourceFormula, SourceLocation)
- HOP validation/entities (Creator, Validations)
- Compaction (CompactionLevel, CompactedAt, OriginalSize)
- Tombstones (DeletedAt, DeletedBy, DeleteReason)

Consider:
1. Extracting field groups into embedded structs (e.g., AgentFields, GateFields)
2. Using composition pattern to make the struct more modular
3. At minimum, grouping related fields together with section comments

Location: internal/types/types.go:12-82


Fix G104 errors unhandled in internal/storage/sqlite/queries.go:1186

Linting issue: G104: Errors unhandled (gosec) at internal/storage/sqlite/queries.go:1186:2. Error: rows.Close()


Migration: Convert pinned work items to hooked

Migrate existing pinned beads that represent work-on-hook to use 'hooked' status.

## Migration Logic

```sql
UPDATE issues
SET status = 'hooked'
WHERE status = 'pinned'
  AND issue_type NOT IN ('agent', 'role')
```

## What Stays Pinned

- Agent identity beads (gt-mayor, gt-witness-gastown, etc.)
- Role definition beads (gt-mayor-role, etc.)

## What Becomes Hooked

- Molecules on hooks (mol_type field set)
- Regular issues/tasks that were pinned to hooks

## Verification

After migration:
- bd list --status=pinned should only show agents/roles
- bd list --status=hooked should show work on hooks


bd validate - Comprehensive health check

Run all validation checks in one command.

Checks:
- Duplicates
- Orphaned dependencies
- Test pollution
- Git conflicts

Supports --fix-all for auto-repair.

Depends on bd-cbed9619.1, bd-0dcea000, bd-31aab707, bd-9826b69a.

Files: cmd/bd/validate.go (new)


Remove deprecated daemon logger function

In cmd/bd/daemon_logger.go:131, there's a TODO to remove a deprecated function once all callers are updated to use the new signature. Need to audit callers and clean up.

File: cmd/bd/daemon_logger.go:131


Run tests and verify build

Run the test suite to verify nothing is broken:

```bash
./scripts/test.sh
```

Or manually:
```bash
go build ./cmd/bd/...
go test ./...
```

Fix any failures before proceeding.


Push release v0.33.2 to remote

Push the commit and tag:

```bash
git push && git push --tags
```

Verify on GitHub that the tag appears in releases.


Update npm-package/package.json to 0.30.4

Update version field in npm-package/package.json:
```json
"version": "0.30.4"
```

```verify
jq -e '.version == "0.30.4"' npm-package/package.json
```


Update version to 0.33.2 in version.go

Edit cmd/bd/version.go line 17:

```go
Version = "0.33.2"
```

Verify with: `grep 'Version =' cmd/bd/version.go`


witness Handoff


Tier 1: Ephemeral repo routing

Simplified: Make mol spawn set ephemeral=true on spawned issues.

## The Fix

Modify cloneSubgraph() in template.go to set Ephemeral: true:

```go
// template.go:474
newIssue := &types.Issue{
    Title:      substituteVariables(oldIssue.Title, vars),
    // ... existing fields ...
    Ephemeral:  true,  // ADD THIS LINE
}
```

## Optional: Add --persistent flag

Add flag to bd mol spawn for when you want spawned issues to persist:

```bash
bd mol spawn mol-code-review --var pr=123  # ephemeral (default)
bd mol spawn mol-code-review --var pr=123 --persistent  # not ephemeral
```

## Why This Is Simpler Than Original Design

Original design proposed separate ephemeral repo routing. After code review:

- Ephemeral field already exists in schema
- bd cleanup --ephemeral already works
- No new config needed
- No multi-repo complexity

## Acceptance Criteria

- bd mol spawn creates issues with ephemeral=true
- bd cleanup --ephemeral -f deletes them after closing
- --persistent flag opts out of ephemeral
- Existing molecules continue to work


Wisp storage: ephemeral molecule tracking

Implement ephemeral molecule storage for patrol cycles.

## Architecture

Wisps are ephemeral molecules stored in `.beads-wisps/` (gitignored).
When squashed, they create digests in permanent `.beads/`.

**Storage is per-rig, not per-role**: Witness and Refinery share mayor/rig's
`.beads-wisps/` since they execute from that context.

## Design Doc
See: gastown/mayor/rig/docs/wisp-architecture.md

## Key Requirements

1. **Ephemeral storage**: `.beads-wisps/` directory, gitignored
2. **Bond with --wisp**: Creates in wisps instead of permanent
3. **Squash**: Deletes wisp, creates digest in permanent beads
4. **Burn**: Deletes wisp, no digest
5. **Wisp commands**: `bd wisp list`, `bd wisp gc`

## Storage Locations

| Context | Location |
|---------|----------|
| Rig (Deacon, Witness, Refinery) | mayor/rig/.beads-wisps/ |
| Polecat (if used) | polecats/<name>/.beads-wisps/ |

## Children (to be created)
- bd mol bond --wisp flag
- .beads-wisps/ storage backend
- bd mol squash handles wisp to permanent
- bd wisp list command
- bd wisp gc command (orphan cleanup)


Code smell: Duplicated step collection functions in cook.go

attached_args: Fix duplicated step collection functions in cook.go

Two pairs of nearly identical functions exist in cook.go:

1. collectStepsRecursive() (line 754) vs collectStepsToSubgraph() (line 415)
   - Both iterate over steps and create issues
   - Main difference: one writes to DB, one builds in-memory subgraph

2. collectDependencies() (line 833) vs collectDependenciesToSubgraph() (line 502)
   - Both collect dependencies from steps
   - Nearly identical logic

Consider:
1. Extract common step/dependency processing logic
2. Use a strategy pattern or callback for the DB vs in-memory difference
3. Create a single function that returns data, then have callers decide how to persist

Location: cmd/bd/cook.go:415-567 and cmd/bd/cook.go:754-898


Add bd reset command for clean slate restart

Implement a `bd reset` command to reset beads to a clean starting state.

## Context
GitHub issue #479 - users sometimes get beads into an invalid state after updates, and there's no clean way to start fresh. The git backup/restore mechanism that protects against accidental deletion also makes it hard to intentionally reset.

## Design

### Command Interface
```
bd reset [--hard] [--force] [--backup] [--dry-run] [--no-init]
```

| Flag | Effect |
|------|--------|
| `--hard` | Also remove from git index and commit |
| `--force` | Skip confirmation prompt |
| `--backup` | Create `.beads-backup-{timestamp}/` first |
| `--dry-run` | Preview what would happen |
| `--no-init` | Don't re-initialize after clearing |

### Reset Levels
1. **Soft Reset (default)** - Kill daemons, clear .beads/, re-init. Git history unchanged.
2. **Hard Reset (`--hard`)** - Also git rm and commit the removal, then commit fresh state.

### Implementation Flow
1. Validate .beads/ exists
2. If not --force: show impact summary, prompt confirmation
3. If --backup: copy .beads/ to .beads-backup-{timestamp}/
4. Kill daemons
5. If --hard: git rm + commit
6. rm -rf .beads/*
7. If not --no-init: bd init (and git add+commit if --hard)
8. Print summary

### Safety Mechanisms
- Confirmation prompt (skip with --force)
- Impact summary (issue/tombstone counts)
- Backup option
- Dry-run preview
- Git dirty check warning

### Code Structure
- `cmd/bd/reset.go` - CLI command
- `internal/reset/` - Core logic package


Run go install

go install ./cmd/bd


Re: Thread Test 2

Great! Thread is working well.


Add 'bd where' command to show active beads location

## Problem

When using redirects, it's unclear which beads database is actually being used. This caused debugging confusion during the v0.39.0 release.

## Proposed Solution

Add `bd where` command:

```bash
bd where
# → /Users/stevey/gt/beads/mayor/rig/.beads (via redirect from crew/dave/.beads)

bd where --json
# → {"path": "...", "redirected_from": "...", "prefix": "bd"}
```

## Alternatives Considered

- `bd info --beads-path` - but `bd where` is more discoverable
- Just fix the UX so it's never confusing - defense in depth is better

## Desire Paths

This supports the 'desire paths' design principle: make the system's behavior visible so agents can debug and understand it.


Design auto-squash behavior for wisps

Explore the design space for automatic wisp squashing.

**Context:**
Wisps are ephemeral molecules that should be squashed (digest) or burned (no trace)
when complete. Currently this is manual. Should it be automatic?

**Questions to answer:**
1. When should auto-squash trigger?
   - On molecule completion?
   - On session end/handoff?
   - On patrol detection?

2. What's the default summary for auto-squash?
   - Generic: 'Auto-squashed on completion'
   - Step-based: List closed steps
   - AI-generated: Require agent to provide

3. Should this be configurable?
   - Per-molecule setting in formula?
   - Global config: auto_squash: true/false
   - Per-wisp flag at creation time?

4. Who decides - Beads or Gas Town?
   - Beads: Provides operators (squash, burn)
   - Gas Town: Makes policy decisions
   - Proposal: GT patrol molecules call bd mol squash

**Constraints:**
- Don't lose important context (summary matters)
- Don't create noise in digest history
- Respect agent's intent (some wisps should burn, not squash)

**Recommendation:**
Gas Town patrol molecules should have explicit squash/burn steps.
Beads provides primitives, GT makes policy decisions.
Auto-squash at Beads level is probably wrong layer.


Documentation for messaging and graph links

Document all new features.

Files to update:
- README.md - brief mention of messaging capability
- AGENTS.md - update for AI agents using bd mail
- docs/messaging.md (new) - full messaging reference
- docs/graph-links.md (new) - graph link reference
- CHANGELOG.md - v0.30.2 release notes

Topics to cover:
- Mail commands with examples
- Graph link types and use cases
- Identity configuration
- Hooks setup for notifications
- Migration notes


bd repair: Add --json output flag

bd repair lacks --json output for machine-readable results, unlike most other bd commands.

Add --json flag that outputs structured JSON with:
- orphan counts by type
- list of orphaned references
- repair status (success/failure)
- backup path


Create and push git tag v0.33.2

Create the release tag and push it:

```bash
git tag v0.33.2
git push origin v0.33.2
```

This triggers the GoReleaser GitHub Action to build release binaries.


Merge: bd-m8ro

branch: polecat/max
target: main
source_issue: bd-m8ro
rig: beads


GH#405: Fix prefix parsing with hyphens - multi-hyphen prefixes parsed incorrectly

Fixed: ExtractIssuePrefix was falling back to first-hyphen for word-like suffixes, breaking multi-hyphen prefixes like 'hacker-news' and 'me-py-toolkit'.


Gate await fields cleared by --no-daemon CLI access (not multi-repo)


Detect/prevent child→parent dependency anti-pattern

## Problem

When filing issues with dependencies, it's easy to fall into the "temporal trap":
- Thinking "Phase 1 comes before Phase 2"
- Writing `bd dep add phase1 phase2`
- But that means "phase1 depends on phase2" (backwards!)

A specific variant: children depending on their parent epic. This is ALWAYS wrong because:
1. Parent-child relationship already captures the dependency (parent closes when children done)
2. Child→parent dep creates a block: child can't start because parent is "open"
3. Parent can't close because children aren't done
4. Deadlock

## Solution

### 1. Prevent at creation (`bd dep add`)

When user runs `bd dep add X Y`:
- Check if X is a child of Y (X.id starts with Y.id + ".")
- If so, error: "Cannot add dependency: X is already a child of Y. Children inherit dependency on parent completion via hierarchy."

### 2. Detect in `bd doctor`

Add a check that finds all cases where:
- Issue A depends on Issue B
- A.id matches pattern B.id + ".*" (A is child of B)

Report as: "Child→parent dependency detected (anti-pattern)"
Offer repair: "Remove N backwards dependencies? [y/N]"

## Implementation

- `dep_add.go`: Add parent-child check before adding dependency
- `doctor.go`: Add backwards-dep detection to health checks


Upgrade local Homebrew installation

Upgrade bd via Homebrew:

```bash
brew update
brew upgrade bd
/opt/homebrew/bin/bd version  # Verify shows 0.33.2
```


Refinery Patrol

Merge queue processor patrol loop with verification gates.


WaitsFor directive: Fanout gate for dynamic children

Implement WaitsFor directive for molecules that spawn dynamic children.

## Syntax
```markdown
## Step: aggregate
Collect outcomes from all dynamically-bonded children.
WaitsFor: all-children
Needs: survey-workers
```

## Behavior
1. Parse WaitsFor directive during molecule step parsing
2. Track which steps spawn dynamic children (the spawner)
3. Gate step waits until ALL children of the spawner complete
4. Works with bd ready - gate step not ready until children done

## Gate Types
- `WaitsFor: all-children` - Wait for all dynamic children
- `WaitsFor: any-children` - Proceed when first child completes (future)
- `WaitsFor: <step-ref>.children` - Wait for specific spawner's children

## Integration
- bd ready should skip gate steps until children complete
- bd show <mol> should display gate status and child count


bd cook: waits_for field not preserved in cooked protos

The `waits_for` field in formula steps is not preserved in the cooked proto.

## Formula

```yaml
- id: aggregate
  title: Aggregate arm results
  needs: [survey-workers]
  waits_for: all-children
  description: |
    This is a **fanout gate**...
```

## Expected

The cooked proto step should have metadata indicating it's a gate:
- Label: `gate:all-children` or
- Field in issue metadata indicating wait behavior

## Actual

The `waits_for` field is silently ignored. Only the description mentions 'fanout gate'.

## Impact

The execution engine cannot determine which steps are gates that need to wait
for dynamically-bonded children. This breaks the Christmas Ornament pattern
where aggregate steps must wait for all polecat-arm wisps to complete.

## Related

bd-hr39: needs field not converted to step dependencies


Update documentation for CLI consolidation

## Task
Update all documentation to reflect CLI consolidation changes.

## Files requiring updates

### Primary docs
- docs/CLI_REFERENCE.md - Major updates
  - Add admin section (cleanup, compact, reset)
  - Update migrate section (subcommands)
  - Update formula section (add cook)
  - Remove/update standalone command references

### Secondary docs
- docs/DELETIONS.md - migrate-tombstones → migrate tombstones, compact → admin compact
- docs/FAQ.md - compact → admin compact
- docs/QUICKSTART.md - compact, cleanup → admin compact, admin cleanup
- docs/TROUBLESHOOTING.md - compact → admin compact
- docs/CONFIG.md - compact → admin compact
- docs/INSTALLING.md - quickstart reference
- docs/EXTENDING.md - quickstart reference

### Skills docs
- skills/beads/SKILL.md - compact, quickstart references
- skills/beads/references/CLI_REFERENCE.md - mirror main CLI_REFERENCE.md

### Other docs
- commands/quickstart.md - update or remove
- commands/compact.md - update to admin compact
- examples/compaction/README.md - update examples
- README.md - quickstart reference
- CHANGELOG.md - add consolidation changes section

## Implementation notes
- Keep backwards compatibility notices where aliases exist
- Update command examples to use new paths
- Consider adding deprecation warnings section


Test Agent


Implement Jira issue timestamp comparison for sync

In cmd/bd/jira.go:633, there's a TODO for a full implementation that would fetch the Jira issue and compare timestamps to detect conflicts during sync.

File: cmd/bd/jira.go:633


Update hook templates to 0.30.4

Update bd-hooks-version comment in all 4 hook templates:
- cmd/bd/templates/hooks/pre-commit
- cmd/bd/templates/hooks/post-merge
- cmd/bd/templates/hooks/pre-push
- cmd/bd/templates/hooks/post-checkout

Each should have:
```bash
# bd-hooks-version: 0.30.4
```

```verify
grep -l 'bd-hooks-version: 0.30.4' cmd/bd/templates/hooks/* | wc -l | grep -q '4'
```


Update integrations/beads-mcp/pyproject.toml to 0.30.4

Update version in pyproject.toml:
```toml
version = "0.30.4"
```

```verify
grep -q 'version = "0.30.4"' integrations/beads-mcp/pyproject.toml
```


Review jordanhubbard chaos testing PR #752

FINDINGS:
- Implementation quality: HIGH
- Recommendation: MERGE WITH MODIFICATIONS
- Mods: No hard coverage threshold, chaos tests on releases only

DECISION: User agrees. Next steps:
1. Add chaos tests to release-bump formula
2. Merge PR #752

Review doc: docs/pr-752-chaos-testing-review.md


Thread Test

Testing the thread feature


Refactor sync_test to use direct import logic

In cmd/bd/sync_test.go:444, there's a TODO to refactor the test to use direct import logic instead of subprocess calls. This would make the test more reliable and faster.

File: cmd/bd/sync_test.go:444


Merge: quartz-1767106247671

branch: polecat/quartz-1767106247671
target: main
source_issue: quartz-1767106247671
rig: beads


Test child task


Thread Test 2

Testing direct mode


Support wisps in nodb mode for lightweight Gas Town rigs

Enable proper wisp support in --no-db mode to allow lightweight Gas Town rigs (including HQ/Mayor) to run without SQLite overhead.

## Motivation
Gas Town HQ and simple rigs could benefit from nodb mode's simplicity. Currently wisps don't work properly in nodb mode.

## Proposed Design

### Phase 1: Session-scoped wisps
- Memory storage already has Wisp field on Issue
- Wisps exist only for the session lifetime
- Acceptable for patrol cycles that complete within a session
- Depends on: bd-9avq (fix the leak)

### Phase 2: Optional wisp persistence
- Add `.beads/wisps.jsonl` for wisp storage (gitignored)
- Load wisps from this file on nodb startup
- Save wisps to this file on nodb exit
- Wisps persist across restarts but don't sync via git

### Phase 3: Verify mol commands
- Test mol squash, burn, bond work with memory storage
- These currently check for SQLite storage type
- May need interface-based approach instead of type assertions

## Files affected
- cmd/bd/nodb.go - wisp filtering and optional persistence
- internal/storage/memory/ - verify wisp field handling
- cmd/bd/mol_*.go - verify memory storage compatibility
- .beads/.gitignore - add wisps.jsonl


Improve test coverage for internal/daemon (27.3% → 60%)

The daemon package has only 27.3% test coverage. The daemon is critical for background operations and reliability.

Key areas needing tests:
- Daemon autostart logic
- Socket handling
- PID file management
- Health checks

Current coverage: 27.3%
Target coverage: 60%


Add 'tracks' relation type for convoy tracking

Add a new relation type 'tracks' for convoy → issue relationships.

Unlike 'depends_on':
- Non-blocking (tracked issue doesn't block convoy)
- Cross-prefix capable (convoy in hq-* tracks issues in gt-*, bd-*)
- Supports reverse lookup ('what convoys track this issue?')

Used by convoys to track issues across project chains without creating dependencies.

Related: hq-7h8jx (Convoy System epic in town beads)


Sync race: rebase-based divergence recovery resurrects tombstones

## Problem
When two repos sync simultaneously, tombstones can be resurrected:

1. Repo A deletes issue (creates tombstone), pushes to sync branch
2. Repo B (with 'closed' status) exports and tries to push
3. Push fails (non-fast-forward)
4. fetchAndRebaseInWorktree does git rebase
5. Git rebase applies B's 'closed' patch on top of A's 'tombstone'
6. TEXT-level rebase doesn't invoke beads merge driver
7. 'closed' overwrites 'tombstone' = resurrection

## Root Cause
CommitToSyncBranch uses git rebase for divergence recovery, but rebase is text-level, not content-level. The proper content-level merge in PullFromSyncBranch handles tombstones correctly, but it runs AFTER the problematic push.

## Proposed Fix
Option 1: Don't push in CommitToSyncBranch - let PullFromSyncBranch handle merge+push
Option 2: Replace git rebase with content-level merge in fetchAndRebaseInWorktree
Option 3: Reorder sync steps: Export → Pull/Merge → Commit → Push

## Workaround Applied
Excluded tombstones from orphan detection warnings (commit 1e97d9cc).

See also: bd-3852 (Add orphan detection migration)


Code Review Round 2: Beads Refactoring

Second pass code review of Beads codebase. Found 10 HIGH/MEDIUM priority issues.

**HIGH (P2):**
- bd-6dnt: Duplicated IssueDetails struct (4 definitions)
- bd-vqh9: init.go is 1928 lines
- bd-it19: show.go is 1592 lines

**MEDIUM (P3):**
- bd-qobn: 20+ global variables in main.go
- bd-0e02: Inconsistent error handling patterns
- bd-g6m5: Mixed daemon/direct mode logic
- bd-qe7j: Incomplete TODOs in formula/types.go
- bd-4sxh: Issue struct ~100 fields
- bd-yuxq: 262 uses of interface{}
- bd-jbqx: Repeated validation patterns


Tier 2: Basic bd mol squash command

Add bd mol squash command for basic molecule execution compression.

## Command

bd mol squash <molecule-id> [flags]
  --dry-run         Preview what would be squashed
  --keep-children   Don't delete ephemeral children after squash
  --json            JSON output

## Implementation

1. Find all ephemeral children of molecule (parent-child deps)
2. Concatenate child descriptions/notes into digest
3. Create digest issue in main repo with:
   - Title: 'Molecule Execution Summary: <original-title>'
   - digest_of: [list of squashed child IDs]
   - ephemeral: false (digest is permanent)
4. Delete ephemeral children (unless --keep-children)
5. Link digest to parent work item

## Schema Changes

Add to Issue struct:
- SquashedAt *time.Time
- SquashDigest string (ID of digest)
- DigestOf []string (IDs of squashed children)

## Acceptance Criteria

- bd mol squash <id> creates digest, removes children
- --dry-run shows preview
- Digest has proper metadata linking


Test cross-rig issue creation

Testing --rig flag from town root


Test Thread

Initial message for threading test


Implement bd pin/unpin commands

Add 'bd pin <id>' and 'bd unpin <id>' commands to toggle the pinned status of issues. Should support multiple IDs like other bd commands.


Verify version consistency

Confirm all versions match 0.39.0.

```bash
grep 'Version = ' cmd/bd/version.go
jq -r '.version' .claude-plugin/plugin.json
jq -r '.version' npm-package/package.json
grep 'version = ' integrations/beads-mcp/pyproject.toml
```

All should show 0.39.0.


getLocalSyncBranch silently ignores YAML parse errors

In autoimport.go:170-172, YAML parsing errors are silently ignored. If a user has malformed YAML in config.yaml, sync-branch will just silently be empty with no feedback.

Recommendation: Add debug logging since this function is only called during auto-import, and debugging silent failures is painful.

Add: debug.Logf("Warning: failed to parse config.yaml: %v", err)


Add comments to 30+ silently ignored errors or fix them

Code health review found 30+ instances of error suppression using blank identifier without explanation:

Good examples (with comments):
- merge.go: _ = gitRmCmd.Run() // Ignore errors
- daemon_watcher.go: _ = watcher.Add(...) // Ignore error

Bad examples (no context):
- create.go:213: dbPrefix, _ = store.GetConfig(ctx, "issue_prefix")
- daemon_sync_branch.go: _ = daemonClient.Close()
- migrate_hash_ids.go, version_tracking.go: _ = store.Close()

Fix: Add comments explaining WHY errors are ignored, or handle them properly.


Merge: bd-qioh

branch: polecat/Errata
target: main
source_issue: bd-qioh
rig: beads


Merge: garnet-1767138539231

branch: polecat/garnet-1767138539231
target: main
source_issue: garnet-1767138539231
rig: beads


Document pinned status in CLI_REFERENCE.md

## Task

The `pinned` status is a valid issue status but is not documented in CLI_REFERENCE.md.

## Current Status Values (from types.go:295-301)

- open
- in_progress
- blocked
- deferred
- closed
- tombstone
- **pinned** ← undocumented

## Definition

`StatusPinned Status = "pinned" // Persistent bead that stays open indefinitely (bd-6v2)`

## Where to Document

Add to the 'Issue Types' or create a new 'Issue Statuses' section in docs/CLI_REFERENCE.md.

## Related

The pinned status is heavily used by gt for hook management (`--status=pinned`).


Work on beads-rs0: Namepool configuration for themed pole...

Work on beads-rs0: Namepool configuration for themed polecat names. See bd show beads-rs0 for full details.


Update local installations.

Update local installations.

```bash
# Upgrade Homebrew
brew upgrade steveyegge/beads/bd

# Or install from source
./scripts/bump-version.sh 0.37.0 --install

# Install MCP locally
pip install -e integrations/beads-mcp

# Restart daemons
pkill -f "bd daemon" || true
```

Verify: `bd --version` shows 0.37.0

instantiated_from: bd-mol-xga
step: update-local


Work on beads-ocs: Thin shim hooks to eliminate version d...

Work on beads-ocs: Thin shim hooks to eliminate version drift (GH#615). Replace full hook scripts with thin shims that call bd hooks run. When done, submit MR (not PR) to integration branch for Refinery.


Consider caching external dep resolution results

Each call to GetReadyWork re-checks all external dependencies by:
1. Querying for external deps in the local database
2. Opening each external project's database
3. Querying for closed issues with provides: labels

For workloads with many external deps or slow external databases, this adds latency on every bd ready call.

Potential optimizations:
- In-memory TTL cache for external dep status (e.g., 60 second TTL)
- Store resolved status in a local cache table with timestamp
- Batch resolution of common project/capability pairs

This is not urgent - current implementation is correct and performant for typical workloads. Only becomes an issue with many external deps across many projects.


Auto-repair stale merge driver configs with invalid placeholders

Old bd versions (<0.24.0) installed merge driver with invalid placeholders %L %R instead of %A %B. Add detection to bd doctor --fix: check if git config merge.beads.driver contains %L or %R, auto-repair to 'bd merge %A %O %A %B'. One-time migration for users who initialized with old versions.


bd mol run: Only creates partial children from proto

When running `bd mol run gt-lwuu --var issue=gt-xxx`, only 2 of 4 proto children are created.

## Expected
Proto gt-lwuu has 4 children:
- gt-lwuu.1: load-context
- gt-lwuu.2: implement
- gt-lwuu.3: self-review
- gt-lwuu.8: request-shutdown

All 4 should be instantiated.

## Actual
Only 2 children created:
- load-context
- implement

self-review and request-shutdown are missing.

## Repro
```bash
bd --no-daemon mol run gt-lwuu --var issue=gt-xxx --json
bd list --parent=<created-id>  # Only shows 2 children
```

## Impact
Polecats spawned with mol-polecat-work get incomplete workflow.


Merge: amber-1767138546434

branch: polecat/amber-1767138546434
target: main
source_issue: amber-1767138546434
rig: beads


bd repair: Handle .beads/redirect files

The bd repair command hardcodes .beads/beads.db path but doesn't follow .beads/redirect files like other commands do. This means repair won't work on clones that use redirected beads directories.

Fix: Check for .beads/redirect file and follow it to find the actual database location.


Wait for GoReleaser

gh run list --workflow=release.yml - wait for success


Implement global N-way collision resolution algorithm

**Summary:** Replaced pairwise collision resolution with a global N-way algorithm that deterministically resolves issue ID conflicts across multiple clones. The new approach groups collisions, deduplicates by content hash, and assigns sequential IDs to ensure consistent synchronization.

**Key Decisions:**
- Use content hash for global, stable sorting
- Group collisions by base ID
- Assign sequential IDs based on sorted unique versions
- Eliminate order-dependent remapping logic

**Resolution:** Implemented ResolveNWayCollisions function that guarantees deterministic issue ID assignment across multiple synchronization scenarios, solving the core challenge of maintaining consistency in distributed systems with potential conflicts.


Test issue for pin --for

Testing the pin --for flag


bd doctor --deep: validate full graph integrity

Add a deep validation mode to bd doctor that checks the entire issue graph.

Current doctor checks:
- Orphan sessions
- Orphan processes
- Wisp GC
- Sync status

Proposed --deep checks:
- **Parent consistency**: Every issue with parent field points to existing issue
- **Dependency symmetry**: If A depends on B, B should show A in blocks
- **Orphan issues**: Issues mentioned in commits but never closed
- **Circular dependencies**: Detect dependency cycles
- **Epic completeness**: Epics with all children closed should be closeable
- **Agent bead integrity**: Agent beads have required fields (role_bead, state)
- **Mail thread integrity**: Messages point to valid threads
- **Molecule state**: Molecules have valid current_step pointing to existing step

Use case: After complex operations (reparenting, migrations, bulk updates), run `bd doctor --deep` to catch graph inconsistencies before they cause problems.

Performance: May be slow on large databases - warn user and show progress.


Update info.go versionChanges

Add entry to versionChanges in cmd/bd/info.go.

This powers `bd info --whats-new` for agents.

```go
"0.39.0": {
    "summary": "Brief description",
    "changes": []string{
        "Key change 1",
        "Key change 2",
    },
},
```

Focus on workflow-impacting changes agents need to know.


Fix unparam unused parameter in cmd/bd/doctor.go:1879

Linting issue: checkGitHooks - path is unused (unparam) at cmd/bd/doctor.go:1879:20. Error: func checkGitHooks(path string) doctorCheck {


Unify template.go and mol.go under bd mol

Consolidate the two DAG-template systems into one under the mol command. mol.go (on rictus branch) has the right UX (catalog/show/bond), template.go has the mechanics. Merge them, deprecate bd template commands.


Update CHANGELOG.md with release notes

Add meaningful release notes to CHANGELOG.md describing what changed in test-squash


mol-christmas-launch: 3-day execution plan

Christmas Launch Molecule - execute phases in order, survive restarts.

PIN THIS BEAD. Check progress each session start.

## Step: phase0-beads-foundation
Fix blocking issues before swarming:
1. Verify gastown beads schema works: bd list --status=open
2. Ensure bd mol bond exists (check bd-usro)
3. Verify bd-2vh3 (squash) is filed

## Step: phase1-polecat-loop
Serial work on polecat execution:
1. gt-9nf: Fresh polecats only
2. gt-975: Molecule execution support
3. gt-8v8: Refuse uncommitted work
Then swarm: gt-e1y, gt-f8v, gt-eu9
Needs: phase0-beads-foundation

## Step: phase2-refinery
Serial work on refinery autonomy:
1. gt-5gkd: Refinery CLAUDE.md
2. gt-bj6f: Refinery context in gt prime
3. gt-0qki: Refinery-Witness protocol
Needs: phase1-polecat-loop

## Step: phase3-deacon
Health monitoring infrastructure:
1. gt-5af.4: Simplify daemon
2. gt-5af.7: Crew session patterns
3. gt-976: Crew lifecycle
Needs: phase2-refinery

## Step: phase4-code-review
Self-improvement flywheel:
1. Define mol-code-review (gt-fjvo)
2. Test on open MRs
3. Integrate with Refinery
Needs: phase3-deacon

## Step: phase5-polish
Demo readiness:
1. gt-b2hj: Find orphaned work
2. Doctor checks
3. Clean up open MRs
Needs: phase4-code-review

## Step: verify-flywheel
Success criteria:
- gt spawn works with molecules
- Refinery processes MRs autonomously
- mol-code-review runs on a PR
- bd cleanup --ephemeral works
Needs: phase5-polish


Digest: Release v0.34.0 @ 2025-12-22 12:16

Released v0.34.0: wisp commands, chemistry UX, cross-project deps


Update docs for import bug fix

Update AGENTS.md, README.md, TROUBLESHOOTING.md with import.orphan_handling config documentation. Document resurrection behavior, tombstones, config modes. Add troubleshooting section for import failures with deleted parents.


bd mol squash handles wisp→digest

Update bd mol squash to handle ephemeral molecules.

## Behavior for Ephemeral Molecules
1. Delete wisp from .beads-ephemeral/
2. Create digest issue in .beads/ (permanent)
3. Digest has type:digest and squashed_from field

## Digest Format
```json
{
  "id": "<parent>.digest-NNN",
  "type": "digest",
  "title": "<proto> cycle @ <timestamp>",
  "description": "<summary from --summary flag>",
  "parent": "<proto-id>",
  "squashed_from": "<wisp-id>"
}
```

## Implementation
- Detect if molecule is ephemeral (check storage location or flag)
- Delete from ephemeral store
- Create digest in permanent store
- Return digest ID

## Testing
- Test squash of ephemeral mol creates digest
- Test wisp is deleted after squash
- Test digest is queryable


Refactor: Extract shared getEpicChildren helper for swarm commands

## Context
`getSwarmStatus` and `analyzeEpicForSwarm` in cmd/bd/swarm.go have nearly identical code for:
- Getting epic dependents via GetDependents
- Filtering to parent-child relationships
- Building childIDSet map
- Building dependency maps

## Implementation
1. Extract `getEpicChildren(ctx, store, epicID)` that returns:
   - `[]*types.Issue` - child issues
   - `map[string]bool` - child ID set
   - `map[string][]string` - dependency map (within children)
   - `error`

2. Refactor both `analyzeEpicForSwarm` and `getSwarmStatus` to use it

3. Consider defining a named interface for the store methods:
   ```go
   type SwarmStore interface {
       GetIssue(context.Context, string) (*types.Issue, error)
       GetDependents(context.Context, string) ([]*types.Issue, error)
       GetDependencyRecords(context.Context, string) ([]*types.Dependency, error)
   }
   ```

## Files
- cmd/bd/swarm.go (lines 150-248, 616-682)

## Acceptance
- No duplicated code for fetching epic children
- Both commands produce identical results as before


bd close hooks: context check and notifications

Add hook system to bd close for notifications and custom actions.

## Scope (MVP)

Implement **command hooks only** for bd close. Deferred: notify, webhook types.

## Implementation

### 1. Config Schema

Add to internal/configfile/config.go:

```go
type HooksConfig struct {
    OnClose []HookEntry `yaml:"on_close,omitempty"`
}

type HookEntry struct {
    Command string `yaml:"command"`           // Shell command to run
    Name    string `yaml:"name,omitempty"`    // Optional display name
}
```

Add `Hooks HooksConfig` field to Config struct.

### 2. Hook Execution

Create internal/hooks/close_hooks.go:

```go
func RunCloseHooks(ctx context.Context, cfg *configfile.Config, issue *types.Issue) error {
    for _, hook := range cfg.Hooks.OnClose {
        cmd := exec.CommandContext(ctx, "sh", "-c", hook.Command)
        cmd.Env = append(os.Environ(),
            "BEAD_ID="+issue.ID,
            "BEAD_TITLE="+issue.Title,
            "BEAD_TYPE="+string(issue.IssueType),
            "BEAD_PRIORITY="+strconv.Itoa(issue.Priority),
            "BEAD_CLOSE_REASON="+issue.CloseReason,
        )
        cmd.Stdout = os.Stdout
        cmd.Stderr = os.Stderr
        if err := cmd.Run(); err \!= nil {
            // Log warning but dont fail the close
            fmt.Fprintf(os.Stderr, "Warning: close hook %q failed: %v\n", hook.Name, err)
        }
    }
    return nil
}
```

### 3. Integration Point

In cmd/bd/close.go, after successful close:

```go
// Run close hooks
if cfg := configfile.Load(); cfg \!= nil {
    hooks.RunCloseHooks(ctx, cfg, closedIssue)
}
```

### 4. Example Config

```yaml
# .beads/config.yaml
hooks:
  on_close:
    - name: show-next
      command: bd ready --limit 1
    - name: context-check
      command: echo "Issue $BEAD_ID closed. Check context if nearing limit."
```

## Environment Variables

| Variable | Description |
|----------|-------------|
| BEAD_ID | Issue ID (e.g., bd-abc1) |
| BEAD_TITLE | Issue title |
| BEAD_TYPE | Issue type (task, bug, feature, etc.) |
| BEAD_PRIORITY | Priority (0-4) |
| BEAD_CLOSE_REASON | Close reason if provided |

## Testing

Add test in internal/hooks/close_hooks_test.go:
- Test hook execution with mock config
- Test env vars are set correctly
- Test hook failure doesnt block close

## Files to Create/Modify

1. **Create:** internal/hooks/close_hooks.go
2. **Create:** internal/hooks/close_hooks_test.go
3. **Modify:** internal/configfile/config.go (add HooksConfig)
4. **Modify:** cmd/bd/close.go (call RunCloseHooks)
5. **Modify:** docs/CONFIG.md (document hooks config)

## Out of Scope (Future)

- notify hook type (gt mail integration)
- webhook type (HTTP POST)
- on_create, on_update hooks
- Hook timeout configuration
- Parallel hook execution


Create release tag

Create annotated git tag.

```bash
git tag -a v0.39.0 -m "Release v0.39.0"
```

Verify: `git tag -l | tail -5`


Swarm: Test coordinator display

Swarm molecule orchestrating epic bd-2rkr.

Epic: bd-2rkr
Coordinator:


Code smell: init.go is 1928 lines - needs splitting

cmd/bd/init.go is 1928 lines covering multiple init modes, contributor/team setup, hooks setup, and stealth mode.

**Problem:**
- File is too large to understand and maintain
- Multiple responsibilities within single file
- Harder to test individual components

**Acceptance Criteria:**
- [ ] Split into logical files (init_setup.go, init_hooks.go, init_stealth.go, etc.)
- [ ] Each file under 500 lines
- [ ] No functionality changes
- [ ] Tests pass


Polecats should spawn with auto-accept mode enabled

During swarm bd-784c, polecats (Toast, Nux) were spawned without --dangerously-skip-permissions or equivalent auto-accept mode.

**Problem:**
Every edit, bash command, and tool use required manual confirmation via tmux send-keys. This defeats the purpose of autonomous polecat operation.

**Expected:**
Polecats in a swarm should run with permissions bypassed so they can work autonomously.

**Workaround used:**
- Manually sent Enter keys via tmux to accept prompts
- Eventually polecats entered 'bypass permissions on' mode after restart

**Suggestion:**
- gt sling should pass --dangerously-skip-permissions by default for polecats
- Or polecats should have a .claude/settings.local.json pre-configured for auto-accept


Enrich MutationEvent with title and assignee

Current MutationEvent only has IssueID, no context. Add Title and Assignee fields so activity feeds can display meaningful info without extra lookups. Emit these fields when creating mutation events in server_core.go.


Simplify wisp architecture: single DB with ephemeral flag

## Problem

The current wisp architecture uses a separate .beads-wisp/ directory with its own database. This creates unnecessary complexity:

- Separate directory structure and database initialization
- Parallel routing logic in both beads and gastown
- Merge queries from two stores for inbox/list operations
- All this for what is essentially a boolean flag

## Current State

Gastown mail code has to:
- resolveWispDir() vs resolveBeadsDir()
- Query both, merge results
- Route deletes to correct store

## Proposed Solution

Single database with an ephemeral boolean field on Issue. Behavior:
- bd create --ephemeral sets the flag
- JSONL export SKIPS ephemeral issues (they never sync)
- bd list shows all by default, --ephemeral / --persistent filters
- bd mol squash clears the flag (promotes to permanent, now exports)
- bd wisp gc deletes old ephemeral issues from the db
- No separate directory, no separate routing

## Migration

1. Update beads to support ephemeral flag in main db
2. Update bd wisp commands to work with flag instead of separate dir
3. Update gastown mail to use simple --ephemeral flag (remove all dual-routing)
4. Deprecate .beads-wisp/ directory pattern

## Acceptance Criteria

- Single database for all issues (ephemeral and persistent)
- ephemeral field on Issue type
- JSONL export skips ephemeral issues
- bd create --ephemeral works
- bd mol squash promotes ephemeral to persistent
- Gastown mail uses simple flag, no dual-routing


Add computed .parent field to JSON output for convenience

Currently parent is only in dependencies[]. Add a top-level .parent field to JSON/JSONL output that extracts the parent-child dependency for easier querying.

Example:
```bash
# Current (works but verbose):
bd show <id> --json | jq '.[0].dependencies[] | select(.dependency_type == "parent-child") | .id'

# Desired:
bd show <id> --json | jq '.[0].parent'
```

Low priority enhancement.


Test hooked styling


Update documentation for template system

Update AGENTS.md and help text to document the new template system:

- How to create a template (epic + template label + child issues)
- How to define variables (just use {{name}} placeholders)
- How to instantiate (bd template instantiate)
- Migration from YAML workflows (if any users had custom ones)


test hash length


Implement formula features: repeat, for_each, branch, gate, compose.split

Multiple formula features in internal/formula/types.go are marked as "Not yet implemented":

IMPLEMENTED (TODOs removed):
- Step.Expand: Inline expansion - implemented in ApplyInlineExpansions (expand.go)
- Step.ExpandVars: Variable overrides for expansion - implemented alongside Expand

STILL NOT IMPLEMENTED (TODOs remain):
- Step.Condition: Make step optional based on variable (types.go:177-180)
- Step.Gate: Async wait condition for step (types.go:185-187)
- Gate struct: Schema defined but not processed in bd cook (types.go:209-210)

File: internal/formula/types.go