Deprecate bd mol run after gt absorbs its semantics

bd mol run is a convenience combo (pour + update + pin) that feels like orchestration but lives in the data layer. This blurs the bd/gt architectural boundary.

## Current State
- bd mol run = bd pour + bd update --status=in_progress + bd pin --for me
- gt spawn depends on bd mol run (shells out to it)

## Proposed Change
Once gt spawn inlines the pour+assign+pin logic:
1. Deprecate bd mol run with a warning pointing to gt spawn
2. Eventually remove bd mol run entirely

## Why
- bd should be pure data operations (pour, update, pin, close)
- gt should own orchestration (spawn, sling, hook, handoff)
- Standalone bd users can run the three commands separately or script them

## Blocked By
gastown issue gt-r7jj2 (Move mol run semantics from bd into gt spawn)


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

Code health review found several oversized files:

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

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

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

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


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


Add close_reason field to CLI schema and documentation

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

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

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

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


Merge: topaz-mjwb6bxt

branch: polecat/topaz-mjwb6bxt
target: main
source_issue: topaz-mjwb6bxt
rig: beads
agent_bead: gt-beads-polecat-topaz


GHI #888: Add flag aliases for dep add command to reduce Claude token waste

GitHub Issue #888: Add flag aliases for dep add command to reduce Claude token waste

https://github.com/steveyegge/beads/issues/888

Implemented --blocked-by and --depends-on flag aliases for bd dep add command


Split monolithic sqlite.go into focused files

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

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

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

Effort: 6-8 hours


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


Code smell: Inconsistent error handling patterns

Commands use a mix of error handling patterns:
- FatalError()
- FatalErrorRespectJSON()
- fmt.Fprintf(os.Stderr, ...) + os.Exit(1)
- Silent continue
- Inconsistent resource cleanup (result.Close())

**Example in show.go lines 89-146:**
Different error paths use different patterns, some close resources, some don't.

**Acceptance Criteria:**
- [ ] Document error handling contract
- [ ] Standardize on consistent pattern
- [ ] Use defer for resource cleanup
- [ ] Create helper for common fetch-and-handle pattern
- [ ] Tests pass

Implemented: Added LogError, LogErrorRespectJSON, LogNotFound, LogNotFoundRespectJSON helpers to errors.go. Standardized show.go to use these helpers. Updated ERROR_HANDLING.md documentation. Tests pass.


Merge: jade-1767138549967

branch: polecat/jade-1767138549967
target: main
source_issue: jade-1767138549967
rig: beads


bd doctor should recommend bd prime migration for existing repos

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

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

## Desired behavior
bd doctor should detect and suggest:

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

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

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

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

## Example output

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

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

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


Merge: bd-05a8

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


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

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


Merge: onyx-1767106262992

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


Add GetMoleculeProgress RPC endpoint

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


Implement Jira issue timestamp comparison for sync

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

File: cmd/bd/jira.go:633

Implemented Jira timestamp comparison for sync. Added fetchJiraIssueTimestamp() to fetch timestamps from Jira REST API, updated detectJiraConflicts() to compare actual timestamps, and improved conflict resolution functions to display timestamp details.


Design escalation inbox for stuck workflows

## Summary

Extend the existing `gt escalate` system with tiered routing, structured decision patterns, and integration with gates and patrol lifecycles.

## Background

From gate design discussion and workflow analysis. Gas Town needs:
1. Structured way for workers to request decisions (not just free-form escalation)
2. Tiered routing (Deacon -> Mayor -> Overseer) instead of direct-to-human
3. Integration with gates (timeout -> escalate)
4. Formalized patterns for all worker exit scenarios

## Current State (what exists)

The escalation system is already partially built:

- `gt escalate` command: CRITICAL/HIGH/MEDIUM severity
- Creates bead tagged `escalation`
- Sends mail to `overseer`
- Mayor checks pending escalations on `gt prime`
- Exit type `ESCALATED` for polecats
- Event logging: `escalation_sent`

See: `gastown/docs/escalation.md`

## What's Missing

| Gap | Current | Proposed |
|-----|---------|----------|
| Tiered routing | Direct to overseer | Deacon -> Mayor -> Overseer |
| Decision pattern | Free-form topic | Structured options in issue |
| Categories | Just "escalation" tag | decision, help, blocked, failed, emergency |
| Gate integration | None | Gate timeout -> escalate |
| Patrol integration | Ad-hoc | Formalized paths |

## Design

### Escalation Categories

| Category | Description | Default Destination |
|----------|-------------|---------------------|
| `decision` | Multiple valid paths, need choice | Deacon -> Mayor |
| `help` | Need guidance/expertise | Deacon -> Mayor |
| `blocked` | Waiting on unresolvable dependency | Mayor |
| `failed` | Unexpected error, can't proceed | Deacon |
| `emergency` | Security/data integrity | Overseer (direct) |
| `gate_timeout` | Gate didn't resolve in time | Deacon |
| `lifecycle` | Worker stuck/needs recycle | Witness |

### Extended Command

```bash
# Current (unchanged)
gt escalate "Something went wrong"
gt escalate -s CRITICAL "Data corruption"

# New: Category-based
gt escalate --type decision "Which auth approach?"
gt escalate --type help "Need architecture guidance"
gt escalate --type blocked "Waiting on bd-xyz"
gt escalate --type failed "Tests failing unexpectedly"
gt escalate --type emergency "Security vulnerability found"

# New: Tiered routing
gt escalate --to deacon "Infra issue"
gt escalate --to mayor "Cross-rig coordination needed"
gt escalate --to overseer "Human judgment required"

# New: Structured decision
gt escalate --type decision \
  --question "Which authentication approach?" \
  --options "JWT tokens,Session cookies,OAuth2" \
  --context "Admin panel needs login" \
  --issue bd-xyz
```

### Decision Pattern in Issues

When `--type decision` is used, the issue is updated with structured format:

```markdown
## Decision Needed

**Question:** Which authentication approach?

| Option | Description |
|--------|-------------|
| A | JWT tokens |
| B | Session cookies |
| C | OAuth2 |

**Context:** Admin panel needs login

**Escalated by:** beads/polecats/obsidian
**Escalated at:** 2026-01-01T15:00:00Z

**To resolve:**
1. Comment with chosen option (e.g., "Decision: A")
2. Reassign to work queue or original worker
```

This makes the issue the async communication channel for decisions.

### Tiered Escalation Flow

```
Worker encounters issue
    |
    v
gt escalate --type <category> --to <tier>
    |
    v
[Deacon receives]
    |
    +-- Can resolve? --> Updates issue, re-slings work
    |
    +-- Cannot resolve? --> gt escalate --to mayor --forward
                                |
                                v
                           [Mayor receives]
                                |
                                +-- Can resolve? --> Updates issue, re-slings
                                |
                                +-- Cannot resolve? --> gt escalate --to overseer
                                                            |
                                                            v
                                                       [Overseer resolves]
```

