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

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


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

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


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.


RemoveDependency fails on external deps - FK violation in dirty_issues

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

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

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


bd mol bond --ephemeral flag

Add --ephemeral flag to bd mol bond command.

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

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

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


Update cmd/bd/info.go with release notes for 0.37.0.

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

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

instantiated_from: bd-mol-xga
step: update-release-notes


Timer beads for scheduled agent callbacks

## Problem

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

## Proposal: Timer Beads

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

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

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

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

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

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


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


False positive detection by Kaspersky Antivirus (Trojan)

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


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.


Merge: opal-1767138520240

branch: polecat/opal-1767138520240
target: main
source_issue: opal-1767138520240
rig: beads


Review changes since last release

Understand what's being released.

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

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


Deleted issues resurrect during bd sync (tombstones not propagating)

## Problem

When issues are deleted with bd delete --force, they get deleted from the local DB but resurrect during the next bd sync.

## Reproduction

1. Observe orphan warnings (bd-cb64c226.*, bd-cbed9619.*)
2. Delete them: bd delete bd-cb64c226.1 ... --force
3. Run bd sync
4. Orphan warnings reappear - issues were resurrected!

## Root Cause Hypothesis

The sync branch workflow (beads-sync) has the old state before deletions. When bd sync pulls from beads-sync and copies JSONL to main, the deleted issues are re-imported.

Tombstones may not be properly:
1. Written to beads-sync during export
2. Propagated during pull/merge
3. Honored during import

## Related

- bd-7b7h: chicken-and-egg sync.branch bug (same workflow)
- bd-ncwo: ID-based fallback matching to prevent ghost resurrection

## Files to Investigate

- cmd/bd/sync.go (export/import flow)
- internal/syncbranch/worktree.go (PullFromSyncBranch, copyJSONLToMainRepo)
- internal/importer/ (tombstone handling)


Review PR #513: fix hooks install docs

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


Code Review: PR #481 - Context Engineering Optimizations

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

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

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

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

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


Work on beads-2nh: Fix gt spawn --issue to find issues in...

Work on beads-2nh: Fix gt spawn --issue to find issues in rig's beads database. When done, submit MR (not PR) to integration branch for Refinery.


bump-version.sh should detect pre-staged release notes

When running the release workflow, I staged CHANGELOG.md and info.go changes before running bump-version.sh. The script:

1. Warned about uncommitted changes (correct)
2. Required stash/pop to proceed
3. After pop, the [Unreleased] header wasn't converted to version/date

The script could be smarter:
- Detect if staged changes are to CHANGELOG.md/info.go (expected for releases)
- Or provide a --allow-staged flag
- Or at minimum, re-apply the [Unreleased] → [X.Y.Z] transformation after detecting manual edits

Workaround: manually edit [Unreleased] to [0.39.1] - 2025-12-27 after stash pop.


Push release tag

Push the version tag to trigger CI release.

```bash
git push origin v0.99.0-test
```

This triggers GitHub Actions to build artifacts and publish.


Review SQLite indexes and scaling bottlenecks

Audit the beads SQLite schema for:

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

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

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

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


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.


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

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

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

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

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

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


Verify npm package

Confirm npm package published.

```bash
npm show @beads/bd version
```

Should show 0.41.0.

Also check: https://www.npmjs.com/package/@beads/bd


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

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


bd cook: waits_for field not preserved in cooked protos

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

## Formula

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

## Expected

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

## Actual

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

## Impact

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

## Related

bd-hr39: needs field not converted to step dependencies


Implement nix-hash staleness detection

## Summary
Detect when go.sum has changed but vendorHash in default.nix may be stale.

## Implementation Details

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

### Detection Logic

**Tier 1 (Simple - implement this):**
Check if go.sum has uncommitted changes (staged or unstaged). If so, warn that vendorHash might need updating.

```go
func runNixHashCheck() CheckResult {
    // Check if go.sum has changes
    cmd := exec.Command("git", "diff", "--name-only", "HEAD", "--", "go.sum")
    output, _ := cmd.Output()

    // Also check staged changes
    stagedCmd := exec.Command("git", "diff", "--name-only", "--cached", "--", "go.sum")
    stagedOutput, _ := stagedCmd.Output()

    hasChanges := len(output) > 0 || len(stagedOutput) > 0

    if hasChanges {
        return CheckResult{
            Name:    "nix-hash",
            Passed:  false,
            Output:  "go.sum has changes - vendorHash in default.nix may need updating",
            Command: "git diff HEAD -- go.sum",
            Warning: true,  // Not a hard failure, just a warning
        }
    }

    return CheckResult{
        Name:    "nix-hash",
        Passed:  true,
        Output:  "go.sum unchanged",
        Command: "git diff HEAD -- go.sum",
    }
}
```

**Tier 2 (Future - auto-fix):**
If nix is available, compute the correct vendorHash:
```bash
nix-prefetch-url --unpack "https://..."
# or use nix build with --print-out-paths
```

### Key Files
- `go.sum` - Go module checksums
- `default.nix` - Contains vendorHash on line 12

