Design auto-squash behavior for wisps

Explore the design space for automatic wisp squashing.

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

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

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

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

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

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

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


mol-beads-release

Release checklist for beads version {{version}}.

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

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

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

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

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

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

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

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

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

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

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

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

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

Needs: update-changelog

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

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

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

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

```bash
git add -A
git commit -m "chore: bump version to v{{version}}"
```

Needs: run-tests

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

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

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

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

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

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

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

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

```bash
# Check GitHub release
gh release view v{{version}}

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

# Check npm
npm view @beads/bd version

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

Needs: wait-for-ci

## Step: update-local
Update local installations with proper codesigning (macOS).

```bash
# Build from source in mayor/rig (canonical build location)
cd ~/gt/beads/mayor/rig
git pull
go build -o bd ./cmd/bd

# Sign and install (macOS requires codesigning to avoid "Killed: 9")
# Uses fix-gt script which handles both gt and bd binaries
fix-gt

# Or manually sign if fix-gt not available:
# xattr -cr bd && codesign -f -s - bd
# cp bd ~/go/bin/bd && codesign -f -s - ~/go/bin/bd
# cp bd ~/.local/bin/bd && codesign -f -s - ~/.local/bin/bd

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

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

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

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

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

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

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

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


Update info.go versionChanges

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


Priority: refactor mol.go then bd squash

Two tasks:

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

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

- Mayor


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

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


Merge: onyx-1767083477016

branch: polecat/onyx-1767083477016
target: main
source_issue: onyx-1767083477016
rig: beads


Merge: jasper-1767083473604

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


Implement backup functionality for reset

Add backup capability that can be used by reset command.

## Functionality
- Copy .beads/ to .beads-backup-{timestamp}/
- Timestamp format: YYYYMMDD-HHMMSS
- Preserve file permissions
- Return backup path for user feedback

## Location
`internal/reset/backup.go` - keep with reset package for now (YAGNI)

## Interface
```go
func CreateBackup(beadsDir string) (backupPath string, err error)
```

## Notes
- Simple recursive file copy, no compression needed
- Error if backup dir already exists (unlikely with timestamp)
- Backup directories SHOULD be gitignored
- Add `.beads-backup-*/` pattern to .beads/.gitignore template in doctor package
- Consider: ListBackups() for future `bd backup list` command (not for this PR)


Test stdin body-file


mol-beads-release

Release checklist for beads version 0.99.0.

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

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

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

Needs: run-tests

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