Each tier can resolve OR forward. The escalation bead tracks the chain via comments/notes.

### Integration Points

#### Gate Timeouts (bd-7zka.2)

```go
// In Witness patrol (timer gates)
if gate.Expired() {
    exec.Command("gt", "escalate",
        "--type", "gate_timeout",
        "--severity", "HIGH",
        "--issue", gate.BlockedIssueID,
        fmt.Sprintf("Gate %s timed out after %s", gate.ID, gate.Timeout)).Run()
}
```

#### Witness Patrol

Witness already detects stuck polecats. Formalize as escalation:

```go
// Instead of ad-hoc mail
exec.Command("gt", "escalate",
    "--type", "lifecycle",
    "--to", "mayor",
    "--issue", polecat.CurrentIssue,
    fmt.Sprintf("Polecat %s stuck: no progress for %d minutes", polecat.ID, minutes)).Run()
```

#### Refinery

On merge failures that can't be auto-resolved:

```go
exec.Command("gt", "escalate",
    "--type", "failed",
    "--issue", mr.IssueID,
    "Merge failed: "+reason).Run()
```

### Polecat Exit with Escalation

```bash
# Worker reaches decision point

# 1. Update issue with decision structure
bd update $ISSUE --notes "$(cat <<EOF
## Decision Needed

**Question:** Which approach for caching?

| Option | Description |
|--------|-------------|
| A | Redis (external dependency) |
| B | In-memory (simpler, no persistence) |
| C | SQLite (local persistence) |

**Context:** API response times are slow, need caching layer.
EOF
)"

# 2. Escalate
gt escalate --type decision --issue $ISSUE "Caching approach needs decision"

# 3. Exit cleanly
gt done --exit ESCALATED
```

### New Issue Statuses

Consider adding to beads:

| Status | Meaning |
|--------|---------|
| `decision_needed` | Waiting for decision from escalation |
| `escalated` | General escalation pending |

Or keep using `blocked` with escalation tag - simpler.

## Implementation Plan

### Phase 1: Extend gt escalate
- Add `--type` flag for categories
- Add `--to` flag for routing (deacon, mayor, overseer)
- Add `--forward` flag for tier forwarding
- Keep backward compatibility

### Phase 2: Decision Pattern
- Add `--question`, `--options`, `--context` flags
- Auto-update issue with decision structure
- Parse decision from issue comments on resolution

### Phase 3: Gate Integration
- Add `gate_timeout` escalation type
- Witness: check timer gates, escalate on timeout
- Refinery: check GH gates, escalate on timeout/failure

### Phase 4: Patrol Integration
- Formalize Witness stuck-polecat as escalation
- Formalize Refinery merge-failure as escalation
- Unified escalation handling in Mayor

## Acceptance Criteria

- [ ] `gt escalate --type decision` creates structured issue update
- [ ] `gt escalate --to deacon|mayor|overseer` routes correctly
- [ ] Gate timeouts trigger escalation (after bd-7zka.2)
- [ ] Witness uses escalation for stuck polecats
- [ ] Refinery uses escalation for merge failures
- [ ] Escalation chain tracked in bead (who forwarded to whom)
- [ ] `bd list --tag=escalation --type=decision` filters work

## References

- bd-7zka.2: Gate evaluation (uses escalation for timeouts)
- gastown/docs/escalation.md: Current escalation protocol
- gastown/crew/max/internal/cmd/escalate.go: Current implementation

## Design Complete

The extended escalation design has been documented in gastown/docs/escalation.md.

**Key design decisions:**
1. Categories map to default routing tiers
2. Decision pattern uses issue notes as async communication channel
3. Gate integration via --type gate_timeout
4. Lifecycle escalations formalized for Witness/Refinery

**Implementation phases defined:**
- Phase 1: Extend gt escalate command
- Phase 2: Add decision pattern flags
- Phase 3: Gate integration
- Phase 4: Patrol integration

The acceptance criteria in this issue describe implementation, not design. Those will be verified when the implementation issues are completed.

Design documented in gastown/docs/escalation.md. Implementation issues to follow.


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


Investigate JSONL hash mismatch warnings (bd-160)

During v0.39.1 release, saw repeated warnings:

WARNING: JSONL file hash mismatch detected (bd-160)
  This indicates JSONL and export_hashes are out of sync.
  Clearing export_hashes to force full re-export.

This appeared multiple times during wisp close operations. Questions:
1. What causes export_hashes to drift from actual JSONL content?
2. Is this expected during wisp operations (since wisps do not export)?
3. Should wisp operations skip the hash check entirely?
4. Is there actual data integrity risk or just noise?

The warning references bd-160 - may need to check that issue for context.


Fix update-hooks verification in version-bump.yaml

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


Review PR #834: Add Windows ARM64 build support

Review and potentially merge PR #834 from ramarivera adding native Windows ARM64 (aarch64) build support.

Key changes:
- GoReleaser: Added bd-windows-arm64 build configuration
- CI Workflow: Installed gcc-mingw-w64-aarch64 toolchain
- WinGet Manifest: Added ARM64 installer entry
- Release Notes: Updated to mention Windows ARM64 support

Fixes #833. No code changes - purely build configuration.

Reviewed PR #834. Requested changes: gcc-mingw-w64-aarch64 package doesn't exist in Ubuntu. Recommended using CGO_ENABLED=0 since ncruces/go-sqlite3 is CGO-free.


Add missing hook calls in mail reply and ack

The mail commands are missing hook calls:

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

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

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


Migrate config.yaml to config.json

The beads config file uses YAML format (config.yaml) but should use JSON (config.json) for consistency with the rest of the system. gastown has a fallback in internal/rig/manager.go:initBeads that writes YAML format - this needs to be updated once beads supports JSON config.

Beads already migrating away from YAML config


Commit release v0.33.2

Stage and commit the version bump:

```bash
git add cmd/bd/version.go cmd/bd/info.go CHANGELOG.md
git commit -m "release: v0.33.2"
```

Do NOT push yet - tag first.


Increase ClaudeStartTimeout for slow startup machines

ClaudeStartTimeout is 15s but Claude can take 30s+ to start on some machines. If Claude takes longer than timeout, GUPP nudges arrive before Claude is ready.

Options:
1. Increase timeout to 60s (simple)
2. Improve WaitForClaudeReady detection (harder - has false positives)
3. Retry nudge if no response within N seconds

Location: internal/constants/constants.go

Fixed: increased ClaudeStartTimeout from 15s to 60s in gastown commit 49117c9


Merge: jasper-1767106250817

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


Merge: garnet-1767138539231

