Redesign workflow system: templates as native Beads

## Problem

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

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

## The Right Design

**Templates should be Beads themselves.**

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

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

## Benefits

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

## Tasks

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


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)


Update CHANGELOG header

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


Run bump-version.sh {{version}}

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


Create and push tag

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


bd create -f crashes with nil pointer dereference

GitHub issue #674. The markdown import feature crashes at markdown.go:338 because global variables (store, ctx, actor) aren't initialized when createIssuesFromMarkdown is called. The function uses globals set by cobra command framework but is being called before they're ready. Need to either initialize globals at start of function or pass them as parameters.


CLI API Audit for OSS Launch

Comprehensive CLI API audit before OSS launch.

## Tasks
1. Review gt command groupings - propose consolidation
2. Review bd command groupings
3. Document gt vs bd ownership boundaries
4. Identify naming inconsistencies
5. Document flag vs subcommand criteria

## Context
- Pre-OSS launch cleanup
- Goal: clean, consistent, discoverable CLI surface


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

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

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

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

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

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

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


Schema: Add 'hooked' as valid status

Add 'hooked' to the valid status enum in the beads schema.

## Files to Update

1. `internal/types/types.go` - Add StatusHooked constant
2. `internal/storage/sqlite/schema.go` - Add 'hooked' to CHECK constraint
3. `internal/storage/sqlite/migrations/` - New migration to alter constraint

## Status Values After Change

- open
- in_progress
- blocked
- deferred
- hooked (NEW)
- closed
- pinned
- tombstone

## Validation

- bd update <id> --status=hooked should work
- bd list --status=hooked should filter correctly


Test and document molecular chemistry commands

## Context

Implemented the molecular chemistry UX commands per the design docs:
- gastown/mayor/rig/docs/molecular-chemistry.md
- gastown/mayor/rig/docs/chemistry-design-changes.md

Commit: cadf798b

## New Commands to Test

| Command | Purpose |
|---------|---------|
| `bd pour <proto>` | Instantiate proto as persistent mol |
| `bd wisp create <proto>` | Instantiate proto as ephemeral wisp |
| `bd hook [--agent]` | Inspect what's on an agent's hook |

## Enhanced Commands to Test

| Command | Changes |
|---------|---------|
| `bd mol spawn --pour` | New flag, `--persistent` deprecated |
| `bd mol bond --pour` | Force liquid phase on wisp target |
| `bd pin --for <agent> --start` | Chemistry workflow support |

## Test Scenarios

1. **bd pour**: Create persistent mol from a proto
   - Verify creates in .beads/ (not .beads-wisp/)
   - Verify variable substitution works
   - Verify --dry-run works

2. **bd wisp create**: Create ephemeral wisp from proto
   - Verify creates in .beads-wisp/
   - Verify bd wisp list shows it
   - Verify bd mol squash works
   - Verify bd mol burn works

3. **bd hook**: Inspect pinned work
   - Pin something, verify bd hook shows it
   - Test --agent flag
   - Test --json output

4. **bd pin --for**: Assign work to agent
   - Verify sets pinned=true
   - Verify sets assignee
   - Verify --start sets status=in_progress

5. **bd mol bond --pour**: Force liquid on wisp target
   - Bond a proto to a wisp with --pour
   - Verify spawned issues are in .beads/

## Documentation

- Update CLAUDE.md with new commands
- Add examples to --help output (already done)
- Consider adding to docs/CLI_REFERENCE.md

## Code Review

- Check for edge cases
- Verify error messages are helpful
- Ensure --json output is consistent


Run check-versions.sh - all must pass

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

All versions must match 0.30.4.

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


Merge: bd-tvu3

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


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

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

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

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

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

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

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

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

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


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.


Add 0.33.2 to versionChanges in info.go

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


Update Homebrew formula

Update the Homebrew tap with new version:

```bash
./scripts/update-homebrew.sh 0.33.2
```

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


Pinned issues: persistent context markers

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

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

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


Preflight: Check git status

Ensure working tree is clean before starting release.

```bash
git status
```

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


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.


Merge: jasper-1767083473604

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


Spawn-time bonding: --attach flag

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

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

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

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

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

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

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


Push release tag

Push the version tag to trigger CI release.

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

This triggers GitHub Actions to build artifacts and publish.


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


Create version-bump template as native Beads

Migrate the version-bump workflow from YAML to a native Beads template:

1. Create epic with template label: Release {{version}}
2. Create child tasks for each step (update version files, changelog, commit, push, publish)
3. Set up dependencies between tasks
4. Add verification commands in task descriptions

This serves as both migration and validation of the new system.


Rename ephemeral → wisp throughout codebase

## The Change

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

## Why

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

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

## The Steam Engine Metaphor

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

## Full Vocabulary

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

## Changes Required

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

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

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

## Example Usage After

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


Fix resolvePartialID stub in workflow.go

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


Version Bump: 0.30.7

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


Merge: jasper-1767138512869

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


Re: Thread Test 2

Great! Thread is working well.


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

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