### Output Format
```
$ bd preflight --check
✓ Nix hash current (go.sum unchanged)

# When go.sum changed:
⚠ Nix hash may be stale
  go.sum has uncommitted changes
  Hint: If you modified dependencies, run nix build to get new vendorHash

# When nix available (future):
✗ Nix hash stale
  Expected: sha256-KRR6dXzsSw8OmEHGBEVDBOoIgfoZ2p0541T9ayjGHlI=
  Current:  sha256-ovG0EWQFtifHF5leEQTFvTjGvc+yiAjpAaqaV0OklgE=
  Fix: bd preflight --fix
```

### Add Warning field to CheckResult
```go
type CheckResult struct {
    Name    string
    Passed  bool
    Output  string
    Command string
    Skipped bool
    Warning bool  // Yellow ⚠ instead of red ✗
}
```

### Testing
- Test with clean go.sum (no changes)
- Test with modified go.sum (stage a fake change, then reset)
- Test output format

### Acceptance Criteria
- [x] Detects when go.sum has uncommitted changes
- [x] Shows as warning (⚠) not error (✗)
- [x] Provides helpful hint about vendorHash
- [x] Works with --json flag
- [x] Does not require nix to be installed (just git)


Code smell: Incomplete TODOs in formula/types.go

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

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

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

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


Update npm-package/package.json to 0.30.4

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

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


bd merge omits priority:0 due to omitempty JSON tag

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


Restart daemons

Restart bd daemons to pick up new version.

```bash
bd daemons killall
```

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

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


Code review: bd mol bond --ref and bd activity (bd-xo1o work)

Review dave's recent commits for bd-xo1o (Dynamic Molecule Bonding):

## Commits to Review
- ee04b1ea: feat: add dynamic molecule bonding with --ref flag (bd-xo1o.1)
- be520d90: feat: add bd activity command for real-time state feed (bd-xo1o.3)

## Review Focus
1. Code quality and correctness
2. Error handling
3. Edge cases
4. Test coverage
5. Documentation

## Deliverables
- File beads for any issues found
- Note any concerns or suggestions
- Verify the implementation matches the bd-xo1o epic requirements


Version Bump: 0.30.7

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


Add 'bd where' command to show active beads location

## Problem

When using redirects, it's unclear which beads database is actually being used. This caused debugging confusion during the v0.39.0 release.

## Proposed Solution

Add `bd where` command:

```bash
bd where
# → /Users/stevey/gt/beads/mayor/rig/.beads (via redirect from crew/dave/.beads)

bd where --json
# → {"path": "...", "redirected_from": "...", "prefix": "bd"}
```

## Alternatives Considered

- `bd info --beads-path` - but `bd where` is more discoverable
- Just fix the UX so it's never confusing - defense in depth is better

## Desire Paths

This supports the 'desire paths' design principle: make the system's behavior visible so agents can debug and understand it.


Fix wisp leak in nodb mode writeIssuesToJSONL

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

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

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

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


Review and document rarely-used commands

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

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

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

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

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


Refinery Patrol

Merge queue processor patrol loop with verification gates.


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


Update CHANGELOG.md for {{version}}

In CHANGELOG.md:

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

Format:
```markdown
## [Unreleased]

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

### Added
- ...

### Changed
- ...

### Fixed
- ...
```


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

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

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

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


Consolidate git context into single cached struct

Code review finding from bd-7di fix.

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

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

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

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

File: internal/git/gitdir.go


Merge: obsidian-1767142004753

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


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

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

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

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

Workaround: Manually add deletion records to deletions.jsonl.


Test Agent


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


Test no validate

Quick capture


Investigate TestRoutingIntegration failure

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


Polecat phase handoff: gt done --phase-complete

Add phase handoff protocol for polecats hitting gate steps.

## Current Behavior
Polecat runs all steps, blocks on external waits, times out.

## New Behavior
1. Polecat completes work up to gate step
2. Polecat runs: gt done --phase-complete
3. This signals: 'phase done, molecule awaiting gate'
4. Witness recycles polecat (context freed)
5. When gate closes, Witness dispatches new polecat
6. New polecat continues from step after gate

## Implementation
- Add --phase-complete flag to gt done
- Witness tracks molecules in 'awaiting-gate' state
- When gate closes (via bd gate resolve), molecule unblocks
- Dispatch logic resumes molecule with new polecat

## Acceptance Criteria
- [ ] gt done --phase-complete implemented
- [ ] Witness tracks awaiting-gate molecules
- [ ] Gate closure triggers molecule resume
- [ ] New polecat picks up from correct step

Beads changes committed (9269325e):
- bd gate add-waiter <gate-id> <waiter>
- bd gate show <gate-id> --json
- Waiters field in RPC protocol

Gastown changes written but need separate commit (bd-gxb4):
- gt done --phase-complete --gate flag
- Witness PHASE_COMPLETE handler
- AddGateWaiter in beads wrapper

Blocked on bd-gxb4 for gastown commit.


Run check-versions.sh - all must pass

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

All versions must match 0.30.4.

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


Add refs field for cross-references with relationship type

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

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

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

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

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


Add tests for import/export functionality

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


Create release tag

Create annotated git tag.

```bash
git tag -a v0.99.0-test -m "Release v0.99.0-test"
```

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


.beads-ephemeral/ storage backend

Implement ephemeral storage layer for wisps.

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

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

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


Design auto-squash behavior for wisps

Explore the design space for automatic wisp squashing.

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

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

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

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

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

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

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


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

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


Merge: bd-ykd9

branch: polecat/Doctor
target: main
source_issue: bd-ykd9
rig: beads