branch: polecat/garnet-1767138539231
target: main
source_issue: garnet-1767138539231
rig: beads


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

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


Add Windows code signing for bd.exe releases

## Context

Go binaries (including bd.exe) are commonly flagged by antivirus software as false positives due to heuristic detection. See docs/ANTIVIRUS.md for full details.

## Problem

Kaspersky and other AV software flag bd.exe as PDM:Trojan.Win32.Generic, causing it to be quarantined or deleted.

## Solution

Implement code signing for Windows releases using:
1. An EV (Extended Validation) code certificate
2. Integration with GoReleaser to sign Windows binaries during release

## Benefits

- Reduces false positive rates over time as the certificate builds reputation
- Provides tamper verification for users
- Improves SmartScreen trust rating on Windows
- Professional appearance for enterprise users

## Implementation Steps

1. Acquire EV code signing certificate (annual cost ~$300-500)
2. Set up signtool or osslsigncode in release pipeline
3. Update .goreleaser.yml to sign Windows binaries
4. Update checksums to include signed binary hashes
5. Document signing verification in ANTIVIRUS.md

## References

- docs/ANTIVIRUS.md - Current documentation
- bd-t4u1 - Original Kaspersky false positive report
- https://github.com/golang/go/issues/16292 - Go project discussion


Test actor direct


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


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.


bd formula add: Import formula to local catalog

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

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

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

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

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

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

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

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


bd mol install: Install formula from GitHub

Download and cook a formula from a GitHub repository.

## Usage
```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 (future: via registry lookup)
bd mol install @anthropic/mol-code-review
```

## Implementation
1. Parse URL: extract org, repo, optional version tag
2. Construct raw URL: `https://raw.githubusercontent.com/org/repo/[tag]/formula.json`
3. Fetch formula.json via HTTP
4. Validate formula structure
5. Save to .beads/formulas/
6. Run cook to create local proto
7. Record in .beads/installed.json for update tracking

## installed.json Format
```json
{
  "mol-code-review": {
    "source": "github.com/anthropics/mol-code-review",
    "version": "v1.2.0",
    "installed_at": "2025-12-25T12:00:00Z"
  }
}
```

## Dependencies
- Requires network access
- Uses `gh` CLI or direct HTTP for auth (private repos)


bd mol update: Check and update installed formulas

Check for newer versions of installed formulas and update them.

## Usage
```bash
bd mol update                    # Update all
bd mol update mol-code-review    # Update specific formula
bd mol update --check            # Just check, don't update
```

## Implementation
1. Read .beads/installed.json
2. For each installed formula:
   - Fetch latest tag from GitHub API
   - Compare with installed version
   - If newer: download, cook, update installed.json
3. Report what was updated

## Flags
- `--check` - Only check for updates, don't install
- `--force` - Reinstall even if up to date
- `--json` - Machine-readable output


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


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


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.


Installation tracking: .beads/installed.json

Track installed formulas for update checking and provenance.

## File Format
```json
{
  "mol-code-review": {
    "source": "github.com/anthropics/mol-code-review",
    "version": "v1.2.0",
    "installed_at": "2025-12-25T12:00:00Z",
    "formula_hash": "abc123..."
  },
  "mol-polecat-work": {
    "source": "local",
    "version": "1",
    "installed_at": "2025-12-24T10:00:00Z"
  }
}
```

## Commands That Update This
- `bd mol install` - Adds entry
- `bd mol update` - Updates version/timestamp
- `bd cook` - Adds entry with source:local
- `bd mol uninstall` - Removes entry (new command)

## Usage
```bash
bd mol list --installed    # Shows installed with source/version
bd mol outdated            # Shows formulas with available updates
```

## Sync Behavior
- installed.json is gitignored (local state)
- Only formulas/ directory is synced
- Each clone tracks its own installations


Add bd refile command to move issues between rigs

Add a `bd refile <source-id> <target-rig>` command to move issues between rigs.

Beads already has the routing infrastructure:
- ResolveBeadsDirForID - routes reads by prefix
- ResolveBeadsDirForRig - routes writes by rig name
- GetRoutedStorageForID - opens cross-rig storage connections

Implementation:
1. Get source issue via routing
2. Create new issue in target rig (existing --prefix mechanism)
3. Close source issue with 'Refiled to <new-id>'
4. Optionally: migrate deps, link the two issues

This is pure beads - no gastown involvement needed.


Merge: obsidian-mjxeci6l

branch: polecat/obsidian-mjxeci6l
target: main
source_issue: obsidian-mjxeci6l
rig: beads
agent_bead: bd-beads-polecat-obsidian

Orphaned MR - obsidian stopped (merged no-op), branch never pushed


Time-dependent worktree detection tests may be flaky


Test prefix bd-


Investigate charmbracelet-based TUI for beads

Now that we've merged the create-form command (PR #603) which uses charmbracelet/huh, investigate whether beads should have a more comprehensive TUI.

Considerations:
- Should this be in core or a separate binary (bd-tui)?
- What functionality would benefit from a TUI? (list view, issue details, search, bulk operations)
- Plugin/extension architecture vs build tags vs separate binary
- Dependency cost vs user experience tradeoff
- Target audience: humans who want interactive workflows vs CLI/scripting users

Related: PR #603 added charmbracelet/huh dependency for create-form command.

Foundation is in place (lipgloss, huh), but not a priority right now


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

attached_args: Refactor ComputeContentHash repetitive code

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

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

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

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


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


sync branch not match config

./bd sync
→ Exporting pending changes to JSONL...
→ No changes to commit
→ Pulling from sync branch 'gh-386'...
Error pulling from sync branch: failed to create worktree: failed to create worktree parent directory: mkdir /var/home/matt/dev/beads/worktree-db-fail/.git: not a directory
matt@blufin-framation ~/d/b/worktree-db-fail (worktree-db-fail) [1]> bd config list

Configuration:
  auto_compact_enabled = false
  compact_batch_size = 50
  compact_model = claude-3-5-haiku-20241022
  compact_parallel_workers = 5
  compact_tier1_days = 30
  compact_tier1_dep_levels = 2
  compact_tier2_commits = 100
  compact_tier2_days = 90
  compact_tier2_dep_levels = 5
  compaction_enabled = false
  issue_prefix = worktree-db-fail
  sync.branch = worktree-db-fail

Config mismatch: added warning to bd config list when config.yaml overrides database settings. Worktree issue was already fixed by GH#639.


Upgrade beads-mcp to 0.33.2

Upgrade the MCP server via pip:

```bash
pip install --upgrade beads-mcp
pip show beads-mcp | grep Version  # Verify 0.33.2
```

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


Add 'bd status' command for issue database overview

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

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

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

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

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


Fix JSON error output consistency across all commands

## Problem