Commit release

Stage and commit all version changes.

```bash
git add -A
git commit -m "chore: Bump version to 0.39.0"
```

Review the commit to ensure all expected files are included.


mergeDependencies uses union instead of 3-way merge

## Critical Bug

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

### Root Cause

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

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

### Impact

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

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

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

### Fix

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

Key principle: **REMOVALS ARE AUTHORITATIVE**

### Files to Change

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

### Related

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


Merge: basalt-mjtme50i

branch: polecat/basalt-mjtme50i
target: main
source_issue: basalt-mjtme50i
rig: beads


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?


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

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


Review PR #513: fix hooks install docs

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


Improve test coverage

The test suite reports less than 45% code coverage. Identify the specific uncovered areas of the codebase, including modules, functions, or features. Rank them by potential impact on system reliability and business value, from most to least, and provide actionable recommendations for improving coverage in each area.


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.


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

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


Move pour/ephemeral under bd mol subcommand

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

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

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

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


CLI cleanup: Consolidate bd top-level commands

## Problem
bd has 76 top-level commands, making it hard to discover and remember.

## Proposed Changes

### Remove from top-level (nest instead):
- `bd cook` → `bd formula cook` (cooking is a formula operation)
- `bd pin` → already covered by `bd mol` commands
- `bd thanks` → `bd info --thanks` or just README
- `bd quickstart` → `bd help quickstart` or docs
- `bd detect-pollution` → `bd doctor --check=pollution`

### Consolidate migrations under `bd migrate`:
- `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`

### Consolidate admin under `bd admin`:
- `bd cleanup` → `bd admin cleanup`
- `bd compact` → `bd admin compact`
- `bd reset` → `bd admin reset`

## Backwards Compatibility
Keep old commands as hidden aliases for one release cycle.


.beads-ephemeral/ storage backend

Implement ephemeral storage layer for wisps.

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

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

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


Add 'rm' as alias for 'remove' in bd dep subcommand

Cobra supports command aliases. Add 'rm' as a short alias for 'remove' in the dep subcommand for convenience.

Example: bd dep rm issue1 issue2 should work the same as bd dep remove issue1 issue2

Implementation: Add Aliases: []string{"rm"} to the depRemoveCmd definition.


Run bump-version.sh 0.32.1

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


Review changes since last release

Understand what's being released.

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

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


Restart running daemons

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


test hook pin


Update CHANGELOG.md with release notes

Add meaningful release notes to CHANGELOG.md describing what changed in {{version}}


Update Homebrew formula

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

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

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

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


bd prime should mention when beads is redirected

## Problem

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

## Proposed Solution

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

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

## Why

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

## Related

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


Merge: quartz-1767082908528

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


Add timeout message for long-running git push operations

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


Code Review: PR #481 - Context Engineering Optimizations

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

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

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

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

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


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.


Merge: topaz-1767138533986

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


Distinct prefixes for protos, molecules, wisps

Template/workflow entities should have visually distinct prefixes from regular issues.

**Problem:**
- Protos (bd-7bs4) look like regular issues - invites squashing
- Molecules (poured instances) also use bd- prefix
- Wisps are in separate DB but still use bd- when referenced

**Proposed Prefixes:**
- `proto-` for templates (e.g., proto-release, proto-review)
- `mol-` for active molecules (poured from protos)
- `wisp-` for ephemeral wisps (vapor phase)

**Benefits:**
- Instant visual recognition of entity type
- Prevents accidental modification of templates
- Clear lifecycle: proto → mol → wisp → digest

**Implementation options:**
1. Separate databases with different prefixes
2. Issue type determines prefix generation
3. Naming convention enforced by bd pour/wisp commands


Merge: bd-qioh

branch: polecat/Errata
target: main
source_issue: bd-qioh
rig: beads


Code review: mol squash deletion bypasses tombstone system

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

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

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


Remove redundant edge fields from Issue struct

Remove from Issue struct:
- RepliesTo -> dependency with type replies-to
- RelatesTo -> dependencies with type relates-to
- DuplicateOf -> dependency with type duplicates
- SupersededBy -> dependency with type supersedes

Keep: Sender, Ephemeral (these are attributes, not relationships)


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.


Merge: bd-au0.5

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


Add type: event for state transitions

Events capture operational state changes as immutable records.

Fields needed:
- event_type: string (namespaced category like patrol.muted, agent.started)
- actor: string (entity URI who caused this)
- target: string (entity URI or bead ID affected)
- payload: object (event-specific JSON data)

Example:
  id: evt-x7k2
  type: event
  event_type: patrol.muted
  actor: human:overseer
  target: agent:deacon
  payload: {reason: fixing deadlock, until: gt-abc1}

This enables:
- bd activity --follow showing events
- bd list --type=event --target=agent:deacon
- Full audit trail for operational state
- HOP-compatible transaction records


Add test coverage for external dep edge cases

During code review of bd-zmmy, identified missing test coverage:

1. RemoveDependency with external ref target (will fail - see bd-a3sj)
2. GetBlockedIssues with mix of local and external blockers
3. GetDependencyTree with external deps
4. AddDependency cycle detection with external refs (should be skipped?)
5. External dep resolution with WAL mode database
6. External dep resolution when target project has no .beads directory
7. External dep resolution with invalid external: format variations

Priority 2 because bd-a3sj is a real bug that tests would catch.


Verify the release is complete.

Verify the release is complete.

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

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

# Check npm
npm view @beads/bd version

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

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


Implement Step.Condition evaluation in bd cook

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

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

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

## Implementation

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

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

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

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

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

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

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

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

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


bd distill: Extract formula from mol/epic

Extract a formula from completed work (mol, wisp, or epic).

**Key change**: Distill works on execution artifacts (mols/wisps/epics), not protos.
Protos are ephemeral - they don't persist. Distillation extracts patterns from
actual executed work.

## Usage
```bash
bd distill bd-mol-xyz -o my-workflow.formula.json
bd distill bd-epic-abc -o feature-workflow.formula.json
```

## Use Cases
- **Emergent patterns**: Structured work manually, want to templatize it
- **Modified execution**: Poured a formula, added custom steps, want to capture
- **Learning from success**: Extract what made a complex mol succeed

## Implementation
1. Load mol/wisp/epic subgraph (root + all children)
2. Convert to formula JSON structure
3. Extract variables from patterns (titles, descriptions)
4. Generate step IDs from issue titles (slugify)
5. Write .formula.json file

## Output Format
```json
{
  "formula": "my-workflow",
  "description": "...",
  "version": 1,
  "vars": { ... },
  "steps": [ ... ]
}
```

## Architecture Note
This closes the formula lifecycle loop:
  Formulas ──cook──→ Mols ──distill──→ Formulas

All sharing happens via formulas. Mols contain execution context and aren't shared.


Add close_reason to JSONL export format documentation

PR #551 now persists close_reason to the database, but there's a question about whether this field should be exported to JSONL format.

## Current State
- close_reason is stored in issues.close_reason column
- close_reason is also stored in events table (audit trail)
- The JSONL export format may or may not include close_reason

## Questions
1. Should close_reason be exported to JSONL format?
2. If yes, where should it go (root level or nested in events)?
3. Should there be any special handling to avoid duplication?
4. How should close_reason be handled during JSONL import?

## Why This Matters
- JSONL is the git-friendly sync format
- Other beads instances import from JSONL
- close_reason is meaningful data that should be preserved across clones

## Suggested Action
- Check if close_reason is currently exported in JSONL
- If not, add it to the export schema
- Document the field in JSONL format spec
- Add tests for round-trip (export -> import -> verify close_reason)


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


Swarm: Code Review: Swarm commands consolidation

Swarm molecule orchestrating epic bd-2ubv.

Epic: bd-2ubv
Coordinator:


Fix: Empty coordinator shows blank in bd swarm create output

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

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

Minor polish issue.


Cache GetGitDir() for additional optimization

Code review finding from bd-7di fix.

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

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

File: internal/git/gitdir.go


Polecat sessions terminate unexpectedly during work

During swarm bd-784c, polecat sessions (Toast, Nux) terminated mid-task without completing their work.

**Observed:**
- Polecats were actively working (edits in progress, tests running)
- Sessions suddenly showed 'not running' in gt polecat status
- tmux sessions existed but were empty/reset
- Work was partially complete (some commits made, issue still open)

**Timeline example:**
1. Toast working on bd-1tkd, making edits
2. Check status 30s later - session empty, state shows 'idle'
3. Had to re-sling to restart

**Impact:**
- Lost work progress
- Required manual intervention to restart
- Unclear if work was saved/committed

**Possible causes:**
- Session timeout?
- Claude Code crash?
- Resource limit?

**Suggestion:**
- Add session health monitoring
- Auto-restart on unexpected termination
- Log session exit reasons


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


Test agent with fields


Merge: bd-bqcc

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


bd repair: Add --json output flag

bd repair lacks --json output for machine-readable results, unlike most other bd commands.

Add --json flag that outputs structured JSON with:
- orphan counts by type
- list of orphaned references
- repair status (success/failure)
- backup path


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


False positive detection by Kaspersky Antivirus (Trojan)

Kaspersky Antivirus falsely detects beads (bd.exe v0.23.1) as a Trojan (PDM:Trojan.Win32.Generic) and removes it.
Event: Malicious object detected
Component: System Watcher
Object name: bd.exe


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


Witness Patrol

Per-rig worker monitor patrol loop with progressive nudging.


Merge: bd-by0d

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


Implement bd close --continue flag

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


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


wish: a 'continue' or similar cmd/flag which means alter last issue

so many time I create an issue and then have another thought: 'oh, before I did X and it crashed there was ZZZ happening' or 'actually that is P4 not P2'. It would be nice if when `bd {cmd}` is used without a {title} or {id} it just adds or updates the most recently touched issue.


Work on gt-8tmz.10: Rename Engineer in Box to Shiny. Rena...

