Fix TestRunSync_Timeout failing on macOS

The hooks timeout test fails because exec.CommandContext doesn't properly terminate child processes of shell scripts on macOS. The test creates a hook that runs 'sleep 60' with a 500ms timeout, but it waits the full 60 seconds.

Options to fix:
- Use SysProcAttr{Setpgid: true} to create process group and kill the group
- Skip test on darwin with build tag
- Use a different approach for timeout testing

Location: internal/hooks/hooks_test.go:220-253


Merge: obsidian-1767142004753

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


Implement `bd setup cursor` for Cursor IDE integration

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


Verify CI is green

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


Update Homebrew formula

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


--parent flag reports success but doesn't persist to JSONL

The --parent flag for bd update reports success but the change doesn't persist.

Reported by: Mayor
Location: show.go:810-814 handles the logic

Symptoms:
- bd update <id> --parent=<new-parent> returns success
- But the parent change is not written to JSONL
- Subsequent bd show reveals parent unchanged

This shipped in v0.39.1 as part of bd-cj2e (--parent flag for reparenting).

Investigation: Check the update path from show.go through to JSONL export. The RPC/daemon layer may be dropping the parent field, or the storage layer isn't persisting it.


Implement Jira issue timestamp comparison for sync

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

File: cmd/bd/jira.go:633


Split large cmd/bd files into logical modules for maintainability.

## Current State
| File | Lines | Status |
|------|-------|--------|
| sync.go | 2203 | Too large |
| init.go | 1742 | Too large |
| show.go | 1419 | Too large |
| compact.go | 1190 | Borderline |

## Proposed Splits

### 1. sync.go (2203 lines) → 4 files

**sync.go** (~500 lines) - Main command and coordination
```go
var syncCmd = &cobra.Command{...}
func init() {...}
func doSync(...) {...}
```

**sync_export.go** (~400 lines) - Export operations
```go
func exportToJSONL(...)
func markDirtyAndScheduleFlush(...)
func flushPendingChanges(...)
```

**sync_import.go** (~500 lines) - Import operations
```go
func importFromJSONL(...)
func handleImportConflicts(...)
func mergeImportedIssues(...)
```

**sync_branch.go** (~400 lines) - Git branch operations
```go
func commitToSyncBranch(...)
func pullFromSyncBranch(...)
func handleSyncBranchDivergence(...)
```

### 2. init.go (1742 lines) → 3 files

**init.go** (~400 lines) - Main init command
```go
var initCmd = &cobra.Command{...}
func runInit(...)
func determinePrefix(...)
```

**init_wizard.go** (~500 lines) - Interactive setup
```go
func runContributorWizard(...)
func runTeamWizard(...)
func promptForConfig(...)
```

**init_hooks.go** (~400 lines) - Git hooks setup
```go
func installGitHooks(...)
func configureAutosync(...)
func setupMergeDriver(...)
```

### 3. show.go (1419 lines) → 3 files

**show.go** (~400 lines) - Main show command
```go
var showCmd = &cobra.Command{...}
func showIssue(...)
func showMultipleIssues(...)
```

**show_format.go** (~400 lines) - Output formatting
```go
func formatIssueText(...)
func formatIssueMarkdown(...)
func formatDependencyTree(...)
```

**show_threads.go** (~300 lines) - Thread/conversation display
```go
func showThread(...)
func formatThreadMessages(...)
```

### 4. compact.go (1190 lines) → 2 files

**compact.go** (~600 lines) - Main compact command
```go
var compactCmd = &cobra.Command{...}
func runCompact(...)
```

**compact_tiers.go** (~400 lines) - Tier-specific logic
```go
func compactTier1(...)
func compactTier2(...)
func squashWisps(...)
```

## Implementation Steps

1. **Start with sync.go** (largest file)
2. **Create new files** with same package declaration
3. **Move functions** maintaining related code together
4. **Update any file-local variables** that need to be shared
5. **Run tests** after each split:
   ```bash
   go test -short ./cmd/bd/...
   ```

## File Organization After Split
```
cmd/bd/
├── sync.go           (~500 lines)
├── sync_export.go    (~400 lines)
├── sync_import.go    (~500 lines)
├── sync_branch.go    (~400 lines)
├── init.go           (~400 lines)
├── init_wizard.go    (~500 lines)
├── init_hooks.go     (~400 lines)
├── show.go           (~400 lines)
├── show_format.go    (~400 lines)
├── show_threads.go   (~300 lines)
├── compact.go        (~600 lines)
└── compact_tiers.go  (~400 lines)
```

## Success Criteria
- No file > 600 lines
- All tests pass
- Related code stays together
- Clear file naming indicates purpose


Merge: topaz-1767138533986

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


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 mol run: Support proto lookup by title

`bd mol run` only accepts proto ID (e.g., gt-lwuu), not proto title (e.g., mol-polecat-work).

## Current Behavior
```bash
bd mol run mol-polecat-work --var issue=gt-xxx
# Error: no issue found matching "mol-polecat-work"

bd mol run gt-lwuu --var issue=gt-xxx
# Works
```

## Desired Behavior
Both should work - lookup by ID or by title.

## Why This Matters
- Proto titles are human-readable and memorable
- Proto IDs are opaque (gt-lwuu vs mol-polecat-work)
- Hardcoding IDs in code is fragile (IDs change across databases)
- `bd mol catalog` shows both: `gt-lwuu: mol-polecat-work`

