Review jordanhubbard chaos testing PR #752

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

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

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


Verify release artifacts

Check GitHub releases page - binaries for darwin/linux/windows should be available


Consolidate daemon/daemons commands

Merge daemons functionality into daemon command with subcommands. Currently have both 'daemon' and 'daemons' at top level.


Make merge requests ephemeral wisps instead of permanent issues

## Problem

Merge requests (MRs) are currently created as regular beads issues (type: merge-request). This means they:
- Sync to JSONL and propagate via git
- Accumulate in the issue database indefinitely
- Clutter `bd list` output with closed MRs
- Create permanent records for inherently transient artifacts

MRs are process artifacts, not work products. They exist briefly while code awaits merge, then their purpose is fulfilled. The git merge commit and GitHub PR (if applicable) provide the permanent audit trail - the beads MR is redundant.

## Proposed Solution

Make MRs ephemeral wisps that exist only during the merge process:

1. **Create MRs as wisps**: When a polecat completes work and requests merge, create the MR in `.beads-wisp/` instead of `.beads/`

2. **Refinery visibility**: This works because all clones within a rig share the same database:
   ```
   beads/              ← Rig root
   ├── .beads/         ← Permanent issues (synced to JSONL)
   ├── .beads-wisp/    ← Ephemeral wisps (NOT synced)
   ├── crew/dave/      ← Uses rig's shared DB
   ├── polecats/*/     ← Uses rig's shared DB
   └── refinery/       ← Uses rig's shared DB
   ```
   The refinery can see wisp MRs immediately - same SQLite database.

3. **On merge completion**: Burn the wisp (delete without digest). The git merge commit IS the permanent record. No digest needed since:
   - Digest wouldn't be smaller than the MR itself (~200-300 bytes either way)
   - Git history provides complete audit trail
   - GitHub PR (if used) provides discussion/approval record

4. **On merge rejection/abandonment**: Burn the wisp. Optionally notify the source polecat via mail.

## Benefits

- **Clean JSONL**: MRs never pollute the permanent issue history
- **No accumulation**: Wisps are burned on completion, no cleanup needed
- **Correct semantics**: Wisps are for "operational ephemera" - MRs fit perfectly
- **Reduced sync churn**: Fewer JSONL updates, faster `bd sync`
- **Cleaner queries**: `bd list` shows work items, not process artifacts

## Implementation Notes

### Where MRs are created

Currently MRs are created by the witness or polecat when work is ready for merge. This code needs to:
- Set `wisp: true` on the MR issue
- Or use a dedicated wisp creation path

### Refinery changes

The refinery queries for pending MRs to process. It needs to:
- Query wisp storage as well as (or instead of) permanent storage
- Use `bd mol burn` or equivalent to delete processed MRs

### What about cross-rig MRs?

If an MR needs to be visible outside the rig (e.g., external collaborators):
- They would see the GitHub PR anyway
- Or we could create a permanent "merge completed" notification issue
- But this is likely unnecessary - MRs are internal coordination

### Migration

Existing MRs in permanent storage:
- Can be cleaned up with `bd cleanup` or manual deletion
- Or left to age out naturally
- No migration of open MRs needed (they'll complete under old system

## Alternatives Considered

1. **Auto-cleanup of closed MRs**: Keep MRs as permanent issues but auto-delete after 24h. Simpler but still creates sync churn and temporary JSONL pollution.

2. **MRs as mail only**: Polecat sends mail to refinery with merge details, no MR issue at all. Loses queryability (bd-801b [P2] [merge-request] closed - Merge: bd-bqcc
bd-pvu0 [P2] [merge-request] closed - Merge: bd-4opy
bd-i0rx [P2] [merge-request] closed - Merge: bd-ao0s
bd-u0sb [P2] [merge-request] closed - Merge: bd-uqfn
bd-8e0q [P2] [merge-request] closed - Merge: beads-ocs
bd-hvng [P2] [merge-request] closed - Merge: bd-w193
bd-4sfl [P2] [merge-request] closed - Merge: bd-14ie
bd-sumr [P2] [merge-request] closed - Merge: bd-t4sb
bd-3x9o [P2] [merge-request] closed - Merge: bd-by0d
bd-whgv [P2] [merge-request] closed - Merge: bd-401h
bd-f3ll [P2] [merge-request] closed - Merge: bd-ot0w
bd-fmdy [P3] [merge-request] closed - Merge: bd-kzda).

3. **Separate merge queue**: Refinery maintains internal state for pending merges, not in beads at all. Clean but requires new infrastructure.

Wisps are the cleanest solution - they already exist, have the right semantics, and require minimal changes.

## Related

- Wisp architecture:
- Current MR creation: witness/refinery code paths
- bd-pvu0, bd-801b: Example MRs currently in permanent storage
EOF
)


Run bump-version.sh

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


Run bump-version.sh 0.32.1

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


Beads commands operate on stale database without warning

All beads read operations should validate database is in sync with JSONL before proceeding.

**Current Behavior:**
- Commands can query/read from stale database
- Only mutation operations (like 'bd sync') check if JSONL is newer
- User gets incorrect results without realizing database is out of sync

**Expected Behavior:**
- All beads commands should have pre-flight check for database freshness
- If JSONL is newer than database, refuse to operate with error: "Database out of sync. Run 'bd import' first."
- Same safety check that exists for 'bd sync' should apply to ALL operations