Work on gt-8tmz.10: Rename Engineer in Box to Shiny. Rename mol-engineer-in-box references to mol-shiny or just 'shiny'. Update docs and code in internal/formula/ and .beads/formulas/. When done: 1) bd close gt-8tmz.10, 2) bd sync, 3) git push, 4) gt mq submit


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.


bd doctor: detect issues referenced in commits but still open

Add a doctor check that finds 'orphaned' issues - ones referenced in git commit messages (e.g., 'fix bug (bd-xxx)') but still marked as open in beads.

**Detection logic:**
1. Get all open issue IDs from beads
2. Parse git log for issue ID references matching pattern \(prefix-[a-z0-9.]+\)
3. Report issues that appear in commits but are still open

**Output:**
⚠ Warning: N issues referenced in commits but still open
  bd-xxx: 'Issue title' (commit abc123)
  bd-yyy: 'Issue title' (commit def456)

  These may be implemented but not closed. Run 'bd show <id>' to check.

**Implementation:**
- Add check to doctor/checks.go
- Use git log parsing (already have git utilities)
- Match against configured issue_prefix


Phase 2.4: Git merge driver optimization for TOON format

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

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

## Required Work

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

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

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

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


Test stdin 2

Description from stdin test


Add gate checking to Deacon patrol loop

Integrate gate checking into Deacon's patrol cycle.

## Patrol Integration
```go
func (d *Deacon) checkGates(ctx context.Context) {
    gates, _ := d.store.ListOpenGates(ctx)

    for _, gate := range gates {
        // Check timeout
        if time.Since(gate.CreatedAt) > gate.Timeout {
            d.notifyWaiters(gate, "timeout")
            d.closeGate(gate, "timed out")
            continue
        }

        // Check condition
        if d.checkCondition(gate.AwaitType, gate.AwaitID) {
            d.notifyWaiters(gate, "cleared")
            d.closeGate(gate, "condition met")
        }
    }
}
```

## Note
This task is in Gas Town (gt), not beads. May need to be moved there.


Monitor GoReleaser CI job

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

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

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

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

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


Merge: bd-n386

branch: polecat/immortan
target: main
source_issue: bd-n386
rig: beads


GH#505: Add bd reset/wipe command

Add command to cleanly reset/wipe beads database. User reports painful manual process to start fresh. See GitHub issue #505.


bd delete --from-file should add deleted issues to deletions manifest

When using bd delete --from-file to bulk delete issues, the deleted issue IDs are not being added to the deletions.jsonl manifest.

This causes those issues to be resurrected during bd sync when git history scanning finds them in old commits.

Expected: All deleted issues should be added to deletions.jsonl so they wont be reimported from git history.

Workaround: Manually add deletion records to deletions.jsonl.


Create and push git tag v0.33.2

Create the release tag and push it:

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

This triggers the GoReleaser GitHub Action to build release binaries.


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.


gt handoff deadlock at handoff.go:125

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


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

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

Current coverage: 28.4%
Target coverage: 50%


Tier 5 (Future): JSONL archive rotation

Periodic rotation of issues.jsonl for long-running repos.

## Design

.beads/
├── issues.jsonl           # Current (hot)
├── archive/
│   ├── issues-2025-12.jsonl.gz  # Archived (cold)
│   └── ...
└── index.jsonl            # Merged index for queries

## Commands

bd archive rotate [flags]
  --older-than N    Archive issues closed > N days
  --compress        Gzip archives
  --dry-run         Preview

bd archive list           # Show archived periods
bd archive restore <period>  # Restore from archive

## Config

bd config set archive.enabled true
bd config set archive.rotate_days 90
bd config set archive.compress true
bd config set archive.path '.beads/archive'

## Considerations

- Archives can be gitignored (local only) or committed (shared)
- Query layer must check index, hydrate from archive
- Cold storage tiering (S3/GCS) for enterprise
- Merkle proofs preserved for audit

## Priority

This is post-1.0 work. Current focus is on squash (removes ephemeral).
Archive helps with long-term history but is less critical.


Merge: bd-t4sb

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


Merge: bd-au0.7

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


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.


Merge: bd-l13p

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


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

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


Tier 4: Auto-squash on molecule completion

Automatically squash molecules when they reach terminal state.

## Integration Points

1. Hook into molecule completion handler
2. Detect when all steps are done/failed
3. Trigger squash automatically

## Config

bd config set mol.auto_squash true  # Default: false
bd config set mol.auto_squash_on_success true  # Only on success
bd config set mol.auto_squash_delay '5m'  # Wait before squash

## Implementation Options

### Option A: Post-Completion Hook
In mol completion handler:
- Check if auto_squash enabled
- Call Squash() after terminal state

### Option B: Git Hook
In .beads/hooks/post-commit:
- bd mol squash --auto

### Option C: Daemon Background Task
- Daemon periodically checks for squashable molecules
- Squashes in background

## Acceptance Criteria

- Completed molecules auto-squash without manual intervention
- Configurable delay before squash
- Option to squash only on success vs always
- Works with both daemon and no-daemon modes