When `--json` flag is specified, error output should return JSON format, not plain text. Currently only `bd show` uses `FatalErrorRespectJSON` - all other commands use direct `fmt.Fprintf(os.Stderr, ...)`.

## Audit Findings (bd-au0.7)

| File | stderr writes | Commands affected |
|------|---------------|-------------------|
| show.go | 51 | update, close, edit |
| dep.go | 41 | dep add/remove/tree/cycles |
| label.go | 19 | label add/remove/list |
| comments.go | ~10 | comments add/list |
| epic.go | ~5 | epic status/close-eligible |

## Solution

Replace `fmt.Fprintf(os.Stderr, ...) + os.Exit(1)` patterns with `FatalErrorRespectJSON()` in all commands that support `--json` flag.

## Pattern

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

// After (respects --json)
FatalErrorRespectJSON("%v", err)
```

## Success Criteria

- All commands return JSON errors when `--json` flag is set
- Error format: `{"error": "message"}`
- Exit code 1 preserved for errors


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

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

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

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


Fix JSON errors in dep.go (dependency commands)

Replace ~41 direct stderr writes with FatalErrorRespectJSON() in dep.go.

Commands affected:
- depAddCmd
- depRemoveCmd
- depTreeCmd
- depCyclesCmd

Key error paths:
- ID resolution errors
- Cycle detection errors
- External reference validation


Fix JSON errors in label.go (label commands)

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

Commands affected:
- labelAddCmd
- labelRemoveCmd
- labelListCmd
- labelListAllCmd


Fix JSON errors in comments.go

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

Commands affected:
- commentsCmd (list)
- commentsAddCmd


Fix JSON errors in epic.go

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

Commands affected:
- epicStatusCmd
- epicCloseEligibleCmd


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.


Merge: ruby-1767142029451

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


Remove deprecated daemon logger function

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

File: cmd/bd/daemon_logger.go:131


Update CHANGELOG.md with release notes

Add meaningful release notes to CHANGELOG.md describing what changed in 0.30.7


Merge: obsidian-mjvtl03h

branch: polecat/obsidian-mjvtl03h
target: main
source_issue: obsidian-mjvtl03h
rig: beads
agent_bead: gt-beads-polecat-obsidian

Branch already merged and deleted by previous refinery session


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.


Implement await type handlers (gh:run, gh:pr, timer, human, mail)

Implement condition checking for each await type.

## Handlers Needed
- gh:run:<id> - Check GitHub Actions run status via gh CLI
- gh:pr:<id> - Check PR merged/closed status via gh CLI
- timer:<duration> - Simple elapsed time check
- human:<prompt> - Check for human approval (via mail?)
- mail:<pattern> - Check for mail matching pattern

## Implementation Location
This is Deacon logic, so likely in Gas Town (gt) not beads.

## Interface
```go
type AwaitHandler interface {
    Check(awaitID string) (completed bool, result string, err error)
}
```


Merge: obsidian-1767138468820

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


Refinery Patrol

Merge queue processor patrol loop with verification gates.


Edge Schema Consolidation: Unify all edges in dependencies table

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

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

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


Add metadata and thread_id columns to dependencies table

Schema changes:
- ALTER TABLE dependencies ADD COLUMN metadata TEXT DEFAULT '{}'
- ALTER TABLE dependencies ADD COLUMN thread_id TEXT DEFAULT ''
- CREATE INDEX idx_dependencies_thread ON dependencies(thread_id) WHERE thread_id != ''


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)


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.


Create migration script for edge field to dependency conversion

Migration must:
1. Read existing JSONL with old fields
2. Convert field values to dependency records
3. Write updated JSONL without old fields
4. Handle edge cases (missing refs, duplicates)

Run via: bd migrate or automatic on bd prime


Beads commands operate on stale database without warning

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

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

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

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

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

## Implementation Complete

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

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

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

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

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


Test coordinator display

Stale/spurious - test artifacts, merged PRs, or auto-close candidates


Code Review: Swarm commands consolidation

Code review of bd swarm commands (validate, status, create) identified several improvements.

**Summary:**
- Duplicated child-fetching logic between commands
- Missing duplicate swarm check in create
- Missing swarm list command
- Minor polish issues

Filed after implementing bd-fa1q (swarm create).


Add gate issue type to beads

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

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

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


Ephemeral issue cleanup and history compaction

## Problem

Beads history grows without bound. Every message, handoff, work assignment
stays in issues.jsonl forever. Enterprise users will balk at "git as database."

## Solution: Two-Tier Cleanup

### Tier 1: Ephemeral Cleanup (v1)

bd cleanup --ephemeral --closed

- Deletes closed issues where ephemeral=true from issues.jsonl
- Safe: only removes explicitly marked ephemeral + closed
- Preserves git history (commits still exist)
- Run after swarm completion

### Tier 2: History Compaction (v2)

bd compact --squash

- Rewrites issues.jsonl to remove tombstones
- Optionally squashes git history (interactive rebase equivalent)
- Preserves Merkle proofs for deleted items
- Advanced: cold storage tiering

## HOP Context

| Layer | HOP Role | Persistence |
|-------|----------|-------------|
| Execution trace | None | Ephemeral |
| Work scaffolding | None | Summarizable |
| Work outcome | CV entry | Permanent |
| Validation record | Stake proof | Permanent |

"Execution is ephemeral. Outcomes are permanent. You can't squash your CV."

## Success Criteria

- After cleanup --ephemeral: issues.jsonl only contains persistent work
- Work outcomes preserved (CV entries)
- Validation records preserved (stake proofs)
- Execution scaffolding removed (transient coordination)

## Implementation Plan (REVISED after code review)

See history/EPHEMERAL_MOLECULES_DESIGN.md for comprehensive design + review.

## Key Simplification

After code review, Tier 1 is MUCH simpler than originally designed:

- **Original**: Separate ephemeral repo with routing.ephemeral config
- **Revised**: Just set Wisp: true in cloneSubgraph()

The wisp field and bd cleanup --wisp already exist\!

## Child Tasks (in dependency order)

1. **bd-2vh3.2**: Tier 1 - Ephemeral spawning (SIMPLIFIED) [READY]
   - Just add Wisp: true to template.go:474
   - Add --persistent flag to opt out
2. **bd-2vh3.3**: Tier 2 - Basic bd mol squash command
3. **bd-2vh3.4**: Tier 3 - AI-powered squash summarization
4. **bd-2vh3.5**: Tier 4 - Auto-squash on molecule completion
5. **bd-2vh3.6**: Tier 5 - JSONL archive rotation (DEFERRED: post-1.0)

## What Already Exists

| Component | Location |
|-----------|----------|
| Ephemeral field | internal/types/types.go:45 |
| bd cleanup --wisp | cmd/bd/cleanup.go:72 |
| cloneSubgraph() | cmd/bd/template.go:456 |
| loadTemplateSubgraph() | cmd/bd/template.go |

## HOP Alignment

'Execution is ephemeral. Outcomes are permanent. You can't squash your CV.'


Tier 1: Ephemeral repo routing

Add routing.ephemeral config option to route ephemeral=true issues to separate location.

## Changes Required

1. Add `routing.ephemeral` config option (default: empty = disabled)
2. Update routing logic in `determineRepo()` to check ephemeral flag
3. Update `bd create` to respect ephemeral routing
4. Update import/export for multi-location support
5. Ephemeral repo can be:
   - Separate git repo (~/.beads-ephemeral)
   - Non-git directory (just filesystem)
   - Same repo, different branch (future)

## Config

```bash
bd config set routing.ephemeral "~/.beads-ephemeral"
```

## Acceptance Criteria

- `bd create "test" --ephemeral` creates in ephemeral repo when configured
- `bd list` shows issues from both repos
- Ephemeral repo never synced to remote


Tier 1: Ephemeral repo routing

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

## The Fix

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

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

## Optional: Add --persistent flag

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

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

## Why This Is Simpler Than Original Design

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

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

## Acceptance Criteria

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


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


Tier 3: AI-powered squash summarization

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

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

### API

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

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

### Gas Town Integration Pattern

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

### Why This Design

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

### Implementation Status

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

### Acceptance Criteria

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


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


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.


Test pinned for stats


bd repair: Check comments and events tables for orphans

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

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

Implemented in repair.go: added orphan detection for comments/events tables and --json output flag


rpc: Rich mutation events not emitted

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

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

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

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

Discovered during code review of bd-xo1o implementation.


Document labels-as-state pattern for operational state

Labels on role beads cache current operational state for fast queries.

Convention: <dimension>:<value>
- patrol:muted / patrol:active
- mode:degraded / mode:normal
- status:idle / status:working

Pattern:
1. Create event bead (full context, immutable history)
2. Update role bead labels (current state cache)

No schema change needed - labels already exist. This task:
- Document the convention in beads docs
- Consider helper: bd state <role> <dimension> (queries label)
- Consider helper: bd set-state <role> <dimension>=<value> (creates event + updates label)

Events are source of truth. Labels are cache.

Added 'Labels as State Cache' section to docs/LABELS.md with convention, pattern, examples, and best practices


Merge: granite-mjtmdru5

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

Stale MR - branch no longer exists on remote


Give --all flag actual meaning in bd list

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

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

## Implementation Plan

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

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

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

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

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


Implement `bd setup cursor` for Cursor IDE integration

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


Add orphan detection migration

Create migration to detect orphaned children in existing databases. Query: SELECT id FROM issues WHERE id LIKE '%.%' AND substr(id, 1, instr(id || '.', '.') - 1) NOT IN (SELECT id FROM issues). Log results, let user decide action (delete orphans or convert to top-level).


GetBlockedIssues shows external deps as blocking even when satisfied

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

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

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

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


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.


Rebuild local binary

Build and verify: go build -o bd ./cmd/bd && ./bd version


activity.go: Missing test coverage

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

Discovered during code review of bd-xo1o implementation.


bd close lacks cross-rig routing support

The `bd close` command doesn't support cross-rig routing like `bd show` does.

**Reproduction:**
```
# From gastown directory
bd show bd-xyz    # Works (routes to beads)
bd close bd-xyz   # Fails with 'no issue found matching'
```

**Root cause:**
- `show.go` uses `needsRouting()` and `resolveAndGetIssueWithRouting()`
- `close.go` only uses `utils.ResolvePartialIDs()` without routing

**Other affected commands (likely):**
- reopen
- dep
- comments
- label
- duplicate
- defer/undefer

**Fix:**
Add routing support to `close.go` similar to `show.go` lines 44-147.


Merge: topaz-1767142022153

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


Add 'tracks' relation type for convoy tracking

Add a new relation type 'tracks' for convoy → issue relationships.

Unlike 'depends_on':
- Non-blocking (tracked issue doesn't block convoy)
- Cross-prefix capable (convoy in hq-* tracks issues in gt-*, bd-*)
- Supports reverse lookup ('what convoys track this issue?')

Used by convoys to track issues across project chains without creating dependencies.

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


Auto-repair stale merge driver configs with invalid placeholders

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


GHI #880: Failures with exit status 128 due to path casing mismatch on macOS

GitHub Issue #880: Failures with exit status 128 due to path casing mismatch on macOS

https://github.com/steveyegge/beads/issues/880


Create bd admin parent command and nest cleanup/compact/reset

## Task
Create new `bd admin` parent command and move:
- `bd cleanup` → `bd admin cleanup`
- `bd compact` → `bd admin compact`
- `bd reset` → `bd admin reset`

## Implementation

### 1. Create admin.go
New file with parent command:
```go
var adminCmd = &cobra.Command{
    Use:   "admin",
    Short: "Administrative commands for database maintenance",
    Long: `Administrative commands for beads database maintenance.

These commands are for advanced users and should be used carefully:
  cleanup   Delete closed issues and prune expired tombstones
  compact   Compact old closed issues to save space
  reset     Remove all beads data and configuration

For routine operations, prefer 'bd doctor --fix'.`,
}