## Workaround
Currently using hardcoded proto ID in gt spawn, which is brittle.


Release v{{version}}

Clean release workflow for beads. Variables: version (e.g., 0.37.0), date (YYYY-MM-DD)


Git merge driver uses invalid placeholders (%L, %R instead of %A, %B)

## Problem

The beads git merge driver is configured with invalid Git placeholders:

```
git config merge.beads.driver "bd merge %A %O %L %R"
```

Git doesn't recognize `%L` or `%R` as valid merge driver placeholders. The valid placeholders are:
- `%O` = base (common ancestor)
- `%A` = current version (ours)
- `%B` = other version (theirs)

## Impact

- Affects ALL users when they have `.beads/beads.jsonl` merge conflicts
- Automatic JSONL merge fails with error: "error reading left file: failed to open file: open 7: no such file or directory"
- Users must manually resolve conflicts instead of getting automatic merge

## Root Cause

The `bd init` command (or wherever the merge driver is configured) is using non-standard placeholders. When Git encounters `%L` and `%R`, it either passes them literally or interprets them incorrectly.

## Fix

Update the merge driver configuration to:
```
git config merge.beads.driver "bd merge %A %O %A %B"
```

Where:
- 1st `%A` = output file (current file, will be overwritten)
- `%O` = base (common ancestor)
- 2nd `%A` = left/current version
- `%B` = right/other version

## Action Items

1. Fix `bd init` (or equivalent setup command) to use correct placeholders
2. Add migration/warning for existing users with misconfigured merge driver
3. Update documentation with correct merge driver setup
4. Consider adding validation when `bd init` is run


bd hook: detect agent from cwd instead of defaulting to $USER

**Problem:**
`bd hook` without `--agent` defaults to `$USER` (e.g., "stevey") instead of detecting the agent identity from the current working directory.

**Expected behavior:**
When running from `/Users/stevey/gt/beads/crew/dave`, the agent should be detected as `beads/crew/dave`.

**Current behavior:**
```bash
$ pwd
/Users/stevey/gt/beads/crew/dave
$ bd hook
Hook: stevey
  (empty)

$ bd hook --agent beads/crew/dave
Hook: beads/crew/dave
  📌 bd-hobo (mol) - open
```

**Fix:**
bd hook should use the same cwd-based agent detection that other commands use (similar to how `gt mail` determines identity from cwd).


Test actor flag


Add {{version}} 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.


Beads Messaging & Knowledge Graph (v0.30.2)

Add messaging semantics and extended graph links to Beads, enabling it to serve as
the universal substrate for knowledge work - issues, messages, documents, and threads
as nodes in a queryable graph.

## Motivation

Gas Town (GGT) needs inter-agent communication. Rather than a separate mail system,
collapse messaging into Beads - one system, one sync, one query interface, all in git.

This also positions Beads as a foundation for:
- Company-wide issue tracking (like Notion)
- Threaded conversations (like Reddit/Slack)
- Knowledge graphs with loose associations
- Arbitrary workflow UIs built on top

## New Issue Type

**message** - ephemeral communication between workers
- sender: who sent it
- assignee: recipient
- priority: P0 (urgent) to P4 (routine)
- status: open (unread) -> closed (read)
- ephemeral: true = can be bulk-deleted after swarm

## New Graph Links

**replies_to** - conversation threading
- Messages reply to messages
- Enables Reddit-style nested threads
- Different from parent_id (not hierarchy, its conversation flow)

**relates_to** - loose see also associations
- Bidirectional knowledge graph edges
- Not blocking, not hierarchical, just related
- Enables discovery and traversal

**duplicates** - deduplication at scale
- Mark issue B as duplicate of canonical issue A
- Close B, link to A
- Essential for large issue databases

**supersedes** - version chains
- Design Doc v2 supersedes Design Doc v1
- Track evolution of artifacts

## New Fields (optional, any issue type)

- sender (string) - who created this (for messages)
- ephemeral (boolean) - can be bulk-deleted when closed

## New Commands

Messaging:
- bd mail send <recipient> -s Subject -m Body
- bd mail inbox (list open messages for me)
- bd mail read <id> (show message content)
- bd mail ack <id> (mark as read/close)
- bd mail reply <id> -m Response (reply to thread)

Graph links:
- bd relate <id1> <id2> (create relates_to link)
- bd duplicate <id> --of <canonical> (mark as duplicate)
- bd supersede <id> --with <new> (mark superseded)

Cleanup:
- bd cleanup --ephemeral (delete closed ephemeral issues)

## Identity Configuration

Workers need identity for sender field:
- BEADS_IDENTITY env var
- Or .beads/config.json: identity field

## Hooks (for GGT integration)

Beads as platform - extensible without knowing about GGT.
Hook files in .beads/hooks/:
- on_create (runs after bd create)
- on_update (runs after bd update)
- on_close (runs after bd close)
- on_message (runs after bd mail send)

GGT registers hooks to notify daemons of new messages.

## Schema Changes (Migration Required)

Add to issue schema:
- type: message (new valid type)
- sender: string (optional)
- ephemeral: boolean (optional)
- replies_to: string (issue ID, optional)
- relates_to: []string (issue IDs, optional)
- duplicates: string (canonical issue ID, optional)
- superseded_by: string (new issue ID, optional)

Migration adds fields as optional - existing beads unchanged.

## Success Criteria