Merge: bd-au0.6

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


Merge: obsidian-1767106243818

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


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

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


bd mol bond --ephemeral flag

Add --ephemeral flag to bd mol bond command.

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

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

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


Verify PyPI package

Confirm PyPI package published.

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

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

Should show 0.99.0-test.


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

## Problem

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

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

## Expected Behavior

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

## Workaround

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

## Fix Options

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

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


Code Review: Beads Refactoring Sprint

Epic for code smell fixes identified during Beads code review.

## Scope
11 issues covering:
- P2: Duplicated code in cook.go (2 issues)
- P3: Long functions and God Objects (5 issues)
- P4: Technical debt and cleanup (4 issues)

## Approach
Swarm with polecats, each taking 1-2 issues. All changes should:
1. Pass existing tests
2. Not change external behavior
3. Improve code maintainability

## Success Criteria
All 11 child issues closed with passing CI.


BondRef data model for compound lineage

Add data model support for tracking compound molecule lineage.

NEW FIELDS on Issue:
  bonded_from: []BondRef  // For compounds: constituent protos

NEW TYPE:
  type BondRef struct {
      ProtoID   string  // Source proto ID
      BondType  string  // sequential, parallel, conditional
      BondPoint string  // Attachment site (issue ID or empty for root)
  }

JSONL SERIALIZATION:
  {
    "id": "proto-feature-tested",
    "title": "Feature with tests",
    "bonded_from": [
      {"proto_id": "proto-feature", "bond_type": "root"},
      {"proto_id": "proto-testing", "bond_type": "sequential"}
    ],
    ...
  }

QUERIES:
- GetCompoundConstituents(id) → []BondRef
- IsCompound(id) → bool
- GetCompoundsUsing(protoID) → []Issue  // Reverse lookup


Merge: quartz-1767142008267

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


Update CHANGELOG.md with detailed release notes.

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

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

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

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

Sort by importance, not chronologically.

instantiated_from: bd-mol-xga
step: update-changelog


Restart daemons

bd daemons killall


Review changes since last release

Understand what's being released.

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

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


mol-beads-release

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

Release checklist for beads version 0.38.0.

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

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

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

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

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

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

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

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

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

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

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

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

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

Needs: update-changelog

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

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

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

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

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

Needs: run-tests

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

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

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

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

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

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

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

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

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

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

# Check npm
npm view @beads/bd version

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

Needs: wait-for-ci

## Step: update-local
Update local installations.

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

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

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

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

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

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

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

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

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

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


Implement core reset package (internal/reset)

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

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

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

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

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

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

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

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


Merge: bd-w193

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


beads-release

Beads release workflow - from version bump to verified release.

This formula orchestrates a complete release cycle:
1. Preflight checks (clean git, up to date)
2. Documentation updates (CHANGELOG, info.go)
3. Version bump (all components)
4. Git operations (commit, tag, push)
5. CI verification (GitHub Actions)
6. Artifact verification (GitHub, npm, PyPI)
7. Local installation update
8. Daemon restart

## Usage

```bash
bd wisp create beads-release --var version=0.37.0
```

Or assign to a polecat:
```bash
gt sling beads/polecats/p1 --formula beads-release --var version=0.37.0
```


bd close --continue: auto-advance to next molecule step

Add --continue flag to bd close for seamless molecule step transitions.

## Usage

bd close <step-id> --continue [--no-auto]

## Behavior

1. Closes the specified step
2. Finds next ready step in same molecule (sibling/child)
3. By default, marks it in_progress (--no-auto to skip)
4. Outputs the transition

## Output

[done] Closed gt-abc.3: Implement feature

Next ready in molecule:
  gt-abc.4: Write tests

[arrow] Marked in_progress (use --no-auto to skip)

## If no next step

[done] Closed gt-abc.6: Exit decision

Molecule gt-abc complete! All steps closed.
Consider: bd mol squash gt-abc --summary '...'

## Key behaviors
- Detects parent molecule from closed step
- Finds next unblocked sibling
- Auto-claims by default (propulsion principle)
- Graceful handling when molecule is complete

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


Release v0.34.0

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


Add BD_AGENT_MODE for ultra-compact output

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

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

This is independent but benefits from TTY detection infrastructure.

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

## Implementation Plan

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

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

### Code Changes

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

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

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

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

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


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


Merge: topaz-1767142022153

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


bd close hooks: context check and notifications

Add hook system to bd close for notifications and custom actions.

## Scope (MVP)

Implement **command hooks only** for bd close. Deferred: notify, webhook types.

## Implementation

### 1. Config Schema

Add to internal/configfile/config.go:

```go
type HooksConfig struct {
    OnClose []HookEntry `yaml:"on_close,omitempty"`
}

type HookEntry struct {
    Command string `yaml:"command"`           // Shell command to run
    Name    string `yaml:"name,omitempty"`    // Optional display name
}
```

Add `Hooks HooksConfig` field to Config struct.

### 2. Hook Execution