```bash
git push origin main
git tag v0.99.0
git push origin v0.99.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.99.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.99.0 --install

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

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

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

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

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

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

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

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


gt mq list shows empty when MRs exist


Phase 2.2: Switch bdt storage to TOON format

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

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

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

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

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

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

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

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


Migration invariants should exclude external dependencies from orphan check

## Summary

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

## Location

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

## Current Code (buggy)

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

## Fix

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

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

## Reproduction

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

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

## Files to Modify

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

## Testing

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

# Sync should succeed
bd sync

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

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


GH#692: Code quality and refactoring improvements

Epic for implementing refactoring suggestions from GitHub issue #692 (rsnodgrass). These are code quality improvements that don't change functionality but improve maintainability, type safety, and performance.

Original issue: https://github.com/steveyegge/beads/issues/692

High priority items:
1. Replace map[string]interface{} with typed structs for JSON output
2. Adopt slices.SortFunc instead of sort.Slice (Go 1.21+)
3. Split large files (sync.go, init.go, show.go)
4. Introduce slog for structured logging in daemon

Lower priority:
5. Further CLI helper extraction
6. Preallocate slices in hot paths
7. Polish items (error wrapping, table-driven parsing)


Refactor sync_test to use direct import logic

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

File: cmd/bd/sync_test.go:444


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


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


Technical debt: Multiple deprecated command aliases still exist

attached_args: Remove deprecated command aliases

Several deprecated command aliases are still present in the codebase:

1. cmd/bd/relate.go:51 - 'bd relate' deprecated for 'bd dep relate'
2. cmd/bd/relate.go:56 - 'bd unrelate' deprecated for 'bd dep unrelate'
3. cmd/bd/daemons.go:618 - 'bd daemons' deprecated for 'bd daemon <subcommand>'
4. cmd/bd/migrate_hash_ids.go:433 - alias for 'bd migrate hash-ids'
5. cmd/bd/migrate_tombstones.go:352 - alias for 'bd migrate tombstones'
6. cmd/bd/migrate_sync.go:68 - alias for 'bd migrate sync'
7. cmd/bd/migrate_issues.go:717 - alias for 'bd migrate issues'
8. cmd/bd/comments.go:214 - deprecated for 'bd comments add'
9. cmd/bd/template.go:64,82,147,226 - multiple template commands deprecated for 'bd mol'
10. cmd/bd/admin_aliases.go - multiple admin aliases deprecated
11. cmd/bd/detect_pollution.go:24 - deprecated for 'bd doctor --check=pollution'

Consider:
1. Setting a deprecation timeline (e.g., remove in v2.0)
2. Adding warnings when deprecated commands are used
3. Documenting migration path in CHANGELOG

Location: Various files in cmd/bd/


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

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

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

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

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

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


Add agent identity to commit metadata for forensics

## Problem

When agents (polecats, crew workers) make commits on behalf of a human, the commit only shows:
- **Author**: Human (e.g., Steve Yegge)
- **Co-Authored-By**: Claude Opus 4.5 (generic)

Missing from the audit trail:
- Which agent instance performed the work
- Whether it was a polecat (transient) or crew (persistent)
- What rig/workspace it ran in

Example from a recent release commit:
```
Author: Steve Yegge <steve.yegge@gmail.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
```

No way to trace this back to the specific agent session that executed the release molecule.

## Proposal

Add git trailers to commits made by Gas Town agents:

```
Executed-By: beads/polecat/Nux-1766978911613
Rig: beads
Role: polecat
```

Or for crew workers:
```
Executed-By: beads/crew/emma
Rig: beads
Role: crew
```

## Benefits

1. **Forensics**: If a release goes wrong, trace back to the exact agent session
2. **Audit**: Query commits by agent type (`git log --grep="Role: polecat"`)
3. **Debugging**: Correlate commits with handoff mail and session logs
4. **Accountability**: Distinguish human-direct vs agent-mediated commits

## Implementation

Could be implemented via:
- Claude Code hooks (UserPromptSubmit or pre-commit)
- `gt` commit helper that injects trailers
- CLAUDE.md instructions for agents to include trailers

## Open Questions

- Should molecule ID be included if work was part of a molecule?
- Should session ID or other correlation data be included?
- Privacy considerations for commit metadata?


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.


Edge Schema Consolidation: Unify all edges in dependencies table

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

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

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


Simplify bd onboard to minimal AGENTS.md snippet pointing to bd prime

## Context
GH#604 raised concerns about bd onboard bloating AGENTS.md with ~100+ lines of static instructions that:
- Load every session whether beads is being used or not
- Get stale when bd upgrades
- Waste tokens

## Solution
Simplify `bd onboard` to output a minimal snippet (~2 lines) that points to `bd prime`:

```markdown
## Issue Tracking
This project uses beads (`bd`) for issue tracking.
Run `bd prime` for workflow context, or hooks auto-inject it.
```

## Rationale
- `bd prime` is dynamic, concise (~80 lines), and always matches installed bd version
- Hooks already auto-inject `bd prime` at session start when .beads/ detected
- AGENTS.md only needs to mention beads exists, not contain full instructions

## Implementation
1. Update `cmd/bd/onboard.go` to output minimal snippet
2. Keep `--output` flag for BD_GUIDE.md generation (may still be useful)
3. Update help text to explain the new approach


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


Work on beads-2vn: Add simple built-in beads viewer (GH#6...

Work on beads-2vn: Add simple built-in beads viewer (GH#654). Add bd list --pretty with --watch flag, tree view with priority/status symbols. When done, submit MR (not PR) to integration branch for Refinery.


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.


Verify release artifacts

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


Distill: promote ad-hoc epic to proto

Extract a reusable proto from an existing ad-hoc epic.

COMMAND: bd mol distill <epic-id> [--as <proto-name>]

BEHAVIOR:
- Clone the epic and all children as a new proto
- Set is_template=true on all cloned issues
- Replace concrete values with {{variable}} placeholders (interactive or --var flags)
- Add to proto catalog

FLAGS:
- --as NAME: Custom proto ID (default: proto-<epic-id>)
- --var field=placeholder: Replace value with variable placeholder
- --interactive: Prompt for each field that looks parameterizable
- --dry-run: Preview the proto structure

EXAMPLE:
  bd mol distill bd-o5xe --as proto-feature-workflow \
    --var title=feature_name \
    --var assignee=worker

USE CASES:
- Team develops good workflow organically, wants to reuse it
- Capture tribal knowledge as executable templates
- Create starting point for similar future work

The reverse of spawn: instead of proto → molecule, it's molecule → proto.


Kill running daemons

Stop all bd daemons before release:

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


Move cook to formula subcommand

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

## Implementation

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

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

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

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

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


Merge: bd-thgk

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


Test Parent Epic


Add bd template list command

Add a convenience command to list available templates:

bd template list

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


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

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

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

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


bd activity --town: Aggregated town feed

Add --town flag to bd activity for aggregated cross-rig feed.

Behavior:
- Uses routes.jsonl to discover all rigs
- Aggregates activity from all rig beads
- Essential for Deacon's convoy tracking and cross-rig dep resolution

Usage:
  bd activity --follow --town   # Stream all rig activity

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


Cross-project dependency support

Enable tracking dependencies across project boundaries.

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

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

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


Witness Patrol

Per-rig worker monitor patrol loop with progressive nudging.


Test Agent


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


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.


GH#532: bd sync circular error (suggests running bd sync)

bd sync error message recommends running bd sync to fix the bd sync error. Fix error handling to provide useful guidance. See GitHub issue #532.


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.


Integrate detect-pollution into bd doctor

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

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

## Implementation

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

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

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

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


Auto-recovery mode (bd sync --auto-recover)

Add bd sync --auto-recover flag that: detects problematic sync state, backs up .beads/issues.db with timestamp, rebuilds DB from JSONL atomically, verifies consistency, reports what was fixed. Provides safety valve when sync integrity fails.


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

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


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

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

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

## Functions Needing Tests

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

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

## Implementation Guide

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

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

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

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

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

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


Add content-addressable identity to Issue type

**Summary:** Added content-addressable identity to Issue type by implementing a ContentHash field that generates a unique SHA256 fingerprint based on semantic issue content. This resolves issue identification challenges when multiple system instances create issues with identical IDs but different contents.

**Key Decisions:**
- Use SHA256 for content hashing
- Hash excludes ID and timestamps
- Compute hash automatically at creation/import time
- Add database column for hash storage

**Resolution:** Successfully implemented a deterministic content hashing mechanism that enables reliable issue identification across distributed systems, improving data integrity and collision detection.


Merge: bd-io8c

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


Formula versioning: Version field and git tag integration

Add versioning support to formulas for tracking updates.

## Formula Version Field
```json
{
  "formula": "mol-code-review",
  "version": "1.2.0",
  "min_bd_version": "0.25.0",
  ...
}
```

## Version Semantics
- Use semver: MAJOR.MINOR.PATCH
- MAJOR: Breaking changes to step structure
- MINOR: New optional steps or variables
- PATCH: Documentation, bug fixes

## Git Tag Integration
- `bd mol install repo@v1.2.0` fetches that tag
- `bd mol install repo` fetches latest tag (or main if no tags)
- `bd mol publish --tag v1.2.0` creates tag

## Proto Version Tracking
Local proto should store:
```
source_repo: github.com/anthropics/mol-code-review
source_version: v1.2.0
installed_at: 2025-12-25T12:00:00Z
```

This enables `bd mol update` to check for newer versions.


Add refs field for cross-references with relationship type

The refs field enables rich relationships beyond parent/child and depends-on.

Structure:
  refs: [{id: string, rel: string}]

Relationship types:
- until: active until target closes
- caused_by: triggered by target
- relates_to: general association
- supersedes: replaces target
- validates: approval relationship

Example:
  refs:
    - id: gt-abc1
      rel: until
    - id: hq-xyz9
      rel: caused_by

Enables:
- bd show gt-abc1 --refs (what references this?)
- Conditional state (muted UNTIL issue closes)
- Audit trails (this event CAUSED_BY that issue)


Test daemon RPC delete handler

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

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

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

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


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

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

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

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

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

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


bd doctor --deep: validate full graph integrity

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

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

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

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

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


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


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


Work on beads-wkt: Output control parameters for MCP tool...

Work on beads-wkt: Output control parameters for MCP tools (GH#622). Add brief, fields, max_description_length params to ready/list/show. When done, submit MR (not PR) to integration branch for Refinery.


Create git tag v{{version}}

Create the release tag:

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

Verify: `git tag | grep {{version}}`


Merge: bd-d28c

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


Add sync resilience guardrails for forced pushes and prefix mismatches

Beads can get into unrecoverable sync states when remote forces pushes occur (e.g., rebases) combined with prefix mismatches from multi-worker scenarios. Add detection, prevention, and auto-recovery features to handle this gracefully.


GH#517: Claude sets priority wrong on new install

Claude uses 'medium/high/low' for priority instead of P0-P4. Update bd prime/onboard output to be clearer about priority syntax. See GitHub issue #517.


Merge: quartz-1767082908528

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


bd swarm validate command

Add bd swarm validate to check epic structure for swarming.

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

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

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

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

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


Merge: bd-u2sc.4

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


Fix --as flag: uses title instead of ID in mol bond

In bondProtoProto, the --as flag is documented as 'Custom ID for compound proto' but the implementation uses it as the title, not the issue ID.

**Current behavior (mol.go:637-638):**
```go
if customID != '' {
    compoundTitle = customID  // Used as title, not ID
}
```

**Options:**
1. Change flag description to say 'Custom title' (documentation fix)
2. Actually use it as a custom ID prefix or full ID (feature change)
3. Add separate --title flag and make --as actually set ID

Recommend option 1 for simplest fix - change 'Custom ID' to 'Custom title' in the flag description.


Add close_reason field to CLI schema and documentation

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

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

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

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


Review PR #512: clarify bd ready docs

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


test pin issue


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.


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.


Implement bd mol current command

Show what molecule the agent should currently be working on. Referenced by gt-um6q, gt-lz13. Needed for molecule navigation workflow in templates.


Sync branch integrity guards

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


Polecats should spawn with auto-accept mode enabled

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

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

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

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

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


RemoveDependency fails on external deps - FK violation in dirty_issues

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

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

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


Gate eval: Add error visibility for gh CLI failures

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

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

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

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


Update npm-package/package.json to 0.30.4

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

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


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


Merge: bd-uqfn

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


Update CHANGELOG.md for {{version}}

In CHANGELOG.md:

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

Format:
```markdown
## [Unreleased]

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