1. bd mail send/inbox/read/ack/reply work end-to-end
2. replies_to creates proper thread structure
3. relates_to, duplicates, supersedes links queryable
4. Hooks fire on create/update/close/message
5. Identity configurable via env or config
6. Migration preserves all existing data
7. All new features have tests


bd repair: Limit verbose output for large orphan counts

When there are thousands of orphaned references, bd repair prints them all, flooding the terminal.

Fix: Add --verbose flag; default to summary only (e.g., 'Found 1,234 orphaned dependencies'). Only print individual items with --verbose or when count is small (<20).


Feature: bd swarm list - discover existing swarm molecules

No command to list existing swarm molecules.

Add `bd swarm list` to show all swarm molecules:

```
bd swarm list

Active swarms:
  bd-xyz: Swarm: Feature X (3/10 complete, 2 active)
  bd-abc: Swarm: Bug fixes (0/5 complete)
```

Could also be achieved via `bd list --mol-type=swarm` but dedicated command would show richer info.


Fix TestImportBugIntegration deadlock in importer_test.go

Code health review found internal/importer/importer_test.go has TestImportBugIntegration skipped with:

TODO: Test hangs due to database deadlock - needs investigation

This indicates a potential unresolved concurrency issue in the importer. The test has been skipped for an unknown duration.

Fix: Investigate the deadlock, fix the underlying issue, and re-enable the test.


Update local installation

Update local bd to the new version.

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

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

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

Should show 0.39.0.


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


Merge: bd-05a8

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


Merge: bd-dxtc

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


Ephemeral patrol molecules leaking into beads

## Problem

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

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

## Examples found

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

## Investigation needed

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

## Expected behavior

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


Remove bd mol spawn - use pour/wisp only

Remove the spawn command from bd mol. Replace with:

- bd pour <proto> - Instantiate as mol (liquid, persistent)
- bd wisp <proto> - Instantiate as wisp (vapor, ephemeral)

Rationale:
- 'spawn' doesn't fit the chemistry metaphor
- Two phase transitions (pour/wisp) are clearer than one command with flags
- Avoids confusion about defaults

Implementation:
1. Add bd pour command (currently bd mol spawn --pour)
2. Add bd wisp command (currently bd mol spawn without --pour)
3. Remove bd mol spawn
4. Update all docs/tests

Gas Town tracking: gt-ingm.6


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.


bd cook: needs field not converted to step dependencies

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

## Expected

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

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

## Actual

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

The `needs` field is ignored.

## Impact

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

## Reproduction

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


CLI cleanup: Rename bd mol catalog to bd formula list

## Problem
`bd mol catalog` shows formulas, not molecules. This is confusing.

Current state:
- `bd formula list` - lists formulas
- `bd mol catalog` - ALSO lists formulas (duplicate!)

The "mol" namespace should be for molecules (instantiated work), not formulas (templates).

## Proposed Change
- Remove `bd mol catalog`
- Keep `bd formula list` as the canonical command
- Update docs to use `bd formula list`

## Alternative
If we want a "catalog" concept separate from "list", make it `bd formula catalog`.


Merge: beads-ocs

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


Enhancement: bd swarm status accepts swarm molecule ID

Currently `bd swarm status` only accepts an epic ID.

Would be more intuitive to also accept the swarm molecule ID and follow the relates-to link to find the epic:

```bash
bd swarm status bd-swarm-xyz  # Follow link to epic, show status
bd swarm status bd-epic-123   # Direct epic lookup (current behavior)
```

Detect by checking if the issue has mol_type=swarm.


Redesign workflow system: templates as native Beads

## Problem

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

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

## The Right Design

**Templates should be Beads themselves.**

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

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

## Benefits

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

## Tasks

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


Verify the release is complete.

Verify the release is complete.

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

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

# Check npm
npm view @beads/bd version

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

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


Merge: bd-by0d

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


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

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

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

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

File: internal/formula/types.go


Upgrade beads-mcp

pip3 install --upgrade beads-mcp


mol-beads-release

Release checklist for beads version 0.37.0.

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

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

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

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

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

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

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

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

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

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

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

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

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

Needs: update-changelog

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

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

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

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

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

Needs: run-tests

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

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

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

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

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

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

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

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

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

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

# Check npm
npm view @beads/bd version

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

Needs: wait-for-ci

## Step: update-local
Update local installations.

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

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

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

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

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

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

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

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

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

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


Implement bd close --continue flag

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


Version Bump: 0.32.1

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


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


Code review findings: mol squash deletion bypasses tombstones


Create release tag

Create annotated git tag.

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

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


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.


Runtime bonding: bd mol attach

Attach a molecule to an already-running workflow.

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

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

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

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

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


Wisp storage: ephemeral molecule tracking

Implement ephemeral molecule storage for patrol cycles.

## Architecture

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

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

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

## Key Requirements

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

## Storage Locations

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

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


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

## Problem

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

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

## Root Cause

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

## Current Workaround

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

## Proposed Fix

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

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


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


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.


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.


Release complete

Release v0.99.0-test is complete!

Summary:
- All version files updated
- Git tag pushed
- CI artifacts built
- npm and PyPI packages published
- Local installation updated
- Daemons restarted

Optional next steps:
- Announce on social media
- Update documentation site
- Close related milestone


.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


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)


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


Code smell: Duplicated issue type switch in cook.go

The same switch statement for mapping step type to IssueType appears twice in cook.go:

Location 1 (line 425):
```go
switch step.Type {
case "task":
    issueType = types.TypeTask
case "bug":
    issueType = types.TypeBug
// ...
}
```