func init() {
    rootCmd.AddCommand(adminCmd)
    adminCmd.AddCommand(cleanupCmd)
    adminCmd.AddCommand(compactCmd)
    adminCmd.AddCommand(resetCmd)
}
```

### 2. Update cleanup.go, compact.go, reset.go
- Remove `rootCmd.AddCommand()` from each init()
- Keep all existing functionality

### 3. Create hidden aliases for backwards compatibility
Top-level hidden commands that forward to admin subcommands.

### 4. Update docs (major updates)
- docs/CLI_REFERENCE.md - cleanup, compact references
- docs/QUICKSTART.md - compact, cleanup references
- docs/FAQ.md - compact references
- docs/TROUBLESHOOTING.md - compact references
- docs/DELETIONS.md - compact reference
- docs/CONFIG.md - compact reference
- skills/beads/SKILL.md - compact reference
- commands/compact.md - update all examples
- examples/compaction/README.md - update examples

## Files to create
- cmd/bd/admin.go

## Files to modify
- cmd/bd/cleanup.go
- cmd/bd/compact.go
- cmd/bd/reset.go
- cmd/bd/main.go (aliases)
- Multiple docs (see list above)


Test issue for pin --for

Testing the pin --for flag


Merge: bd-by0d

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


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


Merge: bd-tvu3

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


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

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


bd init --from-jsonl: Skip git history scan

## Problem

`bd init` scans git history to find all issues ever created, which:
- Resurrects deleted/compacted issues
- Takes a long time on large repos
- Defeats manual JSONL cleanup

## Solution

Add `bd init --from-jsonl` flag that:
1. Creates fresh database
2. Imports ONLY from current .beads/issues.jsonl
3. Skips git history scan entirely

## Use Case

After manually cleaning JSONL or receiving a clean JSONL from another source:
```bash
# Clean JSONL manually or via script
bd init --from-jsonl  # Use current JSONL as-is
```

(Moved from hq-c21fj)


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

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

## Use Case

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

## Documentation Needed

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

## Current Support

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

Just needs documentation for the multi-agent workspace pattern.


Add thread-safety warning to git.ResetCaches() doc comment

Code review finding from bd-7di fix.

ResetCaches() reassigns sync.Once values which is not thread-safe. While this is only used in tests (which are single-threaded), the function is exported and could be misused.

Add a warning comment:
```go
// ResetCaches resets all cached git information.
// WARNING: Not thread-safe. Only call from single-threaded test contexts.
```

File: internal/git/gitdir.go


Merge: obsidian-mjxbi027

branch: polecat/obsidian-mjxbi027
target: main
source_issue: obsidian-mjxbi027
rig: beads
agent_bead: bd-beads-polecat-obsidian

Branches merged, cleaning up stale MR beads


Refactor: Extract shared child-fetching logic in swarm.go

Lines 162-181 (analyzeEpicForSwarm) and 630-649 (getSwarmStatus) have identical logic for fetching epic children via parent-child dependencies.

Extract to a shared helper function like:

```go
func getEpicChildren(ctx context.Context, s StorageInterface, epicID string) ([]*types.Issue, error)
```

This will reduce duplication and ensure consistent behavior between validate/status/create commands.


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


Add bd daemon --stop-all command to kill all daemon processes

Currently there's no easy way to stop all running bd daemon processes. Users must resort to pkill -f 'bd daemon' or similar shell commands.

Add a --stop-all flag to bd daemon that:
1. Finds all running bd daemon processes (not just the current repo's daemon)
2. Gracefully stops them all
3. Reports how many were stopped

This is useful when:
- Multiple daemons are running and causing race conditions
- User wants a clean slate before running bd sync
- Debugging daemon-related issues


Workaround for FastMCP outputSchema bug in Claude Code

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

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

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

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


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

Already implemented. GetGitDir() uses getGitContext() which is cached via sync.Once. The gitContext struct (line 13-19) holds gitDir and is populated once by initGitContext(). All dependent functions (GetGitHooksDir, GetGitRefsDir, GetGitHeadPath) benefit from this caching. No changes needed.


Code review findings: mol squash deletion bypasses tombstones


Add gate field to Step schema in formula types

Add Gate field to Step struct in internal/formula/types.go.

## Implementation

```go
type Step struct {
    // ... existing fields ...

    // Gate defines an async wait condition for this step.
    Gate *GateDef `json:"gate,omitempty"`
}