### Added
- ...

### Changed
- ...

### Fixed
- ...
```


Optimization: Tombstones still synced, adding overhead

Tombstoned (deleted) issues are still processed during sync, adding overhead.

## Evidence
```
Import complete: 0 created, 0 updated, 407 unchanged, 100 skipped
```
Those 100 skipped are tombstones - they're read from JSONL, parsed, then filtered out.

## Current State (Beads repo)
- 408 total issues
- 99 tombstones (24% of database)
- Every sync reads and skips these 99 entries

## Impact
- Sync time increases with tombstone count
- JSONL file size grows indefinitely
- Git history accumulates tombstone churn

## Proposed Solutions

### 1. JSONL Compaction (`bd compact`)
Periodically rewrite JSONL without tombstones:
```bash
bd compact  # Removes tombstones, rewrites issues.jsonl
```
Trade-off: Loses delete history, but that's in git anyway.

### 2. Tombstone TTL
Auto-remove tombstones older than N days during sync:
```go
if issue.Deleted && time.Since(issue.UpdatedAt) > 7*24*time.Hour {
    // Skip writing to new JSONL
}
```

### 3. Archive File
Move old closed issues to `issues-archive.jsonl`:
- Not synced regularly
- Available for historical queries
- Main JSONL stays small

### 4. Lazy Tombstone Handling
Don't write tombstones to JSONL at all - just remove the line:
- Simpler, but loses cross-clone delete propagation
- Would need different delete propagation mechanism

## Recommendation
Start with `bd compact` command - simple, explicit, user-controlled.

## Related
- gt-tnss: Analysis - Beads database size and hygiene strategy
- gt-ox67: Maintenance - Regular cleanup of closed MR/gate beads


Dead code: fetchAndRebaseInWorktree() marked DEPRECATED but still exists

attached_args: Remove dead code fetchAndRebaseInWorktree

The function fetchAndRebaseInWorktree() in internal/syncbranch/worktree.go (lines 811-830) is marked as DEPRECATED with a comment:

```go
// fetchAndRebaseInWorktree is DEPRECATED - kept for reference only.
// Use contentMergeRecovery instead to avoid tombstone resurrection.
```

Since contentMergeRecovery() is the replacement and is being used, this dead code should be removed to reduce maintenance burden.

Location: internal/syncbranch/worktree.go:811-830


Run bump-version.sh

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


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.


Update info.go versionChanges

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


bd import reports success but writes nothing to empty database

## Problem

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

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

## Reproduction Steps

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

## Expected

Import should create 416 issues in the database.

## Root Cause Hypothesis

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

## Context

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


gt mail send: support reading message body from stdin

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

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

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

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


Witness Patrol

Per-rig worker monitor patrol loop with progressive nudging.


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.


bd sync fails with orphaned issues and duplicate ID conflict

After fixing the deleted_at TEXT column scanning bug (commit 18b1eb2), bd sync still fails with two issues:

1. Orphan Detection Warning: 12 orphaned child issues whose parents no longer exist (bd-cb64c226.* and bd-cbed9619.*)

2. Import Failure: UNIQUE constraint failed for bd-360 - this tombstone exists in both DB and JSONL

Error: "Import failed: error creating depth-0 issues: bulk insert issues: failed to insert issue bd-360: sqlite3: constraint failed: UNIQUE constraint failed: issues.id"

Fix options:
- Delete orphaned child issues with bd delete
- Resolve bd-360 duplicate (in deletions.jsonl vs tombstone in DB)
- Reset sync branch: git branch -f beads-sync main && git push --force-with-lease origin beads-sync

Fixed tombstone constraint violation bug. When deleting closed issues, the CHECK constraint (status = 'closed') = (closed_at IS NOT NULL) was violated because CreateTombstone didn't clear closed_at. Fix: set closed_at = NULL in tombstone creation SQL.

The sync data corruption (orphaned issues in beads-sync branch) requires manual cleanup: reset sync branch with 'git branch -f beads-sync main && git push --force-with-lease origin beads-sync'


Add mol_type schema field to beads

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

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

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

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

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


Auto-detect non-TTY and adjust output

Detect when stdout is not a TTY (piped, redirected, or agent context) and adjust output:

- Disable ANSI colors
- Disable pager
- Disable emoji decorations
- Use plain text format

Uses golang.org/x/term or similar for TTY detection.

Needs pager support to be meaningful (otherwise nothing to disable).

## Implementation Plan

### Dependencies
```go
import "golang.org/x/term"
// or: github.com/mattn/go-isatty
```

### Code Changes

1. **Add TTY detection helper** (internal/ui/terminal.go):
```go
func IsTerminal() bool {
    return term.IsTerminal(int(os.Stdout.Fd()))
}