Location 2 (line 764): Identical switch statement.

Should extract to a helper function:
```go
func stepTypeToIssueType(stepType string) types.IssueType {
    switch stepType {
    case "task": return types.TypeTask
    case "bug": return types.TypeBug
    // ...
    default: return types.TypeTask
    }
}
```

Location: cmd/bd/cook.go:425 and cmd/bd/cook.go:764


Merge: bd-m8ro

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


Update .claude-plugin/marketplace.json to 0.30.4

Update version field in .claude-plugin/marketplace.json:
```json
"version": "0.30.4"
```

```verify
jq -e '.plugins[0].version == "0.30.4"' .claude-plugin/marketplace.json
```


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

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


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


Blocking logic: Include 'hooked' in blocking statuses

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

## Current Logic (line ~163)

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

## Updated Logic

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

## Files to Update

- internal/storage/sqlite/blocked_cache.go

## Test Cases

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


Merge: bd-thgk

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


Fix update-hooks verification in version-bump.yaml

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


Commit release

git add -A && git commit -m 'release: v{{version}}'


Kill running daemons

Stop all bd daemons before release:

```bash
pkill -f 'bd.*daemon' || true
sleep 1
pgrep -lf 'bd.*daemon'  # Should show nothing
```


Preflight: Check git status

Ensure working tree is clean before starting release.

```bash
git status
```

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


Re: Thread Test 2

Great! Thread is working well.


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.


Code smell: 20+ global variables in main.go

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

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

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

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


Respond to GitHub issue #479 with solution

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

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

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

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

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

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


Update info.go versionChanges

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

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

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

Focus on workflow-impacting changes agents need to know.


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.


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.


Rename ephemeral → wisp throughout codebase

## The Change

Rename 'ephemeral' to 'wisp' throughout the beads codebase.

## Why

**Ephemeral** is:
- 4 syllables (too long)
- Greek/academic (doesn't match bond/burn/squash)
- Overused in tech (K8s, networking, storage)
- Passive/descriptive

**Wisp** is:
- 1 syllable (matches bond/burn/squash)
- Evocative - you can SEE a wisp
- Steam engine metaphor - Gas Town is engines, steam wisps rise and dissipate
- Will-o'-the-wisp - transient spirits that guide then vanish
- Unique - nobody else uses it

## The Steam Engine Metaphor

```
Engine does work → generates steam
Steam wisps rise → execution trace
Steam condenses → digest (distillate)
Steam dissipates → cleaned up (burned)
```

## Full Vocabulary

| Term | Meaning |
|------|---------|
| bond | Attach proto to work (creates wisps) |
| wisp | Temporary execution step |
| squash | Condense wisps into digest |
| burn | Destroy wisps without record |
| digest | Permanent condensed record |

## Changes Required

### Code
- `Ephemeral bool` → `Wisp bool` in types/issue.go
- `--ephemeral` flag → remove (wisp is default)
- `--persistent` flag → keep as opt-out
- `bd cleanup --ephemeral` → `bd cleanup --wisps`
- Update all references in mol_*.go files

### Docs
- Update all documentation
- Update CLAUDE.md examples
- Update CLI help text

### Database Migration
- Add migration to rename field (or keep internal name, just change API)

## Example Usage After

```bash
bd mol bond mol-polecat-work   # Creates wisps (default)
bd mol bond mol-xxx --persistent  # Creates permanent issues
bd mol squash bd-xxx           # Condenses wisps → digest
bd cleanup --wisps             # Clean old wisps
bd list --wisps                # Show wisp issues
```


Create release tag

Create annotated git tag.

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

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


Add BD_AGENT_MODE for ultra-compact output

Optional agent-optimized output mode triggered by BD_AGENT_MODE=1 env var:

- Compact format: just ID and title per line
- Default to open/in_progress only
- No colors, no emojis
- Lower default limit (~20)
- One-line summaries

This is independent but benefits from TTY detection infrastructure.

Consider auto-detecting agent context via CLAUDE_CODE or similar env vars.

## Implementation Plan

### Trigger
```bash
BD_AGENT_MODE=1 bd list
```

Or auto-detect common agent environments:
```go
func IsAgentMode() bool {
    if os.Getenv("BD_AGENT_MODE") != "" {
        return true
    }
    // Auto-detect known agent contexts
    if os.Getenv("CLAUDE_CODE") != "" {
        return true
    }
    // Add other agent env vars as discovered
    return false
}
```

### Code Changes

1. **Add agent output format** (cmd/bd/list.go):
```go
if ui.IsAgentMode() {
    // Ultra-compact: just ID and title
    for _, issue := range issues {
        fmt.Printf("%s: %s\n", issue.ID, issue.Title)
    }
    return
}
```

2. **Agent mode defaults**:
```go
if ui.IsAgentMode() {
    if limit == 0 {
        limit = 20 // Lower default for agents
    }
    if status == "" {
        // Only actionable work
        filter.Status = []types.Status{"open", "in_progress"}
    }
}
```

### Output Format (Agent Mode)
```
bd-mypl: Default bd list to non-closed issues
bd-v5fn: Add default limit to bd list (50 issues)
bd-jdz3: Add pager support to bd list
```

No colors, no emojis, no brackets, no priority display.
Just ID + title for maximum context efficiency.

### Testing
- `BD_AGENT_MODE=1 bd list` outputs compact format
- Verify no ANSI codes in output
- Verify default limit is 20
- Verify only open/in_progress shown by default


Fix JSON errors in epic.go

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

Commands affected:
- epicStatusCmd
- epicCloseEligibleCmd


Read operations should not modify database file

Read-only operations (bd list, bd ready, bd show) modify the database file. Every open sets WAL pragma, runs schema/migrations, and checkpoints on close. Need sqlite.NewReadOnly() that opens with mode=ro, skips WAL/schema/migrations, and skips checkpoint. See GH#804.


Phase 1.2 follow-up: Clarify format storage

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

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

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


Merge: quartz-1767082908528

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


Verify GitHub release

Check the GitHub releases page.

https://github.com/steveyegge/beads/releases/tag/v0.99.0-test

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


Code smell: Repeated validation patterns could be centralized

Similar validation patterns appear in multiple commands (create.go, show.go, update.go, etc.):
- Check if resource exists
- Check if resource is in valid state
- Validate user input
- Check permissions

**Problem:**
- Duplicated validation logic
- Inconsistent validation between commands
- Hard to add new validation rules consistently

**Acceptance Criteria:**
- [ ] Create internal/validation package
- [ ] Centralize common validation logic
- [ ] Use composition for validation chains
- [ ] Update commands to use shared validators
- [ ] Tests pass


Release v0.30.4

## Version Bump Workflow

Coordinating release from 0.30.3 to 0.30.4.

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

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


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

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

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

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


Add composite index (issue_id, type) on dependencies table

GetBlockedIssues uses EXISTS clauses that filter by issue_id AND type together.

**Query pattern (ready.go:427-429):**
```sql
EXISTS (
  SELECT 1 FROM dependencies d2
  WHERE d2.issue_id = i.id AND d2.type = 'blocks'
)
```

**Problem:** Only idx_dependencies_issue exists. SQLite must filter type after index lookup.

**Solution:** Add migration:
```sql
CREATE INDEX IF NOT EXISTS idx_dependencies_issue_type ON dependencies(issue_id, type);
```

**Note:** This complements the existing idx_dependencies_depends_on_type for the reverse direction.


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.


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)


