bd swarm status command

Add bd swarm status to show swarm state derived from beads.

## Context
This is the key command that demonstrates 'discovery over tracking'.
Swarm status is COMPUTED from:
- Epic children statuses
- Dependency structure
- Issue assignments

NOT stored separately. If beads changes, status changes.

## Implementation
1. New command: bd swarm status <swarm-mol-id>
2. Find linked epic from swarm molecule
3. Query all children of epic
4. Compute and display:
   - Ready front (unblocked, open issues)
   - In progress (in_progress status)
   - Completed (closed)
   - Blocked (has unmet dependencies)
   - Progress percentage
   - Coordinator
   - Estimated remaining (based on avg session time)

## Output Format
Ready Front Analysis: gt-epic-xxx

Completed:     ✓ gt-001 (2h ago)
Active:        ⟳ gt-002 [nux], ⟳ gt-003 [toast]
Ready:         ○ gt-004, ○ gt-005
Blocked:       ◌ gt-006 (needs 004, 005)

Progress: 1/6 complete, 2/6 active (17%)

## Reference
~/gt/docs/swarm-architecture.md - 'Ready Front Visualization'

## Acceptance
- bd swarm status <mol> shows accurate state
- State reflects current beads (not cached)
- --json output for programmatic use


Update local installation

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


Work on gt-8tmz.36: Validate expanded step IDs are unique...

Work on gt-8tmz.36: Validate expanded step IDs are unique. After ApplyExpansions in internal/formula/expand.go, new steps can be created that might have duplicate IDs. Add validation in ApplyExpansions to check for duplicate step IDs after expansion. If duplicates found, return an error with the duplicate IDs. Add test in expand_test.go. When done, commit and push to main.


bd cook: needs field not converted to step dependencies

When cooking a formula with `needs` fields on steps, the dependencies are not created between sibling steps.

## Expected

Formula:
```yaml
steps:
  - id: inbox-check
    title: Check inbox
  - id: trigger-spawns
    title: Nudge polecats
    needs: [inbox-check]
```

Should create:
- mol-foo.inbox-check (no deps)
- mol-foo.trigger-spawns → depends on → mol-foo.inbox-check

## Actual

Both steps only depend on the parent proto:
- mol-foo.inbox-check → depends on → mol-foo
- mol-foo.trigger-spawns → depends on → mol-foo

The `needs` field is ignored.

## Impact

This breaks the step execution order. Steps that should wait for predecessors will run in parallel or out of order.

## Reproduction

```bash
bd cook mol-deacon-patrol.formula.yaml
bd show mol-deacon-patrol.trigger-pending-spawns
# Shows: Depends on mol-deacon-patrol (parent only)
# Should show: Depends on mol-deacon-patrol.inbox-check
```


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


Review PR #512: clarify bd ready docs

Review and merge PR #512 from aspiers. This PR clarifies what bd ready does after git pull in README.md. Simple 1-line change. URL: https://github.com/anthropics/beads/pull/512


Move cook to formula subcommand

## Task
Move `bd cook` → `bd formula cook`

## Implementation

### 1. Update formula.go
- Add `cookCmd` as subcommand of `formulaCmd`
- Import necessary packages from cook.go if not already present

### 2. Update cook.go
- Remove `rootCmd.AddCommand(cookCmd)` from init()
- Keep the command implementation as-is

### 3. Create hidden alias for backwards compatibility
- In main.go or aliases file, add:
  ```go
  aliasCmd := &cobra.Command{
    Use:    "cook",
    Hidden: true,
    Run: func(cmd *cobra.Command, args []string) {
      // Forward to formula cook
    },
  }
  ```

### 4. Update docs
- docs/MOLECULES.md - if `bd cook` mentioned
- CHANGELOG.md - add deprecation notice

## Files to modify
- cmd/bd/formula.go (add subcommand)
- cmd/bd/cook.go (change registration)
- cmd/bd/main.go (add hidden alias)


Code Review: Swarm commands consolidation

Code review of bd swarm commands (validate, status, create) identified several improvements.

**Summary:**
- Duplicated child-fetching logic between commands
- Missing duplicate swarm check in create
- Missing swarm list command
- Minor polish issues

Filed after implementing bd-fa1q (swarm create).


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 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.


Witness should auto-nuke idle polecats, not escalate

When witness detects an idle polecat with no work, it escalates to Mayor asking what to do.

Expected: Witness should nuke idle polecats automatically if:
1. No work hooked
2. No uncommitted/unpushed changes (cleanup_status=clean)
3. Idle for >threshold (e.g., 5 minutes)

Only escalate if polecat has unsaved work that needs recovery.

Current behavior creates unnecessary mail noise for the Mayor.


Wait for CI

Monitor GitHub Actions for release completion.

https://github.com/steveyegge/beads/actions

Expected time: 5-10 minutes

Watch for:
- Build artifacts (all platforms)
- Test suite pass
- npm publish
- PyPI publish


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


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


Dynamic Molecule Bonding: Fanout patterns for patrol molecules

## Vision

Enable molecules to dynamically spawn child molecules at runtime based on discovered
work. This is the foundation for the "Christmas Ornament" pattern where a patrol
molecule grows arms per-polecat.

## The Activity Feed Vision

Instead of parsing agent logs, users see structured work state:

```
[14:32:08] + patrol-x7k.arm-ace bonded (5 steps)
[14:32:08] + patrol-x7k.arm-nux 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)
```

This requires beads to track molecule step state transitions in real-time.

## Key Primitives Needed

### 1. Dynamic Bond with Variables
```bash
bd mol bond mol-polecat-arm <parent-wisp-id> \
  --var polecat_name=ace \
  --var rig=gastown
```

Creates wisp children under the parent:
- parent-id.arm-ace
- parent-id.arm-ace.capture
- parent-id.arm-ace.assess
- etc.

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

The `WaitsFor: all-children` directive makes this a fanout gate - it can't
proceed until ALL dynamically-bonded children complete.

### 3. Activity Feed Query
```bash
bd activity --follow              # Real-time state stream
bd activity --mol <id>            # Activity for specific molecule
bd activity --since 5m            # Last 5 minutes
```

### 4. Parallel Step Detection
Steps with no inter-dependencies should be flagged as parallelizable.
When arms are bonded, their steps can run in parallel across arms.

## Use Case: mol-witness-patrol

The Witness monitors N polecats where N varies at runtime:

```
survey-workers discovers: [ace, nux, toast]
For each polecat:
  bd mol bond mol-polecat-arm <patrol-id> --var polecat_name=<name>
aggregate step waits for all arms to complete
```

This creates the Christmas Ornament shape:
- Trunk: preflight steps
- Arms: per-polecat inspection molecules
- Base: cleanup after all arms complete

## Design Docs

See Gas Town docs:
- docs/molecular-chemistry.md (updated with Christmas Ornament pattern)
- docs/architecture.md (activity feed section)

## Dependencies

This epic may depend on:
- Wisp storage (.beads-wisp/) - already implemented
- Variable substitution in molecules - may need enhancement


Respond to GitHub issue #479 with solution

Once bd reset is implemented and released, respond to GitHub issue #479.

## Response should include
- Announce the new bd reset command
- Show basic usage examples
- Link to any documentation
- Thank the user for the feedback

## Example response
```
Thanks for raising this! We've added a `bd reset` command to handle this case.

Usage:
- `bd reset` - Reset to clean state (prompts for confirmation)
- `bd reset --backup` - Create backup first
- `bd reset --hard` - Also clean up git history

This is available in version X.Y.Z.
```

## Notes
- Wait until feature is merged and released
- Consider if issue should be closed or left for user confirmation


No logging/debugging for tombstone resurrection events

Per the design document bd-zvg Open Question 1: Should resurrection log a warning? Recommendation was Yes. Currently, when an expired tombstone loses to a live issue (resurrection), there is no logging or debugging output. This makes it hard to understand why an issue reappeared. Recommendation: Add optional debug logging when resurrection occurs, e.g., Issue bd-abc resurrected (tombstone expired). Files: internal/merge/merge.go:359-366, 371-378, 400-405, 410-415


Add covering index (label, issue_id) for label queries

GetIssuesByLabel joins labels table but requires table lookup after using idx_labels_label.

**Query (labels.go:165):**
```sql
SELECT ... FROM issues i
JOIN labels l ON i.id = l.issue_id
WHERE l.label = ?
```

**Problem:** Current idx_labels_label index doesn't cover issue_id, requiring row lookup.

**Solution:** Add migration:
```sql
CREATE INDEX IF NOT EXISTS idx_labels_label_issue ON labels(label, issue_id);
```

This is a covering index - query can be satisfied entirely from the index without touching the labels table rows.


Merge: bd-4opy

branch: polecat/angharad
target: main
source_issue: bd-4opy
rig: beads


doctor --fix should export when DB has more issues than JSONL

When 'bd doctor' detects a count mismatch (DB has more issues than JSONL), it currently recommends 'bd sync --import-only', which imports JSONL into DB. But JSONL is the source of truth, not the DB.