func ShouldUseColor() bool {
    // Respect NO_COLOR standard
    if os.Getenv("NO_COLOR") \!= "" {
        return false
    }
    // Respect CLICOLOR=0
    if os.Getenv("CLICOLOR") == "0" {
        return false
    }
    // Force color with CLICOLOR_FORCE
    if os.Getenv("CLICOLOR_FORCE") \!= "" {
        return true
    }
    return IsTerminal()
}
```

2. **Apply in lipgloss setup** (internal/ui/styles.go):
```go
func init() {
    if \!ShouldUseColor() {
        lipgloss.SetColorProfile(termenv.Ascii)
    }
}
```

3. **Disable pager for non-TTY** (uses bd-jdz3 infrastructure):
```go
// In pager.go ToPager():
if \!IsTerminal() {
    fmt.Print(content)
    return nil
}
```

### Standards Followed
- NO_COLOR: https://no-color.org/
- CLICOLOR/CLICOLOR_FORCE: de facto BSD standard
- gh cli uses same conventions

### Testing
- `bd list | cat` outputs no ANSI codes
- `NO_COLOR=1 bd list` outputs no colors
- `CLICOLOR_FORCE=1 bd list | cat` keeps colors
- Verify emoji handling in non-TTY mode


bd init UX Improvements

bd init leaves users with incomplete setup, requiring manual bd doctor --fix. Issues found: (1) git hooks not installed if user declines prompt, (2) no auto-migration when CLI is upgraded, (3) stale merge driver configs from old versions. Fix by making bd init more robust with better defaults and auto-migration.


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


bd merge omits priority:0 due to omitempty JSON tag

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


Merge: ruby-1767138542815

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


GH#509: bd doesn't find .beads when running from nested worktrees

When worktrees are nested under main repo (.worktrees/feature/), bd stops at worktree git root instead of continuing to find .beads in parent. See GitHub issue #509 for detailed fix suggestion.


bd validate - Comprehensive health check

Run all validation checks in one command.

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

Supports --fix-all for auto-repair.

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

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


Upgrade beads-mcp to {{version}}

Upgrade the MCP server via pip:

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

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


Delete server_cache_storage.go

Remove the entire cache implementation file (~286 lines)


Wait for CI to pass

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


Integration test: agent waits for CI via gate

End-to-end test of the gate workflow.

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

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


getLocalSyncBranch silently ignores YAML parse errors

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

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

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


Merge: bd-phtv

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


Merge: bd-w193

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


Add integration tests for RPC server and event loops

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

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

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

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


Explore wiki-style documentation structure

Our docs are growing into a book. Evaluate options for wiki-like structure:

**Options to evaluate:**
- GitHub Wiki (built-in, limited features)
- mdBook (Rust, CLI-friendly, produces HTML/PDF/ePub)
- MkDocs / Material for MkDocs (Python, beautiful themes)
- Docusaurus (React, powerful but complex)
- Plain markdown with good structure (current approach)

**Requirements:**
- Git-backed (version control)
- Markdown-native (agents can edit)
- Table of contents / navigation
- Cross-linking between pages
- Searchable
- Can be hosted on GitHub Pages

**Deliverables:**
- Recommendation with pros/cons
- If switching: migration plan
- If staying with markdown: structure guidelines for wiki-like organization

References:
- https://squidfunk.github.io/mkdocs-material/alternatives/
- https://www.archbee.com/blog/gitbook-alternatives


Merge: bd-indn

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


bd mol search: Find formulas using GitHub API

Search for formulas in the Mol Mall using GitHub's search API.

## Usage
```bash
bd mol search "code review"           # Free text search
bd mol search --topic molecule         # By GitHub topic
bd mol search --org anthropics         # By organization
bd mol search --stars ">10"            # By popularity
```

## Implementation
1. Use GitHub Search API: `/search/repositories`
2. Filter by topic:mol-formula or naming convention
3. Parse results, show name/description/stars/version
4. Support pagination for large result sets

## Output
```
NAME                              STARS  DESCRIPTION
github.com/anthropics/mol-code-review  42  AI-assisted code review workflow
github.com/gastown/mol-polecat-work    12  Standard polecat work lifecycle
```

## Discovery Convention
Repos should have:
- Topic: `mol-formula` or `beads-molecule`
- Name starting with `mol-`
- formula.json in root


Add unit tests for reset package

Comprehensive unit tests for internal/reset package.

## Test Cases

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

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

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

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

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

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

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


Merge: oast-1766978911452

branch: polecat/Toast-1766978911452
target: main
source_issue: oast-1766978911452
rig: beads


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.


Full event test


Restart beads daemon

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

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

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

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


Migrate sort.Slice to slices.SortFunc

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

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

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

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

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


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


Consider caching external dep resolution results

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

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

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

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


Mol Mall: Formula marketplace using GitHub as backend

Create a marketplace for sharing molecule formulas using GitHub repos as the hosting backend.

## Architecture Update (Dec 2025)

**Formulas are the sharing layer.** With ephemeral protos (bd-rciw), the architecture is:

```
Formulas ──cook──→ [ephemeral proto] ──pour/wisp──→ Mol/Wisp
    ↑                                                    │
    └────────────────── distill ─────────────────────────┘