type GateDef struct {
    Type     string `json:"type"`              // timer, gh:run, gh:pr, bead, human
    Workflow string `json:"workflow,omitempty"` // GitHub workflow name
    Timeout  string `json:"timeout,omitempty"`  // Max wait before escalation
    AwaitID  string `json:"await_id,omitempty"` // Explicit condition ID
}
```

## Acceptance Criteria
- [ ] GateDef struct defined
- [ ] Step.Gate field added
- [ ] TOML and JSON parsing works
- [ ] Tests for gate field parsing

Added tests for gate field parsing. Gate struct and Step.Gate field already existed. Tests verify JSON and TOML parsing works correctly.


Test epic for duplicate swarm


Widespread double JSON encoding bug in daemon mode RPC calls

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

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

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

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

All commands now tested and working correctly with daemon mode.


bd sync false-positive force-push detection

bd sync incorrectly detected a force-push when comparing against a commit from main branch instead of beads-sync.

Evidence:
- bd sync reported: 'Previous known commit: f4d3f674, Current remote commit: 4d24f794'
- f4d3f674 is a valid beads-sync commit
- 4d24f794 is a MAIN branch commit: 'refactor: remove unused isFirstRig param (gt-fugmy)'

Root cause: bd sync is comparing against wrong branch or storing wrong commit reference.

Impact: False force-push warnings during normal operation, requires manual --accept-rebase.


wish: list & ready show issues as hierarchy tree

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


bd cook creates gate beads for gate steps

When cooking a formula, steps with Gate field create corresponding gate beads.

## Behavior
1. During cook, detect steps with Gate != nil
2. Create gate bead with ID: `<mol-id>.gate-<step-id>`
3. Gate bead has:
   - issue_type: gate
   - await_type: from Gate.Type
   - await_id: from Gate.AwaitID (or auto-discovered)
   - timeout: from Gate.Timeout
4. Dependent steps get blocking dep on gate bead

## Example
Step `await-ci` with gate.type="gh:run" creates:
- bd-mol-xxx.gate-await-ci (type=gate, await_type=gh:run)
- bd-mol-xxx.verify-github depends on bd-mol-xxx.gate-await-ci

## Acceptance Criteria
- [ ] Gate beads created during cook
- [ ] Correct await_type/await_id populated
- [ ] Dependent steps blocked by gate
- [ ] Tests for gate creation

Implementation already existed in cook.go (createGateIssue + collectSteps). Added comprehensive tests in cook_test.go covering: gate issue creation, await_type/await_id population, blocking dependencies, parent-child relationships, and timeout parsing.


Correction: Pinned field already in v0.31.0

Quick correction - the Pinned field is already in the current bd v0.31.0:

```go
// In beads internal/types/types.go
Pinned bool `json:"pinned,omitempty"`
```

So you just need to:
1. Add `Pinned bool `json:"pinned,omitempty"`` to BeadsMessage in types.go
2. Sort pinned messages first in listBeads() after fetching

No migration needed - the field is already there.

-- Mayor


Remove dead test code in info_test.go

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

- TestInfoCommand
- TestInfoCommandNoDaemon

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

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


bd create --rig flag for cross-rig issue filing

## Problem

To create an issue in a different rig, you must cd to that rig's directory. This is clunky when coordinating across rigs.

## Proposed Solution

Add a --rig flag to bd create that leverages existing routes.jsonl:

```bash
# From anywhere in town
bd create --rig beads --title="bug report" --type=bug

# Equivalent to:
cd ~/gt/beads/mayor/rig && bd create --title="..."
```

## How it works

1. Parse --rig flag (rig name, not prefix)
2. Look up rig in routes.jsonl to find beads location
3. Determine prefix from rig config
4. Create issue in that location with auto-generated ID

## Why elegant

- Leverages existing routing infrastructure
- Rig names are human-memorable (not cryptic prefixes)
- Works from anywhere in town
- Prefix auto-determined from rig config
- Reads naturally: "create in beads"

## Alternative

Also consider --prefix for power users who know prefixes:
```bash
bd create --prefix bd "Title here"
```


Refactor long SQLite test files

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

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

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

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

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


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.


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.


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.


Improve bd doctor output formatting for better readability.

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

## Target Output

```
$ bd doctor