**Impact:**
- Users make decisions based on incomplete/outdated data
- Silent failures lead to confusion (e.g., thinking issues don't exist when they do)
- Similar to running git commands on stale repo without being warned to pull

**Example:**
- Searched for bd-g9eu issue file: not found
- Issue exists in .beads/issues.jsonl (in git)
- Database was stale, but no warning was given
- Led to incorrect conclusion that issue was already closed/deleted

## Implementation Complete

**Phase 1: Created staleness check (cmd/bd/staleness.go)**
- ensureDatabaseFresh() function checks JSONL mtime vs last_import_time
- Returns error with helpful message when database is stale
- Auto-skips in daemon mode (daemon has auto-import)

**Phase 2: Added to all read commands**
- list, show, ready, status, stale, info, duplicates, validate
- Check runs before database queries in direct mode
- Daemon mode already protected via checkAndAutoImportIfStale()

**Phase 3: Code Review Findings**
See follow-up issues:
- bd-XXXX: Add warning when staleness check errors
- bd-YYYY: Improve CheckStaleness error handling
- bd-ZZZZ: Refactor redundant daemon checks (low priority)

**Testing:**
- Build successful: go build ./cmd/bd
- Binary works: ./bd --version
- Ready for manual testing

**Next Steps:**
1. Test with stale database scenario
2. Implement review improvements
3. Close issue when tests pass


Push commit and tag to origin

```bash
git push origin main
git push origin v0.30.4
```

This triggers GitHub Actions:
- GoReleaser build
- PyPI publish
- npm publish

```verify
git ls-remote origin refs/tags/v0.30.4 | grep -q 'v0.30.4'
```


activity --follow: Silent error handling

In activity.go:175-179, when the daemon is down or errors occur during polling in --follow mode, errors are silently ignored:

```go
newEvents, err := fetchMutations(lastPoll)
if err != nil {
    // Daemon might be down, continue trying
    continue
}
```

This means:
- Users won't know if the daemon is unreachable
- Could appear frozen when actually failing
- No indication of lost events

Should at least show a warning after N consecutive failures, or show '...' indicator to show polling status.

Discovered during code review of bd-xo1o implementation.


Phase 1.2 follow-up: Clarify format storage

Phase 1.2 created the bdt executable structure but issues.toon is currently stored in JSONL format, not TOON format.

This is intentional for now:
- Phase 1.2 (bd-jv4w): Just infrastructure - separate binary, separate directory
- Phase 1.3 (bd-j0tr): Implement actual TOON encoding/writing

For now, keep as-is: filename '.toon' signals intent, content is JSONL (interim format). Phase 1.3 will switch to actual TOON.


Restart beads daemon

Kill any running daemons so they pick up the new version:
```bash
bd daemons killall
```

Start fresh daemon:
```bash
bd list  # triggers daemon start
```

Verify daemon version:
```bash
bd version --daemon
```

```verify
bd version --daemon 2>&1 | grep -q '0.30.4'
```


bd cook: waits_for field not preserved in cooked protos

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

## Formula

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

## Expected

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

## Actual

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

## Impact

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

## Related

bd-hr39: needs field not converted to step dependencies


Migrate sort.Slice to slices.SortFunc

Go 1.21+ provides slices.SortFunc which is cleaner and slightly faster than sort.Slice.

Found 15+ instances of sort.Slice in:
- cmd/bd/autoflush.go
- cmd/bd/count.go
- cmd/bd/daemon_sync.go
- cmd/bd/doctor.go
- cmd/bd/export.go
- cmd/bd/import.go
- cmd/bd/integrity.go
- cmd/bd/jira.go
- cmd/bd/list.go
- cmd/bd/migrate_hash_ids.go
- cmd/bd/rename_prefix.go
- cmd/bd/show.go

Example migration:
```go
// Before
sort.Slice(issues, func(i, j int) bool {
    return issues[i].Priority < issues[j].Priority
})

// After
slices.SortFunc(issues, func(a, b *types.Issue) int {
    return cmp.Compare(a.Priority, b.Priority)
})
```

Benefits:
- Cleaner 3-way comparison
- Slightly better performance
- Modern idiomatic Go


Design auto-squash behavior for wisps

Explore the design space for automatic wisp squashing.

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

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

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

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

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

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

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


Add 'bd status' command for issue database overview

Implement a bd status command that provides a quick snapshot of the issue database state, similar to how git status shows working tree state.

Expected output: Show summary including counts by state (open, in-progress, blocked, closed), recent activity (last 7 days), and quick overview without needing multiple queries.

Example output showing issue counts, recent activity stats, and pointer to bd list for details.

Proposed options: --all (show all issues), --assigned (show issues assigned to current user), --json (JSON format output)

Use cases: Quick project health check, onboarding for new contributors, integration with shell prompts or CI/CD, daily standup reference


Add empty tasks validation in workflow create

workflow.go:321 will panic if wf.Tasks is empty. Add validation that len(wf.Tasks) > 0 before accessing wf.Tasks[0].


bd doctor should recommend bd prime migration for existing repos

bd doctor should detect old beads integration patterns and recommend migrating to bd prime approach.

## Current behavior
- bd doctor checks if Claude hooks are installed globally
- Doesn't check project-level integration (AGENTS.md, CLAUDE.md)
- Doesn't recommend migration for repos using old patterns

## Desired behavior
bd doctor should detect and suggest:

1. **Old slash command pattern detected**
   - Check for /beads:* references in AGENTS.md, CLAUDE.md
   - Suggest: These slash commands are deprecated, use bd prime hooks instead

2. **No agent documentation**
   - Check if AGENTS.md or CLAUDE.md exists
   - Suggest: Run 'bd onboard' or 'bd setup claude' to document workflow

3. **Old MCP-only pattern**
   - Check for instructions to use MCP tools but no bd prime hooks
   - Suggest: Add bd prime hooks for better token efficiency

4. **Migration path**
   - Show: 'Run bd setup claude to add SessionStart/PreCompact hooks'
   - Show: 'Update AGENTS.md to reference bd prime instead of slash commands'

## Example output

⚠ Warning: Old beads integration detected in CLAUDE.md
  Found: /beads:* slash command references (deprecated)
  Recommend: Migrate to bd prime hooks for better token efficiency
  Fix: Run 'bd setup claude' and update CLAUDE.md

💡 Tip: bd prime + hooks reduces token usage by 80-99% vs slash commands
  MCP mode: ~50 tokens vs ~10.5k for full MCP scan
  CLI mode: ~1-2k tokens with automatic context recovery

## Benefits
- Helps existing repos adopt new best practices
- Clear migration path for users
- Better token efficiency messaging


Blocking logic: Include 'hooked' in blocking statuses

Update the blocked_cache.go SQL to treat 'hooked' as a blocking status.

## Current Logic (line ~163)

```sql
blocker.status IN ('open', 'in_progress', 'blocked', 'deferred')
```

## Updated Logic

```sql
blocker.status IN ('open', 'in_progress', 'blocked', 'deferred', 'hooked')
```

## Files to Update

- internal/storage/sqlite/blocked_cache.go

## Test Cases

1. Issue A (hooked) blocks Issue B → B should NOT appear in bd ready
2. Issue A (pinned, type=agent) → B should appear in bd ready
3. Issue A (hooked) closed → B should appear in bd ready


Verify npm package

Confirm npm package published.

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

Should show 0.99.0-test.

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


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


Standardize error handling: replace direct fmt.Fprintf+os.Exit with FatalError

Standardize error handling in cmd/bd/ using FatalError pattern.

## Current State
~200+ instances of direct `fmt.Fprintf(os.Stderr, ...) + os.Exit(1)` pattern scattered across cmd/bd/*.go files.

## Target Pattern

Use existing FatalError helper (or create if missing):

```go
// In cmd/bd/helpers.go or similar
func FatalError(format string, args ...interface{}) {
    fmt.Fprintf(os.Stderr, "Error: "+format+"\n", args...)
    os.Exit(1)
}

func FatalErrorf(err error, context string) {
    fmt.Fprintf(os.Stderr, "Error: %s: %v\n", context, err)
    os.Exit(1)
}
```

## Transformation

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

// After
FatalError("%v", err)

// Before
fmt.Fprintf(os.Stderr, "Error: invalid --since duration: %v\n", err)
os.Exit(1)

// After
FatalError("invalid --since duration: %v", err)
```

## Files to Update (by occurrence count)
Run: `grep -c "os.Exit(1)" cmd/bd/*.go | sort -t: -k2 -rn | head -20`

Priority files (highest occurrence):
- close.go
- show.go
- sync.go
- init.go
- list.go
- create.go
- update.go

## Implementation Steps

1. **Check if FatalError exists** in cmd/bd/helpers.go or create it
2. **Create migration script** or use sed:
   ```bash
   # Find patterns to replace
   grep -rn "fmt.Fprintf(os.Stderr.*Error.*\n.*os.Exit(1)" cmd/bd/
   ```
3. **Replace systematically** file by file
4. **Run tests** after each file to verify behavior unchanged
5. **Run linter** to catch any missed patterns

## Verification
```bash
# Count remaining direct exits (should be near zero)
grep -c "os.Exit(1)" cmd/bd/*.go | awk -F: "{sum+=\$2} END {print sum}"

# Run tests
go test -short ./cmd/bd/...
```

## Success Criteria
- All error exits use FatalError/FatalErrorf
- Consistent "Error: " prefix on all error messages
- Tests pass
- No behavior changes (exit codes remain 1)

## Progress (Dec 2025)

Standardized error handling in 3 major files:
- compact.go: All 48 os.Exit(1) calls converted to FatalError
- sync.go: All error patterns converted (kept 1 valid summary exit)
- migrate.go: 4 patterns converted

## Remaining Work
~326 fmt.Fprintf(os.Stderr, "Error:") patterns remain across ~30 files.
High-count files remaining: show.go (16), dep.go (14), gate.go (28), init.go (11).

## Verification
- Build compiles successfully
- Tests pass


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

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

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

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

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


Remove legacy autoflush code paths

## Problem

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

## Locations

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

## Current Behavior

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

## Proposed Fix

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

## Risk

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


Gate eval: Consider rate limiting for GitHub API calls

Each gate evaluation makes a network call to GitHub API via gh CLI. With many gates, this could hit API rate limits.

Future improvements:
1. Batch queries where possible
2. Add caching with TTL for recently-checked gates
3. Exponential backoff on rate limit errors

Low priority - unlikely to be an issue until we have many concurrent gates.


Merge: bd-au0.5

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


JSON test issue

Line 1
Line 2
Line 3


bd repair: Check comments and events tables for orphans

bd repair currently checks dependencies and labels tables, but the schema also has comments and events tables with issue_id foreign keys that could be orphaned.

Add orphan detection and cleanup for:
- comments table (issue_id not in issues)
- events table (issue_id not in issues)


Update info.go versionChanges

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


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


Remove molecule.go (simple instantiation)

molecule.go uses is_template field for simple single-issue cloning. This is too simple for what molecules should be - full DAG orchestration. The use case is covered by bd mol bond with a single-issue molecule. Delete molecule.go and its commands.


Test issue for pin --for

Testing the pin --for flag


mergeDependencies uses union instead of 3-way merge

## Critical Bug

The `mergeDependencies` function in internal/merge/merge.go performs a UNION of left and right dependencies instead of a proper 3-way merge. This causes removed dependencies to be resurrected.

### Root Cause

```go
// Current code (lines 795-816):
func mergeDependencies(left, right []Dependency) []Dependency {
    // Just unions left + right
    // NEVER REMOVES anything
    // Doesn't even look at base!
}
```

And `mergeIssue` (line 579) doesn't pass `base`:
```go
result.Dependencies = mergeDependencies(left.Dependencies, right.Dependencies)
```

### Impact

If:
- Base has dependency D
- Left removes D (intentional)
- Right still has D (stale)

Current: D is in result (resurrection!)
Correct: Left removed it, D should NOT be in result

This breaks Gas Town's workflow and data integrity. Closed means closed.

### Fix

Change `mergeDependencies` to take `base` and do proper 3-way merge:
- If dep was in base and removed by left → exclude (left wins)
- If dep was in base and removed by right → exclude (right wins)
- If dep wasn't in base and added by either → include
- If dep was in base and both still have it → include

Key principle: **REMOVALS ARE AUTHORITATIVE**

### Files to Change

1. internal/merge/merge.go:
   - `mergeDependencies(left, right)` → `mergeDependencies(base, left, right)`
   - `mergeIssue` line 579: pass `base.Dependencies`

### Related

This also explains why `ProtectLocalExportIDs` in importer is defined but never used - the protection was never actually implemented.


Merge: bd-au0.6

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


Add 0.30.4 to info.go release notes

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


Update npm-package/package.json to 0.30.4

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

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


bd sync deleted non-.beads files (skills/beads/*)

On 2025-12-23, commit 7b671662 (bd sync) inadvertently deleted 5 files from skills/beads/ that were added by PR #722 (commit e432fcc8). These files are outside .beads/ and should never be touched by bd sync.

Files deleted:
- skills/beads/README.md
- skills/beads/references/INTEGRATION_PATTERNS.md
- skills/beads/references/MOLECULES.md
- skills/beads/references/PATTERNS.md
- skills/beads/references/TROUBLESHOOTING.md

Investigation needed:
1. How did bd sync stage/commit files outside .beads/?
2. Was this a one-time accident or is there a systematic bug?
3. Add safeguards to prevent bd sync from touching non-.beads paths

Reported via GH#738.


Commit and push release

git add -A && git commit && git push to trigger CI


Merge: obsidian-1767106243818

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


Verify release artifacts

Check GitHub releases page - binaries for darwin/linux/windows should be available


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)


Remove Storage Cache from Server Struct

Eliminate cache fields and use s.storage directly


bd mol publish: Push formula to GitHub repo

Publish a formula to a GitHub repository for sharing.

## Usage
```bash
# Create new repo and publish
bd mol publish mol-my-workflow --repo github.com/myorg/mol-my-workflow

# Update existing repo
bd mol publish mol-my-workflow --repo github.com/myorg/mol-my-workflow --tag v1.1.0
```

## Implementation
1. Export proto to formula.json (if not already a file)
2. Validate formula structure
3. Create GitHub repo if --create flag (uses `gh repo create`)
4. Copy formula.json to repo
5. Generate README.md from formula description
6. Commit and push
7. Create git tag if --tag specified
8. Add mol-formula topic to repo

## Flags
- `--repo <url>` - Target GitHub repo (required)
- `--tag <version>` - Create version tag
- `--create` - Create repo if doesn't exist
- `--private` - Create as private repo

## Workflow
```bash
# Full publish flow
bd mol distill my-epic --name my-workflow
bd mol promote bd-proto-xxx --as my-workflow
bd mol publish mol-my-workflow --repo github.com/me/mol-my-workflow --create --tag v1.0.0
```


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


Merge: bd-likt

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


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


Test Thread

Initial message for threading test


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


Integrate detect-pollution into bd doctor

## Task
Move `bd detect-pollution` → `bd doctor --check=pollution`

## Current state
- detect-pollution already shows deprecation hint pointing to doctor
- Doctor command exists with multiple checks

## Implementation

### 1. Update doctor.go
- Add pollution check to the doctor checks
- Add `--check=pollution` flag option
- Integrate detect-pollution logic

### 2. Update detect_pollution.go
- Mark as hidden (deprecated)
- Forward to doctor --check=pollution
- Keep for one release cycle

### 3. Update docs
- Remove detect-pollution from any command lists
- Update doctor docs to include pollution check

## Files to modify
- cmd/bd/doctor.go
- cmd/bd/detect_pollution.go


Work on beads-d8h: Fix prefix mismatch false positive wit...

Work on beads-d8h: Fix prefix mismatch false positive with multi-hyphen prefixes like 'asianops-audit-' (GH#422). When done, submit MR (not PR) to integration branch for Refinery.


Preflight: Check git status

Ensure working tree is clean before starting release.

```bash
git status
```

If there are uncommitted changes, either:
- Commit them first
- Stash them: `git stash`
- Abort and resolve


Consolidate duplicate path-finding utilities (findJSONLPath, findBeadsDir, findGitRoot)

Code health review found these functions defined in multiple places:

- findJSONLPath() in autoflush.go:45-73 and doctor/fix/migrate.go
- findBeadsDir() in autoimport.go:197-239 (with git worktree handling)
- findGitRoot() in autoimport.go:242-269 (Windows path conversion)

The beads package has public FindBeadsDir() and FindJSONLPath() APIs that should be used consistently.

Impact: Bug fixes need to be applied in multiple places. Git worktree handling may not be replicated everywhere.

Fix: Consolidate all implementations to use the beads package APIs. Remove duplicates.


Performance Validation

Confirm no performance regression from cache removal


Add 'bd where' command to show active beads location

## Problem

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

## Proposed Solution

Add `bd where` command:

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

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

## Alternatives Considered

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

## Desire Paths

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


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

branch: polecat/Hooker
target: main
source_issue: bd-g4b4
rig: beads


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'
```


Improve test coverage for internal/compact (18.2% → 70%)

Improve test coverage for internal/compact package from 17% to 70%.

## Current State
- Coverage: 17.3%
- Files: compactor.go, git.go, haiku.go
- Tests: compactor_test.go (minimal tests)

## Functions Needing Tests

### compactor.go (core compaction)
- [ ] New - needs config validation tests
- [ ] CompactTier1 - needs single issue compaction tests
- [ ] CompactTier1Batch - needs batch processing tests
- [ ] compactSingleWithResult - internal, test via public API

### git.go
- [ ] GetCurrentCommitHash - needs git repo fixture tests

### haiku.go (AI summarization) - MOCK REQUIRED
- [ ] NewHaikuClient - needs API key validation tests
- [ ] SummarizeTier1 - needs mock API response tests
- [ ] callWithRetry - needs retry logic tests
- [ ] isRetryable - needs error classification tests
- [ ] renderTier1Prompt - needs template rendering tests

## Implementation Guide

1. **Mock the Anthropic API:**
   ```go
   // Create mock HTTP server
   server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
       json.NewEncoder(w).Encode(map[string]interface{}{
           "content": []map[string]string{{"text": "Summarized content"}},
       })
   }))
   defer server.Close()

   // Point client at mock
   client.baseURL = server.URL
   ```

2. **Test scenarios:**
   - Successful compaction with AI summary
   - API failure with retry
   - Rate limit handling
   - Empty issue handling
   - Large issue truncation

3. **Use test database:**
   ```go
   store, cleanup := testutil.NewTestStore(t)
   defer cleanup()
   ```

## Success Criteria
- Coverage ≥ 70%
- AI calls properly mocked (no real API calls in tests)
- Retry logic verified
- Error paths covered

## Run Tests
```bash
go test -v -cover ./internal/compact
go test -race ./internal/compact
```


Merge: bd-io8c

branch: polecat/Syncer
target: main
source_issue: bd-io8c
rig: beads


swarm status: Handle external dependencies and error cases

Three issues: (1) Silent error swallowing - GetDependencyRecords errors are silently continued, but analyzeEpicForSwarm returns errors. Inconsistent. (2) External dependencies ignored - if issue depends on something outside epic thats still open, we mark it Ready incorrectly. (3) Command usage string says [epic-id] (optional) but requires exactly 1 arg. File: cmd/bd/swarm.go


Improve bd doctor output formatting for better readability.

## Current State
Doctor output is a wall of text with:
- All checks shown (even passing ones)
- No visual hierarchy
- Hard to spot failures in long output

## Target Output

```
$ bd doctor

bd doctor v0.35.0

Summary: 24 checks passed, 1 warning, 0 errors

─────────────────────────────────────────────────
⚠ Warnings (1)
─────────────────────────────────────────────────

[hooks] Git hooks outdated
  Current version: 0.34.0
  Latest version: 0.35.0
  Fix: bd hooks install

─────────────────────────────────────────────────
✓ Passed (24)  [use --verbose to show details]
─────────────────────────────────────────────────
```

With --verbose:
```
$ bd doctor --verbose

bd doctor v0.35.0

Summary: 24 checks passed, 1 warning, 0 errors

─────────────────────────────────────────────────
⚠ Warnings (1)
─────────────────────────────────────────────────

[hooks] Git hooks outdated
  ...

─────────────────────────────────────────────────
✓ Passed (24)
─────────────────────────────────────────────────

  Database
    ✓ Database exists
    ✓ Database readable
    ✓ Schema up to date

  Git Hooks
    ✓ Pre-commit hook installed
    ✓ Post-merge hook installed
    ⚠ Hooks version mismatch (see above)

  Sync
    ✓ Sync branch configured
    ✓ Remote accessible
    ...
```

## Implementation

### 1. Add check categories (cmd/bd/doctor/categories.go)

```go
type Category string

const (
    CatDatabase  Category = "Database"
    CatHooks     Category = "Git Hooks"
    CatSync      Category = "Sync"
    CatDaemon    Category = "Daemon"
    CatConfig    Category = "Configuration"
    CatIntegrity Category = "Data Integrity"
)

// Assign categories to checks
var checkCategories = map[string]Category{
    "database-exists":     CatDatabase,
    "database-readable":   CatDatabase,
    "schema-version":      CatDatabase,
    "pre-commit-hook":     CatHooks,
    "post-merge-hook":     CatHooks,
    "hooks-version":       CatHooks,
    "sync-branch":         CatSync,
    "remote-access":       CatSync,
    // ... etc
}
```

### 2. Add --verbose flag

```go
// In cmd/bd/doctor.go init()
doctorCmd.Flags().BoolP("verbose", "v", false, "Show all checks including passed")
```

### 3. Create formatter (cmd/bd/doctor/format.go)

```go
type Formatter struct {
    verbose bool
    noColor bool
}

func (f *Formatter) Format(results []CheckResult) string {
    var buf strings.Builder

    // Count by status
    passed, warnings, errors := countByStatus(results)

    // Header
    buf.WriteString(fmt.Sprintf("bd doctor v%s\n\n", version.Version))
    buf.WriteString(fmt.Sprintf("Summary: %d passed, %d warnings, %d errors\n\n",
        passed, warnings, errors))

    // Errors section (always show)
    if errors > 0 {
        f.writeSection(&buf, "✗ Errors", filterByStatus(results, StatusError))
    }

    // Warnings section (always show)
    if warnings > 0 {
        f.writeSection(&buf, "⚠ Warnings", filterByStatus(results, StatusWarning))
    }

    // Passed section (only with --verbose)
    if f.verbose && passed > 0 {
        f.writePassedSection(&buf, filterByStatus(results, StatusPassed))
    } else if passed > 0 {
        buf.WriteString(fmt.Sprintf("✓ Passed (%d)  [use --verbose to show details]\n", passed))
    }

    return buf.String()
}

func (f *Formatter) writeSection(buf *strings.Builder, title string, results []CheckResult) {
    buf.WriteString("─────────────────────────────────────────────────\n")
    buf.WriteString(title + "\n")
    buf.WriteString("─────────────────────────────────────────────────\n\n")

    for _, r := range results {
        buf.WriteString(fmt.Sprintf("[%s] %s\n", r.CheckName, r.Message))
        if r.Details != "" {
            buf.WriteString(fmt.Sprintf("  %s\n", r.Details))
        }
        if r.Fix != "" {
            buf.WriteString(fmt.Sprintf("  Fix: %s\n", r.Fix))
        }
        buf.WriteString("\n")
    }
}

func (f *Formatter) writePassedSection(buf *strings.Builder, results []CheckResult) {
    // Group by category
    byCategory := groupByCategory(results)

    buf.WriteString("─────────────────────────────────────────────────\n")
    buf.WriteString(fmt.Sprintf("✓ Passed (%d)\n", len(results)))
    buf.WriteString("─────────────────────────────────────────────────\n\n")

    for _, cat := range categoryOrder {
        if checks, ok := byCategory[cat]; ok {
            buf.WriteString(fmt.Sprintf("  %s\n", cat))
            for _, r := range checks {
                buf.WriteString(fmt.Sprintf("    ✓ %s\n", r.Message))
            }
            buf.WriteString("\n")
        }
    }
}
```

### 4. Update run function

```go
func runDoctor(cmd *cobra.Command, args []string) {
    verbose, _ := cmd.Flags().GetBool("verbose")
    noColor, _ := cmd.Flags().GetBool("no-color")

    results := runAllChecks()

    formatter := &Formatter{verbose: verbose, noColor: noColor}
    fmt.Print(formatter.Format(results))

    // Exit code based on results
    if hasErrors(results) {
        os.Exit(1)
    }
}
```

## Files to Modify

1. **cmd/bd/doctor.go** - Add --verbose flag, update run function
2. **cmd/bd/doctor/format.go** - New file for formatting logic
3. **cmd/bd/doctor/categories.go** - New file for check categorization
4. **cmd/bd/doctor/common.go** - Add Status field to CheckResult if missing

## Testing

```bash
# Default output (concise)
bd doctor

# Verbose output
bd doctor --verbose

# JSON output (should still work)
bd doctor --json
```

## Success Criteria
- Summary line at top with counts
- Only failures/warnings shown by default
- --verbose shows grouped passed checks
- Visual separators between sections
- Exit code 1 if errors, 0 otherwise


bd mol current: soft cursor showing current/next step

Add bd mol current command for molecule navigation orientation.

## Usage

bd mol current [mol-id]

If mol-id given, show status for that molecule.
If not given, infer from in_progress issues assigned to current agent.

## Output

You're working on molecule gt-abc (Feature X)

  [done] gt-abc.1: Design
  [done] gt-abc.2: Scaffold
  [done] gt-abc.3: Implement
  [current] gt-abc.4: Write tests [in_progress] <- YOU ARE HERE
  [pending] gt-abc.5: Documentation
  [pending] gt-abc.6: Exit decision

Progress: 3/6 steps complete

## Key behaviors
- Shows full molecule structure with status indicators
- Highlights current in_progress step
- If no in_progress, highlights first ready step
- Works without explicit cursor tracking (inferred from state)

## Implementation notes
- Query children of mol-id
- Sort by dependency order
- Find first in_progress or first ready
- Format with status indicators

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


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


Improve version bump molecule with missing steps

During v0.32.1 release, discovered missing steps in the release molecule:

**Missing from molecule:**
1. Rebuild ~/go/bin/bd (only did ~/.local/bin/bd)
2. Install beads-mcp from local source: `uv tool install --reinstall ./integrations/beads-mcp`
3. Restart daemons with `bd daemons killall`
4. (Optional) Publish beads-mcp to PyPI

**Current molecule steps (bd-6s61):**
1. Update CHANGELOG.md
2. Update info.go versionChanges
3. Run bump-version.sh
4. Run tests and linting
5. Update local installation
6. Commit and push release
7. Wait for CI
8. Verify release artifacts

**Proposed additions:**
- After "Update local installation": rebuild BOTH ~/.local/bin/bd AND ~/go/bin/bd
- Add: "Install beads-mcp from source" step
- Add: "Restart daemons" step
- Add: "Verify all versions match" step that checks all artifacts

**Also learned:**
- Must run from mayor/rig to avoid git conflicts with bd sync (already documented in bump-version.sh)

CORRECTION: npm publishing IS automated and working!

**Package naming:**
- OLD: `beads` (npm) - deprecated, stuck at 0.2.1
- CURRENT: `@beads/bd` (npm) - scoped package, auto-published by CI

**How it works:**
- CI uses OIDC trusted publishing (no token needed)
- Workflow: .github/workflows/release.yml → publish-npm job
- Permissions: `id-token: write` enables GitHub OIDC
- To install: `npm install -g @beads/bd` (not `npm install beads`)

**All publishing is automated on tag push:**
1. GitHub Release - goreleaser ✓
2. PyPI - publish-pypi job ✓
3. Homebrew - update-homebrew job ✓
4. npm (@beads/bd) - publish-npm job ✓

**Remaining molecule improvements (local steps only):**
- Rebuild BOTH ~/.local/bin/bd AND ~/go/bin/bd
- Install beads-mcp from source: `uv tool install --reinstall ./integrations/beads-mcp`
- Restart daemons: `bd daemons killall`
- Run from mayor/rig to avoid git conflicts with bd sync
- Final verification step to check all local versions match


Witness Patrol

Per-rig worker monitor patrol loop with progressive nudging.


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.


Preflight: Pull latest

Ensure we're up to date with origin.

```bash
git pull --rebase
```

Resolve any conflicts before proceeding.


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


GetBlockedIssues shows external deps as blocking even when satisfied

GetBlockedIssues (ready.go:385-493) shows external:* refs in the blocked_by list but doesn't check if they're actually satisfied using CheckExternalDep.

This can be confusing - an issue shows as blocked by external:project:capability even if that capability has been shipped (closed issue with provides: label exists).

Options:
1. Call CheckExternalDep for each external ref and filter satisfied ones from blocked_by
2. Add a note in output indicating external deps need lazy resolution
3. Document this is expected behavior (bd blocked shows all deps, bd ready shows resolved state)

Related: GetReadyWork correctly filters by external deps, but GetBlockedIssues doesn't.


Update version to 0.33.2 in version.go

Edit cmd/bd/version.go line 17:

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

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


bd formula add: Import formula to local catalog

Import a formula file to the local catalog (search path).

**Replaces**: "bd mol promote" (proto-to-proto concept is obsolete with ephemeral protos)

## Usage
```bash
# Add a formula file to project catalog
bd formula add my-workflow.formula.json

# Add to user-level catalog
bd formula add my-workflow.formula.json --scope user

# Add from URL
bd formula add https://example.com/workflow.formula.json
```

## Implementation
1. Parse the formula file (validate JSON structure)
2. Determine target directory based on scope:
   - project: .beads/formulas/
   - user: ~/.beads/formulas/
   - town: ~/gt/.beads/formulas/
3. Copy/download formula to target
4. Verify it is loadable: bd formula show <name>

## Flags
- `--scope <level>` - Where to add (project|user|town, default: project)
- `--name <name>` - Override formula name (default: from file)

## Note
This is for manually adding formulas. For GitHub-hosted formulas, use:
  bd mol install github.com/org/formula-name


Test suite hangs/never finishes

Running 'go test ./... -count=1' hangs indefinitely. The full test suite never completes, making it difficult to verify changes. Need to investigate which tests are hanging and fix or add timeouts.


Phase 2.2: Switch bdt storage to TOON format

Currently bdt stores issues in JSONL format in issues.toon file. Phase 2.2 must implement actual TOON format storage - this is the fundamental goal of the bdtoon project.

## Current State (Phase 2.1)
- issues.toon stores JSONL (intermediate format)
- --toon flag allows output in TOON format for LLM consumption
- Problem: We're not actually using TOON as the fundamental storage format

## Required Work (Phase 2.2)
1. Switch issue file I/O to write TOON format instead of JSONL
   - Update cmd/bdt/storage.go to use EncodeTOON for writing
   - Update cmd/bdt/storage.go to decode TOON (currently decodes JSON)
   - Ensure round-trip: write TOON → read TOON → write TOON is byte-identical

2. Update command implementations
   - cmd/bdt/create.go: Write newly created issues to TOON format
   - cmd/bdt/list.go: Read issues from TOON format
   - cmd/bdt/show.go: Read from TOON format
   - cmd/bdt/import.go: Convert imported JSONL to TOON
   - cmd/bdt/export.go: Export TOON to JSONL (for bd compatibility)

3. Implement TOON parser that handles gotoon's encoder-only limitation
   - Since gotoon doesn't decode TOON, need custom TOON→JSON decoder
   - OR continue storing TOON but decoding via intermediate JSON conversion

4. Git merge driver optimization
   - TOON is line-oriented, better for 3-way merges than binary formats
   - Configure git merge driver for .toon files

5. Comprehensive testing
   - Round-trip tests: Issue → TOON → storage → read → Issue
   - Merge conflict resolution tests with TOON format
   - Large issue set performance tests

## Success Criteria
- issues.toon stores actual TOON format (not JSONL)
- bdt list reads from TOON file
- bdt create writes to TOON file
- Round-trip: create issue → list → show returns identical data
- All 65+ tests still passing
- Performance comparable to JSONL storage


Give --all flag actual meaning in bd list

The --all flag exists but is currently a no-op ('flag provided for CLI familiarity'). Once we change the default to non-closed, --all should override to show all issues including closed.

This provides backwards compatibility for scripts that depend on seeing all issues.

## Implementation Plan

### Code Changes (cmd/bd/list.go)

1. **Update --all flag handling** (~line 889):
```go
// Currently: listCmd.Flags().Bool("all", false, "Show all issues (default behavior; flag provided for CLI familiarity)")
// Change to:
listCmd.Flags().Bool("all", false, "Show all issues including closed (overrides default filter)")
```

2. **Check for --all in Run function** (before applying default filter):
```go
allFlag, _ := cmd.Flags().GetBool("all")
if status == "" && !allFlag {
    // Apply default non-closed filter
    filter.ExcludeStatus = []types.Status{"closed"}
}
// If --all is set, no ExcludeStatus applied = show everything
```

### Testing
- `bd list --all` shows all issues including closed
- `bd list` shows non-closed (from bd-mypl)
- `bd list --all --status=open` - --all is ignored when explicit status given

### Documentation
- Update --help text
- Add to migration guide: "Use --all for previous default behavior"


gt mail send: support reading message body from stdin

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

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

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

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


Wait for CI to pass

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


Swarm: Code Review: Swarm commands consolidation

Swarm molecule orchestrating epic bd-2ubv.

Epic: bd-2ubv
Coordinator:


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


Consolidate git context into single cached struct

Code review finding from bd-7di fix.

Currently there are 3 separate caches:
- isWorktreeOnce/isWorktreeResult
- mainRepoRootOnce/mainRepoRootResult/mainRepoRootErr
- repoRootOnce/repoRootResult

These have redundant git calls - e.g. both isWorktreeUncached() and getMainRepoRootUncached() call getGitDirNoError("--git-common-dir").

Could consolidate into a single cached struct:
```go
type gitContext struct {
    gitDir     string
    commonDir  string
    repoRoot   string
    isWorktree bool
}
```

This would reduce git calls further and simplify ResetCaches().

File: internal/git/gitdir.go


Add bd reset command for clean slate restart

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

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

## Design

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

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

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

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

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

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


Swarm: 13 beads backlog issues for polecat execution

## Swarm Overview

13 issues prepared for parallel polecat execution. All issues have been enhanced with concrete implementation guidance, file lists, and success criteria.

## Issue List

### Bug (1) - HIGH PRIORITY
| ID | Priority | Title |
|----|----------|-------|
| bd-phtv | P1 | Pinned field overwritten by subsequent commands |

### Test Coverage (3)
| ID | Package | Target |
|----|---------|--------|
| bd-io8c | internal/syncbranch | 27% → 70% |
| bd-thgk | internal/compact | 17% → 70% |
| bd-tvu3 | internal/beads | 48% → 70% |

### Code Quality (3)
| ID | Task |
|----|------|
| bd-qioh | FatalError pattern standardization |
| bd-rgyd | Split queries.go (1704 lines → 5 files) |
| bd-u2sc.3 | Split cmd/bd files (sync/init/show/compact) |

### Features (4)
| ID | Task |
|----|------|
| bd-au0.5 | Search date/priority filters |
| bd-ykd9 | Doctor --fix auto-repair |
| bd-g4b4 | Close hooks system |
| bd-likt | Gate daemon RPC |

### Polish (2)
| ID | Task |
|----|------|
| bd-4qfb | Doctor output formatting |
| bd-u2sc.4 | slog structured logging |

## Issue Details

All issues have been enhanced with:
- Concrete file lists to modify
- Code snippets and patterns
- Success criteria
- Test commands

Run `bd show <id>` for full details on any issue.

## Execution Notes

- All issues are independent (no blockers between them)
- bd-phtv (P1 bug) should get priority - affects bd pin functionality
- Test coverage tasks are straightforward but time-consuming
- File split tasks (bd-rgyd, bd-u2sc.3) are mechanical but important

## Completed During Prep

- bd-ucgz (P2 bug) - Fixed inline: external deps orphan check (commit f2db0a1d)
- Moved 5 gastown issues out of beads backlog (gt-dh65, gt-ng6g, gt-fqcz, gt-gswn, gt-rw2z)
- Deferred 4 premature/post-1.0 issues
- Closed bd-udsi epic (core implementation complete)


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

Linting issue: checkHooksQuick - path is unused (unparam) at cmd/bd/doctor.go:541:22. Error: func checkHooksQuick(path string) string {


Test body-file

This is a test description from a file.

It has multiple lines.


Fix daemon/direct mode inconsistency in relate and duplicate commands

The relate.go and duplicate.go commands have inconsistent daemon/direct mode handling:

When daemonClient is connected, they resolve IDs via RPC but then perform updates directly via store.UpdateIssue(), bypassing the daemon.

Affected locations:
- relate.go:125-139 (runRelate update)
- relate.go:235-246 (runUnrelate update)
- duplicate.go:120 (runDuplicate update)
- duplicate.go:207 (runSupersede update)

Should either use RPC for updates when daemon is running, or document why direct access is intentional.


Add daemon RPC endpoints for config and stale check

Two TODOs request daemon RPC endpoints:

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

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

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


Verify GitHub release

Check the GitHub releases page.

https://github.com/steveyegge/beads/releases/tag/v0.41.0

Verify:
- Release created
- Binaries attached (linux, darwin, windows)
- Checksums present


Audit Current Cache Usage

**Summary:** Comprehensive audit of storage cache usage revealed minimal dependency across server components, with most calls following a consistent pattern. Investigation confirmed cache was largely unnecessary in single-repository daemon architecture.

**Key Decisions:**
- Remove all cache-related environment variables
- Delete server struct cache management fields
- Eliminate cache-specific test files
- Deprecate req.Cwd routing logic

**Resolution:** Cache system will be completely removed, simplifying server storage access and reducing unnecessary complexity with negligible performance impact.

AUDIT COMPLETE

getStorageForRequest() callers: 17 production + 11 test
- server_issues_epics.go: 8 calls
- server_labels_deps_comments.go: 4 calls
- server_export_import_auto.go: 2 calls
- server_compact.go: 2 calls
- server_routing_validation_diagnostics.go: 1 call
- server_eviction_test.go: 11 calls (DELETE entire file)

Pattern everywhere: store, err := s.getStorageForRequest(req) → store := s.storage

req.Cwd usage: Only for multi-repo routing. Local daemon always serves 1 repo, so routing is unused.

MCP server: Uses separate daemons per repo (no req.Cwd usage found). NOT affected by cache removal.

Cache env vars to deprecate:
- BEADS_DAEMON_MAX_CACHE_SIZE (used in server_core.go:63)
- BEADS_DAEMON_CACHE_TTL (used in server_core.go:72)
- BEADS_DAEMON_MEMORY_THRESHOLD_MB (used in server_cache_storage.go:47)

Server struct fields to remove:
- storageCache, cacheMu, maxCacheSize, cacheTTL, cleanupTicker, cacheHits, cacheMisses

Tests to delete:
- server_eviction_test.go (entire file - 9 tests)
- limits_test.go cache assertions

Special consideration: ValidateDatabase endpoint uses findDatabaseForCwd() outside cache. Verify if used, then remove or inline.

Safe to proceed with removal - cache always had 1 entry in local daemon model.


Fix JSON errors in label.go (label commands)

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

Commands affected:
- labelAddCmd
- labelRemoveCmd
- labelListCmd
- labelListAllCmd


Test work item


Edge Schema Consolidation: Unify all edges in dependencies table

Consolidate all edge types into the dependency table per decision 004.

## Changes
- Add metadata column to dependencies table
- Add thread_id column for conversation grouping
- Remove redundant Issue fields: replies_to, relates_to, duplicate_of, superseded_by
- Update all code to use dependencies API
- Migration script for existing data
- JSONL format change (breaking)

Reference: ~/gt/hop/decisions/004-edge-schema-consolidation.md


Update cmd/bd/info.go with release notes for 0.37.0.

Add a new VersionChange entry at the top of versionChanges slice:
```go
{
    Version: "0.37.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.

instantiated_from: bd-mol-xga
step: update-release-notes


test pin issue


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?


Unify template.go and mol.go under bd mol

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


Reduce global state in cmd/bd/main.go (25+ variables)

Code health review found main.go has 25+ global variables (lines 57-112):

- dbPath, actor, store, jsonOutput, daemonClient, noDaemon
- rootCtx, rootCancel, autoFlushEnabled
- isDirty (marked 'USED BY LEGACY CODE')
- needsFullExport (marked 'USED BY LEGACY CODE')
- flushTimer (marked 'DEPRECATED')
- flushMutex, storeMutex, storeActive
- flushFailureCount, lastFlushError, flushManager
- skipFinalFlush, autoImportEnabled
- versionUpgradeDetected, previousVersion, upgradeAcknowledged

Impact:
- Hard to test individual commands
- Race conditions possible
- State leakage between commands

Fix: Move toward dependency injection. Remove deprecated variables. Consider cmd/bd/internal package.

COMPLETED: Migration of legacy flush state to FlushManager. The main work (isDirty, needsFullExport, flushTimer) moved to local vars in FlushManager.run() lines 170-173. This fixes the race conditions. Remaining globals are: Cobra flag bindings (standard), runtime state (store, daemonClient - structural), store coordination (storeActive/storeMutex). Future: move error tracking to FlushManager, create cmd/bd/internal for encapsulation.


Update Metrics and Health Endpoints

Remove cache-related metrics from health/metrics endpoints


Merge: bd-w193

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


Consider caching external dep resolution results

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

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

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

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


Test deleteViaDaemon RPC client integration

Add comprehensive tests for the deleteViaDaemon function (cmd/bd/delete.go:21) which handles client-side RPC deletion calls.

## Function under test
- deleteViaDaemon: CLI command handler that sends delete requests to daemon via RPC

## Test scenarios needed
1. Successful deletion via daemon
2. Cascade deletion through daemon
3. Force deletion through daemon
4. Dry-run mode (no actual deletion)
5. Error handling:
   - Daemon unavailable
   - Invalid issue IDs
   - Dependency conflicts
6. JSON output validation
7. Human-readable output formatting

## Coverage target
Current: 0%
Target: >80%

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


Merge: bd-t4sb

branch: polecat/capable
target: main
source_issue: bd-t4sb
rig: beads


Cache GetGitDir() for additional optimization

Code review finding from bd-7di fix.

GetGitDir() at line 17 is not cached but is called by:
- GetGitHooksDir()
- GetGitRefsDir()
- GetGitHeadPath()

If any of those are called multiple times, we still spawn extra git processes. Consider adding sync.Once caching similar to the other functions.

File: internal/git/gitdir.go


Clean up orphaned child issues (bd-cb64c226.*, bd-cbed9619.*)

## Problem

Every bd command shows warnings about 12 orphaned child issues:
- bd-cb64c226.1, .6, .8, .9, .10, .12, .13
- bd-cbed9619.1, .2, .3, .4, .5

These are hierarchical IDs (parent.child format) where the parent issues no longer exist.

## Impact

- Clutters output of every bd command
- Confusing for users
- Indicates incomplete cleanup of deleted parent issues

## Proposed Solution

1. Delete the orphaned issues since their parents no longer exist:
   ```bash
   bd delete bd-cb64c226.1 bd-cb64c226.6 bd-cb64c226.8 ...
   ```

2. Or convert them to top-level issues if they contain useful content

## Investigation Needed

- What were the parent issues bd-cb64c226 and bd-cbed9619?
- Why were they deleted without their children?
- Should bd delete cascade to children automatically?


Add bd mol run command (bond + assign + pin)

bd mol run = bond + assign root to caller + pin to startup mail. This is the Gas Town integration point. When agent restarts, check startup mail, find pinned molecule root, query bd ready for next step. Makes molecules immortal.


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.


bd wisp gc command

Add bd wisp gc command to garbage collect orphaned wisps.

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

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

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

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


MCP plugin: _find_beads_db_in_tree() should follow .beads/redirect files

## Problem
The `_find_beads_db_in_tree()` function in `integrations/beads-mcp/src/beads_mcp/tools.py` does not follow `.beads/redirect` files.

## Impact
MCP beads tools fail from polecat/crew directories that use redirect files to share a beads database.

## Root Cause
When auto-detecting workspace, the MCP plugin looks for `.beads/*.db` files directly but doesn't check for or follow `redirect` files:

```python
beads_dir = os.path.join(current, '.beads')
if os.path.isdir(beads_dir):
    db_files = glob.glob(os.path.join(beads_dir, '*.db'))  # Only looks for .db files
    valid_dbs = [f for f in db_files if '.backup' not in os.path.basename(f)]
    if valid_dbs:
        return current
```

Polecat directories have `.beads/redirect` files (e.g., containing `../../mayor/rig/.beads`) but no `.db` files, so `_find_beads_db_in_tree()` doesn't find them.

## Expected Behavior
The function should:
1. Check for `.beads/redirect` file first
2. If found, follow the redirect to the actual beads location
3. Verify the redirected location has a valid database

## Reference
The Go implementation handles this correctly in `ResolveBeadsDir()` - see gastown `internal/beads/beads.go:23-60`.

## Related
- gt-tnw: MCP beads tools fail from polecat directories


Merge: bd-indn

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


Release v0.30.4

## Version Bump Workflow

Coordinating release from 0.30.3 to 0.30.4.

### Components Updated
- Go CLI (cmd/bd/version.go)
- Claude Plugin (.claude-plugin/*.json)
- MCP Server (integrations/beads-mcp/)
- npm Package (npm-package/package.json)
- Git hooks (cmd/bd/templates/hooks/)

### Release Channels
- GitHub Releases (GoReleaser)
- PyPI (beads-mcp)
- npm (@beads/cli)
- Homebrew (homebrew-beads tap)


Add composite index (status, priority) for common list queries

SearchIssues and GetReadyWork frequently filter by status and sort by priority. Currently uses two separate indexes.

**Common query pattern (queries.go:1646-1647):**
```sql
WHERE status = ?
ORDER BY priority ASC, created_at DESC
```

**Problem:** Index merge or full scan when both columns are used.

**Solution:** Add migration:
```sql
CREATE INDEX IF NOT EXISTS idx_issues_status_priority ON issues(status, priority);
```

**Expected impact:** Faster bd list, bd ready with filters. Particularly noticeable at 10K+ issues.


Test Agent


Agent beads: structured labels for filtering

## Agent Beads Need Queryable Labels

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

## Current State

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

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

## Required

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

## Where to Fix

gt polecat/crew/agent creation commands should add labels:
```bash
bd create --type=agent ... --labels="role_type:witness,rig:gastown"
```

## Queries Enabled

```bash
bd list --type=agent --label=role_type:witness    # All witnesses
bd list --type=agent --label=rig:gastown          # All gastown agents
bd list --type=agent --label=role_type:dog        # All dogs
```

## Acceptance
- New agent beads created with role_type/rig labels
- Existing agent beads backfilled (one-time script)
- @group patterns work in gt mail router


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}}
```


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


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

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

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

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

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


Work on beads-tip: Fix broken Claude integration link in ...

Work on beads-tip: Fix broken Claude integration link in bd doctor (GH#623). Update URL that doesn't exist. When done, submit MR (not PR) to integration branch for Refinery.


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.


Fix resolvePartialID stub in workflow.go

The resolvePartialID function at workflow.go:921-925 is a stub that just returns the ID unchanged. Should use utils.ResolvePartialID for proper partial ID resolution in direct mode (non-daemon).


Preflight: Pull latest

Ensure we're up to date with origin.

```bash
git pull --rebase
```

Resolve any conflicts before proceeding.


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


Improve test coverage for cmd/bd/setup (28.4% → 50%)

The setup package has only 28.4% test coverage. Setup commands are critical for first-time user experience.

Current coverage: 28.4%
Target coverage: 50%


Implement `bd setup cursor` for Cursor IDE integration

Create a `bd setup cursor` command that integrates Beads workflow into Cursor IDE via .cursorrules file. Unlike Claude Code (which has hooks), Cursor uses a static rules file to provide context to its AI.


Merge: bd-bqcc

branch: polecat/capable
target: main
source_issue: bd-bqcc
rig: beads


Add close_reason field to CLI schema and documentation

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

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

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

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


Test hook


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

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

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

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

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

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


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


Fix wisp leak in nodb mode writeIssuesToJSONL

writeIssuesToJSONL in nodb.go calls writeJSONLAtomic without filtering wisps. This means any wisps created during a nodb session would be written to issues.jsonl, leaking ephemeral data into the git-tracked file.

Fix: Add the same wisp filter pattern used in sync_export.go, autoflush.go, and export.go:

```go
// Filter out wisps before writing
filtered := make([]*types.Issue, 0, len(issues))
for _, issue := range issues {
    if !issue.Wisp {
        filtered = append(filtered, issue)
    }
}
issues = filtered
```

Location: cmd/bd/nodb.go:writeIssuesToJSONL()


Update CHANGELOG.md

Write the [Unreleased] section with all changes for 0.99.0-test.

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

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

The bump script will stamp the date automatically.


bd mol burn should work on mols, not just wisps

Currently `bd mol burn` only works on wisps (Ephemeral=true):

```
$ bd mol burn bd-mol-p7i
Error: molecule bd-mol-p7i is not a wisp (Ephemeral=false)
Hint: mol burn only works with wisp molecules
      Use 'bd delete' to remove non-wisp issues
```

The name 'burn' implies destruction - it should work on any molecule, not just wisps. When called on a regular mol, it should convert to `bd delete --cascade`.

**Proposed behavior:**
- If wisp: current behavior (delete without tombstones)
- If mol: `bd delete <id> --cascade` (delete with tombstones)

This makes the metaphor consistent: burn = destroy a molecule, regardless of phase.


Improve test coverage for internal/syncbranch (33.0% → 70%)

Improve test coverage for internal/syncbranch package from 27% to 70%.

## Current State
- Coverage: 27.0%
- Files: syncbranch.go, worktree.go
- Tests: syncbranch_test.go (basic tests exist)

## Functions Needing Tests

### syncbranch.go (config management)
- [x] ValidateBranchName - has tests
- [ ] Get - needs store mock tests
- [ ] GetFromYAML - needs YAML parsing tests
- [ ] IsConfigured - needs file system tests
- [ ] IsConfiguredWithDB - needs DB path tests
- [ ] Set - needs store mock tests
- [ ] Unset - needs store mock tests

### worktree.go (git operations) - PRIORITY
- [ ] CommitToSyncBranch - needs git repo fixture tests
- [ ] PullFromSyncBranch - needs merge scenario tests
- [ ] CheckDivergence - needs ahead/behind tests
- [ ] ResetToRemote - needs reset scenario tests
- [ ] performContentMerge - needs 3-way merge tests
- [ ] extractJSONLFromCommit - needs git show tests
- [ ] hasChangesInWorktree - needs dirty state tests
- [ ] commitInWorktree - needs commit scenario tests

## Implementation Guide

1. **Use testutil fixtures:**
   ```go
   import "github.com/steveyegge/beads/internal/testutil/fixtures"

   func TestCommitToSyncBranch(t *testing.T) {
       repo := fixtures.NewGitRepo(t)
       defer repo.Cleanup()
       // ... test scenarios
   }
   ```

2. **Test scenarios for worktree.go:**
   - Clean commit (no conflicts)
   - Non-fast-forward push (diverged)
   - Merge conflict resolution
   - Empty changes (nothing to commit)

3. **Mock storage for syncbranch.go:**
   ```go
   store := memory.New()
   // Set up test config
   syncbranch.Set(ctx, store, "beads-sync")
   ```

## Success Criteria
- Coverage ≥ 70%
- All public functions have at least one test
- Edge cases covered for git operations
- Tests pass with `go test -race ./internal/syncbranch`

## Run Tests
```bash
go test -v -cover ./internal/syncbranch
go test -race ./internal/syncbranch
```


Code review: bd mol bond --ref and bd activity (bd-xo1o work)

Review dave's recent commits for bd-xo1o (Dynamic Molecule Bonding):

## Commits to Review
- ee04b1ea: feat: add dynamic molecule bonding with --ref flag (bd-xo1o.1)
- be520d90: feat: add bd activity command for real-time state feed (bd-xo1o.3)

## Review Focus
1. Code quality and correctness
2. Error handling
3. Edge cases
4. Test coverage
5. Documentation

## Deliverables
- File beads for any issues found
- Note any concerns or suggestions
- Verify the implementation matches the bd-xo1o epic requirements


GH#403: bd doctor --fix circular error message

bd doctor --fix suggests running bd doctor --fix for deletions manifest issue. Fix to provide actual resolution. See GitHub issue #403.


Add bd template list command

Add a convenience command to list available templates:

bd template list

This is equivalent to 'bd list --label=template' but more discoverable.
Could also show variable placeholders found in each template.


Merge: bd-phtv

branch: polecat/Pinner
target: main
source_issue: bd-phtv
rig: beads


Update local installation

Update local bd to the new version.

Option 1 - Homebrew:
```bash
brew upgrade bd
```

Option 2 - Install script:
```bash
curl -fsSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash
```

Verify:
```bash
bd --version
```

Should show 0.39.0.


bd graph crashes with --no-daemon on closed issues

The `bd graph` command panics with nil pointer dereference when using `--no-daemon` flag on an issue with closed children.

**Reproduction:**
```bash
bd graph bd-qqc --no-daemon
# panic: runtime error: invalid memory address or nil pointer dereference
# in main.computeDependencyCounts
```

**Stack trace:**
```
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x20 pc=0x1010bdfb0]

goroutine 1 [running]:
main.computeDependencyCounts(...)
    /Users/stevey/gt/beads/crew/emma/cmd/bd/graph.go:428
main.renderGraph(0x1400033bb80, 0x0)
    /Users/stevey/gt/beads/crew/emma/cmd/bd/graph.go:307 +0x300
```

**Location:** cmd/bd/graph.go:428 - computeDependencyCounts() not handling nil case


Support wisps in nodb mode for lightweight Gas Town rigs

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

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

## Proposed Design

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

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

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

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


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

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

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

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

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

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


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


Update CHANGELOG.md for {{version}}

In CHANGELOG.md:

1. Change `## [Unreleased]` section header to `## [{{version}}] - {{date}}`
2. Add new empty `## [Unreleased]` section above it
3. Review and clean up the changes list

Format:
```markdown
## [Unreleased]

## [{{version}}] - {{date}}

### Added
- ...

### Changed
- ...

### Fixed
- ...
```


Restart daemon with 0.33.2

Restart the bd daemon to pick up new version:

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


Test pinned issue


Remove deprecated daemon logger function

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

File: cmd/bd/daemon_logger.go:131


Implement bd mol stale command

Add command to detect complete-but-unclosed molecules.

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

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

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

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

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

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

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


Refactor: Extract shared getEpicChildren helper for swarm commands

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

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

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

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

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

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