```

- **Formulas**: JSON source files (.formula.json) - the thing you share
- **Protos**: Transient compilation artifacts - auto-deleted after use
- **Mols/Wisps**: Execution instances - not shared directly

**Key operations:**
- `bd distill <mol-id>` → Extract formula from completed work
- `bd mol publish <formula>` → Share to GitHub
- `bd mol install <url>` → Fetch from GitHub
- `bd pour <formula>` → Cook and spawn (proto is ephemeral)

## Why GitHub?

GitHub solves multiple problems at once:
- **Hosting**: Raw file URLs for formula.json
- **Versioning**: Git tags (v1.0.0, v1.2.0)
- **Auth**: GitHub tokens for private formulas
- **Discovery**: GitHub search, topics, stars
- **Collaboration**: PRs for contributions, issues for bugs
- **Organizations**: Natural scoping (@anthropic/, @gastown/)

## URL Scheme

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

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

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

## Architecture

Each formula lives in its own repo (like Go modules):
```
github.com/anthropics/mol-code-review/
├── formula.json      # The formula
├── README.md         # Documentation
└── CHANGELOG.md      # Version history
```

## ID Namespace

| Entity | ID Format | Example |
|--------|-----------|---------|
| Formula (GitHub) | `github.com/org/repo` | `github.com/anthropics/mol-code-review` |
| Installed formula | `mol-name` | `mol-code-review` |
| Poured instance | `<db>-mol-xxx` | `bd-mol-b8c` |

Deferred - focusing on Christmas launch first


Review jordanhubbard chaos testing PR #752

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

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

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


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


refinery Handoff


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


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

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

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

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


Remove all deletions.jsonl code except migration

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

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

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


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.


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

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

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

## Proposed Split

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

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

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

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

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

## Implementation Steps

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

   import (...)
   ```

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

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

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

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

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

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