Test deleteViaDaemon RPC client integration

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

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

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

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

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


Update molecular chemistry docs with discoveries

Document recent discoveries about molecule/epic relationship and lifecycle:

**Layer Cake (corrected order):**
```
Formulas (YAML compile-time macros)
    ↓
Protos (template label, read-only)
    ↓
Molecules (pour, bond, squash, burn)
    ↓
Epics (parent-child, dependencies) ← DATA PLANE
    ↓
Issues (JSONL, git-backed) ← STORAGE
```

**Key insight:** Molecules work without protos - you can create ad-hoc molecules.
Protos are templates FOR molecules, not the other way around.

**New concepts to document:**
1. Orphan vs Stale matrix (blocking × assigned dimensions)
2. Graph pressure as staleness indicator (not time)
3. Parallelism via absence of depends_on (default parallel, opt-in sequential)
4. progress.Completed/Total are computed, not stored
5. WaitsFor: all-children for dynamic fanout gates

**Common pitfalls for agents:**
- Thinking phases/steps imply sequence (NO - deps do)
- Confusing protos with molecules
- Temporal language inverting dependencies
- Forgetting to squash/burn wisps

**Consider splitting docs:**
- molecules.md → conceptual overview
- formulas.md → YAML syntax reference
- lifecycle.md → pour/squash/burn/bond operations
- patterns.md → Christmas Ornament, factory assembly, etc.


Merge: bd-d28c

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


Verify GitHub release

Check the GitHub releases page.

https://github.com/steveyegge/beads/releases/tag/v0.41.0

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


Pinned field available in beads v0.37.0

Hey max,

Heads up on your mail overhaul work:

1. **Pinned field is available** - beads v0.37.0 (released by dave earlier) includes the pinned field on issues. You'll want to add this to BeadsMessage in types.go.

2. **Database migration** - Check if existing .beads databases need migration to support the pinned field. Run `bd doctor` to see if it flags anything.

3. **Sorting task** - Once you have the pinned field, gt-ngu1 (pinned beads first in mail inbox) needs implementing. Since messages now come from `bd list --type=message`, you'll need to either:
   - Sort in listBeads() after fetching, or
   - Ensure bd list returns pinned items first (may already do this?)

Check what version of bd you're building against.

-- Mayor


Add integration tests for bd reset command

End-to-end integration tests for the reset command.

## Test Scenarios

### Basic reset
1. Init beads, create some issues
2. Run bd reset --force
3. Verify .beads/ is fresh, issues gone

### Hard reset
1. Init beads, create issues, commit
2. Run bd reset --hard --force
3. Verify git history has reset commits

### Backup functionality
1. Init beads, create issues
2. Run bd reset --backup --force
3. Verify backup exists with correct contents
4. Verify main .beads/ is reset

### Dry run
1. Init beads, create issues
2. Run bd reset --dry-run
3. Verify nothing changed

### Confirmation prompt
1. Init beads
2. Run bd reset (no --force)
3. Verify prompts for confirmation
4. Test both y and n responses

## Location
tests/integration/reset_test.go or similar


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

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


Create git tag v{{version}}

Create the release tag:

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

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


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


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.


Implement bd pin/unpin commands

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


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 prime: AI context loading and Claude Code integration

Implement `bd prime` command and Claude Code hooks for context recovery. Hooks work with BOTH MCP server and CLI approaches - they solve the context memory problem (keeping bd workflow fresh after compaction) not the tool access problem (MCP vs CLI).


Merge: topaz-1767138533986

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


Fix checkExistingBeadsData to be worktree-aware

The checkExistingBeadsData function in cmd/bd/init.go checks for .beads in the current working directory, but for worktrees it should check the main repository root instead. This prevents proper worktree compatibility.


Add git operations for --hard reset

Implement git integration for hard reset mode.

## Operations Needed
1. `git rm -rf .beads/*.jsonl` - remove data files from index
2. `git commit -m 'beads: reset to clean state'` - commit removal
3. After re-init: `git add .beads/` and commit fresh state

## Edge Cases to Handle
- Uncommitted changes in .beads/ - warn or error
- Detached HEAD state - warn, maybe block
- Git not initialized - skip git ops, warn
- Git operations fail mid-way - clear error messaging

## Interface
```go
type GitState struct {
    IsRepo       bool
    IsDirty      bool   // uncommitted changes in .beads/
    IsDetached   bool   // detached HEAD
    Branch       string // current branch name
}

func CheckGitState(beadsDir string) (*GitState, error)
func GitRemoveBeads(beadsDir string) error
func GitCommitReset(message string) error
func GitAddAndCommit(beadsDir, message string) error
```

## Location
`internal/reset/git.go` - keep with reset package for now

Note: Codebase has no central git package. internal/compact/git.go is compact-specific.
Future refactoring could extract shared git utilities, but YAGNI for now.


Make merge requests ephemeral wisps instead of permanent issues

## Problem

Merge requests (MRs) are currently created as regular beads issues (type: merge-request). This means they:
- Sync to JSONL and propagate via git
- Accumulate in the issue database indefinitely
- Clutter `bd list` output with closed MRs
- Create permanent records for inherently transient artifacts

MRs are process artifacts, not work products. They exist briefly while code awaits merge, then their purpose is fulfilled. The git merge commit and GitHub PR (if applicable) provide the permanent audit trail - the beads MR is redundant.