bd doctor v0.35.0

Summary: 24 checks passed, 1 warning, 0 errors

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

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

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

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

bd doctor v0.35.0

Summary: 24 checks passed, 1 warning, 0 errors

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

[hooks] Git hooks outdated
  ...

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

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

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

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

## Implementation

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

```go
type Category string

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

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

### 2. Add --verbose flag

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

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

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

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

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

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

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

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

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

    return buf.String()
}

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

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

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

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

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

### 4. Update run function

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

    results := runAllChecks()

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

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

## Files to Modify

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

## Testing

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

# Verbose output
bd doctor --verbose

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

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


Fix TestFallbackToDirectModeEnablesFlush deadlock causing 10min test timeout

## Problem

TestFallbackToDirectModeEnablesFlush in direct_mode_test.go deadlocks for 9m59s before timing out, causing the entire test suite to take 10+ minutes instead of <10 seconds.

## Root Cause

Database lock contention between test cleanup and flushToJSONL():
- Test cleanup (line 36) tries to close DB via defer
- flushToJSONL() (line 132) is still accessing DB
- Results in deadlock: database/sql.(*DB).Close() waits for mutex while GetJSONLFileHash() holds it

## Stack Trace Evidence

```
goroutine 512 [sync.Mutex.Lock, 9 minutes]:
database/sql.(*DB).Close(0x14000643790)
  .../database/sql/sql.go:927 +0x84
github.com/steveyegge/beads/cmd/bd.TestFallbackToDirectModeEnablesFlush.func1()
  .../direct_mode_test.go:36 +0xf4

While goroutine running flushToJSONL() holds DB connection via GetJSONLFileHash()
```

## Impact

- Test suite: 10+ minutes → should be <10 seconds
- ALL other tests pass in ~4 seconds
- This ONE test accounts for 99.9% of test runtime

## Related

This is the EXACT same issue documented in MAIN_TEST_REFACTOR_NOTES.md for why main_test.go refactoring was deferred - global state manipulation + DB cleanup = deadlock.

## Fix Approaches

1. **Add proper cleanup sequencing** - stop flush goroutines BEFORE closing DB
2. **Use test-specific DB lifecycle** - ensure flush completes before cleanup
3. **Mock the flush mechanism** - avoid real DB for testing this code path
4. **Add explicit timeout handling** - fail fast with clear error instead of hanging

## Files

- cmd/bd/direct_mode_test.go:36-132
- cmd/bd/autoflush.go:353 (validateJSONLIntegrity)
- cmd/bd/autoflush.go:508 (flushToJSONLWithState)

## Acceptance

- Test passes without timeout
- Test suite completes in <10 seconds
- No deadlock between cleanup and flush operations


Merge: bd-14ie

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


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

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

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

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

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

Refactored Issue struct using composition: created 4 embedded structs (IssueCompaction, IssueTombstone, IssueGate, IssueAgent) to group ~19 specialized fields while keeping core fields flat for ergonomic struct literals. JSON serialization unchanged.


Code Review Followup Summary: PR #481 + PR #551

## Merged PRs Summary

### PR #551: Persist close_reason to issues table
- ✅ Merged successfully
- ✅ Bug fix: close_reason now persisted in database column (not just events table)
- ✅ Comprehensive test coverage added
- ✅ Handles reopen case (clearing close_reason)

**Followup Issues Filed:**
- bd-lxzx: Document close_reason in JSONL export format
- bd-077e: Update CLI documentation for close_reason field

---

### PR #481: Context Engineering Optimizations (80-90% context reduction)
- ✅ Merged successfully
- ✅ Lazy tool discovery: discover_tools() + get_tool_info()
- ✅ Minimal issue models: IssueMinimal (~80% smaller than full Issue)
- ✅ Result compaction: Auto-compacts results >20 items
- ✅ All 28 tests passing
- ⚠ Breaking change: ready() and list() return type changed

**Followup Issues Filed:**
- bd-b318: Add integration tests for CompactedResult
- bd-4u2b: Make compaction settings configurable (THRESHOLD, PREVIEW_COUNT)
- bd-2kf8: Document CompactedResult response format in CONTEXT_ENGINEERING.md
- bd-pdr2: Document backwards compatibility considerations

---

## Overall Assessment

Both PRs are production-ready with solid implementations. All critical functionality works and tests pass. Followup issues focus on:
1. Documentation improvements (5 issues)
2. Integration test coverage (1 issue)
3. Configuration flexibility (1 issue)
4. Backwards compatibility guidance (1 issue)

No critical bugs or design issues found.

## Review Completed By
Code review process completed. Issues auto-created for tracking improvements.


Bump version in all files

Run ./scripts/bump-version.sh {{version}} to update 10 version files. Then run with --commit after info.go is updated.


Merge: ux-1766978911613

branch: polecat/Nux-1766978911613
target: main
source_issue: ux-1766978911613
rig: beads


Verify release artifacts

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


Merge: quartz-mjxcafcl

branch: polecat/quartz-mjxcafcl
target: main
source_issue: quartz-mjxcafcl
rig: beads
agent_bead: bd-beads-polecat-quartz


Merge: obsidian-1767083466920

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

Branch polecat/obsidian-1767083466920 no longer exists on remote


Review PR #514: fix plugin install docs

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


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)


HANDOFF: Version 0.30.7 release in progress

## Context
Doing a 0.30.7 patch release with bug fixes.

## What's done
- Fixed #657: bd graph nil pointer crash (graph.go:102)
- Fixed #652: Windows npm installer file lock (postinstall.js)
- Updated CHANGELOG.md and info.go
- Pushed to main, CI running (run 20390861825)
- Created version-bump molecule template (bd-6s61) and instantiated for 0.30.7 (bd-8pyn)

## In progress
Molecule bd-8pyn has 3 remaining tasks:
  - bd-dxo7: Wait for CI to pass
  - bd-7l70: Verify release artifacts
  - bd-5c91: Update local installation

## Check CI
  gh run list --repo steveyegge/beads --limit 1
  gh run view 20390861825 --repo steveyegge/beads

## New feature filed
bd-n777: Timer beads for scheduled agent callbacks
Design for Deacon-managed timers that can interrupt agents via tmux

## Resume commands
  bd --no-daemon show bd-8pyn
  gh run list --repo steveyegge/beads --limit 1


Add tests for helper functions (GetDirtyIssueHash, GetAllDependencyRecords, export hashes)

Several utility functions have 0% coverage:
- GetDirtyIssueHash (dirty.go)
- GetAllDependencyRecords (dependencies.go)
- GetExportHash, SetExportHash, ClearAllExportHashes (hash.go)

These are lower priority but should have basic coverage.


GHI #889: postinstall fails on Windows - zip being used by another process

GitHub Issue #889: postinstall fails on Windows: zip 'being used by another process'

https://github.com/steveyegge/beads/issues/889

Fixed: Added retry logic with exponential backoff for Windows zip extraction to handle file lock delays from antivirus/Node.js


Test Agent


Add backwards-compatible aliases and deprecation warnings

## Task
Ensure backwards compatibility by adding hidden aliases for all moved commands.

## Implementation

### Create aliases.go
New file to centralize alias management:
```go
package main