bd ship command for publishing capabilities

Add `bd ship <capability>` command that:

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

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

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


Remove top-level comment alias

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


Test hooked styling


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

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


Formula parser: YAML schema for .formula.yaml files

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

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

Gas Town tracking: gt-8tmz.12


Consolidate migrate-* commands under bd migrate

## Task
Move standalone migrate commands under `bd migrate` parent:
- `bd migrate-hash-ids` → `bd migrate hash-ids`
- `bd migrate-issues` → `bd migrate issues`
- `bd migrate-sync` → `bd migrate sync`
- `bd migrate-tombstones` → `bd migrate tombstones`

## Implementation

### 1. Update migrate.go
Add subcommands to migrateCmd:
```go
func init() {
    migrateCmd.AddCommand(migrateHashIDsCmd)
    migrateCmd.AddCommand(migrateIssuesCmd)
    migrateCmd.AddCommand(migrateSyncCmd)
    migrateCmd.AddCommand(migrateTombstonesCmd)
}
```

### 2. Update each migrate_*.go file
- Change `Use:` from `migrate-foo` to `foo`
- Remove `rootCmd.AddCommand()` from init()

### 3. Create hidden aliases for backwards compatibility
In main.go, add hidden top-level commands that forward to subcommands.

### 4. Update docs
- docs/DELETIONS.md - references `bd migrate-tombstones`
- docs/CLI_REFERENCE.md - migration section