## Proposed Solution

Make MRs ephemeral wisps that exist only during the merge process:

1. **Create MRs as wisps**: When a polecat completes work and requests merge, create the MR in `.beads-wisp/` instead of `.beads/`

2. **Refinery visibility**: This works because all clones within a rig share the same database:
   ```
   beads/              ← Rig root
   ├── .beads/         ← Permanent issues (synced to JSONL)
   ├── .beads-wisp/    ← Ephemeral wisps (NOT synced)
   ├── crew/dave/      ← Uses rig's shared DB
   ├── polecats/*/     ← Uses rig's shared DB
   └── refinery/       ← Uses rig's shared DB
   ```
   The refinery can see wisp MRs immediately - same SQLite database.

3. **On merge completion**: Burn the wisp (delete without digest). The git merge commit IS the permanent record. No digest needed since:
   - Digest wouldn't be smaller than the MR itself (~200-300 bytes either way)
   - Git history provides complete audit trail
   - GitHub PR (if used) provides discussion/approval record

4. **On merge rejection/abandonment**: Burn the wisp. Optionally notify the source polecat via mail.

## Benefits

- **Clean JSONL**: MRs never pollute the permanent issue history
- **No accumulation**: Wisps are burned on completion, no cleanup needed
- **Correct semantics**: Wisps are for "operational ephemera" - MRs fit perfectly
- **Reduced sync churn**: Fewer JSONL updates, faster `bd sync`
- **Cleaner queries**: `bd list` shows work items, not process artifacts

## Implementation Notes

### Where MRs are created

Currently MRs are created by the witness or polecat when work is ready for merge. This code needs to:
- Set `wisp: true` on the MR issue
- Or use a dedicated wisp creation path

### Refinery changes

The refinery queries for pending MRs to process. It needs to:
- Query wisp storage as well as (or instead of) permanent storage
- Use `bd mol burn` or equivalent to delete processed MRs

### What about cross-rig MRs?

If an MR needs to be visible outside the rig (e.g., external collaborators):
- They would see the GitHub PR anyway
- Or we could create a permanent "merge completed" notification issue
- But this is likely unnecessary - MRs are internal coordination

### Migration

Existing MRs in permanent storage:
- Can be cleaned up with `bd cleanup` or manual deletion
- Or left to age out naturally
- No migration of open MRs needed (they'll complete under old system

## Alternatives Considered

1. **Auto-cleanup of closed MRs**: Keep MRs as permanent issues but auto-delete after 24h. Simpler but still creates sync churn and temporary JSONL pollution.

2. **MRs as mail only**: Polecat sends mail to refinery with merge details, no MR issue at all. Loses queryability (bd-801b [P2] [merge-request] closed - Merge: bd-bqcc
bd-pvu0 [P2] [merge-request] closed - Merge: bd-4opy
bd-i0rx [P2] [merge-request] closed - Merge: bd-ao0s
bd-u0sb [P2] [merge-request] closed - Merge: bd-uqfn
bd-8e0q [P2] [merge-request] closed - Merge: beads-ocs
bd-hvng [P2] [merge-request] closed - Merge: bd-w193
bd-4sfl [P2] [merge-request] closed - Merge: bd-14ie
bd-sumr [P2] [merge-request] closed - Merge: bd-t4sb
bd-3x9o [P2] [merge-request] closed - Merge: bd-by0d
bd-whgv [P2] [merge-request] closed - Merge: bd-401h
bd-f3ll [P2] [merge-request] closed - Merge: bd-ot0w
bd-fmdy [P3] [merge-request] closed - Merge: bd-kzda).

3. **Separate merge queue**: Refinery maintains internal state for pending merges, not in beads at all. Clean but requires new infrastructure.

Wisps are the cleanest solution - they already exist, have the right semantics, and require minimal changes.

## Related

- Wisp architecture:
- Current MR creation: witness/refinery code paths
- bd-pvu0, bd-801b: Example MRs currently in permanent storage
EOF
)


Test agent with fields


Merge: bd-g4b4

branch: polecat/Hooker
target: main
source_issue: bd-g4b4
rig: beads


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)


Investigate Claude Code crash logging improvements

## Problem

Claude Code doesn't leave useful crash logs when it terminates unexpectedly. Investigation of a crash on 2025-12-26 showed:

- Debug logs in ~/.claude/debug/ just stop mid-stream with no error/exit message
- No signal handlers appear to log SIGTERM/SIGKILL/SIGINT
- No dedicated crash log file exists
- When Node.js crashes hard or gets killed, there's no record of why

## What we found

- Session debug log (02080b1a-...) stopped at 22:58:40 UTC mid-operation
- No 'exit', 'error', 'crash', 'signal' entries at end of file
- macOS DiagnosticReports showed Chrome crashes but no Node crashes
- System logs showed no relevant kill/OOM events

## Desired improvements

1. Exit handlers that log graceful shutdown
2. Signal handlers that log SIGTERM/SIGINT before exiting
3. A dedicated crash log or at least a 'last known state' file
4. Possibly CLI flags to enable verbose crash debugging

## Investigation paths

- Check if Claude Code has --debug or similar flags
- Look at Node.js crash handling best practices
- Consider if we can wrap claude invocations to capture crashes

## Related

This came up while investigating why a crew worker session crashed at end of a code review task.


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

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


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