Create internal/hooks/close_hooks.go:

```go
func RunCloseHooks(ctx context.Context, cfg *configfile.Config, issue *types.Issue) error {
    for _, hook := range cfg.Hooks.OnClose {
        cmd := exec.CommandContext(ctx, "sh", "-c", hook.Command)
        cmd.Env = append(os.Environ(),
            "BEAD_ID="+issue.ID,
            "BEAD_TITLE="+issue.Title,
            "BEAD_TYPE="+string(issue.IssueType),
            "BEAD_PRIORITY="+strconv.Itoa(issue.Priority),
            "BEAD_CLOSE_REASON="+issue.CloseReason,
        )
        cmd.Stdout = os.Stdout
        cmd.Stderr = os.Stderr
        if err := cmd.Run(); err \!= nil {
            // Log warning but dont fail the close
            fmt.Fprintf(os.Stderr, "Warning: close hook %q failed: %v\n", hook.Name, err)
        }
    }
    return nil
}
```

### 3. Integration Point

In cmd/bd/close.go, after successful close:

```go
// Run close hooks
if cfg := configfile.Load(); cfg \!= nil {
    hooks.RunCloseHooks(ctx, cfg, closedIssue)
}
```

### 4. Example Config

```yaml
# .beads/config.yaml
hooks:
  on_close:
    - name: show-next
      command: bd ready --limit 1
    - name: context-check
      command: echo "Issue $BEAD_ID closed. Check context if nearing limit."
```

## Environment Variables

| Variable | Description |
|----------|-------------|
| BEAD_ID | Issue ID (e.g., bd-abc1) |
| BEAD_TITLE | Issue title |
| BEAD_TYPE | Issue type (task, bug, feature, etc.) |
| BEAD_PRIORITY | Priority (0-4) |
| BEAD_CLOSE_REASON | Close reason if provided |

## Testing

Add test in internal/hooks/close_hooks_test.go:
- Test hook execution with mock config
- Test env vars are set correctly
- Test hook failure doesnt block close

## Files to Create/Modify

1. **Create:** internal/hooks/close_hooks.go
2. **Create:** internal/hooks/close_hooks_test.go
3. **Modify:** internal/configfile/config.go (add HooksConfig)
4. **Modify:** cmd/bd/close.go (call RunCloseHooks)
5. **Modify:** docs/CONFIG.md (document hooks config)

## Out of Scope (Future)

- notify hook type (gt mail integration)
- webhook type (HTTP POST)
- on_create, on_update hooks
- Hook timeout configuration
- Parallel hook execution


Implement global N-way collision resolution algorithm

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

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

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


Add bd state and bd set-state helper commands

Convenience commands for the labels-as-state pattern documented in LABELS.md.

`bd state <role> <dimension>` - Query the current value of a state dimension
Example: bd state witness-alpha patrol → outputs: active

`bd set-state <role> <dimension>=<value> [--reason "..."]` - Atomically creates an event bead and updates the label
Example: bd set-state witness-alpha patrol=muted --reason "Investigating stuck polecat"

This would combine the manual steps:
1. bd create "<reason>" -t event --parent <role>
2. bd label remove <role> <dimension>:<old-value>
3. bd label add <role> <dimension>:<new-value>

Into a single atomic operation.

Discovered from: bd-31ae


Test role bead


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


Push release v0.33.2 to remote

Push the commit and tag:

```bash
git push && git push --tags
```

Verify on GitHub that the tag appears in releases.


Add tests for daemon functionality

Critical daemon functions have 0% test coverage including daemon lifecycle, health checks, and RPC server functionality. These are essential for system reliability and need comprehensive test coverage.


Verify release artifacts

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


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


bd list --parent: filter by parent issue

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

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

Would show all open children of gt-h5n.

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


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

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


Test comment display


Refinery Patrol

Merge queue processor patrol loop with verification gates.


Test Agent


bd dep tree doesn't display external dependencies

GetDependencyTree (dependencies.go:464-624) uses a recursive CTE that JOINs with the issues table, which means external refs (external:project:capability) are invisible in the tree output.

When an issue has an external blocking dependency, running 'bd dep tree <id>' won't show it.

Options:
1. Query dependencies table separately for external refs and display them as leaf nodes
2. Add a synthetic 'external' node type that shows the ref and resolution status
3. Document that external deps aren't shown in tree view (use bd show for full deps)

Lower priority since bd show <id> displays all dependencies including external refs.


Upgrade Homebrew bd

brew update && brew upgrade bd


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

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

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

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

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

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


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


Update beads_mcp/__init__.py to 0.30.4

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

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


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

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

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


Push release v{{version}} to remote


Update info.go versionChanges

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

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

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

Focus on workflow-impacting changes agents need to know.


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


bd wisp list command

Add bd wisp list command to show ephemeral molecules.

## Usage
```bash
bd wisp list              # List all wisps in current context
bd wisp list --json       # JSON output
bd wisp list --all        # Include orphaned wisps
```