## Files to modify
- cmd/bd/migrate.go
- cmd/bd/migrate_hash_ids.go
- cmd/bd/migrate_issues.go
- cmd/bd/migrate_sync.go
- cmd/bd/migrate_tombstones.go
- cmd/bd/main.go (aliases)


Support redirect files in .beads/ directory

Gas Town creates polecat worktrees with .beads/redirect files that point to a shared beads database. The bd CLI should:

1. When finding a .beads/ directory, check if it contains a 'redirect' file
2. If redirect exists, read the relative path and use that as the beads directory
3. This allows multiple git worktrees to share a single beads database

Example:
- polecats/alpha/.beads/redirect contains '../../mayor/rig/.beads'
- bd commands from alpha should use mayor/rig/.beads

Currently bd ignores redirect files and either uses the local .beads/ or walks up to find a parent .beads/.

Related: gt-nriy (test message that can't be retrieved due to missing redirect support)


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


bd activity: Real-time molecule state feed

Implement activity feed command for watching molecule state transitions.

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

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

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

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


Systematic bd sync stability investigation

## Context

bd sync has chronic instability issues that have persisted since inception:
- issues.jsonl is always dirty after push
- bd sync often creates messes requiring manual cleanup
- Problems escalating despite accumulated bug fixes
- Workarounds are getting increasingly draconian

## Goal

Systematically observe and diagnose bd sync failures rather than applying band-aid fixes.

## Approach

1. Start fresh session with latest binary (all fixes applied)
2. Run bd sync and carefully observe what happens
3. Document exact sequence of events when things go wrong
4. File specific issues for each discrete problem identified
5. Track the root causes, not just symptoms

## Test Environment

- Fresh clone or clean state
- Latest bd binary with all bug fixes
- Monitor both local and remote JSONL state
- Check for timing issues, race conditions, merge conflicts

## Success Criteria

- Identify root causes of sync instability
- Create actionable issues for each problem
- Eventually achieve stable bd sync (no manual intervention needed)


Gate eval: Consider rate limiting for GitHub API calls

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

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

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


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.


Code smell: runCook function is ~275 lines

attached_args: Refactor runCook function (~275 lines)

The runCook() function in cmd/bd/cook.go (lines 83-357) is ~275 lines handling:

1. Flag parsing and validation
2. Mode determination (compile vs runtime)
3. Formula parsing and resolution
4. Control flow, advice, and expansion application
5. Dry-run output
6. Ephemeral mode JSON output
7. Persist mode database operations
8. Result display

Consider extracting:
- parseAndValidateFlags() - flag handling
- loadAndResolveFormula() - parse, resolve, apply transformations
- outputDryRun() - dry run display logic
- outputEphemeral() - ephemeral JSON output
- persistFormula() - database persistence logic

The function has multiple exit points and nested conditionals that make it hard to follow.

Location: cmd/bd/cook.go:83-357


Update CHANGELOG.md for 0.30.4

1. Change `## [Unreleased]` to `## [0.30.4] - 2025-12-17`
2. Add new empty `## [Unreleased]` section at top
3. Ensure all changes since 0.30.3 are documented


Review SQLite indexes and scaling bottlenecks

Audit the beads SQLite schema for:

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

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

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

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


Create and push git tag v{{version}}

Create the release tag and push it:

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

This triggers the GoReleaser GitHub Action to build release binaries.


Compound visualization in bd mol show

Enhance bd mol show to display compound structure.

ENHANCEMENTS:
- Show constituent protos and how they're bonded
- Display bond type (sequential/parallel) between components
- Indicate attachment points
- Show combined variable requirements across all protos

EXAMPLE OUTPUT:

  Compound: proto-feature-with-tests
  Bonded from:
    └─ proto-feature (root)
       └─ proto-testing (sequential, after completion)

  Variables: {{name}}, {{version}}, {{test_suite}}

  Structure:
    proto-feature-with-tests
    ├─ Design feature {{name}}
    ├─ Implement core
    ├─ Write unit tests      ← from proto-testing
    └─ Run test suite {{test_suite}}  ← from proto-testing


Commit release

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


Merge: bd-tvu3

branch: polecat/Beader
target: main
source_issue: bd-tvu3
rig: beads


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

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

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

Expected: The sender field should use BEADS_AGENT_NAME when set.

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

Related: gt mail routing integration with Gas Town


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

Code health review found several oversized files:

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

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

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

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


Fix wisp leak in nodb mode writeIssuesToJSONL

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

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

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

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


Graph Link: replies_to for conversation threading

Implement replies_to link type for message threading.

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

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

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

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


Auto-repair stale merge driver configs with invalid placeholders

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


Refactor long SQLite test files

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

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

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

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

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


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.


Release v{{version}}

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


Wait for CI

Monitor GitHub Actions for release completion.

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

Expected time: 5-10 minutes

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


Test message

Hello world


bd cook: waits_for field not preserved in cooked protos

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

## Formula

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

## Expected

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

## Actual

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

## Impact

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

## Related

bd-hr39: needs field not converted to step dependencies


Merge: bd-49kw

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


gt spawn/crew setup should create .beads/redirect for worktrees

Crew clones and polecats need a .beads/redirect file pointing to the shared beads database (../../mayor/rig/.beads). Currently:

- redirect files can get deleted by git clean
- not auto-created during gt spawn or worktree setup
- missing redirects cause 'no beads database found' errors

Found missing in: gastown/joe, beads/zoey (after git clean)

Fix options:
1. gt spawn creates redirect during worktree setup
2. gt prime regenerates missing redirects
3. bd commands auto-detect worktree and find shared beads

This should be standard Gas Town rig configuration.


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)