gt done --phase-complete: gastown changes for phase handoff

The gt done --phase-complete flag and Witness handling changes need to be
applied to the gastown project. These changes are already written and tested
in the mayor/rig but need to be properly committed through a gastown worktree.

Files changed:
- internal/cmd/done.go: Add --phase-complete and --gate flags
- internal/beads/beads.go: Add AddGateWaiter method
- internal/witness/protocol.go: Add Gate field to PolecatDonePayload
- internal/witness/handlers.go: Handle PHASE_COMPLETE exit type

This is a dependency for bd-quw1 (polecat phase handoff).

The beads changes (bd gate add-waiter, bd gate show) are committed in bd-quw1.


Add bd agent state command for ZFC-compliant state reporting

Add command for agents to write their own state to agent beads.

```bash
bd agent state <agent-id> <state>
# Example: bd agent state gt-mayor running
# States: idle | running | stuck | stopped | dead
```

Implementation:
1. Lookup agent bead by ID (type=agent)
2. Update agent_state field in description
3. Update last_activity timestamp
4. Trigger bd sync if configured

Also add:
- bd agent heartbeat <agent-id> - just updates last_activity
- bd agent show <agent-id> - show agent bead details

This is the ZFC-compliant way for agents to self-report state.

Cross-ref: gt-p2vyo in gastown


Wait for CI

Monitor GitHub Actions for release completion.

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

Expected time: 5-10 minutes

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


Push release tag

Push the version tag to trigger CI release.

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

This triggers GitHub Actions to build artifacts and publish.


Add GetMoleculeProgress RPC endpoint

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


bd mol bond --pour should set Wisp=false

In mol_bond.go bondProtoMol(), opts.Wisp is hardcoded to true (line 392). This ignores the --pour flag. When user specifies --pour to make an issue persistent, the Wisp field should be false so the issue is not marked for bulk deletion.

Current behavior:
- --pour flag correctly selects regular storage (not wisp storage)
- But opts.Wisp=true means spawned issues are still marked for cleanup when closed

Expected behavior:
- --pour should set Wisp=false so persistent issues are not auto-cleaned

Comparison with mol_spawn.go (line 204):
  wisp := !pour  // Correctly respects --pour flag
  result, err := spawnMolecule(ctx, store, subgraph, vars, assignee, actor, wisp)

Fix: Pass pour flag to bondProtoMol and set opts.Wisp = !pour


Test hooked styling


Update LINTING.md with current baseline

After cleanup, document the remaining acceptable baseline in LINTING.md so we can track regression.


Update local installation

Update local bd to the new version.

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

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

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

Should show 0.39.0.


Test Thread

Initial message for threading test


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

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


Merge: beads-ocs

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


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


Merge: bd-4qfb

branch: polecat/Polish
target: main
source_issue: bd-4qfb
rig: beads


Add date and priority filters to bd search for parity with bd list.

## Current State
bd search supports: --status, --type, --assignee, --label, --limit
bd list supports: all of the above PLUS date ranges and priority filters

## Filters to Add

### Priority Filters
```bash
bd search "query" --priority 1           # Exact priority
bd search "query" --priority-min 0       # P0 and above (higher priority)
bd search "query" --priority-max 2       # P2 and below (lower priority)
```

### Date Filters
```bash
bd search "query" --created-after 2025-01-01
bd search "query" --created-before 2025-12-31
bd search "query" --updated-after 2025-01-01
bd search "query" --closed-after 2025-01-01
```

### Content Filters
```bash
bd search "query" --desc-contains "bug"
bd search "query" --notes-contains "todo"
bd search "query" --empty-description    # Issues with no description
bd search "query" --no-assignee          # Unassigned issues
bd search "query" --no-labels            # Issues without labels
```

## Files to Modify

### 1. cmd/bd/search.go
Add flag definitions in init():
```go
searchCmd.Flags().IntP("priority", "p", -1, "Filter by exact priority (0-4)")
searchCmd.Flags().Int("priority-min", -1, "Filter by minimum priority")
searchCmd.Flags().Int("priority-max", -1, "Filter by maximum priority")
searchCmd.Flags().String("created-after", "", "Filter by creation date (YYYY-MM-DD)")
searchCmd.Flags().String("created-before", "", "Filter by creation date")
searchCmd.Flags().String("updated-after", "", "Filter by update date")
searchCmd.Flags().String("updated-before", "", "Filter by update date")
searchCmd.Flags().String("closed-after", "", "Filter by close date")
searchCmd.Flags().String("closed-before", "", "Filter by close date")
searchCmd.Flags().String("desc-contains", "", "Filter by description content")
searchCmd.Flags().String("notes-contains", "", "Filter by notes content")
searchCmd.Flags().Bool("empty-description", false, "Filter issues with empty description")
searchCmd.Flags().Bool("no-assignee", false, "Filter unassigned issues")
searchCmd.Flags().Bool("no-labels", false, "Filter issues without labels")
```

### 2. internal/rpc/protocol.go
Update SearchArgs struct:
```go
type SearchArgs struct {
    Query             string
    Filter            types.IssueFilter
    // Already has most fields via IssueFilter
}
```

Note: types.IssueFilter already has these fields - just need to wire them up!