**Current behavior:**
- Doctor detects: DB has 355 issues, JSONL has 292
- Recommends: 'bd sync --import-only'
- User runs it: Returns '0 created, 0 updated' (no-op, because JSONL hasn't changed)
- User is stuck

**Root cause:**
The doctor fix is one-directional (JSONL→DB) when it should be bidirectional. If DB has MORE issues, they haven't been exported yet - the fix should be 'bd export' (DB→JSONL), not import.

**Desired fix:**
In fix.DBJSONLSync(), detect which has more data:
- If DB > JSONL: Run 'bd export' to sync JSONL (since DB is the working copy)
- If JSONL > DB: Run 'bd sync --import-only' to import (JSONL is source of truth)
- If equal but timestamps differ: Detect based on file mtime

This makes 'bd doctor --fix' actually fix the problem instead of being a no-op.


Merge: bd-au0.6

branch: polecat/furiosa
target: main
source_issue: bd-au0.6
rig: beads


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


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


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


Add Validation type and validations field to Issue

Add Validation struct (Validator *EntityRef, Outcome string, Timestamp time.Time, Score *float32) and Validations []Validation field to Issue. Tracks who validated/approved work completion. Core to HOP proof-of-stake concept - validators stake reputation on approvals.


Implement bd gate create/show/list/close/wait commands

Implement the gate CLI commands.

## Commands
```bash
# Create gate (returns gate ID)
bd gate create --await <type>:<id> --timeout <duration> --notify <addr>

# Check gate status
bd gate show <id>

# List open gates
bd gate list

# Close gate (usually done by Deacon)
bd gate close <id> --reason "completed"

# Add waiter to existing gate
bd gate wait <id> --notify <addr>
```

## Implementation
- Add cmd/bd/gate.go with subcommands
- Gate create creates wisp issue of type gate
- Gate list filters for open gates
- Gate wait adds to waiters[] array
- All support --json output


Merge: garnet-1767138539231

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


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


gt mq list shows empty when MRs exist


Command Set Standardization & Flag Consistency

Comprehensive improvements to bd command set based on 2025 audit findings.

## Background
See docs/command-audit-2025.md for detailed analysis.

## Goals
1. Standardize flag naming and behavior across all commands
2. Add missing flags for feature parity
3. Fix naming confusion
4. Improve consistency in JSON output

## Success Criteria
- All mutating commands support --dry-run (no --preview variants)
- bd update supports label operations
- bd search has filter parity with bd list
- Priority flags accept both int and P0-P4 format everywhere
- JSON output is consistent across all commands


Crew worker grip in beads - human-managed persistent workspace.

role_type: crew
rig: beads
agent_state: idle
hook_bead: null
role_bead: gt-crew-role
cleanup_status: null


Deacon Patrol

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


Integrate migration detection into bd doctor

Multiple migration commands (migrate_hash_ids.go, migrate_issues.go, migrate_sync.go, migrate_tombstones.go, migrate.go) have TODOs suggesting integration into 'bd doctor' for automatic migration detection. This would allow bd doctor to detect when migrations are needed and suggest running them.

Files:
- cmd/bd/migrate_hash_ids.go:24
- cmd/bd/migrate_issues.go:15
- cmd/bd/migrate_sync.go:16
- cmd/bd/migrate_tombstones.go:72
- cmd/bd/migrate.go:22


Refinery Patrol

Merge queue processor patrol loop with verification gates.


Graph Link: replies_to for conversation threading

Implement replies_to link type for message threading.

New command:
- bd mail reply <id> -m 'Response' creates a message with replies_to set

Query support:
- bd show <id> --thread shows full conversation thread
- Thread traversal in storage layer

Storage:
- replies_to column in issues table
- Index for efficient thread queries

This enables Reddit-style nested threads where messages reply to other messages.


Create release tag

Create annotated git tag.

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

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


Add --hard flag to bd cleanup to permanently cull tombstones before cutoff date

Currently tombstones persist for 30 days before cleanup prunes them. Need an official way to force-cull tombstones earlier than the default TTL, for scenarios like cleaning house after extended absence where resurrection from old clones is not a concern. Proposed: bd cleanup --hard --older-than N to bypass the 30-day tombstone TTL.


Import reports misleading '0 created, 0 updated' when actually importing all issues

When running 'bd import' on a fresh database (no existing issues), the command reports 'Import complete: 0 created, 0 updated' even though it successfully imported all issues from the JSONL file.

**Steps to reproduce:**
1. Delete .beads/beads.db
2. Run: bd import .beads/issues.jsonl
3. Observe output: 'Import complete: 0 created, 0 updated'
4. Run: bd list
5. Confirm: All issues are actually present in the database

**Expected behavior:**
Report the actual number of issues imported, e.g., 'Import complete: 523 created, 0 updated'

**Actual behavior:**
'Import complete: 0 created, 0 updated' (misleading - makes user think import failed)

**Impact:**
- Users think import failed when it succeeded
- Confusing during database sync operations (e.g., after git pull)
- Makes debugging harder (can't tell if import actually worked)

**Context:**
Discovered during VC session when syncing database after git pull. The misleading message caused confusion about whether the database was properly synced with the canonical JSONL file.


Update all code to use dependencies API for edges

Find and update all code that reads/writes:
- replies_to field -> use dependency API
- relates_to field -> use dependency API
- duplicate_of field -> use dependency API
- superseded_by field -> use dependency API

Commands affected: bd mail, bd relate, bd duplicate, bd supersede, bd show, etc.


bd mol bond --ephemeral flag

Add --ephemeral flag to bd mol bond command.

## Behavior
- `bd mol bond <proto> --ephemeral` creates molecule in .beads-ephemeral/
- Without flag, creates in .beads/ (current behavior)
- Ephemeral molecules have `ephemeral: true` in their issue record

## Implementation
- Add --ephemeral bool flag to mol bond command
- Route to EphemeralStore when flag set
- Set ephemeral:true on created issue

## Testing
- Test mol bond creates in correct location
- Test ephemeral flag is persisted
- Test regular mol bond still works


Work on gt-8tmz.31: Formula validation specification. Wri...

Work on gt-8tmz.31: Formula validation specification. Write docs/formula-validation.md specifying all validation rules for formulas. When done: 1) bd close gt-8tmz.31, 2) bd sync, 3) git push, 4) gt mq submit


Fix JSON errors in epic.go

Replace direct stderr writes with FatalErrorRespectJSON() in epic.go.

Commands affected:
- epicStatusCmd
- epicCloseEligibleCmd


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


Refactor long SQLite test files

The SQLite test files have grown unwieldy. Review and refactor.

## Goals
- Break up large test files into focused modules
- Improve test organization by feature area
- Reduce test duplication
- Make tests easier to maintain and extend

## Areas to Review
- main_test.go (likely the largest)
- Any test files over 500 lines
- Shared test fixtures and helpers
- Test coverage gaps

## Approach
- Group tests by feature (CRUD, sync, queries, transactions)
- Extract common fixtures to test helpers
- Consider table-driven tests where appropriate
- Ensure each test file has clear focus

## Reference
See docs/dev-notes/ for any existing test audit notes


Mega-molecule progress support for Hanoi demo

Beads-side work for million-step molecule monitoring.

## Context
Towers of Hanoi with 20 disks = 1,048,575 steps.
Need efficient progress queries and display guards.

## Requirements
1. bd mol progress command (summary without listing all steps)
2. Display guards in bd mol current (detect large molecules)
3. Efficient child count queries (already indexed)


Update info.go versionChanges

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

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

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

Focus on workflow-impacting changes agents need to know.


Merge: bd-au0.5

branch: polecat/Searcher
target: main
source_issue: bd-au0.5
rig: beads


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


Fix JSON errors in dep.go (dependency commands)

Replace ~41 direct stderr writes with FatalErrorRespectJSON() in dep.go.

Commands affected:
- depAddCmd
- depRemoveCmd
- depTreeCmd
- depCyclesCmd

Key error paths:
- ID resolution errors
- Cycle detection errors
- External reference validation


Schema: Add message type and new fields

Add to internal/storage/sqlite/schema.go and models:

New issue_type value:
- message

New optional fields on Issue struct:
- Sender string (who sent this)
- Ephemeral bool (can be bulk-deleted)
- RepliesTo string (issue ID for threading)
- RelatesTo []string (issue IDs for knowledge graph)
- Duplicates string (canonical issue ID)
- SupersededBy string (replacement issue ID)

Update:
- internal/storage/sqlite/schema.go - add columns
- internal/models/issue.go - add fields to struct
- internal/storage/sqlite/sqlite.go - CRUD operations
- Create migration from v0.30.1

Ensure backward compatibility - all new fields optional.


Crew worker emma in beads - human-managed persistent workspace.

role_type: crew
rig: beads
agent_state: idle
hook_bead: null
role_bead: gt-crew-role
cleanup_status: null


Add --pretty flag to bd ready

bd list has --pretty but bd ready doesn't. Should support same tree format with status/priority symbols for consistency.


Merge: jasper-1767083473604

branch: polecat/jasper-1767083473604
target: main
source_issue: jasper-1767083473604
rig: beads


Version Bump: 0.30.7

Release checklist for version 0.30.7. This molecule ensures all release steps are completed properly.


Update beads_mcp/__init__.py to 0.30.4

Update __version__ in integrations/beads-mcp/src/beads_mcp/__init__.py:
```python
__version__ = "0.30.4"
```

```verify
grep -q '__version__ = "0.30.4"' integrations/beads-mcp/src/beads_mcp/__init__.py
```


Push release tag

Push the version tag to trigger CI release.

```bash
git push origin v0.41.0
```

This triggers GitHub Actions to build artifacts and publish.


Merge: jade-1767138549967

branch: polecat/jade-1767138549967
target: main
source_issue: jade-1767138549967
rig: beads


Merge: bd-likt

branch: polecat/Gater
target: main
source_issue: bd-likt
rig: beads


Run tests and linting

Run the full test suite and linter:

```bash
TMPDIR=/tmp go test -short ./...
golangci-lint run ./...
```

Fix any failures. Linting warnings acceptable (see LINTING.md).


Work on gt-8tmz.34: Expansion var overrides not implement...

Work on gt-8tmz.34: Expansion var overrides not implemented. The ExpandRule.Vars field exists in internal/formula/types.go but is ignored during expansion in internal/formula/expand.go. Implement: 1) Pass rule.Vars to expandStep in ApplyExpansions, 2) Merge vars with formula defaults (rule.Vars wins), 3) Substitute vars in template placeholders, 4) Add test in expand_test.go. When done, commit and push to main.


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