Test Parent Epic


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

## Problem

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

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

## Impact

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

## Proposed Solution

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

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

## Investigation Needed

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


Test Molecule

A test molecule


Time-dependent worktree detection tests may be flaky


> bd sync --no-daemon
→ Exporting pending changes to JSONL...
→ Committing changes to sync branch 'beads-sync'...
Error committing to sync branch: failed to create worktree: failed to create worktree parent directory: mkdir /var/home/matt/dev/beads/fix-ci/.git: not a directory

**Problem Diagnosed**: The `bd sync` command was failing with "mkdir /var/home/matt/dev/beads/fix-ci/.git: not a directory" because it was being executed from the wrong directory.

**Root Cause**: The command was run from `/var/home/matt/dev/beads` (where the `fix-ci` worktree exists) instead of the main repository directory `/var/home/matt/dev/beads/main`. Since `fix-ci` is a git worktree with a `.git` file (not directory), the worktree creation logic failed when trying to create `<current_dir>/.git/beads-worktrees/<branch>`.

**Solution Verified**: Execute `bd sync` from the main repository directory:
```bash
cd main && bd sync --dry-run
```


Test parent task


Release v{{version}}

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


Update CHANGELOG header

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


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.


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

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

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


Relocate thanks command to bd info --thanks

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

## Implementation

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

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

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

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


Update Homebrew formula

After GoReleaser completes, the Homebrew tap should be auto-updated.

If manual update needed:
```bash
./scripts/update-homebrew.sh v0.30.4
```

Or manually update steveyegge/homebrew-beads with new SHA256.

Verify:
```bash
brew update
brew info beads
```


Tier 1: Ephemeral repo routing

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

## The Fix

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

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

## Optional: Add --persistent flag

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

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

## Why This Is Simpler Than Original Design

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

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

## Acceptance Criteria

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


bd mol bond --ephemeral flag

Add --ephemeral flag to bd mol bond command.

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

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

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


Run tests and linting

Run the full test suite and linter:

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

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


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.


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)


Push release tag

Push the version tag to trigger CI release.

```bash
git push origin v0.39.0
```

This triggers GitHub Actions to build artifacts and publish.


gt polecat done should auto-stop running session

Currently 'gt polecat done' fails if session is running, requiring a separate 'gt session stop' first. This is unnecessary friction - done should just stop the session automatically since that's always what you want.


Test deleteViaDaemon RPC client integration

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

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

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

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

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


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


Document BEADS_DIR pattern for multi-agent workspaces (Gas Town)

Gas Town and similar multi-agent systems need to configure separate beads databases per workspace/rig, distinct from any project-level beads.

## Use Case

In Gas Town:
- Each 'rig' (managed project) has multiple agents (polecats, refinery, witness)
- All agents in a rig should share a single beads database at the rig level
- This should be separate from any .beads/ the project itself uses
- The BEADS_DIR env var enables this

## Documentation Needed

1. Add a section to docs explaining BEADS_DIR for multi-agent setups
2. Example: setting BEADS_DIR in agent startup scripts/hooks
3. Clarify interaction with project-level .beads/ (BEADS_DIR takes precedence)

## Current Support

Already implemented in internal/beads/beads.go:FindDatabasePath():
- BEADS_DIR env var is checked first (preferred)
- BEADS_DB env var still supported (deprecated)
- Falls back to .beads/ search in tree

Just needs documentation for the multi-agent workspace pattern.