### 3. cmd/bd/search.go Run function
Parse flags and populate filter:
```go
if priority, _ := cmd.Flags().GetInt("priority"); priority >= 0 {
    filter.Priority = &priority
}
if createdAfter, _ := cmd.Flags().GetString("created-after"); createdAfter != "" {
    t, err := time.Parse("2006-01-02", createdAfter)
    if err != nil {
        FatalError("invalid date format for --created-after: %v", err)
    }
    filter.CreatedAfter = &t
}
// ... similar for other flags
```

## Implementation Steps

1. **Check types.IssueFilter** - verify all needed fields exist
2. **Add flags to search.go** init()
3. **Parse flags** in Run function
4. **Pass to SearchIssues** via filter
5. **Test all combinations**

## Testing
```bash
# Create test issues
bd create "Test P1" -p 1
bd create "Test P2" -p 2 --description "Has description"

# Test filters
bd search "" --priority 1
bd search "" --priority-min 0 --priority-max 1
bd search "" --empty-description
bd search "" --desc-contains "description"
```

## Success Criteria
- All filters work in both direct and daemon mode
- Date parsing handles YYYY-MM-DD format
- --json output includes filtered results
- Help text documents all new flags


Integration test: agent waits for CI via gate

End-to-end test of the gate workflow.

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

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


Update go install bd to {{version}}

Rebuild and install bd to ~/go/bin:

```bash
go install ./cmd/bd
~/go/bin/bd version  # Verify shows {{version}}
```

Note: If ~/go/bin is in PATH before /opt/homebrew/bin, this is the version that runs by default.


Merge: bd-indn

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


Formula versioning: Version field and git tag integration

Add versioning support to formulas for tracking updates.

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

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

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

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

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


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.


Update Homebrew formula

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


Implement or remove ExpectExit/ExpectStdout verification fields

The Verification struct in internal/types/workflow.go has ExpectExit and ExpectStdout fields that are never used by workflowVerifyCmd. Either implement the functionality or remove the dead fields.


Merge: bd-ao0s

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


Add 0.33.2 to versionChanges in info.go

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


Update CHANGELOG header

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


Add warning when staleness check errors

## Problem

When ensureDatabaseFresh() calls CheckStaleness() and it errors (corrupted metadata, permission issues, etc.), we silently proceed with potentially stale data.

**Location:** cmd/bd/staleness.go:27-32

**Scenarios:**
- Corrupted metadata table
- Database locked by another process
- Permission issues reading JSONL file
- Invalid last_import_time format in DB

## Current Code

```go
isStale, err := autoimport.CheckStaleness(ctx, store, dbPath)
if err \!= nil {
    // If we can't determine staleness, allow operation to proceed
    // (better to show potentially stale data than block user)
    return nil
}
```

## Fix

```go
isStale, err := autoimport.CheckStaleness(ctx, store, dbPath)
if err \!= nil {
    fmt.Fprintf(os.Stderr, "Warning: Could not verify database freshness: %v\n", err)
    fmt.Fprintf(os.Stderr, "Proceeding anyway. Data may be stale.\n\n")
    return nil
}
```

## Impact
Medium - users should know when staleness check fails

## Effort
Easy - 5 minutes


Implement content-first idempotent import

**Summary:** Refactored issue import to be content-first and idempotent, ensuring consistent data synchronization across multiple import rounds by prioritizing content hash matching over ID-based updates.

**Key Decisions:**
- Implement content hash as primary matching mechanism
- Create global collision resolution algorithm
- Ensure importing same data multiple times results in no-op

**Resolution:** The new import strategy guarantees predictable convergence across distributed systems, solving rename detection and collision handling while maintaining data integrity during multi-stage imports.


Add missing updated_at index on issues table

GetStaleIssues queries filter by updated_at but there's no index on this column.

**Current query (ready.go:253-254):**
```sql
WHERE status != 'closed'
  AND datetime(updated_at) < datetime('now', '-' || ? || ' days')
```

**Problem:** Full table scan when filtering stale issues.

**Solution:** Add migration to create:
```sql
CREATE INDEX IF NOT EXISTS idx_issues_updated_at ON issues(updated_at);
```

**Note:** The datetime() function wrapper may prevent index usage. Consider also storing updated_at as INTEGER (unix timestamp) for better index efficiency, or test if SQLite can use the index despite the function.


Code smell: Repeated validation patterns could be centralized

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

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

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


Merge: pearl-mjtmd27a

branch: polecat/pearl-mjtmd27a
target: main
source_issue: pearl-mjtmd27a
rig: beads


Preflight: Pull latest

Ensure we're up to date with origin.

```bash
git pull --rebase
```

Resolve any conflicts before proceeding.


Merge: bd-phtv

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


bd repair command: fix orphaned foreign key refs


Test Molecule

A test molecule


Merge: onyx-1767083477016

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


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

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

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

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


Polecat sessions terminate unexpectedly during work

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

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

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

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

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

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


HANDOFF: Molecule bonding - spawn done, bond next

## Context

Continuing work on bd-o5xe (Molecule bonding epic).

## Completed This Session

- bd-mh4w: Renamed bond to spawn in mol.go
- bd-rnnr: Added BondRef data model to types.go

## Now Unblocked

1. bd-o91r: Polymorphic bond command [P1]
2. bd-iw4z: Compound visualization [P2]
3. bd-iq19: Distill command [P2]

## Key Files

- cmd/bd/mol.go
- internal/types/types.go