import (
    "fmt"
    "os"

    "github.com/spf13/cobra"
)

// Deprecated command aliases for backwards compatibility
// These will be removed in v0.XX.0

func init() {
    // migrate-* → migrate *
    addDeprecatedAlias("migrate-hash-ids", "migrate hash-ids")
    addDeprecatedAlias("migrate-issues", "migrate issues")
    addDeprecatedAlias("migrate-sync", "migrate sync")
    addDeprecatedAlias("migrate-tombstones", "migrate tombstones")

    // top-level → admin *
    addDeprecatedAlias("cleanup", "admin cleanup")
    addDeprecatedAlias("compact", "admin compact")
    addDeprecatedAlias("reset", "admin reset")

    // top-level → formula *
    addDeprecatedAlias("cook", "formula cook")
}

func addDeprecatedAlias(old, new string) {
    cmd := &cobra.Command{
        Use:    old,
        Hidden: true,
        Run: func(cmd *cobra.Command, args []string) {
            fmt.Fprintf(os.Stderr, "⚠  '%s' is deprecated, use '%s' instead\n", old, new)
            // Forward execution to new command
        },
    }
    rootCmd.AddCommand(cmd)
}
```

### Deprecation behavior
1. First release: Show warning, execute command
2. One release later: Show warning, still execute
3. Third release: Remove aliases entirely

## Files to create
- cmd/bd/aliases.go

## Files to modify
- cmd/bd/main.go (ensure aliases loaded)


Merge: bd-49kw

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


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


Code smell: Issue struct is a God Object (50+ fields)

attached_args: Refactor Issue struct God Object

The Issue struct in internal/types/types.go has 50+ fields covering many different concerns:

- Basic issue tracking (ID, Title, Description, Status, Priority)
- Messaging/communication (Sender, Ephemeral, etc.)
- Agent identity (HookBead, RoleBead, AgentState, RoleType, Rig)
- Gate/async coordination (AwaitType, AwaitID, Timeout, Waiters)
- Source tracing (SourceFormula, SourceLocation)
- HOP validation/entities (Creator, Validations)
- Compaction (CompactionLevel, CompactedAt, OriginalSize)
- Tombstones (DeletedAt, DeletedBy, DeleteReason)

Consider:
1. Extracting field groups into embedded structs (e.g., AgentFields, GateFields)
2. Using composition pattern to make the struct more modular
3. At minimum, grouping related fields together with section comments

Location: internal/types/types.go:12-82


Mega-molecule progress support for Hanoi demo

Beads-side work for million-step molecule monitoring.

## Context
Towers of Hanoi with 20 disks = 1,048,575 steps.
Need efficient progress queries and display guards.

## Requirements
1. bd mol progress command (summary without listing all steps)
2. Display guards in bd mol current (detect large molecules)
3. Efficient child count queries (already indexed)

All children complete: mol progress command and display guards implemented


Optimize GetReadyWork performance - 752ms on 10K database (target: <50ms)

# Performance Analysis (10K Issue Database)

Analyzed using CPU profiles from benchmark suite on Apple M2 Pro.

## Operation Performance

| Operation                        | Time    | Allocations | Memory |
|----------------------------------|---------|-------------|--------|
| bd ready (GetReadyWork)          | ~752ms  | 167,466     | 16MB   |
| bd list (SearchIssues no filter) | ~11.6ms | 89,214      | 5.8MB  |
| bd list (SearchIssues filtered)  | ~9.2ms  | 62,365      | 3.5MB  |
| bd create (CreateIssue)          | ~2.6ms  | 146         | 8.6KB  |
| bd update (UpdateIssue)          | ~0.32ms | 364         | 15KB   |
| bd close (UpdateIssue)           | ~0.32ms | 364         | 15KB   |

**Target: <50ms for all operations on 10K database**

**Current issue: GetReadyWork is 15x over target (752ms vs 50ms)**

## Root Cause

GetReadyWork (internal/storage/sqlite/ready.go:90-128) uses recursive CTE to propagate blocking:
- 65x slower than SearchIssues
- Recalculates entire blocked issue tree on every call
- Algorithm:
  1. Find directly blocked issues via 'blocks' dependencies
  2. Recursively propagate blockage to descendants (max depth: 50)
  3. Exclude all blocked issues from results

## CPU Profile Analysis

- Database syscalls (pthread_cond_signal, syscall6): ~75%
- SQLite engine overhead: inherent to recursive CTE
- Application code (query construction): <1%

**Bottleneck is the recursive CTE query execution, not application code.**

## Optimization Recommendations

### High Impact (Likely to achieve <50ms target)

1. **Cache blocked issue calculation**
   - Add `blocked_issues` table updated on dependency changes
   - Trade write complexity for read speed (ready called >> dependency changes)
   - Eliminates recursive CTE on every read

2. **Add/verify database indexes**
   ```sql
   CREATE INDEX IF NOT EXISTS idx_dependencies_blocked
     ON dependencies(issue_id, type, depends_on_id);
   CREATE INDEX IF NOT EXISTS idx_issues_status
     ON issues(status);
   ```

### Medium Impact

3. **Reduce allocations** (167K allocations for GetReadyWork)
   - Profile `scanIssues()` for object pooling opportunities
   - Reuse slice capacity for repeated calls

### Low Impact (Not recommended)
- Query optimization for CRUD operations (already <3ms)
- Connection pooling tuning (not showing in profiles)

## Verification

Run benchmarks to validate optimization:
```bash
make bench-quick
go tool pprof -http=:8080 internal/storage/sqlite/bench-cpu-*.prof
```

Profile files automatically generated in `internal/storage/sqlite/`.


Merge: bd-gqxd

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


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


bd swarm status command

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

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

NOT stored separately. If beads changes, status changes.

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

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

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

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

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

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


Add ValidateTemplate() function

Add internal/validation package with ValidateTemplate(issueType, description) that checks for missing sections and returns actionable errors.

Implemented ValidateTemplate() and LintIssue() with tests