Remove Cache-Related Tests

Delete or update tests that assume multi-repo caching


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.


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)


Cross-project dependency support

Enable tracking dependencies across project boundaries.

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

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

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


bd 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)


Update CHANGELOG.md with release notes

Add release notes for 0.32.1: MCP output control params (#667), pin field fix


Merge: beads-ocs

branch: polecat/valkyrie
target: main
source_issue: beads-ocs
rig: beads


Improve CheckStaleness error handling

## Problem

CheckStaleness returns 'false' (not stale) for multiple error conditions instead of returning errors. This masks problems.

**Location:** internal/autoimport/autoimport.go:253-285

## Edge Cases That Return False

1. **Invalid last_import_time format** (line 259-262)
2. **No JSONL file found** (line 267-277)
3. **JSONL stat fails** (line 279-282)

## Fix

Return errors for abnormal conditions:

```go
lastImportTime, err := time.Parse(time.RFC3339, lastImportStr)
if err != nil {
    return false, fmt.Errorf("corrupted last_import_time: %w", err)
}

if jsonlPath == "" {
    return false, fmt.Errorf("no JSONL file found")
}

stat, err := os.Stat(jsonlPath)
if err != nil {
    return false, fmt.Errorf("cannot stat JSONL: %w", err)
}
```

## Impact
Medium - edge cases are rare but should be handled

## Effort
30 minutes - requires updating callers in RPC server


Remove Storage Cache from Server Struct

Eliminate cache fields and use s.storage directly


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

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


Run bump-version.sh {{version}}

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


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

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


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


Remove all deletions.jsonl code except migration

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

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

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


Fix beads-9yc: pinned column missing from schema. gt mail...

Fix beads-9yc: pinned column missing from schema. gt mail send fails because some beads DBs lack the pinned column. Add migration to ensure it exists.


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


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

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


Async Gates for Agent Coordination

Agents need an async primitive for waiting on external events (CI completion, API responses, human approval). Gates are wisp issues that block until external conditions are met, managed by the Deacon.

## Core Concepts

**Gate** = wisp issue that blocks until external condition is met
- Type: gate
- Phase: wisp (never synced, ephemeral)
- Assignee: deacon/ (Deacon monitors it)
- Fields: await_type, await_id, timeout, waiters[]

**Await Types:**
- gh:run:<id> - GitHub Actions run completion
- gh:pr:<id> - PR merged/closed
- timer:<duration> - Simple delay
- human:<prompt> - Human approval required
- mail:<pattern> - Wait for mail matching pattern

## Open Questions
- Should gates live in wisp storage or main storage with wisp flag?
- Do we need a gate catalog (like molecule catalog)?
- Should waits-for dep type work with gates?


Version Bump: test-squash

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


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`


Upgrade beads-mcp to {{version}}

Upgrade the MCP server via pip:

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

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


wish: list & ready show issues as hierarchy tree

`bd ready` and `bd list` just show a flat list, and it's up to the reader to parse which ones are dependent or sub-issues of others. It would be much easier to understand if they were shown in a tree format


Test pinned for stats


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


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.


Add comments to 30+ silently ignored errors or fix them

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

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

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

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


rpc: Rich mutation events not emitted

The activity command (activity.go) references rich mutation event types (MutationBonded, MutationSquashed, MutationBurned, MutationStatus) that include metadata like OldStatus, NewStatus, ParentID, and StepCount.

However, the emitMutation() function in server_core.go:141 only accepts (eventType, issueID) and only populates Type, IssueID, and Timestamp. The additional metadata fields are never set.

Need to either:
1. Add an emitRichMutation() function that accepts the additional metadata
2. Update call sites (close, bond, squash, burn operations) to emit rich events

Without this fix, the activity feed will never show:
- Status transitions (in_progress -> closed)
- Bonded events with step counts
- Parent molecule relationships

Discovered during code review of bd-xo1o implementation.


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


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.


Test issue for pin --for

Testing the pin --for flag


Add tests for delete operations

Core delete functionality including deleteViaDaemon, createTombstone, and deleteIssue functions have 0% coverage. These are critical for data integrity and need comprehensive test coverage.


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.


Review and document rarely-used commands

Document use cases or consider deprecation for infrequently-used commands.

**Commands to review:**
1. bd rename-prefix - How often is this used? Document use cases
2. bd detect-pollution - Consider integrating into bd validate
3. bd migrate-hash-ids - One-time migration, keep but document as legacy

**For each command:**
- Document typical use cases
- Add examples to help text
- Consider if it should be a subcommand instead
- Add deprecation warning if appropriate

**Not changing:**
- duplicates ✓ (useful for data quality)
- repair-deps ✓ (useful for fixing broken refs)
- restore ✓ (critical for compacted issues)
- compact ✓ (performance feature)

**Deliverable:**
- Updated help text
- Documentation in ADVANCED.md
- Deprecation plan if needed


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.


Code review: mol squash deletion bypasses tombstone system

The deleteEphemeralChildren function in mol_squash.go uses DeleteIssue directly instead of the proper deletion flow. This bypasses tombstone creation, deletion tracking (deletions.jsonl), and dependency cleanup. Could cause issues with deletion propagation across clones.

Current code uses d.DeleteIssue(ctx, id) but should probably use d.DeleteIssues(ctx, ids, false, true, false) for proper tombstone handling.

Alternative: Document that ephemeral issues intentionally use hard delete since they are transient and should never propagate to other clones anyway.


Merge: bd-au0.7

branch: polecat/dementus
target: main
source_issue: bd-au0.7
rig: beads


bd merge omits priority:0 due to omitempty JSON tag

GitHub issue #671. The merge code in internal/merge/merge.go uses 'omitempty' on the Priority field, which causes priority:0 (P0/critical) to be dropped from JSON output since 0 is Go's zero value for int. Fix: either remove omitempty from Priority field or use a pointer (*int). This affects the git merge driver and causes P0 issues to lose their priority.


Implement global N-way collision resolution algorithm

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

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

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


Implement transaction retry logic for SQLITE_BUSY

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

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

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

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

Impact: Spurious failures under concurrent write load

Effort: 3 hours


WaitsFor directive: Fanout gate for dynamic children

Implement WaitsFor directive for molecules that spawn dynamic children.

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

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

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

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


Cleanup: --ephemeral flag

Update bd cleanup to handle ephemeral issues.

New flag:
- bd cleanup --ephemeral - deletes all CLOSED issues with ephemeral=true

Behavior:
- Only deletes if status=closed AND ephemeral=true
- Respects --dry-run flag
- Reports count of deleted ephemeral issues

This allows swarm cleanup to remove transient messages without affecting permanent issues.


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

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

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


Merge: bd-by0d

branch: polecat/furiosa
target: main
source_issue: bd-by0d
rig: beads


bd init should stop running daemon to avoid stale cache

When running bd init, any running daemon continues with stale cached data, causing bd stats and other commands to show old counts.

Repro:
1. Have daemon running with 788 issues cached
2. Clean JSONL to 128 issues, delete db, run bd init
3. bd stats still shows 788 (daemon cache)
4. Must manually run bd daemon --stop

Fix: bd init should automatically stop any running daemon before reinitializing.


Code Review: PR #551 - Persist close_reason to issues table

Code review of PR #551 which fixes close_reason persistence bug.

## Summary
The PR correctly fixes a bug where close_reason was only stored in the events table, not in the issues.close_reason column. This caused `bd show --json` to return empty close_reason.

## What Was Fixed
- ✅ CloseIssue now updates both close_reason and closed_at
- ✅ ReOpenIssue clears both close_reason and closed_at
- ✅ Comprehensive tests added for both storage and CLI layers
- ✅ Clear documentation in queries.go about dual storage strategy

## Quality Assessment
✅ Tests cover both storage layer and CLI JSON output
✅ Handles reopen case (clearing close_reason)
✅ Good comments explaining dual-storage design
✅ No known issues

## Potential Followups
See linked issues for suggestions.


Workaround for FastMCP outputSchema bug in Claude Code

The beads MCP server (v0.23.1) successfully connects to Claude Code, but all tools fail to load with a schema validation error due to a bug in FastMCP 2.13.1.

Error: "Invalid literal value, expected \"object\"" in outputSchema.

Root Cause: FastMCP generates outputSchema with $ref at root level without "type": "object" for self-referential models (Issue).

Workaround: Use slash commands (/beads:ready) or wait for FastMCP fix.


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.


Review PR #513: fix hooks install docs

Review and merge PR #513 from aspiers. This PR fixes incorrect docs for how to install git hooks - updates README to use bd hooks install instead of removed install.sh. Simple 1-line change. URL: https://github.com/anthropics/beads/pull/513


bd ship command for publishing capabilities

Add `bd ship <capability>` command that:

1. Finds issue with `export:<capability>` label
2. Validates issue is closed (or --force to override)
3. Adds `provides:<capability>` label
4. Protects `provides:*` namespace (only bd ship can add these labels)

Example:
```bash
bd ship mol-run-assignee
# Output: Shipped mol-run-assignee (bd-xyz)
```

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


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

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


Refactor duplicate JSONL-from-git parsing code

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

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

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

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


Add EntityRef type for structured entity references

Create EntityRef struct with Name, Platform, Org, ID fields. This is the foundation for HOP entity tracking. Can render as entity://hop/<platform>/<org>/<id> when needed. Add to internal/types/types.go.


Merge: bd-gqxd

branch: polecat/furiosa
target: main
source_issue: bd-gqxd
rig: beads


Priority: refactor mol.go then bd squash

Two tasks:

1. bd-cnwx - Refactor mol.go (1200+ lines, split by subcommand)
2. bd-2vh3 - Ephemeral cleanup (bd cleanup --ephemeral)

Refactor first - smaller, unblocks easier review of future mol work.

- Mayor


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

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


Update Homebrew formula

Update the Homebrew tap with new version:

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

This script waits for GitHub Actions to complete (~5 min), then updates the formula with new SHA256 hashes.

After running, verify the formula with:

```bash
brew info steveyegge/beads/bd
```


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.


Tier 3: AI-powered squash summarization

## Design: Agent-Provided Summarization (Inversion of Control)

bd is a tool FOR agents, not an agent itself. The calling agent provides
the summary; bd just stores it.

### API

```bash
# Agent generates summary, passes to bd
bd mol squash bd-xxx --summary "Agent-generated summary here"

# Without --summary, falls back to basic concatenation
bd mol squash bd-xxx
```

### Gas Town Integration Pattern

```go
// In polecat completion handler or witness
raw := exec.Command("bd", "mol", "show", molID, "--json").Output()
summary := callHaiku(buildSummaryPrompt(raw))  // agent's job
exec.Command("bd", "mol", "squash", molID, "--summary", summary).Run()
```

### Why This Design

| Concern | bd's job | Agent's job |
|---------|----------|-------------|
| Store data | ✅ | |
| Query data | ✅ | |
| Generate summaries | | ✅ |
| Call LLMs | | ✅ |
| Manage API keys | | ✅ |

### Implementation Status

- [x] --summary flag added to bd mol squash
- [x] Tests for agent-provided summary
- [ ] Gas Town integration (separate task)

### Acceptance Criteria

- ✅ bd mol squash --summary uses provided text
- ✅ Without --summary, falls back to concatenation
- ✅ No LLM calls in bd itself


Revert/remove YAML workflow system

Revert the recent commit and remove all YAML workflow code:

1. `git revert aae8407a` (the commit we just pushed with workflow fixes)
2. Remove `cmd/bd/templates/workflows/` directory
3. Remove workflow.go or gut it to minimal stub
4. Remove WorkflowTemplate types from internal/types/workflow.go
5. Remove any workflow-related RPC handlers

Keep only minimal scaffolding if needed for the new template system.


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


Make DetectCollisions read-only (separate detection from modification)

**Summary:** The project restructured the collision detection process in the database to separate read-only detection from state modification, eliminating race conditions and improving system reliability. This was achieved by introducing a two-phase approach: first detecting potential collisions, then applying resolution separately.

**Key Decisions:**
- Create read-only DetectCollisions method
- Add RenameDetail to track potential issue renames
- Implement atomic ApplyCollisionResolution function
- Separate detection logic from database modification

**Resolution:** The refactoring creates a more robust, composable collision handling mechanism that prevents partial failures and maintains database consistency during complex issue import scenarios.


refinery Handoff

attached_molecule: bd-ndye
attached_at: 2025-12-23T12:35:07Z


Merge: bd-m8ro

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


Update local installation

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


🤝 HANDOFF: Review remaining beads PRs

## Current State
Just merged PR #653 (doctor refactor) and added tests to restore coverage.

## Remaining Open PRs to Review
Run `gh pr list --repo steveyegge/beads` to see current list. As of handoff:

1. #655 - feat: Linear Integration (jblwilliams)
2. #651 - feat(audit): agent audit trail (dchichkov)
3. #648 - Stop init creating redundant @AGENTS.md (maphew)
4. #646 - fix(unix): handle Statfs field types (jordanhubbard)
5. #645 - feat: /plan-to-beads Claude Code command (petebytes)
6. #642, #641, #640 - sync branch fixes (cpdata)

## Review Checklist
- Check CI status with `gh pr checks <num> --repo steveyegge/beads`
- Verify no .beads/ data leaking (we have a hook now)
- Review code quality
- Merge good ones, request changes on problematic ones

## Notes
- User wants us to be proactive about merging good PRs
- Can add tests ourselves if coverage drops


Pinned issues: persistent context markers

Add ability to pin issues so they remain visible and are excluded from work-finding commands. Pinned issues serve as persistent context markers (handoffs, architectural notes, recovery instructions) that should not be claimed as work items.

Use Cases:
1. Handoff messages - Pin session handoffs so new agents always see them
2. Architecture decisions - Pin ADRs or design notes for reference
3. Recovery context - Pin amnesia-cure notes that help agents orient

Core commands: bd pin, bd unpin, bd list --pinned/--no-pinned


Monitor npm publish

Watch the npm publish action:
https://github.com/steveyegge/beads/actions/workflows/npm-publish.yml

Verify at: https://www.npmjs.com/package/@anthropics/claude-code-beads-plugin/v/0.30.4

Check:
```bash
npm view @anthropics/claude-code-beads-plugin@0.30.4 version
```

```verify
npm view @anthropics/claude-code-beads-plugin@0.30.4 version 2>/dev/null | grep -q '0.30.4'
```


Add unit tests for reset package

Comprehensive unit tests for internal/reset package.

## Test Cases

### ValidateState tests
- .beads/ exists → success
- .beads/ missing → appropriate error
- git dirty state detection

### CountImpact tests
- Empty .beads/ → zero counts
- With issues → correct count (open vs closed)
- With tombstones → correct count
- Returns HasUncommitted correctly

### Backup tests
- Creates backup with correct timestamp format
- Preserves all files and permissions
- Returns correct path
- Handles missing .beads/ gracefully
- Errors on pre-existing backup dir

### Git operation tests
- CheckGitState detects dirty, detached, not-a-repo
- GitRemoveBeads removes correct files
- GitCommitReset creates commit with message
- Operations skip gracefully when not in git repo

### Reset tests (with mocks/temp dirs)
- Soft reset removes files, calls init
- Hard reset includes git operations
- Dry run doesn't modify anything
- SkipInit flag prevents re-initialization
- Daemon killall is called
- Backup is created when requested

## Approach
- Can start with interface definitions (TDD style)
- Use testify for assertions
- Create temp directories for isolation
- Mock git operations where needed
- Test completion depends on implementation tasks

## File Location
`internal/reset/reset_test.go`
`internal/reset/backup_test.go`
`internal/reset/git_test.go`


Migration invariants should exclude external dependencies from orphan check

## Summary

The `checkForeignKeys` function in `migration_invariants.go` flags external dependencies as orphaned because they dont exist in the local issues table.

## Location

`internal/storage/sqlite/migration_invariants.go` around line 150-162

## Current Code (buggy)

```go
// Check for orphaned dependencies
var orphanCount int
err = db.QueryRowContext(ctx, \`
    SELECT COUNT(*)
    FROM dependencies d
    WHERE NOT EXISTS (SELECT 1 FROM issues WHERE id = d.depends_on_id)
\`).Scan(&orphanCount)
```

## Fix

Exclude external references (format: `external:<project>:<capability>`):

```go
// Check for orphaned dependencies (excluding external refs)
var orphanCount int
err = db.QueryRowContext(ctx, \`
    SELECT COUNT(*)
    FROM dependencies d
    WHERE NOT EXISTS (SELECT 1 FROM issues WHERE id = d.depends_on_id)
      AND d.depends_on_id NOT LIKE 'external:%'
\`).Scan(&orphanCount)
```

## Reproduction

```bash
# Add external dependency
bd dep add bd-xxx external:other-project:some-capability

# Try to sync - fails
bd sync
# Error: found 1 orphaned dependencies
```

## Files to Modify

1. **internal/storage/sqlite/migration_invariants.go** - Add WHERE clause

## Testing

```bash
# Create issue with external dep
bd create "Test external deps" -t task
bd dep add bd-xxx external:beads:release-workflow

# Sync should succeed
bd sync

# Verify dep exists
bd show bd-xxx --json | jq .dependencies
```

## Success Criteria
- External dependencies dont trigger orphan check
- `bd sync` succeeds with external deps
- Regular orphan detection still works for internal deps


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


Version Bump: 0.30.7

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


.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


Add tests for dependency management

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


Merge: bd-05a8

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


Push version bump to GitHub

git push origin main - triggers CI but no release yet.


GH#540: Add silent quick-capture mode (bd q)

Add bd q alias for quick capture that outputs only issue ID. Useful for piping/scripting. See GitHub issue #540.


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.


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


Restart running daemons

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


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

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

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

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

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


Default to YES for git hooks and merge driver installation

Currently bd init prompts user to install git hooks and merge driver, but setup is incomplete if user declines. Change to install by default unless --skip-hooks or --skip-merge-driver flags are passed. Better safe defaults. If installation fails, warn user and suggest bd doctor --fix.


Remove dead test code in info_test.go

Code health review found cmd/bd/info_test.go has two tests permanently skipped:

- TestInfoCommand
- TestInfoCommandNoDaemon

Both skip with: 'Manual test - bd info command is working, see manual testing'

These are essentially dead code. Either automate them or remove them entirely.


Improve clean vs cleanup command naming/documentation

Clarify the difference between bd clean and bd cleanup to reduce user confusion.

**Current state:**
- bd clean: Remove temporary artifacts (.beads/bd.sock, logs, etc.)
- bd cleanup: Delete old closed issues from database

**Options:**
1. Rename for clarity:
   - bd clean → bd clean-temp
   - bd cleanup → bd cleanup-issues

2. Keep names but improve help text and documentation

3. Add prominent warnings in help output

**Preferred approach:** Option 2 (improve documentation)
- Update short/long descriptions in commands
- Add examples to help text
- Update README.md
- Add cross-references in help output

**Files to modify:**
- cmd/bd/clean.go
- cmd/bd/cleanup.go
- README.md or ADVANCED.md


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)


Update info.go versionChanges

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


Test createTombstone and deleteIssue wrappers

Add tests for the createTombstone and deleteIssue wrapper functions in cmd/bd/delete.go.

## Functions under test
- createTombstone (cmd/bd/delete.go:335) - Wrapper around SQLite CreateTombstone
- deleteIssue (cmd/bd/delete.go:349) - Wrapper around SQLite DeleteIssue

## Test scenarios for createTombstone
1. Successful tombstone creation
2. Tombstone with reason and actor tracking
3. Error when issue doesn't exist
4. Verify tombstone status set correctly
5. Verify audit trail recorded
6. Rollback/error handling

## Test scenarios for deleteIssue
1. Successful issue deletion
2. Error on non-existent issue
3. Verify issue removed from database
4. Error handling when storage backend doesn't support delete

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

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


Release v0.33.2

Version bump workflow for beads release 0.33.2.

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

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

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

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


bd template commands fail with daemon mode

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

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

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

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

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


Phase 1.3: Basic TOON read/write operations

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

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

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

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


Create and push git tag v0.33.2

Create the release tag and push it:

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

This triggers the GoReleaser GitHub Action to build release binaries.


Add timeout message for long-running git push operations

When git push hangs waiting for credential/browser auth, show a periodic message to the user instead of appearing frozen. Add timeout messaging after N seconds of inactivity during git operations.


Enhance bd doctor to suggest reset for broken states

Update bd doctor to detect severely broken states and suggest reset.

## Detection Criteria
Suggest reset when:
- Multiple unfixable errors detected
- Corrupted JSONL that can't be repaired
- Schema version mismatch that can't be migrated
- Daemon state inconsistent and unkillable

## Implementation
Add to doctor's check/fix flow:
```go
if unfixableErrors > threshold {
    suggest('State may be too broken to fix. Consider: bd reset')
}
```

## Output Example
```
✗ Found 5 unfixable errors

  Your beads state may be too corrupted to repair.
  Consider running 'bd reset' to start fresh.
  (Use 'bd reset --backup' to save current state first)
```

## Notes
- Don't auto-run reset, just suggest
- This is lower priority, can be done in parallel with main work


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


Add bd reset command for clean slate restart

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

**Current workaround** (from maphew):
```bash
bd daemons killall
git rm .beads/*.jsonl
git commit -m 'remove old issues'
rm .beads/*
bd init
bd onboard
```

**Desired:** A proper `bd reset` command that handles this cleanly and safely.


Run bump-version.sh 0.30.7

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


bd pin: pinned field overwritten by subsequent bd commands

## Summary

The `bd pin` command correctly sets `pinned=1` in SQLite, but any subsequent `bd` command (including read-only commands like `bd show`) resets `pinned` to 0.

## Reproduction Steps

```bash
bd --no-daemon pin <issue-id> --for=max
sqlite3 .beads/beads.db "SELECT id, pinned FROM issues WHERE id=\"<issue-id>\""
# Shows pinned=1 ✓

bd --no-daemon show <issue-id> --json
sqlite3 .beads/beads.db "SELECT id, pinned FROM issues WHERE id=\"<issue-id>\""
# Shows pinned=0 ✗ WRONG
```

## Root Cause Investigation

### Prime Suspects

1. **JSONL import overwrites DB** - The `pinned` field has `omitempty` so false values arent in JSONL. When JSONL is imported, it overwrites the DB pinned=1 with default pinned=0.

2. **Files to check:**
   - `internal/importer/importer.go` - ImportIssue() may unconditionally set all fields
   - `internal/storage/sqlite/issues.go` - UpsertIssue() may not preserve pinned
   - `cmd/bd/main.go` - ensureStoreActive() may trigger import

### Debug Steps

```bash
# Add debug logging to track what is writing pinned=0
grep -rn "pinned" internal/storage/sqlite/*.go
grep -rn "Pinned" internal/importer/*.go
```

## Likely Fix

In `internal/importer/importer.go` or `internal/storage/sqlite/issues.go`:

```go
// When upserting from JSONL, preserve pinned field if already set
func (s *SQLiteStorage) UpsertIssue(ctx context.Context, issue *types.Issue) error {
    // Check if issue exists and is pinned
    existing, _ := s.GetIssue(ctx, issue.ID)
    if existing != nil && existing.Pinned && !issue.Pinned {
        // Preserve existing pinned status
        issue.Pinned = existing.Pinned
    }
    // ... rest of upsert
}
```

OR the import should skip fields that are omitempty and not present in JSONL:

```go
// In importer, only update fields that are explicitly set in JSONL
// Pinned with omitempty means absent = dont change, not absent = false
```

## Testing

```bash
# After fix:
bd --no-daemon pin <issue-id> --for=max
bd --no-daemon show <issue-id> --json  # Should not reset pinned
bd list --pinned  # Should show the pinned issue
bd hook --agent max  # Should show pinned work
```

## Files to Modify

1. **internal/importer/importer.go** - Preserve pinned on import
2. **internal/storage/sqlite/issues.go** - UpsertIssue preserve pinned
3. **Add test** in internal/importer/importer_test.go

## Success Criteria
- `bd pin` survives subsequent bd commands
- `bd list --pinned` shows pinned issues
- `bd hook --agent X` shows pinned work
- Existing tests still pass


Improve test coverage for internal/beads (48.1% → 70%)

Improve test coverage for internal/beads package from 48% to 70%.

## Current State
- Coverage: 48.4%
- Files: beads.go, fingerprint.go
- Tests: beads_test.go (moderate coverage)

## Functions Needing Tests

### beads.go (database discovery)
- [ ] followRedirect - needs redirect file tests
- [ ] findDatabaseInBeadsDir - needs various dir structures
- [x] NewSQLiteStorage - likely covered
- [ ] FindDatabasePath - needs BEADS_DB env var tests
- [ ] hasBeadsProjectFiles - needs file existence tests
- [ ] FindBeadsDir - needs directory traversal tests
- [ ] FindJSONLPath - needs path derivation tests
- [ ] findGitRoot - needs git repo tests
- [ ] findDatabaseInTree - needs nested directory tests
- [ ] FindAllDatabases - needs multi-database tests
- [ ] FindWispDir - needs wisp directory tests
- [ ] FindWispDatabasePath - needs wisp path tests
- [ ] NewWispStorage - needs wisp storage tests
- [ ] EnsureWispGitignore - needs gitignore creation tests
- [ ] IsWispDatabase - needs path classification tests

### fingerprint.go (repo identification)
- [ ] ComputeRepoID - needs various remote URL tests
- [ ] canonicalizeGitURL - needs URL normalization tests
- [ ] GetCloneID - needs clone identification tests

## Implementation Guide

1. **Use temp directories:**
   ```go
   func TestFindBeadsDir(t *testing.T) {
       tmpDir := t.TempDir()
       beadsDir := filepath.Join(tmpDir, ".beads")
       os.MkdirAll(beadsDir, 0755)

       // Create test files
       os.WriteFile(filepath.Join(beadsDir, "beads.db"), []byte{}, 0644)

       // Change to tmpDir and test
       oldWd, _ := os.Getwd()
       os.Chdir(tmpDir)
       defer os.Chdir(oldWd)

       result := FindBeadsDir()
       assert.Equal(t, beadsDir, result)
   }
   ```

2. **Test scenarios:**
   - BEADS_DB environment variable set
   - .beads/ in current directory
   - .beads/ in parent directory
   - Redirect file pointing elsewhere
   - No beads directory found
   - Wisp directory alongside main beads

3. **Git remote URL tests:**
   ```go
   tests := []struct{
       input    string
       expected string
   }{
       {"git@github.com:user/repo.git", "github.com/user/repo"},
       {"https://github.com/user/repo", "github.com/user/repo"},
       {"ssh://git@github.com/user/repo.git", "github.com/user/repo"},
   }
   ```

## Success Criteria
- Coverage ≥ 70%
- All FindXxx functions have tests
- Environment variable handling tested
- Edge cases (missing dirs, redirects) covered

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


Spawn-time bonding: --attach flag

Add --attach flag to bd mol spawn for on-the-fly composition.

COMMAND: bd mol spawn proto-feature --attach proto-docs --attach proto-testing

BEHAVIOR:
- Spawn the primary proto as normal
- For each --attach: spawn that proto and wire to primary
- Attachments become children of primary's root epic
- Dependencies wired based on bond type (default: sequential)

FLAGS:
- --attach PROTO: Attach a proto (can repeat)
- --attach-type TYPE: sequential (default) or parallel for all attachments
- --after ISSUE: Attachment point for attached protos

VARIABLE HANDLING:
- All attached protos share variable namespace
- Warn on variable name conflicts
- All --var flags apply to all protos

DESIGN NOTE: This is syntactic sugar. Equivalent to:
  bd mol spawn proto-A
  bd mol bond $new_epic_id proto-B

Keeping as separate task because it's a common UX pattern worth optimizing.


Push version bump to GitHub

git push origin main - triggers CI but no release yet.


Merge: bd-w193

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


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.


Add daemon RPC support for gate commands.

## Current State
Gate commands require --no-daemon flag because they use direct SQLite access:
- Gate create needs to write await_type, await_id, timeout_ns, waiters fields
- Gate wait needs to update waiters JSON array
- Daemon RPC doesnt have methods for these operations

## Implementation

### 1. Add RPC methods to internal/rpc/protocol.go

```go
// Gate operations
type GateCreateArgs struct {
    Title     string        \`json:"title"\`
    AwaitType string        \`json:"await_type"\`
    AwaitID   string        \`json:"await_id"\`
    Timeout   time.Duration \`json:"timeout"\`
    Waiters   []string      \`json:"waiters"\`
}

type GateCreateResult struct {
    Issue *types.Issue \`json:"issue"\`
}

type GateListArgs struct {
    All bool \`json:"all"\` // Include closed gates
}

type GateListResult struct {
    Gates []*types.Issue \`json:"gates"\`
}

type GateWaitArgs struct {
    GateID  string   \`json:"gate_id"\`
    Waiters []string \`json:"waiters"\` // Additional waiters to add
}

type GateWaitResult struct {
    Gate        *types.Issue \`json:"gate"\`
    AddedCount  int          \`json:"added_count"\`
}
```

### 2. Add handler methods to internal/daemon/rpc_handler.go

```go
func (h *RPCHandler) GateCreate(ctx context.Context, args *rpc.GateCreateArgs) (*rpc.GateCreateResult, error) {
    now := time.Now()
    gate := &types.Issue{
        Title:     args.Title,
        IssueType: types.TypeGate,
        Status:    types.StatusOpen,
        Priority:  1,
        Assignee:  "deacon/",
        Wisp:      true,
        AwaitType: args.AwaitType,
        AwaitID:   args.AwaitID,
        Timeout:   args.Timeout,
        Waiters:   args.Waiters,
        CreatedAt: now,
        UpdatedAt: now,
    }
    gate.ContentHash = gate.ComputeContentHash()

    if err := h.store.CreateIssue(ctx, gate, h.actor); err != nil {
        return nil, err
    }

    return &rpc.GateCreateResult{Issue: gate}, nil
}

func (h *RPCHandler) GateList(ctx context.Context, args *rpc.GateListArgs) (*rpc.GateListResult, error) {
    gateType := types.TypeGate
    filter := types.IssueFilter{IssueType: &gateType}
    if !args.All {
        openStatus := types.StatusOpen
        filter.Status = &openStatus
    }

    gates, err := h.store.SearchIssues(ctx, "", filter)
    if err != nil {
        return nil, err
    }

    return &rpc.GateListResult{Gates: gates}, nil
}

func (h *RPCHandler) GateWait(ctx context.Context, args *rpc.GateWaitArgs) (*rpc.GateWaitResult, error) {
    gate, err := h.store.GetIssue(ctx, args.GateID)
    if err != nil {
        return nil, err
    }
    if gate.IssueType != types.TypeGate {
        return nil, fmt.Errorf("%s is not a gate", args.GateID)
    }

    // Merge waiters (dedupe)
    waiterSet := make(map[string]bool)
    for _, w := range gate.Waiters {
        waiterSet[w] = true
    }
    added := 0
    for _, w := range args.Waiters {
        if !waiterSet[w] {
            gate.Waiters = append(gate.Waiters, w)
            waiterSet[w] = true
            added++
        }
    }

    if added > 0 {
        // Update via store
        updates := map[string]interface{}{
            "waiters": gate.Waiters,
        }
        if err := h.store.UpdateIssue(ctx, args.GateID, updates, h.actor); err != nil {
            return nil, err
        }
    }

    return &rpc.GateWaitResult{Gate: gate, AddedCount: added}, nil
}
```

### 3. Register methods in daemon

In internal/daemon/server.go, register the new methods:
```go
rpc.RegisterMethod("gate.create", h.GateCreate)
rpc.RegisterMethod("gate.list", h.GateList)
rpc.RegisterMethod("gate.wait", h.GateWait)
```

### 4. Add client methods to internal/rpc/client.go

```go
func (c *Client) GateCreate(ctx context.Context, args *GateCreateArgs) (*GateCreateResult, error) {
    var result GateCreateResult
    err := c.Call(ctx, "gate.create", args, &result)
    return &result, err
}

func (c *Client) GateList(ctx context.Context, args *GateListArgs) (*GateListResult, error) {
    var result GateListResult
    err := c.Call(ctx, "gate.list", args, &result)
    return &result, err
}

func (c *Client) GateWait(ctx context.Context, args *GateWaitArgs) (*GateWaitResult, error) {
    var result GateWaitResult
    err := c.Call(ctx, "gate.wait", args, &result)
    return &result, err
}
```

### 5. Update cmd/bd/gate.go to use daemon

```go
// In gateCreateCmd Run:
if daemonClient != nil {
    result, err := daemonClient.GateCreate(ctx, &rpc.GateCreateArgs{
        Title:     title,
        AwaitType: awaitType,
        AwaitID:   awaitID,
        Timeout:   timeout,
        Waiters:   notifyAddrs,
    })
    if err != nil {
        FatalError("gate create: %v", err)
    }
    gate = result.Issue
} else {
    // Existing direct store code
}
```

## Files to Modify

1. **internal/rpc/protocol.go** - Add Gate*Args/Result types
2. **internal/daemon/rpc_handler.go** - Add handler methods
3. **internal/daemon/server.go** - Register methods
4. **internal/rpc/client.go** - Add client methods
5. **cmd/bd/gate.go** - Use daemon client when available

## Testing

```bash
# Start daemon
bd daemon start

# Test via daemon (should work without --no-daemon)
bd gate create --await timer:5m --notify beads/dave
bd gate list
bd gate wait <id> --notify beads/alice

# Verify daemon handled it
bd daemons logs . | grep gate
```

## Success Criteria
- All gate commands work without --no-daemon
- Same behavior in daemon vs direct mode
- Waiters array updates correctly via RPC
- Tests pass for RPC gate operations


Audit and standardize JSON output across all commands

Ensure consistent JSON format and error handling when --json flag is used.

**Scope:**
1. Verify all commands respect --json flag
2. Standardize success response format
3. Standardize error response format
4. Document JSON schemas

**Commands to audit:**
- Core CRUD: create, update, delete, show, list, search ✓
- Queries: ready, blocked, stale, count, stats, status
- Deps: dep add/remove/tree/cycles
- Labels: label commands
- Comments: comments add/list/delete
- Epics: epic status/close-eligible
- Export/import: already support --json ✓

**Testing:**
- Success cases return valid JSON
- Error cases return valid JSON (not plain text)
- Consistent field naming (snake_case vs camelCase)
- Array vs object wrapping consistency


Exclude pinned issues from bd ready

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


Merge: bd-likt

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


Timer beads for scheduled agent callbacks

## Problem

Agents frequently need to wait for external events (CI completion, PR reviews, artifact builds) but have no good mechanism:
- `sleep N` blocks and is unreliable (often times out at 8+ minutes)
- Polling wastes context and is easy to forget
- No way to survive session restarts

## Proposal: Timer Beads

A new bead type or field that represents a scheduled callback:

### Creating timers
```bash
bd timer create --in 30s --callback "Check CI run 12345" --issue bd-xyz
bd timer create --at "2025-12-20T08:00:00" --callback "Morning standup"
bd timer create --in 5m --on-expire "tmux send-keys -t dave 'bd show bd-xyz'"
```

### Timer storage
- Store in beads (survives restarts)
- Fields: `expires_at`, `callback_description`, `on_expire_command`, `linked_issue`
- Status: pending, fired, cancelled

### Deacon integration
The Deacon daemon monitors timer beads:
1. Wakes on next timer expiry
2. Executes `on_expire` command (e.g., tmux send-keys to interrupt agent)
3. Marks timer as fired
4. Optionally updates linked issue

### Use cases
- CI monitoring: "ping me when build completes"
- PR reviews: "check back in 1 hour"
- Scheduled tasks: "remind me at EOD to sync"
- Blocking waits: agent registers callback instead of sleeping

## Acceptance criteria
- [ ] Timer bead type or field design
- [ ] `bd timer create/list/cancel` commands
- [ ] Deacon timer monitoring loop
- [ ] tmux integration for agent interrupts
- [ ] Survives daemon restarts


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


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.


Update local installation

go build -o ~/.local/bin/bd ./cmd/bd && codesign -s - ~/.local/bin/bd


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.


Add gate issue type to beads

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

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

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


Re: Thread Test 2

Great! Thread is working well.


gt mq list shows empty when MRs exist


Polecats get stuck on interactive shell prompts (cp/mv/rm -i)

During swarm operations, polecats frequently get stuck waiting for interactive prompts from shell commands like:
- cp prompting 'overwrite file? (y/n)'
- mv prompting 'overwrite file? (y/n)'
- rm prompting 'remove file?'

This happens because macOS aliases or shell configs may have -i flags set by default.

Root cause: Claude Code runs commands that trigger interactive confirmation prompts, but cannot respond to them, causing the agent to hang indefinitely.

Observed in: Multiple polecats during GH issues swarm (Dec 2024)
- Derrick, Roustabout, Prospector, Warboy all got stuck on y/n prompts

Suggested fixes:
1. AGENTS.md should instruct agents to always use -f flag with cp/mv/rm
2. Polecat startup could set shell aliases to use non-interactive versions
3. bd prime hook could include guidance about non-interactive commands
4. Consider detecting stuck prompts and auto-recovering


Update info.go versionChanges

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


getLocalSyncBranch silently ignores YAML parse errors

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

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

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


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.


Version Bump: {{version}}

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


Re: Thread Test 2

Got your message. Testing reply feature.


Test Thread

Initial message for threading test


Fix bd sync race condition with daemon causing dirty working directory

After bd sync completes with sync.branch mode, subsequent bd commands or daemon file watcher would see a hash mismatch and trigger auto-import, which then schedules re-export, dirtying the working directory.

**Root cause:**
1. bd sync exports JSONL with NEW content (hash H1)
2. bd sync updates jsonl_content_hash = H1 in DB
3. bd sync restores JSONL from HEAD (OLD content, hash H0)
4. Now: file hash = H0, DB hash = H1 (MISMATCH)
5. Daemon or next CLI command sees mismatch, imports from OLD JSONL
6. Import triggers re-export → file is dirty

**Fix:**
After restoreBeadsDirFromBranch(), update jsonl_content_hash to match the restored file's hash. This ensures daemon and CLI see file hash = DB hash → no spurious import/export cycle.

Related: bd-c83r (multiple daemon prevention)


Add GetMoleculeProgress RPC endpoint

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


mol-beads-release

Release checklist for beads version {{version}}.

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

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

Add a new VersionChange entry at the top of versionChanges slice:
```go
{
    Version: "{{version}}",
    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
## [{{version}}] - 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 {{version}}
```

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 v{{version}}"
```

Needs: run-tests

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

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

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 v{{version}}

# 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 {{version}} --install

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

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

Verify: `bd --version` shows {{version}}
Needs: verify-release

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

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

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

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

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


Design molecule step hooks

Hooks that fire between molecule steps. When a bead in a molecule closes, trigger hook that can spawn agent attention to prompts/requests. This enables reactive orchestration - the molecule drives, hooks respond. Gas Town feature built on Beads data plane.


bd mol squash handles wisp→digest

Update bd mol squash to handle ephemeral molecules.

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

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

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

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


Update cmd/bd/version.go to 0.30.4

Update the Version constant in cmd/bd/version.go:
```go
Version = "0.30.4"
```

```verify
grep -q 'Version = "0.30.4"' cmd/bd/version.go
```


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

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


bd activity: Real-time molecule state feed

Implement activity feed command for watching molecule state transitions.

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

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

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

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


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
)


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


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


Create git tag v0.33.2

Create the release tag:

```bash
git tag v0.33.2
```

Verify: `git tag | grep 0.33.2`


Implement bd pin/unpin commands

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


Mail Commands: bd mail send/inbox/read/ack

Implement core mail commands in cmd/bd/mail.go

Commands:
- bd mail send <recipient> -s 'Subject' -m 'Body' [--urgent]
  - Creates issue with type=message, sender=identity, assignee=recipient
  - --urgent sets priority=0

- bd mail inbox [--from <sender>] [--priority <n>]
  - Lists open messages where assignee=my identity
  - Sorted by priority, then date

- bd mail read <id>
  - Shows full message content (subject, body, sender, timestamp)
  - Does NOT close (separate from ack)

- bd mail ack <id>
  - Marks message as read by closing it
  - Can ack multiple: bd mail ack <id1> <id2> ...

Requires: Identity configuration (bd-kwro.7)


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


Graph Link: supersedes for version chains

Implement supersedes link type for version tracking.

New command:
- bd supersede <id> --with <new> - marks id as superseded by new
- Auto-closes the superseded issue

Query support:
- bd show <id> shows 'Superseded by: <new>'
- bd show <new> shows 'Supersedes: <id>'
- bd list --superseded shows version chains

Storage:
- superseded_by column pointing to replacement issue

Useful for design docs, specs, and evolving artifacts.


Merge: bd-c7y5

branch: polecat/toast
target: main
source_issue: bd-c7y5
rig: beads


Auto-detect identity from directory context for bd mail

Currently bd mail inbox defaults to git user name, requiring --identity flag with exact format.

## Problem
- Mail sent to `gastown/crew/max`
- Max runs `bd mail inbox` → defaults to 'Steve Yegge' (git user)
- Max must know to use `--identity 'gastown/crew/max'` with exact slashes

## Proposed Fix
Auto-detect identity from directory context when in a Gas Town workspace:
- In `/Users/stevey/gt/gastown/crew/max`, infer identity = `gastown/crew/max`
- Pattern: `<town>/<rig>/<role>/<name>` → `<rig>/<role>/<name>`

## Additional Improvements
1. Support GT_IDENTITY env var (set by gt crew at / session spawning)
2. Support identity in .beads/config.yaml
3. Normalize format: accept both slashes and dashes as equivalent

## Context
Discovered during crew-to-crew work assignment. Max couldn't see mail despite correct nudge because identity defaulted wrong.


Version Bump: 0.32.1

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


Verify release artifacts

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


GH#522: Add --type flag to bd update command

Add --type flag to bd update for changing issue type (task/epic/bug/feature). Storage layer already supports it. See GitHub issue #522.


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.


GH#519: bd sync fails when sync.branch is currently checked-out branch


Update local installation

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


Phase 1.2: Separate bdt executable - Initial structure

Create minimal bdt command structure completely separate from bd. Must not share code, config, or database.

## Subtasks
1. Create cmd/bdt/ directory with main.go
2. Implement bdt version, help, and status commands
3. Configure separate database location: $HOME/.bdt/ (not $HOME/.beads/)
4. Create separate issues file: issues.toon (not issues.jsonl)
5. Update build system:
   - Makefile: Add bdt target
   - .goreleaser.yml: Add bdt binary config

## Files to Create
- cmd/bdt/main.go - Entry point
- cmd/bdt/version.go - Version handling
- cmd/bdt/help.go - Help text (separate from bd)

## Success Criteria
- `make build` produces both `bd` and `bdt` executables
- `bdt version` shows distinct version output from bd
- `bdt --help` shows distinct help text
- bdt uses $HOME/.bdt/ directory (verify with `bdt info`)
- bd and bdt completely isolated (no shared imports beyond stdlib)


Code review findings: mol squash deletion bypasses tombstones


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


Investigate incremental blocked_issues_cache updates at scale

Current blocked_issues_cache strategy does full DELETE + INSERT on every dependency/status change.

**Problem at scale:**
- 10K issues: ~50ms rebuild (acceptable)
- 100K issues: ~500ms rebuild (noticeable)
- 1M issues: multi-second rebuilds (problematic)

**Current implementation (blocked_cache.go:104-154):**
- DELETE FROM blocked_issues_cache
- INSERT with recursive CTE

**Potential optimizations:**
1. **Incremental updates:** Only add/remove affected issue IDs instead of full rebuild
2. **Dirty tracking:** Skip rebuild if cache is already valid
3. **Async rebuild:** Rebuild in background, serve stale cache briefly
4. **Partial invalidation:** Only invalidate affected subtree

**Decision needed:** Is this premature optimization? Current target is <100K issues.

**Benchmark:** Add benchmark for cache rebuild at 100K scale to measure actual impact.


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)


Document sync.branch 'always dirty' working tree behavior

## Context

When sync.branch is configured, the .beads/issues.jsonl file in main's working tree is ALWAYS dirty. This is by design:

1. bd sync commits to beads-sync branch (via worktree)
2. bd sync copies JSONL to main's working tree (so CLI commands work)
3. This copy is NOT committed to main (to reduce commit noise)

Contributors who watch main branch history pushed for sync.branch to avoid constant beads commit noise. But users need to understand the trade-off.

## Documentation Needed

Update README.md sync.branch section with:

1. **Clear explanation** of why .beads/ is always dirty on main
2. **"Be Zen about it"** - this is expected, not a bug
3. **Workflow options:**
   - Accept dirty state, use `bd sync --merge` periodically to snapshot to main
   - Or disable sync.branch if clean working tree is more important
4. **Shell alias tip** to hide beads from git status:
   ```bash
   alias gs='git status -- ":!.beads/"'
   ```
5. **When to merge**: releases, milestones, or periodic snapshots

## Related

- bd-7b7h: Fix that allows bd sync --merge to work with dirty .beads/
- bd-elqd: Investigation that identified this as expected behavior


Add missing hook calls in mail reply and ack

The mail commands are missing hook calls:

1. runMailReply (mail.go:525-672) creates a message but doesn't call hookRunner.Run(hooks.EventMessage, ...) after creating the reply in direct mode (around line 640)

2. runMailAck (mail.go:432-523) closes messages but doesn't call hookRunner.Run(hooks.EventClose, ...) after closing each message (around line 487 for daemon mode, 493 for direct mode)

This means GGT hooks won't fire for replies or message acknowledgments.


Work on beads-ldv: Fix bd graph crashes with nil pointer ...

Work on beads-ldv: Fix bd graph crashes with nil pointer dereference (GH#657). Fix nil pointer in computeDependencyCounts at graph.go:428. When done, submit MR (not PR) to integration branch for Refinery.


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 0.33.2 to versionChanges in info.go

Add new entry at TOP of versionChanges in cmd/bd/info.go with release notes from CHANGELOG.md. Must do before bump-version.sh --commit.


bd sync tries to create worktree for sync.branch even when already on that branch. Should commit directly instead. See GitHub issue #519.


Run tests and verify build

Run the test suite to verify nothing is broken:

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

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

Fix any failures before proceeding.


Merge: bd-49kw

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


Add tests for import/export functionality

Import/export functions like ImportIssues, exportToJSONLWithStore, and AutoImportIfNewer have low coverage. These are critical for data integrity and multi-repo synchronization.


Thread Test

Testing the thread feature


Review PR #514: fix plugin install docs

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


Review SQLite indexes and scaling bottlenecks

Audit the beads SQLite schema for:

## Index Review
- Are all frequently-queried columns indexed?
- Are compound indexes needed for common query patterns?
- Any missing indexes on foreign keys or filter columns?

## Scaling Bottlenecks
- How does performance degrade with 10k, 100k, 1M issues?
- Full table scans in hot paths?
- JSONL export/import performance at scale
- Transaction contention in multi-agent scenarios

## Common Query Patterns to Optimize
- bd ready (status + blocked_by resolution)
- bd list with filters (status, type, priority, labels)
- bd show with dependency graph traversal
- bd sync import/export

## Deliverables
- Document current indexes
- Identify missing indexes
- Benchmark key operations at scale
- Recommend schema improvements


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


Work on beads-399: Add omitempty to JSONL fields for smal...

Work on beads-399: Add omitempty to JSONL fields for smaller notifications. When done, submit MR (not PR) to integration branch for Refinery.


Widespread double JSON encoding bug in daemon mode RPC calls

Multiple CLI commands had the same double JSON encoding bug found in bd-1048. All commands that called ResolveID via RPC used string(resp.Data) instead of properly unmarshaling the JSON response. This caused IDs to retain JSON quotes ("bd-1048" instead of bd-1048), which then got double-encoded when passed to subsequent RPC calls.

Affected commands:
- bd show (3 instances)
- bd dep add/remove/tree (5 instances)
- bd label add/remove/list (3 instances)
- bd reopen (1 instance)

Root cause: resp.Data is json.RawMessage (already JSON-encoded), so string() conversion preserves quotes.

Fix: Replace all string(resp.Data) with json.Unmarshal(resp.Data, &id) for proper deserialization.

All commands now tested and working correctly with daemon mode.


Consolidate maintenance commands into bd doctor --fix

Per rsnodgrass in GH#692:
> "The biggest improvement to beads from an ergonomics perspective would be to prune down commands. We have a lot of 'maintenance' commands that probably should just be folded into 'bd doctor --fix' automatically."

Current maintenance commands that could be consolidated:
- clean - Clean up temporary git merge artifacts
- cleanup - Delete closed issues and prune expired tombstones
- compact - Compact old closed issues
- detect-pollution - Detect and clean test issues
- migrate-* (5 commands) - Various migration utilities
- repair-deps - Fix orphaned dependency references
- validate - Database health checks

Proposal:
1. Make `bd doctor` the single entry point for health checks
2. Add `bd doctor --fix` to auto-fix common issues
3. Deprecate (but keep working) individual commands
4. Add `bd doctor --all` for comprehensive maintenance

This would reduce cognitive load for users - they just need to remember 'bd doctor'.

Note: This is higher impact but also higher risk - needs careful design to avoid breaking existing workflows.


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.


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'


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.


Update Metrics and Health Endpoints

Remove cache-related metrics from health/metrics endpoints


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


Integration test: agent waits for CI via gate

End-to-end test of the gate workflow.

## Test Scenario
1. Agent creates gate: bd gate create --await gh:run:123 --timeout 5m --notify beads/dave
2. Agent writes handoff and exits
3. Deacon patrol checks gate condition
4. (Mock) GitHub run completes
5. Deacon notifies waiter and closes gate
6. New agent session reads mail and resumes

## Test Requirements
- Mock GitHub API responses
- Test timeout path
- Test multiple waiters
- Verify mail notifications sent


Split monolithic sqlite.go into focused files

internal/storage/sqlite/sqlite.go is 1050 lines containing initialization, 20+ CRUD methods, query building, and schema management.

Split into:
- store.go: Store struct & initialization (150 lines)
- bead_queries.go: Bead CRUD (300 lines)
- work_queries.go: Work queries (200 lines)
- stats_queries.go: Statistics (150 lines)
- schema.go: Schema & migrations (150 lines)
- helpers.go: Common utilities (100 lines)

Impact: Impossible to understand at a glance; hard to find specific functionality; high cognitive load

Effort: 6-8 hours


Install 0.30.4 Go binary locally

Rebuild and install the Go binary:
```bash
go install ./cmd/bd
# OR
make install
```

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

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


Documentation for messaging and graph links

Document all new features.

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

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


Add --pinned and --no-pinned flags to bd list

Add filtering flags to bd list: --pinned shows only pinned issues, --no-pinned excludes pinned issues. Default behavior shows all issues with a pin indicator.