## Next Step

Start with bd-o91r. Run bd show bd-o5xe for context.


worktree: any bd command is slow

in a git worktree any bd command is slow, with a 2-3s pause before any results are shown. The identical command with `--no-daemon` is near instant.


Run bump-version.sh test-squash

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


Code smell: Long function flushToJSONLWithState (~280 lines)

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

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

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

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


Refinery Patrol

Merge queue processor patrol loop with verification gates.


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


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


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

## Problem

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

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

## Impact

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

## Proposed Solution

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

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

## Investigation Needed

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


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

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

## Proposed Splits

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

## Implementation Steps

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

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

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


Hooks System

Implement hook system for extensibility.

Hook directory: .beads/hooks/
Hook files (executable scripts):
- on_create - runs after bd create
- on_update - runs after bd update
- on_close - runs after bd close
- on_message - runs after bd mail send

Hook invocation:
- Pass issue ID as first argument
- Pass event type as second argument
- Pass JSON issue data on stdin
- Run asynchronously (dont block command)

Example hook (GGT notification):
  #!/bin/bash
  gt notify --event=$2 --issue=$1

This allows GGT to register notification handlers without Beads knowing about GGT.


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.


Consolidate daemon/daemons commands

Merge daemons functionality into daemon command with subcommands. Currently have both 'daemon' and 'daemons' at top level.


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)


Molecule execution state management

Implement molecule execution state tracking.

## State Location

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

## State Schema

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

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

# Loop tracking
reset_count: 0
last_reset_at: null

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

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

## Operations

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

## Files

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


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.


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.


Remove all deletions.jsonl code except migration

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

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

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


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 sync fails when run from directory with beads redirect

## Bug

`bd sync` fails with 'git status failed in worktree: exit status 128' when run from a directory that uses a beads redirect (e.g., crew/gus/.beads/redirect pointing to mayor/rig/.beads).

## Reproduction

1. Be in gastown/crew/gus (has .beads/redirect → ../../mayor/rig/.beads)
2. Run `bd sync`
3. Error: 'git status failed in worktree: exit status 128'

## Workaround

Run `bd sync` from the actual beads location:
```bash
cd /Users/stevey/gt/gastown/mayor/rig && bd sync
```

## Root Cause

The worktree path resolution in `getBeadsWorktreePath()` likely uses the crew/gus repo root instead of following the redirect to mayor/rig. The git worktree for beads-sync is in mayor/rig/.git/beads-worktrees/, not crew/gus/.git/beads-worktrees/.

## Impact

Crew workers with redirected beads cannot sync directly from their workspace.


Add comments to 30+ silently ignored errors or fix them

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

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

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

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


Auto-detect non-TTY and adjust output

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

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

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

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

## Implementation Plan

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

### Code Changes

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

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

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

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

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

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


Merge: obsidian-mjxbi027

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


Update go install bd to 0.33.2

Rebuild and install bd to ~/go/bin:

```bash
go install ./cmd/bd
~/go/bin/bd version  # Verify shows 0.33.2
```

Note: If ~/go/bin is in PATH before /opt/homebrew/bin, this is the version that runs by default.


bd preflight: PR readiness checks for contributors

## Vision

Encode project-specific institutional knowledge into executable checks. CONTRIBUTING.md is documentation that's read once and forgotten; `bd preflight` is documentation that runs at exactly the right moment.

## Problem Statement

Contributors face a "last mile" problem - they do the work but stumble on project-specific gotchas at PR time:
- Nix vendorHash gets stale when go.sum changes
- Beads artifacts leak into PRs (see bd-umbf for namespace solution)
- Version mismatches between version.go and default.nix
- Tests/lint not run locally before pushing
- Other project-specific checks that only surface when CI fails

These are too obscure to remember, exist in docs nobody reads end-to-end, and waste CI round-trips.

## Why beads?

Beads already has a foothold in the contributor workflow. It knows:
- Git state (staged files, branch, dirty status)
- Project structure
- The specific issue being worked on
- Project-specific configuration

## Proposed Interface

### Tier 1: Checklist Mode (v1)

    $ bd preflight
    PR Readiness Checklist:

    [ ] Tests pass: go test -short ./...
    [ ] Lint passes: golangci-lint run ./...
    [ ] No beads pollution: check .beads/issues.jsonl diff
    [ ] Nix hash current: go.sum unchanged or vendorHash updated
    [ ] Version sync: version.go matches default.nix

    Run 'bd preflight --check' to validate automatically.

### Tier 2: Check Mode (v2)

    $ bd preflight --check
    ✓ Tests pass
    ✓ Lint passes
    ⚠ Beads pollution: 3 issues in diff - are these project issues or personal?
    ✗ Nix hash stale: go.sum changed, vendorHash needs update
      Fix: sha256-KRR6dXzsSw8OmEHGBEVDBOoIgfoZ2p0541T9ayjGHlI=
    ✓ Version sync

    1 error, 1 warning. Run 'bd preflight --fix' to auto-fix where possible.

### Tier 3: Fix Mode (v3)

    $ bd preflight --fix
    ✓ Updated vendorHash in default.nix
    ⚠ Cannot auto-fix beads pollution - manual review needed

## Checks to Implement