Create and push tag

git tag v{{version}} && git push origin main && git push origin v{{version}}


Phase 2.4: Git merge driver optimization for TOON format

Optimize git 3-way merge for TOON line-oriented format.

## Overview
TOON is line-oriented (unlike binary formats), enabling smarter git merge strategies. Implement custom merge driver to handle TOON-specific merge patterns.

## Required Work

### 2.4.1 TOON Merge Driver
- [ ] Create .git/info/attributes entry for *.toon files
- [ ] Implement custom merge driver script/command
- [ ] Handle tabular format row merges (line-based 3-way)
- [ ] Handle YAML-style format merges
- [ ] Conflict markers for unsolvable conflicts

### 2.4.2 Merge Patterns
- [ ] Row addition: both branches add different rows → union
- [ ] Row deletion: one branch deletes, other modifies → conflict (manual review)
- [ ] Row modification: concurrent field changes → intelligent merge or conflict
- [ ] Field ordering changes: ignore (TOON format resilient to order)

### 2.4.3 Testing & Documentation
- [ ] Unit tests for merge scenarios (3-way merge logic)
- [ ] Integration tests with actual git merges
- [ ] Conflict scenario testing
- [ ] Documentation of merge strategy

## Success Criteria
- Git merge handles TOON conflicts intelligently
- Fewer manual merge conflicts than JSONL
- Round-trip preserved through merges
- All 70+ tests still passing
- Git history stays clean (minimal conflict markers)


hq- prefix routing fails: '.' resolves relative to cwd, not town root

## Problem
The hq- prefix in routes.jsonl maps to '.' which is interpreted relative to the current working directory, not the town root.

## Reproduction
```bash
cd ~/gt && bd show hq-2cchm    # Works
cd ~/gt/beads/crew/emma && bd show hq-2cchm  # Fails: 'no issue found'
```

## Root Cause
routes.jsonl has: {"prefix": "hq-", "path": "."}

The '.' is resolved relative to cwd, so from crew/emma it looks for ~/gt/beads/crew/emma/.beads/ instead of ~/gt/.beads/

## Fix
The routing logic should resolve '.' as the town root, or the route should use an absolute path or a sentinel like 'TOWN_ROOT'.


bd sync: redirect triggers 'External BEADS_DIR' mode, bypasses sync.branch

## Summary

When `.beads/redirect` points to a location in the same git repo, `bd sync` incorrectly triggers "External BEADS_DIR detected, using direct commit" mode, bypassing the configured `sync.branch` workflow.

## Environment

- beads version: 0.41.0
- Setup: bare repo with worktrees (Gas Town pattern)
- Redirect: `.beads/redirect` contains `../../mayor/rig/.beads`
- Config: `git config beads.sync.branch beads-sync`

## Expected Behavior

With `sync.branch = beads-sync` configured:
1. Export changes to JSONL
2. Commit to beads-sync branch (via worktree at `.git/beads-worktrees/beads-sync`)
3. Push/pull beads-sync
4. Import JSONL
5. **No commits on main or current working branch**

## Actual Behavior

```
$ bd sync
→ Exporting pending changes to JSONL...
→ External BEADS_DIR detected, using direct commit...  ← Wrong!
→ Commits to current branch (mayor-rig)
→ Tries to push to origin/main
```

The "External BEADS_DIR" detection takes precedence over sync.branch config.

## Root Cause (suspected)

In `cmd/bd/sync.go`, the redirect resolution likely marks the BEADS_DIR as "external" because the path crosses directory boundaries, even though:
1. It resolves to the same git repository
2. `sync.branch` is explicitly configured

## Suggested Fix

When determining sync mode, check:
```go
if syncBranch != "" && isInSameGitRepo(resolvedBeadsDir, cwd) {
    // Use sync.branch mode, not direct commit
    useSyncBranch = true
} else if isExternalBeadsDir {
    // Only use direct commit for truly external repos
    useDirectCommit = true
}
```

## Workaround

Created a `mayor-rig` branch that tracks origin/main so git operations succeed, but commits still land on wrong branch.

## Related

- Affects Gas Town multi-clone setups using redirects
- GH#810 was a related sync.branch issue (worktree → local copy direction)


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


Add tests for validation functions

Validation functions like ParseIssueType have 0% coverage. These are critical for ensuring data quality and preventing invalid data from entering the system.


Code Health Review Dec 2025: Technical Debt Cleanup

Epic grouping technical debt identified in the Dec 16, 2025 code health review.

## Overall Health Grade: B (Solid foundation, needs cleanup)

### P1 (High Priority):
- bd-74w1: Consolidate duplicate path-finding utilities
- [deleted:bd-b6xo]: Remove/fix ClearDirtyIssues() race condition
- [deleted:bd-b3og]: Fix TestImportBugIntegration deadlock

### P2 (Medium Priority):
- bd-05a8: Split large files (doctor.go, sync.go)
- bd-qioh: Standardize error handling patterns
- bd-rgyd: Split queries.go (1586 lines)
- bd-9g1z: Fix/remove TestFindJSONLPathDefault

### P3 (Low Priority):
- bd-ork0: Add comments to 30+ ignored errors
- bd-4nqq: Remove dead test code in info_test.go
- bd-dhza: Reduce global state in main.go

## Key Areas:
1. Code duplication in path utilities
2. Large monolithic files (5 files >1000 lines)
3. Global state (25+ variables, 3 deprecated)
4. Silent error suppression (30+ instances)
5. Test gaps and dead test code
6. Atomicity risks in batch operations


Implement core reset package (internal/reset)

Create the core reset logic in internal/reset/ package.

## Responsibilities
- ResetOptions struct with all flag options
- CountImpact() - count issues/tombstones that will be deleted
- ValidateState() - check .beads/ exists, check git dirty state
- ExecuteReset() - main reset logic (without CLI concerns)
- Integrate with daemon killall

## Interface Design
```go
type ResetOptions struct {
    Hard      bool   // Include git operations (git rm, commit)
    Backup    bool   // Create backup before reset
    DryRun    bool   // Preview only, don't execute
    SkipInit  bool   // Don't re-initialize after reset
}

type ResetResult struct {
    IssuesDeleted     int
    TombstonesDeleted int
    BackupPath        string  // if backup was created
    DaemonsKilled     int
}

type ImpactSummary struct {
    IssueCount      int
    OpenCount       int
    ClosedCount     int
    TombstoneCount  int
    HasUncommitted  bool  // git dirty state
}

func Reset(opts ResetOptions) (*ResetResult, error)
func CountImpact() (*ImpactSummary, error)
func ValidateState() error
```

## IMPORTANT: CLI vs Core Separation
- `Force` (skip confirmation) is NOT in ResetOptions - that's a CLI concern
- Core always executes when called; CLI decides whether to prompt first
- Keep CLI-agnostic: no prompts, no colored output, no user interaction
- Return errors for CLI to handle with user-friendly messages
- Unit testable in isolation

## Dependencies
- Uses daemon.KillAllDaemons() from internal/daemon/
- Calls bd init logic after reset (unless SkipInit)


Test work item


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'


mol-beads-release

attached_args: Release v0.38.0 - execute each step in order, verify before proceeding to next

Release checklist for beads version 0.38.0.

This molecule ensures all release steps are completed properly.
Variable: 0.38.0 - target version (e.g., 0.35.0)

## Step: update-release-notes
Update cmd/bd/info.go with release notes for 0.38.0.

Add a new VersionChange entry at the top of versionChanges slice:
```go
{
    Version: "0.38.0",
    Date:    "YYYY-MM-DD",
    Changes: []string{
        "NEW: Feature description",
        "FIX: Bug fix description",
        "IMPROVED: Enhancement description",
    },
},
```

Run `git log --oneline v<previous>..HEAD` to see what changed.

## Step: update-changelog
Update CHANGELOG.md with detailed release notes.

Add a new section after [Unreleased]:
```markdown
## [0.38.0] - YYYY-MM-DD

### Added
- **Feature name** (issue-id) - Description

### Changed
- **Change description** (issue-id)

### Fixed
- **Bug fix** (issue-id) - Description
```

Sort by importance, not chronologically.
Needs: update-release-notes

## Step: bump-version
Run the version bump script.

```bash
./scripts/bump-version.sh 0.38.0
```