## Output
- Shows in-progress ephemeral molecules
- Columns: ID, Title, Started, Last Update, Status
- Warns about orphaned wisps (old updated_at)

## Implementation
- New 'wisp' command group
- Read from .beads-ephemeral/issues.jsonl
- Filter to ephemeral:true issues


Update all code to use dependencies API for edges

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

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


Upgrade local Homebrew installation

Upgrade bd via Homebrew:

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

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

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

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


Version Bump: test-squash

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


Add 'convoy' issue type with reactive completion

Add convoy as a new issue type with reactive completion semantics.

Behavior:
- Convoy has list of tracked issues (via 'tracks' relation)
- When all tracked issues close (including wontfix), convoy auto-closes
- Supports cross-prefix tracking (convoy in hq-* tracks gt-*, bd-*)

Implementation:
- New type: convoy
- Reactive completion trigger on tracked issue closure
- Query support: 'bd list --type=convoy'

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


Tier 2: Basic bd mol squash command

Add bd mol squash command for basic molecule execution compression.

## Command

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

## Implementation

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

## Schema Changes

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

## Acceptance Criteria

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


bd mol advance: Step through molecule execution

Implement bd mol advance for stepping through molecules.

## Command

```bash
bd mol advance <mol-id>
```

## Behavior

1. Load molecule state from .beads/molecules/<mol-id>.state.yaml
2. Find current step
3. Mark current step completed
4. Find next available step (respecting needs/deps)
5. Update state file
6. Output next step info (or COMPLETE if done)

## Step State Machine

```
pending → in_progress → completed
```

When advancing:
- Current step: in_progress → completed
- Next step: pending → in_progress

## Output

```bash
$ bd mol advance mol-deacon-patrol
✓ Step inbox-check completed
→ Next step: spawn-work

Title: Spawn polecat for ready work
Description: ...
```

Or if complete:
```bash
$ bd mol advance mol-deacon-patrol
✓ Step self-inspect completed
✓ Molecule COMPLETE

Run bd mol reset to loop, or gt handoff to cycle.
```

## Files

- cmd/bd/mol_advance.go
- internal/mol/state.go


Update git hooks

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

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

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


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

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

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

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

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


Warn on invalid depends_on references in workflow templates

workflow.go:780-781 silently skips invalid dependency names. Should log a warning when a depends_on reference doesn't match any task ID in the template.


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


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

Code health review found these functions defined in multiple places:

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

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

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

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


Merge: granite-mjtmdru5

branch: polecat/granite-mjtmdru5
target: main
source_issue: granite-mjtmdru5
rig: beads


Commit the release changes.

Commit the release changes.

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

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


Replace map[string]interface{} with typed JSON response structs

Many CLI commands use map[string]interface{} for JSON output which loses type safety and compile-time error detection.

Files with map[string]interface{}:
- cmd/bd/compact.go (10+ instances)
- cmd/bd/cleanup.go
- cmd/bd/daemons.go
- cmd/bd/daemon_lifecycle.go

Example fix:
```go
// Before
result := map[string]interface{}{
    "status": "ok",
    "count": 42,
}

// After
type CompactResponse struct {
    Status string `json:"status"`
    Count  int    `json:"count"`
}
result := CompactResponse{Status: "ok", Count: 42}
```

Benefits:
- Compile-time type checking
- IDE autocompletion
- Easier refactoring
- Self-documenting API


Design contributor namespace isolation for beads pollution prevention

## Problem

When contributors work on beads-the-project using beads-the-tool, their personal work-tracking issues leak into PRs. The .beads/issues.jsonl is intentionally tracked (it's the project's issue database), but contributors' local issues pollute the diff.

This is a recursion problem unique to self-hosting projects.

## Possible Solutions to Explore

1. **Contributor namespaces** - Each contributor gets a private prefix (e.g., `bd-steve-xxxx`) that's gitignored or filtered
2. **Separate database** - Contributors use BEADS_DIR pointing elsewhere for personal tracking
3. **Issue ownership/visibility flags** - Mark issues as "local-only" vs "project"
4. **Prefix-based filtering** - Configure which prefixes are committed vs ignored

## Design Considerations

- Should be zero-friction for contributors (no manual setup)
- Must not break existing workflows
- Needs to work with sync/collaboration features
- Consider: what if a "personal" issue graduates to "project" issue?

## Expansion Needed

This is a placeholder. Needs detailed design exploration before implementation.


bd cook: Proto ID prefix should match project prefix

When `bd cook` creates a proto, it uses the formula name as the proto ID:

```yaml
formula: mol-deacon-patrol
```

Creates proto with ID `mol-deacon-patrol`.

## Problem

If the project uses a different prefix (e.g., `gt-`), the cooked protos have
a different prefix (`mol-`), causing:

1. `bd sync` import warnings about prefix mismatch
2. Mixed prefix lists in `bd mol list`
3. Confusion about naming conventions

## Options

1. **Use project prefix**: Cook as `gt-mol-deacon-patrol`
2. **Allow mol- as special prefix**: Recognize mol-* as valid for protos
3. **Make prefix configurable**: `bd cook --prefix gt-`
4. **Keep current behavior**: Document that formulas use their own namespace