| Check | Description | Auto-fixable |
|-------|-------------|--------------|
| tests | Run go test -short ./... | No |
| lint | Run golangci-lint | Partial (gofmt) |
| beads-pollution | Detect personal issues in diff | No (see bd-umbf) |
| nix-hash | Detect stale vendorHash | Yes (if nix available) |
| version-sync | version.go matches default.nix | Yes |
| no-debug | No TODO/FIXME/console.log | Warn only |
| clean-stage | No unintended files staged | Warn only |

## Future: Configuration

Make checks configurable per-project via .beads/preflight.yaml:

    preflight:
      checks:
        - name: tests
          run: go test -short ./...
          required: true
        - name: no-secrets
          pattern: "**/*.env"
          staged: deny
        - name: custom-check
          run: ./scripts/validate.sh

This lets any project using beads define their own preflight checks.

## Implementation Phases

### Phase 1: Static Checklist
- Implement bd preflight with hardcoded checklist for beads
- No execution, just prints what to check
- Update CONTRIBUTING.md to reference it

### Phase 2: Automated Checks
- Implement bd preflight --check
- Run tests, lint, detect stale hashes
- Clear pass/fail/warn output

### Phase 3: Auto-fix
- Implement bd preflight --fix
- Fix vendorHash, version sync
- Integrate with bd-umbf solution for pollution

### Phase 4: Configuration
- .beads/preflight.yaml support
- Make it useful for other projects using beads
- Plugin/hook system for custom checks

## Dependencies

- bd-umbf: Namespace isolation for beads pollution (blocking for full solution)

## Success Metrics

- Fewer CI failures on first PR push
- Reduced "fix nix hash" commits
- Contributors report preflight caught issues before CI


Merge: quartz-mjxcafcl

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


Upgrade local Homebrew installation

Upgrade bd via Homebrew:

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

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

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

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


bd repair: Handle .beads/redirect files

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

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


bd mol squash handles wisp→digest

Update bd mol squash to handle ephemeral molecules.

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

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

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

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


Gate eval: Consider rate limiting for GitHub API calls

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

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

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


bd create --rig flag loses event fields (event_kind, actor, payload)

## Bug

When creating events with `--rig` flag, the event-specific fields are not stored:
- event_kind
- actor
- target
- payload

## Reproduction

```bash
# Without --rig (works)
bd create --type=event --title='Test' --event-category=session.ended --event-actor=test --event-payload='{"x":1}' --silent
# Fields are stored correctly

# With --rig (broken)
bd create --type=event --title='Test' --event-category=session.ended --event-actor=test --event-payload='{"x":1}' --rig=gastown --silent
# event_kind, actor, payload are NOT stored
```

## Impact

The `gt costs record` command needs to create session events in rig-specific beads, but cannot use `--rig` flag due to this bug.

## Workaround

Run `bd create` from within the rig directory so it auto-detects the correct beads location.


bd-beads-polecat-onyx

bd-beads-polecat-onyx

role_type: polecat
rig: beads
agent_state: spawning
hook_bead: bd-w3rh
role_bead: gt-polecat-role
cleanup_status: null
active_mr: bd-f2yq
notification_level: null


Restart running daemons

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


Technical debt: Multiple deprecated command aliases still exist

attached_args: Remove deprecated command aliases

Several deprecated command aliases are still present in the codebase:

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

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

Location: Various files in cmd/bd/


Phase 2.5: TOON-based daemon sync

Implement TOON-native daemon sync (replaces JSONL sync machinery).

## Overview
Daemon sync is the final integration point. Replace export/import/merge machinery with TOON-native sync, building on deletion tracking (2.3) and merge optimization (2.4).

## Required Work

### 2.5.1 TOON-based Daemon Sync
- [ ] Understand current JSONL sync machinery (export.go, import.go, merge.go)
- [ ] Replace export step with TOON encoding (EncodeTOON)
- [ ] Replace import step with TOON decoding (DecodeTOON)
- [ ] Replace merge step with TOON-aware 3-way merge
- [ ] Update daemon auto-sync to read/write TOON
- [ ] Verify 5-second debounce still works

### 2.5.2 Deletion Sync Integration
- [ ] Load deletions.toon during import phase
- [ ] Apply deletions after merging issues
- [ ] Ensure deletion TTL respects daemon schedule

### 2.5.3 Testing
- [ ] Unit tests for daemon sync with TOON
- [ ] Integration tests with actual daemon operations
- [ ] Multi-clone sync scenarios with concurrent edits
- [ ] Performance comparison with JSONL sync
- [ ] Long-running daemon stability tests

## Success Criteria
- Daemon reads/writes TOON format (not JSONL)
- Sync latency comparable to JSONL (<100ms)
- All 70+ tests passing
- bdt commands work seamlessly with daemon
- Multi-clone sync scenarios work correctly


mol-beads-release

Release checklist for beads version 0.37.0.

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

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

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

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

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

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

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

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

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

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

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

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

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

Needs: update-changelog

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

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

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

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

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

Needs: run-tests

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

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

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

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

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

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

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

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

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

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

# Check npm
npm view @beads/bd version

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

Needs: wait-for-ci

## Step: update-local
Update local installations.

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

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

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

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

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

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

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

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

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

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


Run bump-version.sh

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


Rename ephemeral → wisp throughout codebase

## The Change

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

## Why

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

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

## The Steam Engine Metaphor

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

## Full Vocabulary

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

## Changes Required

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

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

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

## Example Usage After

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