This updates version in all files:
- cmd/bd/version.go
- .claude-plugin/*.json
- integrations/beads-mcp/pyproject.toml
- npm-package/package.json
- Hook templates

Needs: update-changelog

## Step: run-tests
Run tests and verify lint passes.

```bash
go test -short ./...
```

CI will run full lint, but fix any obvious issues first.
Needs: bump-version

## Step: commit-release
Commit the release changes.

```bash
git add -A
git commit -m "chore: bump version to v0.38.0"
```

Needs: run-tests

## Step: push-and-tag
Push commit and create release tag.

```bash
git push origin main
git tag v0.38.0
git push origin v0.38.0
```

This triggers GitHub Actions release workflow.
Needs: commit-release

## Step: wait-for-ci
Wait for GitHub Actions to complete.

Monitor: https://github.com/steveyegge/beads/actions

CI will:
- Build binaries via GoReleaser
- Create GitHub Release with assets
- Publish to npm (@beads/bd)
- Publish to PyPI (beads-mcp)
- Update Homebrew tap

Wait until all jobs succeed (~5-10 min).
Needs: push-and-tag

## Step: verify-release
Verify the release is complete.

```bash
# Check GitHub release
gh release view v0.38.0

# Check Homebrew
brew update && brew info steveyegge/beads/bd

# Check npm
npm view @beads/bd version

# Check PyPI
pip index versions beads-mcp
```

Needs: wait-for-ci

## Step: update-local
Update local installations.

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

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

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

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

Verify: `bd --version` shows 0.38.0
Needs: verify-release

## Step: manual-publish
(Optional) Manual publish if CI failed.

```bash
# npm (requires npm login)
./scripts/bump-version.sh 0.38.0 --publish-npm

# PyPI (requires TWINE credentials)
./scripts/bump-version.sh 0.38.0 --publish-pypi

# Or both
./scripts/bump-version.sh 0.38.0 --publish-all
```

Only needed if CI publishing failed.
Needs: wait-for-ci


Add test files for internal/storage

The internal/storage package has no test files at all. This package provides the storage interface abstraction.

Current coverage: N/A (no test files)
Target: Add basic interface tests


Merge: quartz-1767106247671

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


Merge: bd-indn

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


Relocate thanks command to bd info --thanks

## Task
Move `bd thanks` → `bd info --thanks`

## Implementation

### 1. Update info.go
- Add `--thanks` flag
- Import thanks.go display logic
- When --thanks flag set, call thanks display function

### 2. Update thanks.go
- Export the display function (rename to public)
- Remove rootCmd.AddCommand()
- Optionally keep as hidden alias

### 3. Update docs
- README.md if mentioned
- CHANGELOG.md - deprecation notice

## Files to modify
- cmd/bd/info.go
- cmd/bd/thanks.go


Run bump-version.sh 0.32.1

Execute ./scripts/bump-version.sh 0.32.1 to update all version references


Runtime bonding: bd mol attach

Attach a molecule to an already-running workflow.

COMMAND: bd mol attach <epic-id> <proto> [--after <issue-id>]

BEHAVIOR:
- Resolve running epic and proto
- Spawn proto as new subtree
- Wire to specified attachment point (or epic root)
- Handle in-progress issues: new work doesn't block completed work

USE CASES:
- Discovered need for docs while implementing feature
- Hotfix needs attaching to release workflow
- Additional testing scope identified mid-flight

FLAGS:
- --after ISSUE: Specific attachment point within epic
- --type: sequential (default) or parallel
- --var: Variables for the attached proto

CONSIDERATIONS:
- What if epic is already closed? Error or reopen?
- What if attachment point issue is closed? Attach as ready-to-work?


Increase ClaudeStartTimeout for slow startup machines

ClaudeStartTimeout is 15s but Claude can take 30s+ to start on some machines. If Claude takes longer than timeout, GUPP nudges arrive before Claude is ready.

Options:
1. Increase timeout to 60s (simple)
2. Improve WaitForClaudeReady detection (harder - has false positives)
3. Retry nudge if no response within N seconds

Location: internal/constants/constants.go


Fix or remove TestFindJSONLPathDefault (issue #356)

Code health review found .test-skip permanently skips TestFindJSONLPathDefault.

The test references issue #356 about wrong JSONL filename expectations (issues.jsonl vs beads.jsonl).

Test file: internal/beads/beads_test.go

The underlying migration from beads.jsonl to issues.jsonl may be complete, so either:
1. Fix the test expectations
2. Remove the test if no longer needed
3. Document why it remains skipped


Formula parser: YAML schema for .formula.yaml files

Implement formula parsing from YAML:
- Define YAML schema for .formula.yaml files
- Parse steps, compose rules, vars
- Support extends for formula inheritance
- Validate formula structure

Schema should support:
- formula: name
- description: text
- version: number
- type: workflow|expansion|aspect
- vars: variable definitions
- steps: step definitions
- compose: composition rules

Gas Town tracking: gt-8tmz.12


Add mol_type schema field to beads

Add mol_type field to distinguish molecule types (swarm/patrol/work).

## Context
Swarms need to be distinguishable from regular work molecules. The mol_type field enables:
- Filtering: bd list --type=swarm
- Queries: Find all active swarm molecules
- Coordinator logic: Different handling for different mol types

## Implementation
1. Add mol_type column to SQLite schema (nullable, default null = work)
2. Add mol_type to JSONL format
3. Update create command to accept --mol-type
4. Update list/ready commands to filter by mol_type
5. Values: 'swarm', 'patrol', 'work', null (null = work)

## Reference
~/gt/docs/swarm-architecture.md - 'Data Model' section

## Acceptance
- bd create --mol-type=swarm works
- bd list --mol-type=swarm filters correctly
- Existing molecules work unchanged (backward compatible)


Code review findings: mol squash deletion bypasses tombstones


Add integration tests for RPC server and event loops

After adding basic unit tests for daemon utilities, the complex daemon functions still need integration tests:

Core daemon lifecycle:
- startRPCServer: Initializes and starts RPC server with proper error handling
- runEventLoop: Polling-based sync loop with parent monitoring and signal handling
- runDaemonLoop: Main daemon initialization and setup

Health checking:
- isDaemonHealthy: Checks daemon responsiveness and health metrics
- checkDaemonHealth: Periodic health verification

These require more complex test infrastructure (mock RPC, test contexts, signal handling) and should be tackled after the unit test foundation is in place.


Hooks System

Implement hook system for extensibility.

Hook directory: .beads/hooks/
Hook files (executable scripts):
- on_create - runs after bd create
- on_update - runs after bd update
- on_close - runs after bd close
- on_message - runs after bd mail send

Hook invocation:
- Pass issue ID as first argument
- Pass event type as second argument
- Pass JSON issue data on stdin
- Run asynchronously (dont block command)

Example hook (GGT notification):
  #!/bin/bash
  gt notify --event=$2 --issue=$1

This allows GGT to register notification handlers without Beads knowing about GGT.


Fix update-hooks verification in version-bump.yaml

The update-hooks task verification command at version-bump.yaml:358 always succeeds due to '|| echo ...' fallback. Remove the fallback so verification actually fails when hooks aren't installed.


bd find-duplicates - AI-powered duplicate detection

Find semantically duplicate issues.

Approaches:
1. Mechanical: Exact title/description matching
2. Embeddings: Cosine similarity (cheap, scalable)
3. AI: LLM-based semantic comparison (expensive, accurate)

Uses embeddings by default for >100 issues.

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


Commit changes and create v0.30.4 tag

```bash
git add -A
git commit -m "chore: Bump version to 0.30.4"
git tag -a v0.30.4 -m "Release v0.30.4"
```

```verify
git describe --tags --exact-match HEAD 2>/dev/null | grep -q 'v0.30.4'
```


Phase 2.5: TOON-based daemon sync

Implement TOON-native daemon sync (replaces JSONL sync machinery).

## Overview
Daemon sync is the final integration point. Replace export/import/merge machinery with TOON-native sync, building on deletion tracking (2.3) and merge optimization (2.4).

## Required Work

### 2.5.1 TOON-based Daemon Sync
- [ ] Understand current JSONL sync machinery (export.go, import.go, merge.go)
- [ ] Replace export step with TOON encoding (EncodeTOON)
- [ ] Replace import step with TOON decoding (DecodeTOON)
- [ ] Replace merge step with TOON-aware 3-way merge
- [ ] Update daemon auto-sync to read/write TOON
- [ ] Verify 5-second debounce still works

### 2.5.2 Deletion Sync Integration
- [ ] Load deletions.toon during import phase
- [ ] Apply deletions after merging issues
- [ ] Ensure deletion TTL respects daemon schedule

### 2.5.3 Testing
- [ ] Unit tests for daemon sync with TOON
- [ ] Integration tests with actual daemon operations
- [ ] Multi-clone sync scenarios with concurrent edits
- [ ] Performance comparison with JSONL sync
- [ ] Long-running daemon stability tests

## Success Criteria
- Daemon reads/writes TOON format (not JSONL)
- Sync latency comparable to JSONL (<100ms)
- All 70+ tests passing
- bdt commands work seamlessly with daemon
- Multi-clone sync scenarios work correctly


bd mol run: panic slice bounds out of range in mol_run.go:130

## Problem
bd mol run panics after successfully creating the molecule:

```
✓ Molecule running: created 9 issues
  Root issue: gt-i4lo (pinned, in_progress)
  Assignee: stevey

Next steps:
  bd ready                # Find unblocked work in this molecule
panic: runtime error: slice bounds out of range [:8] with length 7

goroutine 1 [running]:
main.runMolRun(0x1014fc0c0, {0x140001e0f80, 0x1, 0x10089daad?})
  /Users/stevey/gt/beads/crew/dave/cmd/bd/mol_run.go:130 +0xc38
```

## Reproduction
```bash
bd --no-daemon mol run gt-lwuu --var issue=gt-test123
```
Where gt-lwuu is a mol-polecat-work proto with 8 child steps.

## Impact
The molecule IS created successfully - the panic happens after creation when formatting the "Next steps" output.


Monitor GoReleaser CI job

Watch the GoReleaser action:
https://github.com/steveyegge/beads/actions/workflows/release.yml

Should complete in ~10 minutes and create:
- GitHub Release with binaries for all platforms
- Checksums and signatures

Check status:
```bash
gh run list --workflow=release.yml -L 1
gh run watch  # to monitor live
```

Verify release exists:
```bash
gh release view v0.30.4
```

```verify
gh release view v0.30.4 --json tagName -q .tagName | grep -q 'v0.30.4'
```


Verify version consistency

Confirm all versions match 0.99.0-test.

```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.99.0-test.


worktree: any bd command is slow

in a git worktree any bd command is slow, with a 2-3s pause before any results are shown. The identical command with `--no-daemon` is near instant.


Fix multi-round convergence for N-way collisions

**Summary:** Multi-round collision resolution was identified as a critical issue preventing complete synchronization across distributed clones. The problem stemmed from incomplete final pulls that didn't fully propagate all changes between system instances.

**Key Decisions:**
- Implement multi-round sync mechanism
- Ensure bounded convergence (≤N rounds)
- Guarantee idempotent import without data loss

**Resolution:** Developed a sync strategy that ensures all clones converge to the same complete set of issues, unblocking the bd-cbed9619 epic and improving distributed system reliability.


Merge: jasper-1767142011373

branch: polecat/jasper-1767142011373
target: main
source_issue: jasper-1767142011373
rig: beads


Upgrade local Homebrew installation

Upgrade bd via Homebrew:

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

**Note**: If `brew upgrade` fails with CLT (Command Line Tools) errors on bleeding-edge macOS versions (e.g., Tahoe 26.x):

```bash
# Reinstall CLT
sudo rm -rf /Library/Developer/CommandLineTools
xcode-select --install
# Wait for GUI installer to complete, then retry brew upgrade
```

Alternative: Skip Homebrew and use go install:
```bash
go install ./cmd/bd
~/go/bin/bd version  # Verify shows {{version}}
```


Implement Step.Condition evaluation in bd cook

## Summary
Implement conditional step execution based on variable values.

## Location
- Schema: `internal/formula/types.go:177-180`
- Implement in: `internal/formula/cook.go` (or new file `condition.go`)

## Current Schema
```go
// Condition makes this step optional based on a variable.
// Format: "{{var}}" (truthy) or "{{var}} == value".
// TODO(bd-7zka): Not yet implemented in bd cook. Filed as future work.
Condition string `json:"condition,omitempty"`
```

## Implementation

### Parsing
Support two formats:
1. `"{{var}}"` - truthy check (non-empty, non-"false", non-"0")
2. `"{{var}} == value"` - equality check

```go
func evaluateCondition(condition string, vars map[string]string) (bool, error) {
    // Check for equality format
    if strings.Contains(condition, "==") {
        parts := strings.SplitN(condition, "==", 2)
        varExpr := strings.TrimSpace(parts[0])
        expected := strings.TrimSpace(parts[1])

        // Extract variable name from {{var}}
        varName := extractVarName(varExpr)
        actual := vars[varName]
        return actual == expected, nil
    }

    // Truthy check
    varName := extractVarName(condition)
    val := vars[varName]
    return val != "" && val != "false" && val != "0", nil
}
```

### Integration with bd cook
In the step processing loop, check condition before creating issue:
```go
if step.Condition != "" {
    include, err := evaluateCondition(step.Condition, vars)
    if err != nil {
        return fmt.Errorf("invalid condition %q: %w", step.Condition, err)
    }
    if !include {
        continue // Skip this step
    }
}
```

## Example Usage
```yaml
steps:
  - title: "Run integration tests"
    condition: "{{run_integration}}"

  - title: "Deploy to staging"
    condition: "{{environment}} == staging"
```

## Testing
- Test truthy evaluation (empty, "false", "0", valid value)
- Test equality check
- Test invalid condition format
- Test missing variable (should be falsy)

## Acceptance Criteria
- [x] Truthy conditions work
- [x] Equality conditions work
- [x] Steps are skipped when condition is false
- [x] Clear error on malformed condition
- [x] Works with variable substitution


Bump version in all files

Run ./scripts/bump-version.sh 0.33.2 to update 10 version files. Then run with --commit after info.go is updated.


Release v0.34.0

Minor version release for beads v0.34.0. This bead serves as my persistent work assignment; the actual release steps are tracked in an attached wisp.


Fix G304 potential file inclusion in cmd/bd/tips.go:259

Linting issue: G304: Potential file inclusion via variable (gosec) at cmd/bd/tips.go:259:18. Error: if data, err := os.ReadFile(settingsPath); err == nil {


Upgrade beads-mcp

pip3 install --upgrade beads-mcp


Fix TestFallbackToDirectModeEnablesFlush deadlock causing 10min test timeout

## Problem

TestFallbackToDirectModeEnablesFlush in direct_mode_test.go deadlocks for 9m59s before timing out, causing the entire test suite to take 10+ minutes instead of <10 seconds.

## Root Cause

Database lock contention between test cleanup and flushToJSONL():
- Test cleanup (line 36) tries to close DB via defer
- flushToJSONL() (line 132) is still accessing DB
- Results in deadlock: database/sql.(*DB).Close() waits for mutex while GetJSONLFileHash() holds it

## Stack Trace Evidence

```
goroutine 512 [sync.Mutex.Lock, 9 minutes]:
database/sql.(*DB).Close(0x14000643790)
  .../database/sql/sql.go:927 +0x84
github.com/steveyegge/beads/cmd/bd.TestFallbackToDirectModeEnablesFlush.func1()
  .../direct_mode_test.go:36 +0xf4

While goroutine running flushToJSONL() holds DB connection via GetJSONLFileHash()
```

## Impact

- Test suite: 10+ minutes → should be <10 seconds
- ALL other tests pass in ~4 seconds
- This ONE test accounts for 99.9% of test runtime

## Related

This is the EXACT same issue documented in MAIN_TEST_REFACTOR_NOTES.md for why main_test.go refactoring was deferred - global state manipulation + DB cleanup = deadlock.

## Fix Approaches

1. **Add proper cleanup sequencing** - stop flush goroutines BEFORE closing DB
2. **Use test-specific DB lifecycle** - ensure flush completes before cleanup
3. **Mock the flush mechanism** - avoid real DB for testing this code path
4. **Add explicit timeout handling** - fail fast with clear error instead of hanging

## Files

- cmd/bd/direct_mode_test.go:36-132
- cmd/bd/autoflush.go:353 (validateJSONLIntegrity)
- cmd/bd/autoflush.go:508 (flushToJSONLWithState)

## Acceptance

- Test passes without timeout
- Test suite completes in <10 seconds
- No deadlock between cleanup and flush operations


Update CHANGELOG.md

Write the [Unreleased] section with all changes for 0.41.0.

Format: Keep a Changelog (https://keepachangelog.com)

Sections:
- ### Added
- ### Changed
- ### Fixed
- ### Documentation

The bump script will stamp the date automatically.


Merge: bd-uqfn

branch: polecat/cheedo
target: main
source_issue: bd-uqfn
rig: beads


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


Add gate timeout tracking and notification

Implement timeout and notification logic for gates.

## Timeout Behavior
1. Gate created with timeout (e.g., 30m)
2. Deacon tracks elapsed time during patrol
3. If timeout reached:
   - Notify all waiters: "Gate timed out"
   - Close gate with timeout reason
   - Waiter can retry, escalate, or fail gracefully

## Notification
- Use gt mail send to notify waiters
- Include gate ID, await type, and reason in message
- Support multiple waiters notification

## Escalation Path
- Witness sees stuck worker, nudges them
- Worker can escalate to human if needed


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.


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


gt handoff deadlock at handoff.go:125

When running 'gt handoff -m "message"' after successful MR submit, go panics with 'fatal error: all goroutines are asleep - deadlock\!' at handoff.go:125. The shutdown request still appears to be sent successfully but the command crashes. Stack trace shows issue is in runHandoff select statement.


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


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)


bd doctor --fix destroys child→parent dependencies without confirmation

GH#740: User reports that `bd doctor --fix` removed their intentional child→parent dependencies, calling them an 'anti-pattern'.

The fix in `cmd/bd/doctor/fix/validation.go` (ChildParentDependencies function) automatically deletes dependencies where a child issue depends on its parent epic.

**Problem**: This assumes all such dependencies are mistakes, but users may intentionally want children blocked by their parent (e.g., 'don't start subtasks until parent is fully scoped').

**Solution options**:
1. Remove this fix entirely (let users manage their own deps)
2. Require explicit opt-in flag like `--fix-child-parent`
3. Add confirmation prompt before destructive action
4. Add `--dry-run` to preview what would be removed
5. Only warn, never auto-fix

**Files involved**:
- `cmd/bd/doctor/validation.go:311-382` (check)
- `cmd/bd/doctor/fix/validation.go:165-227` (fix)


Code Review: PR #481 - Context Engineering Optimizations

Comprehensive code review of the merged context engineering PR (PR #481) that reduces MCP context usage by 80-90%.

## Summary
The PR successfully implements lazy tool schema loading and minimal issue models to reduce context window usage. Overall implementation is solid and well-tested.

## Positive Findings
✅ Well-designed models (IssueMinimal, CompactedResult)
✅ Comprehensive test coverage (28 tests, all passing)
✅ Clear documentation and comments
✅ Backward compatibility preserved (show() still returns full Issue)
✅ Sensible defaults (COMPACTION_THRESHOLD=20, PREVIEW_COUNT=5)
✅ Tool catalog complete with all 15 tools documented

## Issues Identified
See linked issues for specific followup tasks.

## Context Engineering Architecture
- discover_tools(): List tool names only (~500 bytes vs ~15KB)
- get_tool_info(name): Get specific tool details on-demand
- IssueMinimal: Lightweight model for list views (~80 bytes vs ~400 bytes)
- CompactedResult: Auto-compacts results with >20 issues
- _to_minimal(): Conversion function (efficient, no N+1 issues)


Update CHANGELOG.md for 0.33.2

In CHANGELOG.md:

1. Change `## [Unreleased]` section header to `## [0.33.2] - 2025-12-21`
2. Add new empty `## [Unreleased]` section above it
3. Review and clean up the changes list

Format:
```markdown
## [Unreleased]

## [0.33.2] - 2025-12-21

### Added
- ...

### Changed
- ...

### Fixed
- ...
```


enhance 'bd status' to show recent activity

It would be nice to be able to quickly view the last N changes in the database, to see wha's recently been worked on. I'm imagining something like 'bd status activity'.


Swarm: Test epic for duplicate swarm

Swarm molecule orchestrating epic bd-4d9d.

Epic: bd-4d9d
Coordinator:


JSON test issue

Line 1
Line 2
Line 3


bd list --parent: filter by parent issue

Add --parent flag to bd list to filter issues by parent.

Example:
```bash
bd list --parent=gt-h5n --status=open
```

Would show all open children of gt-h5n.

Useful for:
- Checking epic progress
- Finding swarmable work within an epic
- Molecule step listing


Final release verification

Verify all release artifacts are accessible:

- [ ] `bd --version` shows 0.30.4
- [ ] `bd version --daemon` shows 0.30.4
- [ ] GitHub release exists: https://github.com/steveyegge/beads/releases/tag/v0.30.4
- [ ] `brew upgrade beads && bd --version` shows 0.30.4 (if using Homebrew)
- [ ] `pip show beads-mcp` shows 0.30.4
- [ ] npm package available at 0.30.4
- [ ] `bd info --whats-new` shows 0.30.4 notes

Run final checks:
```bash
bd --version
bd version --daemon
pip show beads-mcp | grep Version
bd info --whats-new
```


Restart daemons

Restart bd daemons to pick up new version.

```bash
bd daemons killall
```

Daemons will auto-restart with new version on next bd command.

Verify:
```bash
bd daemons list
```


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.


Full event test


bd import reports success but writes nothing to empty database

## Problem

In a fresh crew clone with no database, `bd import` and `bd sync --import-only` report:
```
Import complete: 0 created, 0 updated, 416 unchanged, 203 skipped
```

But the database remains empty:
```
sqlite3 .beads/beads.db 'SELECT COUNT(*) FROM issues'
0
```

## Reproduction Steps

1. Fresh clone (crew/dave) with .beads/issues.jsonl containing 619 issues
2. `bd init --skip-hooks` creates empty database
3. `bd import -i .beads/issues.jsonl` reports 416 unchanged
4. Database still has 0 issues

## Expected

Import should create 416 issues in the database.

## Root Cause Hypothesis

The 'unchanged' count suggests it's comparing against something (maybe in-memory state?) rather than the actual database. The daemon running from a different clone (mayor/rig) may be confusing the import logic.

## Context

Discovered during v0.39.0 release. Worked around by doing the release manually instead of using the beads-release molecule.


Merge: bd-d28c

branch: polecat/testcat
target: main
source_issue: bd-d28c
rig: beads


🤝 HANDOFF: v0.31.0 released, molecules discussion

Session completed 0.31.0 release and had important molecules discussion.

## Completed
- v0.31.0 released (deferred status, audit trail, directory labels, etc.)
- Fixed lint issues, hook version markers, codesigning
- All CI green, artifacts verified

## Filed Issues
- bd-usro: Rename template instantiate → bd mol bond
- bd-y8bj: Auto-detect identity for bd mail (P1 bug)
- gt-975: Molecule execution support for polecats/crew
- gt-976: Crew lifecycle support in Deacon

## Key Insight
Molecules are the future - TodoWrite is ephemeral, molecules are persistent institutional memory on the world chain. I tried to use TodoWrite for version bump and missed steps (codesigning, MCP verification). Molecules would have caught this.

## Next Steps
- bd mol bond implementation is priority
- Max has gt-976 for crew lifecycle (enables automated refresh mid-molecule)

Check bd ready and gt-975/976 status.


Merge: bd-05a8

branch: polecat/valkyrie
target: main
source_issue: bd-05a8
rig: beads


Merge: quartz-1767082908528

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


Move pour/ephemeral under bd mol subcommand

For consistency, bd pour and bd ephemeral should become bd mol pour and bd mol ephemeral:

Current:
  bd mol list                  # Available protos
  bd mol show <id>             # Proto details
  bd pour <proto>              # Create mol ← sticks out
  bd ephemeral <proto>         # Create ephemeral ← sticks out
  bd mol bond <proto> <parent> # Attach to existing mol
  bd mol squash <id>           # Condense to digest
  bd mol burn <id>             # Discard

Proposed:
  bd mol list
  bd mol show <id>
  bd mol pour <proto>          # Moved under mol
  bd mol ephemeral <proto>     # Moved under mol
  bd mol bond <proto> <parent>
  bd mol squash <id>
  bd mol burn <id>

All molecule operations should be under bd mol for discoverability and consistency.


bd mol bond: Dynamic bond with variable substitution

Implement dynamic molecule bonding with runtime variable substitution.

## Command
```bash
bd mol bond <proto-id> <parent-wisp-id> --var key=value --var key2=value2
```

## Behavior
1. Parse proto molecule template
2. Substitute {{key}} placeholders with provided values
3. Create wisp children under the parent molecule
4. Child IDs follow pattern: parent-id.child-ref (e.g., patrol-x7k.arm-ace)
5. Nested children: parent-id.child-ref.step-ref (e.g., patrol-x7k.arm-ace.capture)

## Variable Substitution
- In step titles: "Inspect {{polecat_name}}" -> "Inspect ace"
- In descriptions: Full template substitution
- In Needs directives: Allow referencing parent steps

## Output
```
✓ Bonded mol-polecat-arm to patrol-x7k
  Created: patrol-x7k.arm-ace (5 steps)
```


Issue lifecycle hooks (on-close, on-complete)

Add hooks that fire on issue state transitions, enabling automation like closing linked GitHub issues.

## Problem

We have `external_ref` to link beads issues to external systems (GitHub, Linear, Jira), but no mechanism to trigger actions when issues close. Currently:

```
bd-u2sc (external_ref: gh-692) closes → nothing happens
```

## Proposed Solution

### Phase 1: Shell Hooks

Add `.beads-hooks/on-close.sh` (and similar lifecycle hooks):

```bash
# .beads-hooks/on-close.sh
# Called by bd close with issue JSON on stdin
#\!/bin/bash
issue=$(cat)
external_ref=$(echo "$issue" | jq -r '.external_ref // empty')
if [[ "$external_ref" == gh-* ]]; then
    number="${external_ref#gh-}"
    gh issue close "$number" --repo steveyegge/beads \
      --comment "Completed via beads epic $(echo $issue | jq -r .id)"
fi
```

### Lifecycle Events

| Event | Trigger | Use Cases |
|-------|---------|-----------|
| `on-close` | Issue closed | Close external refs, notify, archive |
| `on-complete` | Epic children all done | Roll-up completion, close parent refs |
| `on-status-change` | Any status transition | Sync to external systems |

### Phase 2: Molecule Completion Handlers

Molecules could define completion actions:

```yaml
name: github-issue-tracker
on_complete:
  - action: shell
    command: gh issue close {{external_ref}} --repo {{repo}}
  - action: mail
    to: mayor/
    subject: "Epic {{id}} completed"
```

### Phase 3: Gas Town Integration

For full Gas Town deployments:
- Witness observes closures via beads events
- Routes to integration agents via mail
- Agents handle external system interactions

## Implementation Notes

- Hooks should be async (don't block bd close)
- Pass full issue JSON to hook via stdin
- Support hook timeout and failure handling
- Consider `--no-hooks` flag for bulk operations

## Related

- `external_ref` field already exists (GH#142)
- Cross-project deps: bd-h807, bd-d9mu
- Git hooks: .beads-hooks/ pattern established

## Use Cases

1. **GitHub integration**: Close GH issues when beads epic completes
2. **Linear sync**: Update Linear status when beads status changes
3. **Notifications**: Send mail/Slack when high-priority issues close
4. **Audit**: Log all closures to external system


Fix: Empty coordinator shows blank in bd swarm create output

Line 992 prints 'Coordinator: ' even when coordinator is empty string.

Should either:
- Print 'Coordinator: (none)'
- Skip the line entirely when empty

Minor polish issue.


bd mail uses git user.name for sender instead of BEADS_AGENT_NAME

When sending mail via `bd mail send`, the sender field in the stored issue uses git config user.name instead of the BEADS_AGENT_NAME environment variable.

Reproduction:
1. Set BEADS_AGENT_NAME=gastown-alpha
2. Run: bd mail send mayor/ -s 'Test' -m 'Body'
3. Check the issue.jsonl: sender is 'Steve Yegge' (git user.name) not 'gastown-alpha'

Expected: The sender field should use BEADS_AGENT_NAME when set.

This breaks the mail system for multi-agent workflows where agents need to identify themselves by their role (polecat, refinery, etc.) rather than the human user's git identity.

Related: gt mail routing integration with Gas Town


Add bd daemon --stop-all command to kill all daemon processes

Currently there's no easy way to stop all running bd daemon processes. Users must resort to pkill -f 'bd daemon' or similar shell commands.

Add a --stop-all flag to bd daemon that:
1. Finds all running bd daemon processes (not just the current repo's daemon)
2. Gracefully stops them all
3. Reports how many were stopped

This is useful when:
- Multiple daemons are running and causing race conditions
- User wants a clean slate before running bd sync
- Debugging daemon-related issues


Push to main

Push the release commit to origin.

```bash
git push origin main
```

If rejected, someone else pushed. Pull, rebase, try again.


Update git hooks

Install the updated hooks:
```bash
bd hooks install
```

Verify hook version:
```bash
grep 'bd-hooks-version' .git/hooks/pre-commit
```

```verify
grep -q 'bd-hooks-version: 0.30.4' .git/hooks/pre-commit 2>/dev/null || echo 'Hooks may not be installed - verify manually'
```


.beads-ephemeral/ storage backend

Implement ephemeral storage layer for wisps.

## Requirements
- New storage location: .beads-ephemeral/issues.jsonl (sibling to .beads/)
- Gitignored by default (add to .beads/.gitignore)
- Same JSONL format as regular beads
- Config option: ephemeral.directory (relative path)
- ephemeral.enabled config flag

## Storage Behavior
- Ephemeral issues have `ephemeral: true` field
- No sync to remote (local only)
- No daemon tracking needed (transient)

## Implementation
- Add EphemeralStore in storage package
- Initialize on demand when --ephemeral flag used
- Share Issue struct, just different storage path


Improve test coverage for cmd/bd/doctor/fix (23.9% → 50%)

The doctor/fix package has only 23.9% test coverage. The doctor fix functionality is important for troubleshooting.

Current coverage: 23.9%
Target coverage: 50%


Add metadata and thread_id columns to dependencies table

Schema changes:
- ALTER TABLE dependencies ADD COLUMN metadata TEXT DEFAULT '{}'
- ALTER TABLE dependencies ADD COLUMN thread_id TEXT DEFAULT ''
- CREATE INDEX idx_dependencies_thread ON dependencies(thread_id) WHERE thread_id != ''


Code smell: show.go is 1592 lines - needs splitting

cmd/bd/show.go is 1592 lines mixing data fetching, transformation, output formatting, and thread reconstruction.

**Problem:**
- Too large to understand and maintain
- Poor separation of concerns
- Hard to test individual components

**Acceptance Criteria:**
- [ ] Extract formatting into show_formatter.go
- [ ] Extract thread reconstruction into show_threads.go
- [ ] Each file under 500 lines
- [ ] No functionality changes
- [ ] Tests pass


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


Redesign workflow system: templates as native Beads

## Problem

The current workflow system (YAML templates in cmd/bd/templates/workflows/) is architecturally flawed:

1. **Out-of-band data plane** - YAML files are a parallel system outside Beads itself
2. **Heavyweight DSL** - YAML is gross; even TOML would have been better, but neither is ideal
3. **Not graph-native** - Beads IS already a dependency graph with priorities, so why reinvent it?
4. **Can't use bd commands on templates** - They're opaque YAML, not viewable/editable Beads

## The Right Design

**Templates should be Beads themselves.**

A "workflow template" should be:
- An epic marked as a template (via label, type, or prefix like `tpl-`)
- Child issues with dependencies between them (using normal bd dep)
- Titles and descriptions containing `{{variable}}` placeholders
- Normal priorities that control serialization order

"Instantiation" becomes:
1. Clone the template subgraph (epic + children + dependencies)
2. Substitute variables in titles/descriptions
3. Generate new IDs for all cloned issues
4. Return the new epic ID

## Benefits

- **No YAML** - Templates are just Beads
- **Use existing tools** - `bd show`, `bd edit`, `bd dep` work on templates
- **Graph-native** - Dependencies are real Beads dependencies
- **Simpler codebase** - Remove all the YAML parsing/workflow code
- **Composable** - Templates can reference other templates

## Tasks

1. Delete the YAML workflow system code (revert recent push + remove existing workflow code)
2. Design template marking convention (label? type? id prefix?)
3. Implement `bd template create` or `bd clone --as-template`
4. Implement `bd template instantiate <template-id> --var key=value`
5. Migrate version-bump workflow to native Beads template
6. Update documentation


Consider consolidating config.yaml parsing into shared utility

Multiple places parse config.yaml with custom structs:

1. **autoimport.go:148** - `localConfig{SyncBranch}`
2. **main.go:310** - strings.Contains for no-db (fragile, see bd-r6k2)
3. **doctor.go:863** - strings.Contains for no-db (fragile, see bd-r6k2)
4. **internal/config/config.go** - Uses viper (but caches at startup, problematic for tests)

Consider creating a shared utility in `internal/configfile/` or extending the viper config:

```go
// internal/configfile/yaml.go
type YAMLConfig struct {
    SyncBranch string `yaml:"sync-branch"`
    NoDb       bool   `yaml:"no-db"`
    IssuePrefix string `yaml:"issue-prefix"`
    Author     string `yaml:"author"`
}

func LoadYAML(beadsDir string) (*YAMLConfig, error) {
    // Parse config.yaml with proper YAML library
}
```

Benefits:
- Single source of truth for config.yaml structure
- Proper YAML parsing everywhere
- Easier to add new config fields

Trade-off: May add complexity for simple one-off reads.


Test epic for duplicate swarm


Push to main


bd sync --from-main fails: unknown flag --no-git-history


Merge: quartz-1767083470444

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


Cross-rig external dependency support

Support dependencies on issues in other rigs/repos.

## Use Case

Gas Town issues often depend on Beads issues (and vice versa). Currently we use labels like `external:beads/bd-xxx` as documentation, but:
- `bd blocked` doesn't recognize external deps
- `bd ready` doesn't filter them out
- No way to query cross-rig status

## Proposed UX

### Adding external deps
```bash
# New syntax for bd dep add
bd dep add gt-a07f external:beads:bd-kwjh

# Or maybe cleaner
bd dep add gt-a07f --external beads:bd-kwjh
```

### Showing blocked status
```bash
bd blocked
# → gt-a07f blocked by external:beads:bd-kwjh (unverified)

# With optional cross-rig query
bd blocked --resolve-external
# → gt-a07f blocked by external:beads:bd-kwjh (closed) ← unblocked!
```

### Storage
Could use:
- Special dependency type: `type: external`
- Label convention: `external:rig:id`
- New field: `external_deps: ["beads:bd-kwjh"]`

## Implementation Notes

Cross-rig queries would need:
- Known rig locations (config or discovery)
- Read-only beads access to external rigs
- Caching to avoid repeated queries

For MVP, just recognizing external deps and marking them as 'unverified' blockers would be valuable.


Add backwards-compatible aliases and deprecation warnings

## Task
Ensure backwards compatibility by adding hidden aliases for all moved commands.

## Implementation

### Create aliases.go
New file to centralize alias management:
```go
package main

import (
    "fmt"
    "os"

    "github.com/spf13/cobra"
)

// Deprecated command aliases for backwards compatibility
// These will be removed in v0.XX.0

func init() {
    // migrate-* → migrate *
    addDeprecatedAlias("migrate-hash-ids", "migrate hash-ids")
    addDeprecatedAlias("migrate-issues", "migrate issues")
    addDeprecatedAlias("migrate-sync", "migrate sync")
    addDeprecatedAlias("migrate-tombstones", "migrate tombstones")

    // top-level → admin *
    addDeprecatedAlias("cleanup", "admin cleanup")
    addDeprecatedAlias("compact", "admin compact")
    addDeprecatedAlias("reset", "admin reset")

    // top-level → formula *
    addDeprecatedAlias("cook", "formula cook")
}

func addDeprecatedAlias(old, new string) {
    cmd := &cobra.Command{
        Use:    old,
        Hidden: true,
        Run: func(cmd *cobra.Command, args []string) {
            fmt.Fprintf(os.Stderr, "⚠  '%s' is deprecated, use '%s' instead\n", old, new)
            // Forward execution to new command
        },
    }
    rootCmd.AddCommand(cmd)
}
```

### Deprecation behavior
1. First release: Show warning, execute command
2. One release later: Show warning, still execute
3. Third release: Remove aliases entirely

## Files to create
- cmd/bd/aliases.go

## Files to modify
- cmd/bd/main.go (ensure aliases loaded)


Work on beads-2nh: Fix gt spawn --issue to find issues in...

Work on beads-2nh: Fix gt spawn --issue to find issues in rig's beads database. When done, submit MR (not PR) to integration branch for Refinery.


bd create --parent allocates colliding child IDs when child_counters not backfilled

GH#728: When explicit child IDs are created (--id bd-test.1) or imported from JSONL, child_counters table isn't updated. Then --parent auto-allocation starts at 1 and collides.

Fix approach: Update child_counters when creating issues with explicit hierarchical IDs that match <parent>.<n> pattern.


Daemon should be singleton and aggressively kill stale instances

Found 2 bd daemons running (PIDs 76868, 77515) during shutdown. The daemon should:

1. Be a singleton - only one instance per rig allowed
2. On startup, check for existing daemon and kill it before starting
3. Use a PID file or lock file to enforce this

Currently stale daemons can accumulate, causing confusion and resource waste.

**Investigation 2025-12-21:**

The singleton mechanism is already implemented and working correctly:

1. **daemon.lock** uses flock (exclusive non-blocking) to prevent duplicate daemons
2. **bd.sock.startlock** coordinates concurrent auto-starts via O_CREATE|O_EXCL
3. **Registry** tracks all daemons globally in ~/.beads/registry.json

Testing shows:
- Trying to start a second daemon gives: 'Error: daemon already running (PID X)'
- Multiple daemons for *different* rigs is expected/correct behavior

The original report ('Found 2 bd daemons running PIDs 76868, 77515') was likely:
1. Two daemons for different rigs (expected), OR
2. An edge case that's since been fixed

Consider closing as RESOLVED or clarifying the original scenario.


UI: Style 'hooked' status in bd list output

Add styling for 'hooked' status in bd list and bd show output.

## Semantic

'hooked' means actively being worked by an agent (GUPP). Should be visually prominent.

## Suggested Styling

- Color: Cyan or bright blue (active, in-flight)
- Distinct from 'in_progress' (which is more passive/cooperative)

## Files to Update

- internal/ui/styles.go - Add case for 'hooked' status


Add versionChanges to info.go

Add entry at TOP of versionChanges in cmd/bd/info.go


Test issue 2


Version Bump: test-squash

Release checklist for version test-squash. This molecule ensures all release steps are completed properly.


Support --reason flag in daemon mode for reopen command

The reopen.go command has a TODO at line 61 to add reason as a comment once RPC supports AddComment. Currently --reason flag is ignored in daemon mode with a warning.


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.


Create migration script for edge field to dependency conversion

Migration must:
1. Read existing JSONL with old fields
2. Convert field values to dependency records
3. Write updated JSONL without old fields
4. Handle edge cases (missing refs, duplicates)

Run via: bd migrate or automatic on bd prime


GH#405: Prefix parsing with hyphens treats first segment as prefix

Prefix me-py-toolkit gets parsed as just me- when detecting mismatches. Fix prefix parsing to handle multi-hyphen prefixes. See GitHub issue #405.


Release v{{version}}

SUPERSEDED by bd-7bs4. Use the new proto for releases.


Update local binary

cp ./bd ~/.local/bin/bd


Merge: opal-1767106266450

branch: polecat/opal-1767106266450
target: main
source_issue: opal-1767106266450
rig: beads


bd mol update: Check and update installed formulas

Check for newer versions of installed formulas and update them.

## Usage
```bash
bd mol update                    # Update all
bd mol update mol-code-review    # Update specific formula
bd mol update --check            # Just check, don't update
```

## Implementation
1. Read .beads/installed.json
2. For each installed formula:
   - Fetch latest tag from GitHub API
   - Compare with installed version
   - If newer: download, cook, update installed.json
3. Report what was updated

## Flags
- `--check` - Only check for updates, don't install
- `--force` - Reinstall even if up to date
- `--json` - Machine-readable output


Fix update-homebrew.sh awk script corrupts formula

The awk script in scripts/update-homebrew.sh incorrectly removes platform conditionals (on_macos do, on_linux do, if Hardware::CPU.arm?, etc.) when updating SHA256 hashes. This corrupts the Homebrew formula.

The issue is the awk script uses 'next' to skip lines containing platform conditionals but never reconstructs them, resulting in a syntax-invalid formula.

Found during v0.34.0 release - had to manually fix the formula.

Fix options:
1. Rewrite awk script to properly preserve structure while updating sha256 lines only
2. Use sed instead with targeted sha256 replacements
3. Template approach - store formula template and fill in version/hashes


Add bd agent state command for ZFC-compliant state reporting

Add command for agents to write their own state to agent beads.

```bash
bd agent state <agent-id> <state>
# Example: bd agent state gt-mayor running
# States: idle | running | stuck | stopped | dead
```

Implementation:
1. Lookup agent bead by ID (type=agent)
2. Update agent_state field in description
3. Update last_activity timestamp
4. Trigger bd sync if configured

Also add:
- bd agent heartbeat <agent-id> - just updates last_activity
- bd agent show <agent-id> - show agent bead details

This is the ZFC-compliant way for agents to self-report state.

Cross-ref: gt-p2vyo in gastown


Verify the release is complete.

Verify the release is complete.

```bash
# Check GitHub release
gh release view v0.37.0

# Check Homebrew
brew update && brew info steveyegge/beads/bd

# Check npm
npm view @beads/bd version

# Check PyPI
pip index versions beads-mcp
```

instantiated_from: bd-mol-xga
step: verify-release


Ghost resurrection: remote status:closed wins during git merge

During bd sync, the 3-way git merge sometimes keeps remote's status:closed instead of local's status:tombstone. This causes ghost issues to resurrect even when tombstones exist.

Root cause: Git 3-way merge doesn't understand tombstone semantics. If base had closed, local changed to tombstone, and remote has closed, git might keep remote's version.

The early tombstone check in importer.go only prevents CREATION when tombstones exist in DB. But if applyDeletionsFromMerge hard-deletes the tombstones before import runs (because they're not in the merged result), the check doesn't help.

Potential fixes:
1. Make tombstones 'win' in the beads merge driver (internal/merge/merge.go)
2. Don't hard-delete tombstones in applyDeletionsFromMerge if they're in the DB
3. Export tombstones to a separate file that's not subject to merge

Ghost issues: bd-cb64c226.*, bd-cbed9619.*


Split large cmd/bd files: doctor.go (2948 lines), sync.go (2121 lines)

Code health review found several oversized files:

1. doctor.go - 2948 lines, 48 functions mixed together
   - Should split into doctor/checks/*.go for individual diagnostics
   - applyFixes() and previewFixes() are nearly identical

2. sync.go - 2121 lines
   - ZFC (Zero Flush Check) logic embedded inline (lines 213-247)
   - Multiple mode handlers should be extracted

3. init.go - 1732 lines
4. compact.go - 1097 lines
5. show.go - 1069 lines

Recommendation: Extract into focused sub-packages or split into logical files.


Audit and fix JSONL filename mismatches across all repo clones

## Problem

Multiple clones of repos are configured with different JSONL filenames (issues.jsonl vs beads.jsonl), causing:
1. JSONL files to be resurrected after deletion (one clone pushes issues.jsonl, another pushes beads.jsonl)
2. Agents unable to see issues filed by other agents after sync
3. Merge conflicts and data inconsistencies

## Root Cause

When repos were "bd doctored" or initialized at different times, some got issues.jsonl (old default) and others got beads.jsonl (Beads repo specific). These clones push their respective files, creating duplicates.

## Task

Scan all repo clones under ~/src/ (1-2 levels deep) and standardize their JSONL configuration.

### Step 1: Find all beads-enabled repos

```bash
# Find all directories named 'beads' at levels 1-2 under ~/src/
find ~/src -maxdepth 2 -type d -name beads
```

### Step 2: For each repo found, check configuration

For each directory from Step 1, check:
- Does `.beads/metadata.json` exist?
- What is the `jsonl_export` value?
- What JSONL files actually exist in `.beads/`?
- Are there multiple JSONL files (problem!)?

### Step 3: Create audit report

Generate a report showing:
```
Repo Path                           | Config        | Actual Files           | Status
----------------------------------- | ------------- | ---------------------- | --------
~/src/beads                         | beads.jsonl   | beads.jsonl            | OK
~/src/dave/beads                    | issues.jsonl  | issues.jsonl           | MISMATCH
~/src/emma/beads                    | issues.jsonl  | issues.jsonl, beads.jsonl | DUPLICATE!
```

### Step 4: Determine canonical name for each repo

For repos that are the SAME git repository (check `git remote -v`):
- Group them together
- Determine which JSONL filename should be canonical (majority wins, or beads.jsonl for the beads repo itself)
- List which clones need to be updated

### Step 5: Generate fix script

Create a script that for each mismatched clone:
1. Updates `.beads/metadata.json` to use the canonical name
2. If JSONL file needs renaming: `git mv .beads/old.jsonl .beads/new.jsonl`
3. Removes any duplicate JSONL files: `git rm .beads/duplicate.jsonl`
4. Commits the change
5. Syncs: `bd sync`

### Expected Output

1. Audit report showing all repos and their config status
2. List of repos grouped by git remote (same repository)
3. Fix script or manual instructions for standardizing each repo
4. Verification that after fixes, all clones of the same repo use the same JSONL filename

### Edge Cases

- Handle repos without metadata.json (use default discovery)
- Handle repos with no git remote (standalone/local)
- Handle repos that are not git repositories
- Don't modify repos with uncommitted changes (warn instead)

### Success Criteria

- All clones of the same git repository use the same JSONL filename
- No duplicate JSONL files in any repo
- All configurations documented in metadata.json
- bd doctor passes on all repos


Parallel step detection in molecules

Detect and flag parallelizable steps in molecules.

## Detection Rules
Steps can run in parallel when:
1. No Needs dependencies between them
2. Not in same sequential chain
3. Across dynamic arms (arm-ace and arm-nux can parallelize)

## Output in bd mol show
```
patrol-x7k (mol-witness-patrol)
├── inbox-check [completed]
├── survey-workers [completed]
│   ├── arm-ace [parallel group A]
│   │   ├── capture [can parallelize]
│   │   ├── assess [needs: capture]
│   │   └── execute [needs: assess]
│   └── arm-nux [parallel group A]
│       ├── capture [can parallelize]
│       └── ...
├── aggregate [gate: waits for all-children]
```

## Flags
- `bd mol show <id> --parallel` - Highlight parallel opportunities
- `bd ready --mol <id>` - List all steps that can run now

## Future: Parallel Execution Hints
For agents using Task tool subagents:
- Identify independent arms that can run simultaneously
- Suggest parallelization strategy


Wait for CI to pass

Monitor GitHub Actions - all checks must pass before release artifacts are built


Code smell: Long function flushToJSONLWithState (~280 lines)

The flushToJSONLWithState() function in cmd/bd/autoflush.go (lines 490-772) is ~280 lines with:

1. Multiple nested conditionals
2. Inline helper function definitions (recordFailure, recordSuccess)
3. Complex multi-repo filtering logic
4. Mixed concerns (validation, export, error handling)

Consider breaking into:
- validateJSONLIntegrity() - already exists but could absorb more
- getIssuesToExport(fullExport bool) - determines what to export
- mergeWithExistingJSONL(issueMap, dirtyIDs) - handles incremental merge
- filterByPrefix(issues) - multi-repo prefix filtering
- performExport(issues, jsonlPath) - the actual write
- updateMetadataAfterExport() - hash/timestamp updates

Location: cmd/bd/autoflush.go:490-772


bd cleanup --hard should skip tombstone creation for true permanent deletion

## Problem

When using bd cleanup --hard --older-than N --force, the command:
1. Deletes closed issues older than N days (converting them to tombstones with NOW timestamp)
2. Then tries to prune tombstones older than N days (finds none because they were just created)

This leaves the database bloated with fresh tombstones that will not be pruned.

## Expected Behavior

In --hard mode, the deletion should be permanent without creating tombstones, since the user explicitly requested bypassing sync safety.

## Workaround

Manually delete from database: sqlite3 .beads/beads.db 'DELETE FROM issues WHERE status=tombstone'

## Fix Options

1. In --hard mode, use a different delete path that does not create tombstones
2. After deleting, immediately prune the just-created tombstones regardless of age
3. Pass a skip_tombstone flag to the delete operation

Option 1 is cleanest - --hard should mean permanent delete without tombstone.


Test refile v2

Testing improved refile


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


Version Bump: 0.32.1

Release checklist for version 0.32.1. Patch release with MCP output control params and pin field fix.