## Current Workaround

The prefix mismatch warning can be ignored, but `bd sync` fails the import step.

## Related

gt-i6k1: Duplicate proto cleanup


Move relate/unrelate under dep command

Make relate and unrelate subcommands of dep (dep relate, dep unrelate). They're dependency operations and belong there.


Exclude pinned issues from bd ready

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


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

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


Tests for messaging and graph links

Comprehensive test coverage for all new features.

Test files:
- cmd/bd/mail_test.go - mail command tests
- internal/storage/sqlite/graph_links_test.go - graph link tests
- internal/hooks/hooks_test.go - hook execution tests

Test cases:
- Mail send/inbox/read/ack lifecycle
- Thread creation and traversal (replies_to)
- Bidirectional relates_to
- Duplicate marking and queries
- Supersedes chains
- Ephemeral cleanup
- Identity resolution priority
- Hook execution (mock hooks)
- Schema migration preserves data

Target: >80% coverage on new code


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


Code smell: Incomplete TODOs in formula/types.go

internal/formula/types.go has multiple TODO items for features that are defined in the schema but not implemented:

- Line 170: Expand field not implemented in bd cook
- Line 174: ExpandVars field not implemented
- Line 179: Gate integration pending (bd-udsi gates)
- Line 186: Condition evaluation not implemented
- Line 210: Additional expansion features

**Problem:**
- Schema defines features that aren't implemented
- Can confuse users who try to use these fields
- Creates maintenance burden of unused code paths

**Acceptance Criteria:**
- [ ] Either implement the features OR
- [ ] Remove unimplemented fields from schema OR
- [ ] Document clearly which fields are future-only
- [ ] Update related beads issues if they exist


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

Linting issue: checkGitHooks - path is unused (unparam) at cmd/bd/doctor.go:1879:20. Error: func checkGitHooks(path string) doctorCheck {


Runtime bonding: bd mol attach

Attach a molecule to an already-running workflow.

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

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

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

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

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


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

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

## Problem

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

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

## Proposed Solution

### Phase 1: Shell Hooks

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

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

### Lifecycle Events

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

### Phase 2: Molecule Completion Handlers

Molecules could define completion actions:

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

### Phase 3: Gas Town Integration

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

## Implementation Notes

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

## Related

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

## Use Cases

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


Update local binary

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


Support wisps in nodb mode for lightweight Gas Town rigs

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

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

## Proposed Design

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

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

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

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


Merge: bd-05a8

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


bd mol publish: Push formula to GitHub repo

Publish a formula to a GitHub repository for sharing.

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

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

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

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

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


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

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


Support --reason flag in daemon mode for reopen command

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


Command Set Standardization & Flag Consistency

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

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

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

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


Merge: bd-gqxd

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


Add stale molecule check to bd doctor

Extend bd doctor to detect stale molecules.

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

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

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

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

Depends on: bd mol stale command


Implement lint check (golangci-lint)

## Summary
Add lint check using golangci-lint to the preflight --check mode.

## Implementation Details

### Location
File: `cmd/bd/preflight.go`

### Changes Required

1. **Implement lint checker**:
```go
func runLintCheck() CheckResult {
    // First check if golangci-lint is available
    if _, err := exec.LookPath("golangci-lint"); err != nil {
        return CheckResult{
            Name:    "lint",
            Passed:  false,
            Output:  "golangci-lint not found in PATH",
            Command: "golangci-lint run ./...",
            Skipped: true,
        }
    }

    cmd := exec.Command("golangci-lint", "run", "./...")
    output, err := cmd.CombinedOutput()
    return CheckResult{
        Name:    "lint",
        Passed:  err == nil,
        Output:  string(output),
        Command: "golangci-lint run ./...",
    }
}
```

2. **Add Skipped field to CheckResult** (if not already added by bd-lfak.2):
```go
type CheckResult struct {
    Name    string
    Passed  bool
    Output  string
    Command string
    Skipped bool  // Tool not available
}
```

3. **Output format**:
```
$ bd preflight --check
✓ Lint passes
  Command: golangci-lint run ./...

# When tool missing:
⚠ Lint skipped (golangci-lint not found)
  Install: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest

# On failure:
✗ Lint fails
  Command: golangci-lint run ./...
  Output: [lint errors]
```

### Dependencies
- Depends on bd-lfak.2 for CheckResult struct and runChecks() framework

### Testing
- Test with clean lint (this repo should pass)
- Test with golangci-lint not in PATH (mock or temp PATH)
- Test with lint errors (intentional bad code)

### Notes
- This repo has baseline warnings documented in docs/LINTING.md
- golangci-lint respects .golangci.yml config if present

### Acceptance Criteria
- [x] Runs golangci-lint when --check flag is used
- [x] Gracefully handles missing golangci-lint binary
- [x] Shows clear pass/fail/skip output
- [x] Shows lint errors on failure
- [x] Works with --json flag