Refactor: Extract shared getEpicChildren helper for swarm commands

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

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

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

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

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

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


Remove top-level comment alias

Remove 'comment' as top-level command. Users should use 'comments add' instead. Reduces command surface area.


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

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

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

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


Restart daemons

Restart bd daemons to pick up new version.

```bash
bd daemons killall
```

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

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


Merge: bd-4opy

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


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

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

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

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

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

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

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

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

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


Investigate TestRoutingIntegration failure

TestRoutingIntegration/maintainer_with_SSH_remote failed during pre-commit check with "expected role maintainer, got contributor".
This occurred while running `go test -short ./...` on darwin/arm64.
The failure appears unrelated to storage/sqlite changes.
Need to investigate if this is a flaky test or environmental issue.


Test suite hangs/never finishes

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


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


Run check-versions.sh - all must pass

Run the version consistency check:
```bash
./scripts/check-versions.sh
```

All versions must match 0.30.4.

```verify
./scripts/check-versions.sh
```


Test coordinator display


Add stale molecule check to bd doctor

Extend bd doctor to detect stale molecules.

**New check:**
- Name: 'Stale Molecules'
- Category: Workflow
- Severity: Warning (don't fail overall check)

**Detection:**
Reuse logic from bd mol stale command:
- Find mols where Completed >= Total but root is open
- Filter to orphaned (not assigned, not pinned)
- Extra weight if blocking other work

**Output:**
```
⚠ Stale Molecules
  Found 2 complete-but-unclosed molecules:
  - bd-xyz: Version bump v0.36.0 (blocking 1 issue)
  - bd-uvw: Old patrol (not blocking)
  Fix: bd close <id> or bd mol squash <id>
```

**--fix behavior:**
- Auto-close stale mols (with reason 'Auto-closed by bd doctor')
- Or prompt interactively with -i flag

Depends on: bd mol stale command


bd wisp gc command

Add bd wisp gc command to garbage collect orphaned wisps.

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

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

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

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


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.


Review changes since last release

Understand what's being released.

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

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


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


Consider using types.Status in merge package for type safety

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


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

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

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

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

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

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


bd swarm status command

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

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

NOT stored separately. If beads changes, status changes.

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

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

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

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

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

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


Dynamic Molecule Bonding: Fanout patterns for patrol molecules

## Vision

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

## The Activity Feed Vision

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

```
[14:32:08] + patrol-x7k.arm-ace bonded (5 steps)
[14:32:08] + patrol-x7k.arm-nux bonded (5 steps)
[14:32:09] → patrol-x7k.arm-ace.capture in_progress
[14:32:10] ✓ patrol-x7k.arm-ace.capture completed
[14:32:14] ✓ patrol-x7k.arm-ace.decide completed (action: nudge-1)
```

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

## Key Primitives Needed

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

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

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

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

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

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

## Use Case: mol-witness-patrol

The Witness monitors N polecats where N varies at runtime:

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

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

## Design Docs

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

## Dependencies

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


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


Code smell: Long function flushToJSONLWithState (~280 lines)

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

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

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

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


Add bd update --parent flag to change issue parent

Allow changing an issue's parent with bd update --parent <new-parent-id>. Useful for reorganizing tasks under different epics or moving issues between hierarchies. Should update the parent-child dependency relationship.


Verify PyPI package

Confirm PyPI package published.

```bash
pip index versions beads-mcp 2>/dev/null | head -3
```

Or check: https://pypi.org/project/beads-mcp/

Should show 0.99.0-test.


Unify template.go and mol.go under bd mol

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


swarm status: Handle external dependencies and error cases

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


test hook pin fix


Routing bypassed in daemon mode

## Bug

Prefix-based routing (routes.jsonl) only works in direct mode. When the daemon is running, it bypasses routing logic because:

1. Daemon mode resolves IDs via RPC (`daemonClient.ResolveID`)
2. Daemon uses its own store, which only knows about local beads
3. The routing logic in `resolveAndGetIssueWithRouting` is only called in direct mode

## Reproduction

```bash
# With daemon running:
cd ~/gt
bd show gt-1py3y  # Fails - daemon can't find it

# With daemon bypassed:
bd --no-daemon show gt-1py3y  # Works - uses routing
```

## Fix Options

### Option A: Client-side routing detection (Recommended)
Before calling daemon RPC, check if the ID prefix matches a route to a different beads dir. If so, use direct mode with routing for that ID.

```go
// In show.go, before daemon mode:
if daemonClient != nil && needsRouting(id) {
    // Fall back to direct mode for this ID
    result, err := resolveAndGetIssueWithRouting(ctx, store, id)
    ...
}
```

### Option B: Daemon-side routing
Add routing support to the daemon RPC server. More complex - daemon would need to:
- Load routes.jsonl on startup
- Open connections to multiple databases
- Route requests based on ID prefix

### Option C: Hybrid
Daemon returns "not found + prefix hint", client retries with routing.

## Recommendation

Option A is simplest. The client already has routing logic; just use it when we detect a routed prefix.


Consolidate git context into single cached struct

Code review finding from bd-7di fix.

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

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

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

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

File: internal/git/gitdir.go


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.


Merge: bd-u2sc.4

branch: polecat/Logger
target: main
source_issue: bd-u2sc.4
rig: beads


Run bump-version.sh

Update all component versions atomically.

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

This updates:
- cmd/bd/version.go
- .claude-plugin/*.json
- integrations/beads-mcp/pyproject.toml
- integrations/beads-mcp/src/beads_mcp/__init__.py
- npm-package/package.json
- Hook templates
- README.md
- CHANGELOG.md (adds date)


Update local binary

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


Merge: bd-l13p

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


Time-dependent tests may be flaky near TTL boundary

Several tombstone merge tests use time.Now() to create test data: time.Now().Add(-24 * time.Hour), time.Now().Add(-60 * 24 * time.Hour), etc. While these work reliably in practice (24h vs 30d TTL has large margin), they could theoretically be flaky if: 1) Tests run slowly, 2) System clock changes during test, 3) TTL constants change. Recommendation: Consider using a fixed reference time or time injection for deterministic tests. Lower priority since current margin is large. Files: internal/merge/merge_test.go:1337-1338, 1352-1353, 1548-1549, 1590-1591


Improve test coverage for cmd/bd CLI (26.2% → 50%)

The main CLI package (cmd/bd) has only 26.2% test coverage. CLI commands should have at least 50% coverage to ensure reliability.

Key areas with low/no coverage:
- daemon_autostart.go (multiple 0% functions)
- compact.go (several 0% functions)
- Various command handlers

Current coverage: 26.2%
Target coverage: 50%

## Progress Update (2025-12-30 - Session 2)

### Tests Added
Added 479 lines of new tests across 4 files:
- cmd/bd/utils_unit_test.go (214 lines) - NEW
  - TestTruncateString, TestPluralize, TestFormatTimeAgo
  - TestContainsLabel, TestGetContributorsSorted, TestExtractIDSuffix
  - TestTruncate, TestTruncateDescription
  - TestShowCleanupDeprecationHint, TestClearAutoFlushState

- cmd/bd/hooks_test.go (117 lines added)
  - TestFormatHookWarnings, TestIsRebaseInProgress, TestHasBeadsJSONL

- cmd/bd/list_test.go (140 lines added)
  - TestFormatIssueLong, TestFormatIssueCompact

- cmd/bd/version_tracking_test.go (8 lines)
  - Fixed flaky tests by setting BEADS_DIR env var

### Coverage Results
- Before: 21.6%
- After: 22.0%
- Delta: +0.4%

### Remaining Work
Coverage is still at 22% vs target 50%. The remaining 78% involves:
1. Daemon/RPC operations (runDaemonLoop, tryAutoStartDaemon, etc.)
2. Command handlers requiring database/daemon setup
3. Git operations (runPreCommitHook, runPostMergeHook, etc.)

To reach 50%, would need:
- Integration tests with mocked daemon (use -tags=integration)
- scripttest tests for command handlers
- More database-dependent tests

Commit: 8c7d94d3


Test message

Hello world


Add daemon RPC endpoints for config and stale check

Two TODOs request daemon RPC endpoints:

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

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

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


Phase 2.3: TOON deletion tracking

Implement deletion tracking in TOON format.

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

## Required Work

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

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

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

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


Update git hooks

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

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

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


bd prime should mention when beads is redirected

## Problem

Agents running in a redirected clone don't know they're sharing beads with other clones. This can cause confusion when molecules or issues seem to 'appear' or 'disappear'.

## Proposed Solution

When `bd prime` runs and detects a redirect, include it in the output:

```
Beads: /Users/stevey/gt/beads/mayor/rig/.beads
       (redirected from crew/dave - you share issues with other clones)
```

## Why

Visibility over magic. If agents can see the redirect, they can reason about it.

## Related

- bd where command (shows this on demand)
- gt redirect following (ensures gt matches bd behavior)


Merge: bd-ao0s

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


Give --all flag actual meaning in bd list

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

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

## Implementation Plan

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

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

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

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

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


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.


Fix JSON errors in label.go (label commands)

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

Commands affected:
- labelAddCmd
- labelRemoveCmd
- labelListCmd
- labelListAllCmd


Add {{version}} to versionChanges in info.go

Add new entry at the TOP of versionChanges array in cmd/bd/info.go:

```go
{
    Version: "{{version}}",
    Date:    "{{date}}",
    Changes: []string{
        // Add notable changes here
    },
},
```

Copy changes from CHANGELOG.md [Unreleased] section.


Verify PyPI package

Confirm PyPI package published.

```bash
pip index versions beads-mcp 2>/dev/null | head -3
```

Or check: https://pypi.org/project/beads-mcp/

Should show 0.41.0.


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.


activity.go: Missing test coverage

The new activity.go command (from bd-xo1o.3) has no test coverage. At minimum, tests should cover:
- parseDurationString() for various formats (5m, 1h, 2d, invalid)
- filterEvents() for --mol and --type filtering
- formatEvent() and getEventDisplay() for all mutation types

Discovered during code review of bd-xo1o implementation.


Sling reports 'already pinned' but mol status shows empty hook

During swarm operations, gt sling reported beads were 'already pinned' but gt mol status showed nothing on the hook.

**Observed:**
```
$ gt sling bd-9btu beads/Nux
Error: bead bd-9btu is already pinned to gt-beads-nux

$ gt mol status beads/Nux
Nothing on hook - no work slung
```

**Expected:**
If a bead is pinned, mol status should show it. If mol status shows empty, sling should work.

**Workaround:**
Used --force flag to re-sling, which worked.

**Root cause hypothesis:**
Agent bead state may be stored in town beads but polecat's local view doesn't see it, or the pin record exists but session state is stale.


Test issue 2


Verify MCP Server Compatibility

Ensure MCP server works with cache-free daemon


bd repair: Handle .beads/redirect files

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

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


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.


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

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

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

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

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


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.


test pin issue


Restart daemon with {{version}}

Restart the bd daemon to pick up new version:

```bash
bd daemon --stop
bd daemon --start
bd daemon --health  # Verify Version: {{version}}
```


witness Handoff


Test epic for duplicate swarm


Update CHANGELOG.md with release notes

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


test pin


Remove molecule.go (simple instantiation)

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


Tier 2: Basic bd mol squash command

Add bd mol squash command for basic molecule execution compression.

## Command

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

## Implementation

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

## Schema Changes

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

## Acceptance Criteria

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


Merge: ruby-1767138542815

branch: polecat/ruby-1767138542815
target: main
source_issue: ruby-1767138542815
rig: beads


🤝 HANDOFF: v0.31.0 released, molecules discussion

Session completed 0.31.0 release and had important molecules discussion.

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

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

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

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

Check bd ready and gt-975/976 status.


enhance 'bd status' to show recent activity

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


Add prefix-based routing with routes.jsonl

## Summary

When running `bd show gt-xyz` from a location where that bead doesn't exist locally,
bd should check a `routes.jsonl` file to find where beads with that prefix live.

## Problem

From ~/gt (town root):
- `bd show hq-xxx` works (hq-* beads are local)
- `bd show gt-xxx` fails (gt-* beads live in gastown/mayor/rig)

This breaks `gt hook`, `gt handoff`, and `gt prime` which need to verify beads exist.

## Solution

Add `.beads/routes.jsonl` for prefix-based routing:

\`\`\`json
{"prefix": "gt-", "path": "gastown/mayor/rig"}
{"prefix": "hq-", "path": "."}
{"prefix": "bd-", "path": "beads/crew/dave"}
\`\`\`

### Lookup Algorithm

1. Try local .beads/ first (current behavior)
2. If not found, check for routes.jsonl in current .beads/
3. Match ID prefix against routes
4. If match found, resolve path relative to routes file location and retry
5. If still not found, error as usual

### Affected Commands

Read operations that take a bead ID:
- bd show <id>
- bd update <id>
- bd close <id>
- bd dep add <id> <dep> (both IDs)

Write operations (bd create) still go to local .beads/ - no change needed.

## Implementation Notes

1. New file: routes.jsonl in .beads/ directory
2. New function: resolveBeadsPath(id string) (string, error)
3. Update: beads.Show(), beads.Update(), beads.Close(), etc. to use resolver
4. Caching: Can cache routes in memory since file rarely changes

## Context

This unblocks the gt handoff flow for Mayor. Currently gt handoff gt-xyz fails
because it can't verify the bead exists when running from town root.


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

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


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

Work on gt-8tmz.36: Validate expanded step IDs are unique. In internal/formula/, add validation during cooking that checks for duplicate step IDs after expansion. When done: 1) bd close gt-8tmz.36, 2) bd sync, 3) git push, 4) gt mq submit


bd swarm validate command

Add bd swarm validate to check epic structure for swarming.

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

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

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

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

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


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

attached_args: Refactor ComputeContentHash repetitive code

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

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

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

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


bd wisp create doesn't set Wisp flag on spawned issues

When running 'bd wisp create <proto>', the spawned issues don't have Wisp: true set. They show up in 'bd ready' and get synced to JSONL.

Example: gt-lqc6m and gt-wisp-ry9 are mol-deacon-patrol issues that should be wisps but have wisp: null.

This causes patrol cycles to pollute the issue database and show up as ready work.


Molecule bonding: composable workflow templates

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

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

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

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

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

DATA MODEL: Issues track bonded_from to preserve compound lineage.

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


Add .beads-backup-* pattern to gitignore template

Update the gitignore template in doctor package to include backup directories.

## Change
Add `.beads-backup-*/` to the GitignoreTemplate in `cmd/bd/doctor/gitignore.go`

## Why
Backup directories created by `bd reset --backup` should not be committed to git.
They are local-only recovery tools.

## File
`cmd/bd/doctor/gitignore.go` - look for GitignoreTemplate constant


Molecule execution state management

Implement molecule execution state tracking.

## State Location

```
.beads/molecules/<mol-id>.state.yaml
```

## State Schema

```yaml
id: mol-deacon-patrol
formula: mol-deacon-patrol  # Source formula
bonded_at: ISO timestamp
bonded_by: entity who created it

# Execution state
status: running | paused | completed | failed
current_step: step-id
started_at: ISO timestamp
completed_at: ISO timestamp (if done)

# Loop tracking
reset_count: 0
last_reset_at: null

# Per-step state
steps:
  inbox-check:
    status: completed | in_progress | pending
    started_at: ...
    completed_at: ...
  spawn-work:
    status: pending
  self-inspect:
    status: pending

# Variables (from formula instantiation)
variables:
  rig: gastown
  issue_id: gt-xxx
```

## Operations

- CreateState(molID, formula, variables) - Initialize state
- LoadState(molID) - Read current state
- SaveState(molID, state) - Write state
- AdvanceStep(molID) - Mark current complete, find next
- ResetState(molID) - Reset all steps to pending

## Files

- internal/mol/state.go
- internal/mol/types.go


Protect pinned issues from bd cleanup/compact

Update bd cleanup and bd compact to never delete pinned issues, even if they are closed. Pinned issues should persist indefinitely as reference material.


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