Work on beads-7jl: Fix Windows installer file locking iss...

Work on beads-7jl: Fix Windows installer file locking issue (GH#652). Close file handle before extraction in postinstall.js. When done, submit MR (not PR) to integration branch for Refinery.


Wait for CI to pass

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


Code smell: Issue struct still has ~100 fields - consider composition

internal/types/types.go Issue struct (lines 14-100) still has approximately 100 fields across many categories despite prior cleanup:

- Core identification (ID, ContentHash)
- Content (Title, Description, Design)
- Status & Workflow
- Assignment, Timestamps
- External integration
- Compaction metadata
- Relational data (Labels, Dependencies, Comments)
- Tombstone, Messaging, Context markers
- Bonding, HOP, Gate, Source tracing, Agent identity fields

**Problem:**
- Very large struct is difficult to understand
- Hard to identify required vs optional fields
- High change risk

**Acceptance Criteria:**
- [ ] Group related fields into embedded structs (IssueMetadata, IssueCompaction, IssueAgent, etc.)
- [ ] Use composition instead of single flat struct
- [ ] Maintain backward compatibility for serialization
- [ ] Tests pass


Update Homebrew formula

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


Test comment display


bd sync --merge fails due to chicken-and-egg: .beads/ always dirty

## Problem

When sync.branch is configured (e.g., beads-sync), the bd sync workflow creates a chicken-and-egg problem:

1. `bd sync` commits changes to beads-sync via worktree
2. `bd sync` copies JSONL to main working dir via `copyJSONLToMainRepo()` (sync.go line 364, worktree.go line 678-685)
3. The copy is NOT committed to main - it just updates the working tree
4. `bd sync --merge` checks for clean working dir (sync.go line 1547-1548)
5. `bd sync --merge` FAILS because .beads/issues.jsonl is uncommitted!

## Impact

- sync.branch workflow is fundamentally broken
- Users cannot periodically merge beads-sync → main
- Main branch always shows as dirty
- Creates confusion about git state

## Root Cause

sync.go:1547-1548:
```go
if len(strings.TrimSpace(string(statusOutput))) > 0 {
    return fmt.Errorf("main branch has uncommitted changes, please commit or stash them first")
}
```

This check blocks merge when ANY uncommitted changes exist, including the .beads/ changes that `bd sync` itself created.

## Proposed Fix

Option A: Exclude .beads/ from the clean check in `mergeSyncBranch`:
```go
// Check if there are non-beads uncommitted changes
statusCmd := exec.CommandContext(ctx, "git", "status", "--porcelain", "--", ":!.beads/")
```

Option B: Auto-stash .beads/ changes before merge, restore after

Option C: Change the workflow - do not copy JSONL to main working dir, instead always read from worktree

## Files to Modify

- cmd/bd/sync.go:1540-1549 (mergeSyncBranch function)
- Possibly internal/syncbranch/worktree.go (copyJSONLToMainRepo)

## Fix Implemented

Modified cmd/bd/sync.go mergeSyncBranch function:

1. **Exclude .beads/ from dirty check** (line 1543):
   Changed `git status --porcelain` to `git status --porcelain -- :!.beads/`
   This allows merge to proceed when only .beads/ has uncommitted changes.

2. **Restore .beads/ to HEAD before merge** (lines 1553-1561):
   Added `git checkout HEAD -- .beads/` before merge to prevent
   "Your local changes would be overwritten by merge" errors.
   The .beads/ changes are redundant since they came FROM beads-sync.

## Testing

- All cmd/bd sync/merge tests pass
- All internal/syncbranch tests pass
- Manual verification needed for full workflow


Wait for CI


Verify version consistency

Confirm all versions match 0.39.0.

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

All should show 0.39.0.


Update hook templates to 0.30.4

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

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

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


Final release verification

Verify all release artifacts are accessible:

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

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


UI: Style 'hooked' status in bd list output

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

## Semantic

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

## Suggested Styling

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

## Files to Update

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