Update documentation for hash IDs and aliases

Update all documentation to explain hash-based IDs and aliasing system.

## Files to Update

### 1. README.md
Add section explaining hash IDs:
```markdown
## Issue IDs

Beads uses **hash-based IDs** for collision-free distributed issue tracking:

- **Hash ID**: `bd-af78e9a2` (8-char SHA256 prefix, immutable, globally unique)
- **Alias**: `#42` (sequential number, mutable, human-friendly)

### Using IDs
```bash
bd show bd-af78e9a2   # Use hash ID
bd show #42           # Use alias
bd show 42            # Use alias (shorthand)
```

### Why Hash IDs?
- **Collision-free**: Work offline without ID conflicts
- **Distributed**: No coordination needed between clones
- **Git-friendly**: Different IDs = different JSONL lines, fewer merge conflicts

### Aliases
Aliases are workspace-local shortcuts for hash IDs. They're:
- Automatically assigned on issue creation
- Reassigned deterministically on sync (if conflicts)
- Can be manually controlled with `bd alias` commands
```

### 2. AGENTS.md
Update agent workflow:
```markdown
## Hash-Based IDs (v2.0+)

Beads v2.0 uses hash-based IDs to eliminate collision problems:

**When creating issues**:
```bash
bd create "Fix bug" -p 1
# → Creates bd-af78e9a2 with alias #1
```

**When referencing issues**:
- In text: Use hash IDs (stable): "See bd-af78e9a2 for details"
- In CLI: Use aliases (readable): `bd update #42 --status done`

**After sync**:
- Alias conflicts resolved automatically (content-hash ordering)
- No ID collisions possible
- No remapping needed

**Migration from v1.x**:
```bash
bd migrate --hash-ids  # One-time migration
```
```

### 3. QUICKSTART.md (if exists)
Show alias usage in examples:
```bash
# Create issue (gets hash ID + alias)
bd create "Fix authentication bug" -p 1
# → Created bd-af78e9a2 (alias: #1)

# Reference by alias
bd show #1
bd update #1 --status in_progress
bd close #1 --reason "Fixed"
```

### 4. ADVANCED.md
Add section on hash ID internals:
```markdown
## Hash ID Generation

Hash IDs are generated deterministically:

```go
SHA256(title || description || timestamp || workspace_id)[:8]
```

**Collision probability**:
- 8 hex chars = 2^32 space = ~4 billion IDs
- Birthday paradox: 50% collision probability at ~65,000 issues
- For typical projects (<10,000 issues), collision risk is negligible

**Collision detection**:
If a hash collision occurs (extremely rare), beads:
1. Detects on insert (UNIQUE constraint)
2. Appends random suffix: `bd-af78e9a2-a1b2`
3. Retries insert

## Alias Conflict Resolution

When multiple clones assign same alias to different issues:

**Strategy**: Content-hash ordering (deterministic)
- Sort conflicting issue IDs lexicographically
- Lowest hash ID keeps the alias
- Others reassigned to next available aliases

**Example**:
```
Clone A: Assigns #42 to bd-a1b2c3d4
Clone B: Assigns #42 to bd-e5f6a7b8
After sync: bd-a1b2c3d4 keeps #42 (lower hash)
            bd-e5f6a7b8 gets #100 (next available)
```
```

### 5. MIGRATION.md (new file)
```markdown
# Migrating to Hash-Based IDs (v2.0)

## Overview
Beads v2.0 introduces hash-based IDs to eliminate collision problems. This is a **breaking change** requiring migration.

## Migration Steps

### 1. Backup
```bash
cp -r .beads .beads.backup
git commit -am "Pre-migration backup"
```

### 2. Run Migration
```bash
# Dry run first
bd migrate --hash-ids --dry-run

# Apply migration
bd migrate --hash-ids
```

### 3. Commit Changes
```bash
git add .beads/issues.jsonl
git commit -m "Migrate to hash-based IDs (v2.0)"
git push origin main
```

### 4. Coordinate with Collaborators
All clones must migrate before syncing:
1. Notify team: "Migrating to v2.0 on [date]"
2. All collaborators pull latest
3. All run `bd migrate --hash-ids`
4. All push changes
5. Resume normal work

## Rollback
```bash
# Restore backup
mv .beads.backup .beads
bd export  # Regenerate JSONL
git checkout .beads/issues.jsonl
```

## FAQ

**Q: Can I mix v1.x and v2.0 clones?**
A: No. All clones must be on same version.

**Q: Will my old issue IDs work?**
A: No, but aliases preserve the numbers: bd-1c63eb84 → #1

**Q: What happens to links like "see bd-cb64c226.3"?**
A: Migration updates all text references automatically.
```

### 6. CHANGELOG.md
```markdown
## v2.0.0 (YYYY-MM-DD)

### Breaking Changes
- **Hash-based IDs**: Issues now use collision-free hash IDs (bd-af78e9a2)
  instead of sequential IDs (bd-1c63eb84, bd-9063acda)
- **Aliasing system**: Human-friendly aliases (#42) for hash IDs
- **Migration required**: Run `bd migrate --hash-ids` to convert v1.x databases

### Added
- `bd alias` command for manual alias control
- `bd migrate --hash-ids` migration tool
- Alias conflict resolution (deterministic, content-hash ordering)

### Removed
- ID collision detection and resolution (~2,100 LOC)
- `bd import --resolve-collisions` flag (no longer needed)

### Benefits
- ✅ Zero ID collisions in distributed workflows
- ✅ Simpler codebase (-1,350 net LOC)
- ✅ Better git merge behavior
- ✅ True offline-first operation
```

## Testing
- Build docs locally (if using doc generator)
- Check all links work
- Verify examples are correct
- Spellcheck

## Files to Create/Modify
- README.md (hash ID section)
- AGENTS.md (workflow updates)
- ADVANCED.md (internals)
- MIGRATION.md (new)
- CHANGELOG.md (v2.0 entry)
- docs/ (any other docs)


bd-hv01: Refactor snapshot management into dedicated module

Problem: Snapshot logic is scattered across deletion_tracking.go. Would benefit from abstraction with SnapshotManager type.

Benefits: cleaner separation of concerns, easier to test in isolation, better encapsulation, could add observability/metrics.

Suggested improvements: add magic constants, track merge statistics, better error messages.

Files: cmd/bd/deletion_tracking.go (refactor into new snapshot_manager.go)


Improve Beads test coverage from 46% to 80%


Add unit tests for parent resurrection

Test resurrection with deleted parent (should succeed), resurrection with never-existed parent (should fail gracefully), multi-level resurrection (bd-abc.1.2 with both parents missing). Verify tombstone creation and is_tombstone flag.


Invert control for compact: provide tools for agent-driven compaction

Currently compact requires Anthropic API key because bd calls the AI directly. This is backwards - we should provide tools (like all other bd commands) that let an AI agent perform the compaction. The agent decides what to keep/merge, not bd. Related to GH #243 complaint about API key requirement.


Extract daemon lifecycle functions to daemon_lifecycle.go


Issue with labels


bd doctor --fix should auto-fix prefix mismatch errors

When bd doctor --fix encounters a prefix mismatch during DB-JSONL sync fix, it fails without offering to use --rename-on-import.

**Current behavior:**
```
Fixing DB-JSONL Sync...
→ Importing from JSONL...
Error importing: import failed: exit status 1
Import failed: prefix mismatch detected: database uses 'bd-' but found issues with prefixes: [beads- (2 issues) test- (10 issues)]
```

**Expected behavior:**
- Detect that the mismatched prefixes are all tombstones
- Auto-apply --rename-on-import since tombstones with wrong prefixes are pollution
- Or prompt: 'Found 21 issues with wrong prefixes (all tombstones). Remove them? [Y/n]'

**Context:** Prefix pollution typically comes from contributor PRs that used different test prefixes. These are always safe to remove when they're tombstones.


Extract database search logic into helper function

The logic for finding a database in a beads directory is duplicated:
- FindDatabasePath() BEADS_DIR section (beads.go:141-169)
- findDatabaseInTree() (beads.go:248-280)

Both implement the same search order:
1. Check config.json first (single source of truth)
2. Fall back to canonical beads.db
3. Search for *.db files, filtering backups and vc.db

Refactoring suggestion:
Extract to a helper function like:
  func findDatabaseInBeadsDir(beadsDir string) string

Benefits:
- Single source of truth for database search logic
- Easier to maintain and update search order
- Reduces code duplication

Related to [deleted:bd-e16b] implementation.


Improve bd doctor output formatting for better readability

The current bd doctor output is a wall of text that's hard to process. Consider improvements like:
- Grouping related checks into collapsible sections
- Using color/bold for section headers
- Showing only failures/warnings by default with --verbose for full output
- Better visual hierarchy between major sections
- Summary line at top (e.g., '24 checks passed, 0 warnings, 0 errors')


Sync sanitize incorrectly removes newly created issues

## Problem

The sync sanitize process incorrectly identifies newly created issues as 'deleted issues resurrected by git merge' and removes them from the local JSONL file.

## Reproduction

1. Create a new issue: bd create --title='Test issue'
2. Run bd sync
3. Observe: New issue appears in sanitize removal list
4. Issue is removed from local JSONL but preserved on beads-sync branch

## Observed Behavior

During sync, the sanitize step outputs:
```
→ Sanitized JSONL: removed 738 deleted issue(s) that were resurrected by git merge
  - bd-08ea  (newly created issue!)
  - bd-tnsq  (newly created issue!)
  ...
```

The newly created issues get removed locally but remain on beads-sync branch.

## Expected Behavior

Newly created issues should NOT be removed by sanitize. The sanitize should only remove issues that:
1. Were previously deleted (have tombstones or are in deletions manifest)
2. Are being resurrected from old git history

## Root Cause Investigation

The sanitize logic likely compares the local DB snapshot against some reference and incorrectly classifies new issues as 'resurrected deleted issues'. Possible causes:
- Snapshot protection logic not accounting for new issues
- Deletion manifest containing stale entries
- Race condition between export and sanitize

## Impact

- New issues disappear from local JSONL after sync
- Issues remain on beads-sync but cause confusion
- Multi-agent workflows affected when agents can't see new issues locally
- Requires manual intervention to recover

## Files to Investigate

- cmd/bd/sync.go - sanitize logic
- cmd/bd/snapshot_manager.go - snapshot comparison
- Deletion manifest handling

## Acceptance Criteria

- [ ] Newly created issues survive sync without being sanitized
- [ ] Only truly deleted/resurrected issues are removed
- [ ] Add test case for this scenario


GH#524: Package for Windows (winget)

Request to publish beads on winget for easy Windows installation. See: https://github.com/steveyegge/beads/issues/524


Git history fallback has incorrect logic for detecting deletions

## Problem

The `wasInGitHistory` function in `internal/importer/importer.go:891` returns true if the ID is found **anywhere** in git history. But finding an ID in history doesn't necessarily mean it was deleted - it could mean:

1. The issue was added (appears in a commit adding it)
2. The issue was modified (appears in commits updating it)
3. The issue was deleted (appears in a commit removing it)

The current logic incorrectly treats all three cases as 'deleted'.

## Correct Logic

`git log -S` with `--oneline` shows commits where the string was added OR removed. To detect deletion specifically:

1. ID appears in git history (was once in JSONL)
2. ID is NOT currently in JSONL

The second condition is already checked by the caller (`purgeDeletedIssues`), so technically the logic is correct in context. But the function name and doc comment are misleading.

## Fix Options

1. **Rename function** to `wasEverInJSONL` and update doc comment to clarify
2. **Add explicit check** for current JSONL state in the function itself

Option 1 is simpler and correct since caller already filters.


bd export should verify JSONL line count matches database count

After export completes, bd should verify that the JSONL file line count matches the number of issues exported. This would catch silent failures where the export appears to succeed but doesn't actually write all issues.

Real-world scenario from VC project:
- Ran direct SQL DELETE to remove 240 issues
- Ran 'bd export -o .beads/issues.jsonl'
- No error shown, appeared to succeed
- But JSONL file was not updated (still had old line count)
- Later session found all 240 issues still in JSONL
- Had to repeat the cleanup

If export had verified line count, it would have immediately shown:
  Error: Export verification failed
    Expected: 276 issues
    JSONL file: 516 lines
    Mismatch indicates export failed to write all issues

This is especially important because:
1. JSONL is source of truth in git
2. Silent export failures cause data inconsistency
3. Users assume export succeeded if no error shown
4. The verification is cheap (just count lines)

Implementation:
- After writing JSONL, count lines in file
- Compare to len(exportedIDs)
- If mismatch, remove temp file and return error
- Show clear error message with both counts


Design hash ID generation algorithm

Design and specify the hash-based ID generation algorithm.

## Requirements
- Deterministic: same inputs → same ID
- Collision-resistant: ~2^32 space for 8-char hex
- Fast: <1μs per generation
- Includes timestamp for uniqueness
- Includes creator/workspace for distributed uniqueness

## Proposed Algorithm
```go
func GenerateIssueID(title, desc string, created time.Time, workspaceID string) string {
    h := sha256.New()
    h.Write([]byte(title))
    h.Write([]byte(desc))
    h.Write([]byte(created.Format(time.RFC3339Nano)))
    h.Write([]byte(workspaceID))
    hash := hex.EncodeToString(h.Sum(nil))
    return "bd-" + hash[:8]  // 8-char prefix = 2^32 space
}
```

## Open Questions
1. 8 chars (2^32) or 16 chars (2^64) for collision resistance?
2. Include priority/type in hash? (Pro: more entropy. Con: immutable)
3. How to handle workspace ID generation? (hostname? UUID?)
4. What if title+desc change? (Answer: ID stays same - hash only used at creation)

## Deliverables
- Design doc: docs/HASH_ID_DESIGN.md
- Collision probability analysis
- Performance benchmarks
- Prototype implementation in internal/types/id_generator.go


Add tests for Integration Layer Implementation

While implementing bd-wfmw, noticed missing tests

Reviewed existing coverage:
- Basic test coverage exists in lib/test_beads_mail_adapter.py
- Integration tests cover failure scenarios in tests/integration/test_mail_failures.py
- Good coverage of: enabled/disabled modes, graceful degradation, 409 conflicts, HTTP errors, config
- Missing: authorization headers detail, request body structure validation, concurrent reservation timing, TTL edge cases


Add tests for internal/utils package

Currently 0.0% coverage. Need tests for utility functions including issue ID parsing and validation.


Add tests for daemon lifecycle module


bd delete fails to find closed issues

## Steps to Reproduce
1. Close some issues with `bd close`
2. Try to delete them with `bd delete <ids> --force`
3. Get error "issues not found"

## Expected Behavior
Should delete the closed issues

## Actual Behavior
```
Error: issues not found: bd-74ee, bd-9b13, bd-72w, bd-149, bd-5iv, bd-78w
```

But `bd list --status closed --json` shows they exist.

## Root Cause
Likely the delete command is only looking for open issues, or there's a race condition with auto-import.


Migration tool for existing users

Ensure smooth migration for existing users to separate branch workflow.

Tasks:
- Add bd migrate --separate-branch command
- Detect existing repos, migrate cleanly
- Preserve git history
- Add rollback mechanism
- Test migration on beads' own repo (dogfooding)
- Communication plan (GitHub discussion, docs)
- Version compatibility checks

Estimated effort: 2-3 days


bd export only outputs 1 issue after auto-import corrupts database

When auto-import runs and purges issues (due to git history backfill bug), subsequent 'bd export' only exports 1 issue even though the database should have many.

Reproduction:
1. Have issues.jsonl with 55 issues
2. Auto-import triggers and purges all issues via git history backfill
3. Run 'bd export' - only exports 1 issue (the last one created before corruption)

The database gets into an inconsistent state where most issues are purged but export doesn't realize this.

Workaround: Rebuild database from scratch with 'rm .beads/beads.db && bd init --prefix bd'


GH#405: Prefix parsing with hyphens treats first segment as prefix

Prefix me-py-toolkit gets parsed as just me- when detecting mismatches. Fix prefix parsing to handle multi-hyphen prefixes. See GitHub issue #405.


Add MCP functions for repair commands

Add repair commands to beads-mcp for agent access:
- beads_resolve_conflicts()
- beads_find_duplicates()
- beads_detect_pollution()
- beads_validate()

Files: integrations/beads-mcp/src/beads_mcp/server.py


Add transaction + retry logic for N-way collision resolution

## Problem
Current N-way collision resolution fails on UNIQUE constraint violations during convergence rounds when 5+ clones sync. The RemapCollisions function is non-atomic and performs operations sequentially:
1. Delete old issues (CASCADE deletes dependencies)
2. Create remapped issues (can fail with UNIQUE constraint)
3. Recreate dependencies
4. Update text references

Failure at step 2 leaves database in inconsistent state.

## Solution
Wrap collision resolution in database transaction with retry logic:
- Make entire RemapCollisions operation atomic
- Retry up to 3 times on UNIQUE constraint failures
- Re-sync counters between retries
- Add better error messages for debugging

## Implementation
Location: internal/storage/sqlite/collision.go:342 (RemapCollisions function)

```go
// Retry up to 3 times on UNIQUE constraint failures
for attempt := 0; attempt < 3; attempt++ {
    err := s.db.ExecInTransaction(func(tx *sql.Tx) error {
        // All collision resolution operations
    })
    if !isUniqueConstraintError(err) {
        return err
    }
    s.SyncAllCounters(ctx)
}
```

## Success Criteria
- 5-clone collision test passes reliably
- No partial state on UNIQUE constraint errors
- Automatic recovery from transient ID conflicts

## References
- See beads_nway_test.go:124 for the KNOWN LIMITATION comment
- Related to-7c5915ae (transaction support)

## Progress Made

1. Added `ExecInTransaction` helper to SQLiteStorage for atomic database operations
2. Added `IsUniqueConstraintError` function to detect UNIQUE constraint violations
3. Wrapped `RemapCollisions` with retry logic (up to 3 attempts) with counter sync between retries
4. Enhanced `handleRename` to detect and handle race conditions where target ID already exists
5. Added defensive checks for when old ID has been deleted by another clone

## Test Results

The changes improve N-way collision handling but don't fully solve the problem:
- Original error: `UNIQUE constraint failed: issues.id` during first convergence round
- With changes: Test proceeds further but encounters different collision scenarios
- New error: `target ID already exists with different content` in later convergence rounds

## Root Cause Analysis

The issue is more complex than initially thought. In N-way scenarios:
1. Clone A remaps bd-1c63eb84 → test-2 → test-4
2. Clone B remaps bd-1c63eb84 → test-3 → test-4
3. Both try to create test-4, but with different intermediate states
4. This creates legitimate content collisions that require additional resolution

## Next Steps

The full solution requires:
1. Making remapping fully deterministic across clones (same input → same remapped ID)
2. OR making `handleRename` more tolerant of mid-flight collisions
3. OR implementing full transaction support for multi-step collision resolution -7c5915ae)

The retry logic added here provides a foundation but isn't sufficient for complex N-way scenarios.


WASM port of bd for Claude Code Web sandboxes

Enable beads to work in Claude Code Web sandboxes by compiling bd to WebAssembly.

## Problem
Claude Code Web sandboxes cannot install bd CLI due to network restrictions:
- GitHub releases return 403
- go install fails with DNS errors
- Binary cannot be downloaded

## Solution
Compile bd Go codebase to WASM, publish to npm as drop-in replacement.

## Technical Approach
- Use GOOS=js GOARCH=wasm to compile bd
- modernc.org/sqlite already supports js/wasm target
- Publish to npm as bd-wasm package
- Full feature parity with bd CLI

## Success Criteria
- bd-wasm installs via npm in web sandbox
- All core bd commands work identically
- JSONL output matches native bd
- Performance within 2x of native

WASM port abandoned - Claude Code Web has full VMs not browser restrictions. Better: npm + native binary


Add import metrics for external_ref matching statistics

Add observability for external_ref matching behavior during imports to help debug and optimize import operations.

Metrics to track:
- Number of issues matched by external_ref
- Number of issues matched by ID
- Number of issues matched by content hash
- Number of external_ref updates vs creates
- Average import time with vs without external_ref

Output format:
- Add to ImportResult struct
- Include in import command output
- Consider structured logging

Use cases:
- Debugging slow imports
- Understanding match distribution
- Optimizing import performance

Related: bd-1022


Test parent issue


Implement `bd setup cursor` for Cursor IDE integration

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


Add index usage verification test for external_ref lookups

Currently we test that idx_issues_external_ref index exists, but we don't verify that it's actually being used by the query planner.

Proposed solution:
- Add test using EXPLAIN QUERY PLAN
- Verify that 'SEARCH TABLE issues USING INDEX idx_issues_external_ref' appears in plan
- Ensures O(1) lookup performance is maintained

Related: bd-1022
Files: internal/storage/sqlite/external_ref_test.go:260


Test basic bd commands in WASM (init, create, list)

Compile and verify basic bd functionality works in WASM:
- Test bd init --quiet
- Test bd create with simple issue
- Test bd list --json output
- Verify SQLite database creation and queries work
- Document any runtime issues or workarounds needed


Test all core bd commands in WASM for feature parity

Comprehensive testing of bd-wasm against native bd:
- Test all CRUD operations (create, update, show, close)
- Test dependency management (dep add, dep tree)
- Test sync operations (sync, import, export)
- Verify JSONL output matches native bd
- Run existing Go test suite in WASM if possible
- Benchmark performance (should be within 2x of native)


Implement core reset package (internal/reset)

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

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

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

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

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

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

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

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


Document 3-clone ID non-determinism in collision resolution

Document the known behavior of 3+ way collision resolution where ID assignments may vary based on sync order, even though content always converges correctly.

Updates needed:
- Update bd-71107098 notes to mark 2-clone case as solved
- Document 3-clone ID non-determinism as known limitation
- Add explanation to ADVANCED.md or collision resolution docs
- Explain why this happens (pairwise hash comparison is deterministic, but multi-way ID allocation uses sync-order dependent counters)
- Clarify trade-offs: content convergence ✅ vs ID stability ❌

Key points to document:
- Hash-based resolution is pairwise deterministic
- Content always converges correctly (all issues present with correct data)
- Numeric ID assignments in 3+ way collisions depend on sync order
- This is acceptable for most use cases (content convergence is primary goal)
- Full determinism would require complex multi-way comparison


Add bd doctor --fix flag to automatically repair issues

Implement a --fix flag for bd doctor that can automatically repair detected issues.

Requirements:
- Add --fix flag to bd doctor command
- Show all fixable issues and prompt for confirmation before applying fixes
- Organize fix implementations under doctor/fix/<type_of_fix>.go
- Each fix type should have its own file (e.g., doctor/fix/hooks.go, doctor/fix/sync.go)
- Display what will be fixed and ask user to confirm (Y/n) before proceeding
- Support fixing issues like:
  - Missing or broken git hooks
  - Sync problems with remote
  - File permission issues
  - Any other auto-repairable issues doctor detects

Implementation notes:
- Maintain separation between detection (existing doctor code) and repair (new fix code)
- Each fix should be idempotent and safe to run multiple times
- Provide clear output about what was fixed
- Log any fixes that fail with actionable error messages


Fix TestImportBugIntegration deadlock in importer_test.go

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

TODO: Test hangs due to database deadlock - needs investigation

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

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


Create WASM build infrastructure (Makefile, scripts)

Set up build tooling for WASM compilation:
- Add GOOS=js GOARCH=wasm build target
- Copy wasm_exec.js from Go distribution
- Create wrapper script for Node.js execution
- Add build task to Makefile or build script


Test database naming


GH#505: Add bd reset command to wipe database

Users struggle to fully reset beads (local + remote). Need bd reset command with safety confirmation. Currently requires manual hook/dir removal. See: https://github.com/steveyegge/beads/issues/505


Extract computeJSONLHash helper to eliminate code duplication

SHA256 hash computation is duplicated in 3 places:
- cmd/bd/integrity.go:50-52
- cmd/bd/sync.go:611-613
- cmd/bd/import.go:318-319

Extract to shared helper function computeJSONLHash(jsonlPath string) (string, error) that includes proper #nosec comment and error handling.


Review PR #512: clarify bd ready docs

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


bd import auto-detects wrong prefix from directory name instead of issue IDs

When importing issues.jsonl into a fresh database, 'bd import' prints:

  ✓ Initialized database with prefix 'beads' (detected from issues)

But the issues all have prefix 'bd-' (e.g., bd-03r). It appears to be detecting the prefix from the directory name (.beads/) rather than from the actual issue IDs in the JSONL.

This causes import to fail with:
  validate ID prefix for bd-03r: issue ID 'bd-03r' does not match configured prefix 'beads'

Workaround: Run 'bd config set issue_prefix bd' before import, or use 'bd init --prefix bd'.


Expand checkHooksQuick to verify all hook versions

Currently checkHooksQuick only checks post-merge hook version. Should also check pre-commit, pre-push, and post-checkout for completeness. Keep it lightweight but catch more outdated hooks.


Auto-invoke 3-way merge for JSONL conflicts

Currently when git pull encounters merge conflicts in .beads/issues.jsonl, the post-merge hook fails with an error message pointing users to manual resolution or the beads-merge tool.

This is a poor user experience - the conflict detection is working, but we should automatically invoke the advanced 3-way merging instead of just telling users about it.

**Current behavior:**
- Detect conflict markers in JSONL
- Display error with manual resolution options
- Exit with failure

**Desired behavior:**
- Detect conflict markers in JSONL
- Automatically invoke beads-merge 3-way merge
- Only fail if automatic merge cannot resolve the conflicts

**Reference:**
- beads-merge tool: https://github.com/neongreen/mono/tree/main/beads-merge
- Error occurs in post-merge hook during bd sync after git pull


Add path normalization to getMultiRepoJSONLPaths()

From bd-xo6b code review: getMultiRepoJSONLPaths() does not handle non-standard paths correctly.

Problems:
- No tilde expansion: ~/repos/foo treated as literal path
- No absolute path conversion: ../other-repo breaks if working directory changes
- No duplicate detection: If Primary=. and Additional=[.], same JSONL processed twice
- No empty string handling: Empty paths create invalid /.beads/issues.jsonl

Impact:
Config with tilde or relative paths will fail

Fix needed:
1. Use filepath.Abs() for all paths
2. Add tilde expansion via os.UserHomeDir()
3. Deduplicate paths (use map to track seen paths)
4. Filter out empty strings
5. Validate paths exist and are readable

Files:
- cmd/bd/deletion_tracking.go:333-358 (getMultiRepoJSONLPaths function)


Database pollution in ~/src/dave/vc: 895 issues vs canonical 310

~/src/dave/vc/.beads/beads.db has 895 total issues (675 open, 149 closed), but canonical ~/src/vc/.beads/vc.db has only 310 issues (230 open). This is 585 extra issues - likely pollution from other repositories.

Need to:
1. Identify which issues are polluted (use detect-pollution)
2. Compare issue IDs between dave/vc and canonical vc databases
3. Determine pollution source (beads repo? other repos?)
4. Clean up polluted database
5. Root cause: why did pollution occur?

Investigation findings so far:
- Polluted DB (~/src/dave/vc/.beads/beads.db): 241 issues (180 open, 43 closed)
- Canonical DB (~/src/vc/.beads/vc.db): 310 issues (230 open, 62 closed)
- Contradiction: Polluted has FEWER issues, not more (241 < 310, diff of 69)
- Only 1 unique ID in polluted: vc-55fi
- All source_repo fields are set to "." in both databases
- Issue description claims 895 issues in polluted vs 310 canonical - numbers don't match current state
- Possible: Pollution was already partially cleaned, or issue description refers to different database?


Add transaction helper to replace manual COMMIT/ROLLBACK

Create tx.go with withTx helper that handles transaction lifecycle. Replace manual transaction blocks in create/insert/update paths.

Refactoring complete:
- Created withTx() helper in util.go
- Added ExecInTransaction() as deprecated wrapper for backward compatibility
- Refactored all manual transaction blocks to use withTx():
  - events.go: AddComment
  - dirty.go: MarkIssuesDirty, ClearDirtyIssuesByID
  - labels.go: executeLabelOperation
  - dependencies.go: AddDependency, RemoveDependency
  - compact.go: ApplyCompaction
- All tests pass successfully


Respond to GitHub issue #479 with solution

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

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

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

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

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

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


Refactor daemon.go into internal/daemonrunner

Extract daemon runtime from daemon.go (1,565 lines) into internal/daemonrunner with focused modules: config.go, daemon.go, process.go, rpc_server.go, sync.go, git.go. Keep cobra command thin.


Add tests for bd init --contributor wizard

Write integration tests for the contributor wizard:
- Test fork detection logic
- Test planning repo creation
- Test config setup
- Test with/without upstream remote
- Test with SSH vs HTTPS origins


fix: bd onboard hangs on Windows


CLI accepts both hash IDs and aliases

Update all CLI commands to accept both hash IDs (bd-af78e9a2) and aliases (#42, or just 42).

## Parsing Logic
```go
// internal/utils/id_parser.go
func ParseIssueID(input string) (issueID string, err error) {
    // Hash ID: bd-af78e9a2
    if strings.HasPrefix(input, "bd-") {
        return input, nil
    }

    // Alias: #42 or 42
    aliasStr := strings.TrimPrefix(input, "#")
    alias, err := strconv.Atoi(aliasStr)
    if err != nil {
        return "", fmt.Errorf("invalid issue ID: %s", input)
    }

    // Resolve alias to hash ID
    return storage.GetIssueIDByAlias(alias)
}
```

## Commands to Update
All commands that accept issue IDs:

### 1. bd show
```bash
bd show bd-af78e9a2        # Hash ID
bd show #42                # Alias
bd show 42                 # Alias (shorthand)
bd show bd-af78e9a2 #42    # Mixed (multiple IDs)
```

### 2. bd update
```bash
bd update #42 --status in_progress
bd update bd-af78e9a2 --priority 1
```

### 3. bd close
```bash
bd close #42 --reason "Done"
```

### 4. bd dep add/tree
```bash
bd dep add #42 #1 --type blocks
bd dep tree bd-af78e9a2
```

### 5. bd label add/remove
```bash
bd label add #42 critical
```

### 6. bd merge
```bash
bd merge #42 #43 --into #41
```

## Display Format
Default to showing aliases in output:
```bash
$ bd list
#1   Fix authentication bug          P1  open
#2   Add logging to daemon           P2  open
#42  Investigate jujutsu integration P3  open
```

With `--format=hash` flag:
```bash
$ bd list --format=hash
bd-af78e9a2  Fix authentication bug          P1  open
bd-e5f6a7b8  Add logging to daemon           P2  open
bd-1a2b3c4d  Investigate jujutsu integration P3  open
```

## Files to Modify
- internal/utils/id_parser.go (new)
- cmd/bd/show.go
- cmd/bd/update.go
- cmd/bd/close.go
- cmd/bd/reopen.go
- cmd/bd/dep.go
- cmd/bd/label.go
- cmd/bd/merge.go
- cmd/bd/list.go (add --format flag)

## Testing
- Test hash ID parsing
- Test alias parsing (#42, 42)
- Test mixed IDs in single command
- Test error on invalid ID
- Test alias resolution failure


Isolate RPC server startup into rpc_server.go

Create internal/daemonrunner/rpc_server.go with StartRPC function. Move startRPCServer logic here and return typed handle.


Add unit tests for reset package

Comprehensive unit tests for internal/reset package.

## Test Cases

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

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

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

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

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

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

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


Add performance optimization to hasJSONLChanged with mtime fast-path

hasJSONLChanged() reads entire JSONL file to compute SHA256 hash on every check. For large databases (50MB+), this is expensive and called frequently by daemon.

Optimization: Check mtime first as fast-path. Only compute hash if mtime changed. This catches git operations (mtime changes but content doesn't) while avoiding expensive hash computation in common case (mtime unchanged = content unchanged).

Performance impact: 99% of checks will use fast path, only git operations need slow path.


Support --parent flag in daemon mode

Added support for hierarchical child issue creation using --parent flag in daemon mode. Previously only worked in direct mode.


bd validate - Comprehensive health check

Run all validation checks in one command.

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

Supports --fix-all for auto-repair.

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

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


Implement `bd setup claude` command for Claude Code integration

Create a `bd setup claude` command that installs Claude Code integration files (slash commands and hooks). This is idempotent and safe to run multiple times.


Fix TestExportUpdatesMetadata to test actual daemon functions

## Problem
TestExportUpdatesMetadata (daemon_sync_test.go:296) manually replicates the metadata update logic rather than calling the fixed functions (createExportFunc/createSyncFunc).

This means:
- Test verifies the CONCEPT works
- But doesn't verify the IMPLEMENTATION in the actual daemon functions
- If daemon code is wrong, test still passes

## Current Test Flow
```go
// Test does:
exportToJSONLWithStore()  // ← Direct call
// ... manual metadata update ...
exportToJSONLWithStore()  // ← Direct call again
```

## Should Be
```go
// Test should:
createExportFunc(...)()  // ← Call actual daemon function
// ... verify metadata was updated by the function ...
createExportFunc(...)()  // ← Call again, should not fail
```

## Challenge
createExportFunc returns a closure that's run by the daemon. Testing this requires:
- Mock logger
- Proper context setup
- Git operations might need mocking

## Files
- cmd/bd/daemon_sync_test.go

## Acceptance Criteria
- Test calls actual createExportFunc and createSyncFunc
- Test verifies metadata updated by daemon code, not test code
- Both functions tested (export and sync)


Add test coverage for internal/config package


Improve test coverage for cmd/bd/doctor/fix (23.9% → 50%)

The doctor/fix package has only 23.9% test coverage. The doctor fix functionality is important for troubleshooting.

Current coverage: 23.9%
Target coverage: 50%


bd import fails on duplicate external_ref with no resolution options

When JSONL contains duplicate external_ref values (e.g., two issues both have external_ref='BS-170'), bd import fails entirely with no resolution options.

User must manually edit JSONL to remove duplicates, which is error-prone.

Example error:
```
batch import contains duplicate external_ref values:
external_ref 'BS-170' appears in issues: [opal-39 opal-43]
```

Should handle this similar to duplicate issue detection - offer to merge, pick one, or clear duplicates.


Update python-agent example with Agent Mail integration

Modify examples/python-agent/agent.py to use Agent Mail adapter at 4 integration points.

Acceptance Criteria:
- Import and initialize adapter
- Check inbox before find_ready_work()
- Reserve issue before claim_task()
- Notify on status changes
- Release reservation on complete_task()
- Works identically when Agent Mail disabled
- No changes required to core Beads CLI

File: examples/python-agent/agent.py


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

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


Git history fallback doesn't escape regex special chars in IDs

## Problem

In `batchCheckGitHistory`, IDs are directly interpolated into a regex pattern:

```go
patterns = append(patterns, fmt.Sprintf(\`"id":"%s"\`, id))
searchPattern := strings.Join(patterns, "|")
cmd := exec.Command("git", "log", "--all", "-G", searchPattern, ...)
```

If an ID contains regex special characters (e.g., `bd-foo.bar` or `bd-test+1`), the pattern will be malformed or match unintended strings.

## Location
`internal/importer/importer.go:923-926`

## Impact
- False positives: IDs with `.` could match any character
- Regex errors: IDs with `[` or `(` could cause git to fail
- Security: potential for regex injection (low risk since IDs are validated)

## Fix
Escape regex special characters:

```go
import "regexp"

escapedID := regexp.QuoteMeta(id)
patterns = append(patterns, fmt.Sprintf(\`"id":"%s"\`, escapedID))
```


bd sync --squash: batch multiple syncs into single commit

For solo developers who don't need real-time multi-agent coordination, add a --squash option to bd sync that accumulates changes and commits them in a single commit rather than one commit per sync.

This addresses the git history pollution concern (many 'bd sync: timestamp' commits) while preserving the default behavior needed for orchestration.

**Proposed behavior:**
- `bd sync --squash` accumulates pending exports
- Only commits when explicitly requested or on session end
- Default behavior unchanged (immediate commits for orchestration)

**Use case:** Solo developers who want cleaner git history but don't need real-time coordination between agents.

**Related:** PR #411 (docs: reduce bd sync commit pollution)
**See also:** Multi-repo support as alternative solution (docs/MULTI_REPO_AGENTS.md)


Sync should cleanup snapshot files after completion

After sync completion, orphan .base.jsonl and .left.jsonl snapshot files remain in .beads/ directory. These should be cleaned up on successful sync.


GH#520: Daemon sync-branch commit fails with pre-commit hooks (needs --no-verify)

gitCommitInWorktree in daemon_sync_branch.go missing --no-verify flag, causing commit failures when pre-commit hooks installed. Library function in worktree.go:684 has correct impl. See: https://github.com/steveyegge/beads/issues/520


Add --hard flag to bd cleanup to permanently cull tombstones before cutoff date

Currently tombstones persist for 30 days before cleanup prunes them. Need an official way to force-cull tombstones earlier than the default TTL, for scenarios like cleaning house after extended absence where resurrection from old clones is not a concern. Proposed: bd cleanup --hard --older-than N to bypass the 30-day tombstone TTL.


Implement health checks in daemon event loop

Add health checks to checkDaemonHealth() function in daemon_event_loop.go:170:
- Database integrity check
- Disk space check
- Memory usage check

Currently it's just a no-op placeholder.


Rapid 5


Implement auto-merge functionality in duplicates command

The duplicates.go file has a TODO at line 95 to implement the performMerge function for automatic duplicate merging. Currently it just prints a warning message. This would automate the merge process instead of just suggesting commands.


Add tests for Comments API (AddIssueComment, GetIssueComments)

Comments API currently has 0% coverage. Need tests for:
- AddIssueComment - adding comments to issues
- GetIssueComments - retrieving comments
- Comment ordering and pagination
- Edge cases (non-existent issues, empty comments)


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

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


Add --yes flag to bd doctor --fix for non-interactive mode

## Feature Request

Add a `--yes` or `-y` flag to `bd doctor --fix` that automatically confirms all prompts, enabling non-interactive usage in scripts and CI/CD pipelines.

## Current Behavior
`bd doctor --fix` prompts for confirmation before applying fixes, which blocks automated workflows.

## Desired Behavior
`bd doctor --fix --yes` should apply all fixes without prompting.

## Use Cases
- CI/CD pipelines that need to auto-fix issues
- Scripts that automate repository setup
- Pre-commit hooks that want to silently fix issues


Add tests for internal/importer package

Currently 0.0% coverage. Need tests for JSONL import logic including collision detection and resolution.


GH#488: Support claude.local.md for local-only config

Allow CLAUDE.local.md for testing bd without committing to repo or overriding shared CLAUDE.md. Not every repo wants committed AI config. See: https://github.com/steveyegge/beads/issues/488


Short tests taking 13+ minutes (performance regression)


Add tests for DetectCycles to improve coverage from 29.6%

DetectCycles currently has 29.6% coverage. Need comprehensive tests for:
- Simple cycles (A->B->A)
- Complex multi-node cycles
- Acyclic graphs (should not detect cycles)
- Self-loops
- Multiple independent cycles
- Edge cases (empty graph, single node)


Add tests for getMultiRepoJSONLPaths() edge cases

From bd-xo6b code review: Missing test coverage for getMultiRepoJSONLPaths() edge cases.

Current test gaps:
- No tests for empty paths in config
- No tests for duplicate paths
- No tests for tilde expansion
- No tests for relative paths
- No tests for symlinks
- No tests for paths with spaces
- No tests for invalid/non-existent paths

Test cases needed:

1. Empty path handling:
   Primary = empty, Additional = [empty]
   Expected: Should either use . as default or error gracefully

2. Duplicate detection:
   Primary = ., Additional = [., ./]
   Expected: Should return unique paths only

3. Path normalization:
   Primary = ~/repos/main, Additional = [../other, ./foo/../bar]
   Expected: Should expand to absolute canonical paths

4. Partial failure scenarios:
   What if snapshot capture succeeds for repos 1-2 but fails on repo 3?
   Test that system does not end up in inconsistent state

Files:
- cmd/bd/deletion_tracking_test.go (add new tests)

Dependencies:
Depends on fixing getMultiRepoJSONLPaths() path normalization first.


troubleshoot GH#278 daemon exits every few secs


Publish @beads/bd package to npm registry

Publish the npm package to the public npm registry:

## Prerequisites
- npm account created
- Organization @beads created (or use different namespace)
- npm login completed locally
- Package tested locally (bd-f282 completed)

## Publishing steps
1. Verify package.json version matches current bd version
2. Run npm pack and inspect tarball contents
3. Test installation from tarball one more time
4. Run npm publish --access public
5. Verify package appears on https://www.npmjs.com/package/@beads/bd
6. Test installation from registry: npm install -g @beads/bd

## Post-publish
- Add npm badge to README.md
- Update CHANGELOG.md with npm package release
- Announce in release notes

## Note
- May need to choose different name if @beads namespace unavailable
- Alternative: beads-cli, bd-cli, or unscoped beads-issue-tracker


Issue with desc

This is a description


Fix bd --version and bd completion to work without database


Audit remaining cmd/bd files for error handling consistency

Extend ERROR_HANDLING_AUDIT.md to cover: daemon_sync.go, update.go, list.go, show.go, close.go, reopen.go, dep.go, label.go, comments.go, delete.go, compact.go, config.go, validate.go and other high-usage command files


bd cleanup fails with CHECK constraint on status/closed_at mismatch

## Problem

Running `bd cleanup --force` fails with:

```
Error: failed to create tombstone for bd-okh: sqlite3: constraint failed: CHECK constraint failed: (status = 'closed') = (closed_at IS NOT NULL)
```

## Root Cause

The database has a CHECK constraint ensuring closed issues have `closed_at` set and non-closed issues don't. When `bd cleanup` tries to convert a closed issue to a tombstone, this constraint fails if:
1. The issue has status='closed' but closed_at is NULL, OR
2. The issue is being converted but the tombstone creation doesn't properly handle the closed_at field

## Impact

- `bd cleanup --force` cannot complete
- 722 closed issues cannot be cleaned up
- Blocks routine maintenance

## Investigation Needed

1. Check the bd-okh record in the database:
   ```sql
   SELECT id, status, closed_at, deleted_at FROM issues WHERE id = 'bd-okh';
   ```
2. Determine if this is data corruption or a bug in tombstone creation

## Proposed Solutions

1. **If data corruption**: Add a `bd doctor --fix` check that repairs status/closed_at mismatches
2. **If code bug**: Fix the tombstone creation to properly handle the constraint

## Files to Investigate

- `internal/storage/sqlite/sqlite.go` - DeleteIssue / tombstone creation
- Schema CHECK constraint definition

## Acceptance Criteria

- [ ] Identify root cause (data vs code bug)
- [ ] `bd cleanup --force` completes successfully
- [ ] `bd doctor` detects status/closed_at mismatches


Add simple cleanup command to delete closed issues

Users want a simple command to delete all closed issues without requiring Anthropic API key (unlike compact). Requested in GH #243.


GH#518: Document bd setup command

bd setup is undiscoverable. Add to README/docs. Currently only findable by grepping source. See GitHub issue #518.


Implement fs bridge layer for WASM (Go syscall/js to Node.js fs)

Go's os package in WASM returns 'not implemented on js' for mkdir and other file operations. Need to create a bridge layer that:

1. Detects WASM environment (GOOS=js)
2. Uses syscall/js to call Node.js fs module functions
3. Implements wrappers for:
   - os.MkdirAll
   - os.ReadFile / os.WriteFile
   - os.Open / os.Create
   - os.Stat / os.Lstat
   - filepath operations

Approach:
- Create internal/wasm/fs_bridge.go with //go:build js && wasm
- Export Node.js fs functions to Go using global.readFileSync, global.writeFileSync, etc.
- Wrap in Go API that matches os package signatures
- Update beads.go and storage layer to use bridge when in WASM

This unblocks bd-4462 (basic WASM testing) and [deleted:bd-5bbf] (feature parity testing).


Blocking issue


bd ready includes blocked issues (GH#544)

Issues with 'blocks' dependencies still appear in bd ready. The ready query should exclude issues that have unresolved blockers.

Fix in: cmd/bd/ready.go or internal query logic.


Extract duplicated metadata update code in daemon_sync.go

## Problem
The same 22-line metadata update block appears identically in both:
- createExportFunc (lines 309-328)
- createSyncFunc (lines 520-539)

This violates DRY principle and makes maintenance harder.

## Solution
Extract to helper function:

```go
// updateExportMetadata updates last_import_hash and related metadata after a successful export.
// This prevents "JSONL content has changed since last import" errors on subsequent exports (bd-ymj fix).
func updateExportMetadata(ctx context.Context, store storage.Storage, jsonlPath string, log daemonLogger) {
    currentHash, err := computeJSONLHash(jsonlPath)
    if err != nil {
        log.log("Warning: failed to compute JSONL hash for metadata update: %v", err)
        return
    }

    if err := store.SetMetadata(ctx, "last_import_hash", currentHash); err != nil {
        log.log("Warning: failed to update last_import_hash: %v", err)
    }

    exportTime := time.Now().Format(time.RFC3339)
    if err := store.SetMetadata(ctx, "last_import_time", exportTime); err != nil {
        log.log("Warning: failed to update last_import_time: %v", err)
    }

    // Store mtime for fast-path optimization
    if jsonlInfo, statErr := os.Stat(jsonlPath); statErr == nil {
        mtimeStr := fmt.Sprintf("%d", jsonlInfo.ModTime().Unix())
        if err := store.SetMetadata(ctx, "last_import_mtime", mtimeStr); err != nil {
            log.log("Warning: failed to update last_import_mtime: %v", err)
        }
    }
}
```

## Files
- cmd/bd/daemon_sync.go

## Benefits
- Easier maintenance
- Single source of truth
- Consistent behavior


Issue 2 to reopen


Publish official devcontainer configuration

Devcontainer configuration implemented. Manual testing required in actual devcontainer environment (Codespaces or VSCode Remote Containers). All code changes complete, tests pass, linting clean.


Remove unreachable utility functions

Several small utility functions are unreachable:

Files to clean:
1. `internal/storage/sqlite/hash.go` - `computeIssueContentHash` (line 17)
   - Check if entire file can be deleted if only contains this function

2. `internal/config/config.go` - `FileUsed` (line 151)
   - Delete unused config helper

3. `cmd/bd/git_sync_test.go` - `verifyIssueOpen` (line 300)
   - Delete dead test helper

4. `internal/compact/haiku.go` - `HaikuClient.SummarizeTier2` (line 81)
   - Tier 2 summarization not implemented
   - Options: implement feature OR delete method

Impact: Removes 50-100 LOC depending on decisions


Run bd doctor at end of bd init to verify setup

Run bd doctor diagnostics at end of bd init (after line 398 in init.go). If issues found, warn user immediately: '⚠ Setup incomplete. Run bd doctor --fix to complete setup.' Catches configuration problems before user encounters them in normal workflow.


Unit tests for FileWatcher

Test watcher detects JSONL changes. Test git ref changes trigger import. Test debounce integration. Test watcher recovery from file removal/rename.


Parent: bd-imj

Ensure deletions.jsonl is tracked in git (not ignored).

Update bd init and gitignore upgrade logic to:
1. NOT add deletions.jsonl to .gitignore
2. Ensure it is committed alongside beads.jsonl

The file must be in git for cross-clone propagation to work.

Acceptance criteria:
- bd init does not ignore deletions.jsonl
- Existing .gitignore files are not broken
- File appears in git status when modified


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

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

These are lower priority but should have basic coverage.


GH#402: Add --parent flag documentation to bd onboard

bd onboard output is missing --parent flag for epic subtasks. Agents guess wrong syntax (--deps parent:). See GitHub issue #402.


Investigate why test issues persist in database

Test issues (bd-0do3, bd-cjxp, bd-phr2, etc.) keep appearing in ready/list output, cluttering real work. These appear to be leftover test data from test runs.

Need to investigate:
1. Why are test issues not being cleaned up after tests?
2. Are tests creating issues in the main database instead of test databases?
3. Should we add better test isolation or cleanup hooks?
4. Can we add a label/prefix to distinguish test issues from real issues?

These test issues have characteristics:
- Empty descriptions
- Generic titles like "Test issue 0", "Bug P0", "Issue to reopen with reason"
- Created around 2025-11-07 19:00-19:07
- Some assigned to test users like "alice", "bob", "testuser"

## Root Cause Analysis

**Problem**: Python MCP integration tests created test issues in production `.beads/beads.db` instead of isolated test databases.

**Evidence**:
- 29 test issues created on Nov 7, 2025 at 19:00-19:07
- Patterns: "Bug P0", "Test issue X", assignees "alice"/"bob"/"testuser"
- Git commit 0e8936b shows test issues committed to .beads/beads.jsonl
- Tests were being fixed for workspace isolation around the same time

**Why It Happened**:
1. Before commit 0e8936b, `test_client_lazy_initialization()` didn't set `BEADS_WORKING_DIR`
2. Tests fell back to discovering `.beads/` in the project root directory
3. Auto-sync committed test issues to production database

**Resolution**:
1. ✅ Closed 29 test pollution issues (bd-0do3, bd-cjxp, etc.)
2. ✅ Added `failIfProductionDatabase()` guard in Go test helpers
3. ✅ Added production pollution checks in RPC test setup
4. ✅ Created `conftest.py` with pytest safety checks for Python tests
5. ✅ Added `BEADS_TEST_MODE` env var to mark test execution
6. ✅ Tests now fail fast if they detect production database usage

**Prevention**:
- All test helper functions now verify database paths are in temp directories
- Python tests fail immediately if BEADS_DB points to production
- BEADS_TEST_MODE flag helps identify test vs production execution
- Clear error messages guide developers to use proper test isolation

**Files Modified**:
- cmd/bd/test_helpers_test.go - Added failIfProductionDatabase()
- internal/rpc/rpc_test.go - Added temp directory verification
- integrations/beads-mcp/tests/conftest.py - New file with pytest safeguards


Fix gosec lint warnings in doctor.go, main.go, and fix subdirectory

CI lint job failing with 4 gosec warnings:
- cmd/bd/doctor.go:664 (G304: file inclusion via variable)
- cmd/bd/doctor/fix/database_config.go:166 (G304: file inclusion via variable)
- cmd/bd/doctor/fix/untracked.go:61 (G204: subprocess launched with variable)
- cmd/bd/main.go:645 (G304: file inclusion via variable)

Either suppress with `// #nosec` if false positives, or refactor to validate paths properly.


Handle unchecked errors (errcheck - 683 issues)

683 unchecked error returns, mostly in tests (Close, Rollback, RemoveAll). Many already excluded in config but still showing up.

Fixed all errcheck warnings in production code:
- Enabled errcheck linter (was disabled)
- Set tests: false in .golangci.yml to focus on production code
- Fixed 27 total errors in production code using Oracle guidance:
  * Database patterns: defer func() { _ = rows.Close() }() and defer func() { _ = tx.Rollback() }()
  * Best-effort closers: _ = store.Close(), _ = client.Close()
  * Proper error handling for file writes, fmt.Scanln(), os.Remove()
- All tests pass
- Only 2 "unused" linter warnings remain (not errcheck)


Bug P0


bd-hv01: Storage backend extensibility broken by type assertion

Problem: deletion_tracking.go:69-82 uses type assertion for DeleteIssue which breaks if someone adds a new storage backend.

Fix: Check capability before starting merge or add DeleteIssue to Storage interface.

Files: cmd/bd/deletion_tracking.go:69-82, internal/storage/storage.go


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.


Unit tests for Debouncer

Test debouncer batches multiple triggers into single action. Test timer reset on subsequent triggers. Test cancel during wait. Test thread safety.


Add MCP server functions for repair commands

Expose new repair commands via MCP server for agent access:

Functions to add:
- beads_repair_deps()
- beads_detect_pollution()
- beads_validate()
- beads_resolve_conflicts() (when implemented)

Update integrations/beads-mcp/src/beads_mcp/server.py

See repair_commands.md lines 803-884 for design.


Daemon gets stuck when auto-import blocked by git conflicts

CRITICAL: The daemon enters a corrupt state that breaks RPC commands when auto-import is triggered but git pull fails due to uncommitted changes.

Impact: This is a data integrity and usability issue that could cause users to lose trust in Beads. The daemon silently fails for certain commands while appearing healthy.

Reproduction:
1. Make local changes to issues (creates uncommitted .beads/beads.jsonl)
2. Remote has updates (JSONL newer, triggers auto-import)
3. Daemon tries to pull but fails: 'cannot pull with rebase: You have unstaged changes'
4. Daemon enters bad state - 'bd show' and other commands return EOF
5. 'bd list' still works, daemon process is running, no errors logged

Technical details:
- Auto-import check runs in handleRequest() before processing RPC commands
- When import is blocked, it appears to corrupt daemon state
- Likely: deadlock, unclosed transaction, or storage handle corruption
- Panic recovery (server_lifecycle_conn.go:183) didn't catch anything - not a panic

Required fix:
- Auto-import must not block RPC command execution
- Handle git pull failures gracefully without corrupting state
- Consider: skip auto-import if git is dirty, queue import for later, or use separate goroutine
- Add timeout/circuit breaker for import operations
- Log clear warnings when auto-import is skipped

Without this fix, users in collaborative environments will frequently encounter mysterious EOF errors that require daemon restarts.


Multi-repo contributor workflow support

Implement separate repository support for OSS contributors to prevent PR pollution while maintaining git ledger and multi-clone sync. Based on contributor-workflow-analysis.md Solution #4.


Sync validation false positive: legitimate deletions trigger 'data loss detected'

## Problem
`bd sync` fails with false positive data loss detection when legitimate deletions occur:
```
Post-import validation failed: import reduced issue count: 26 → 25 (data loss detected!)
```

## Root Cause
The validation in `sync.go:329-340` counts DB issues BEFORE import, but `purgeDeletedIssues()` in `importer.go:159` legitimately removes issues DURING import. The validation doesn't account for expected deletions.

**The Flow:**
```
sync.go:293     → beforeCount = countDBIssues() = 26
sync.go:310-319 → sanitizeJSONL removes deleted issues from JSONL (RemovedCount=N)
sync.go:323     → importFromJSONL() runs subprocess
                  └→ importer.go:159 purgeDeletedIssues() removes issues from DB
sync.go:331     → afterCount = countDBIssues() = 25
sync.go:335     → validatePostImport(26, 25) → ERROR
```

## Fix
Pass `sanitizeResult.RemovedCount` to validation and account for expected deletions:

```go
// sync.go around line 335
expectedDecrease := 0
if sanitizeResult != nil {
    expectedDecrease = sanitizeResult.RemovedCount
}
if err := validatePostImportWithDeletions(beforeCount, afterCount, expectedDecrease); err != nil {
    // ...
}
```

```go
// integrity.go - new or modified function
func validatePostImportWithDeletions(before, after, expectedDeletions int) error {
    if after < before - expectedDeletions {
        return fmt.Errorf("unexpected data loss: %d → %d (expected max decrease: %d)",
            before, after, expectedDeletions)
    }
    // ...
}
```

## Files
- cmd/bd/sync.go:329-340
- cmd/bd/integrity.go:289-301


Multi-repo hydration layer

Build core infrastructure to hydrate database from N repos (N≥1), with smart caching via file mtime tracking and routing writes to correct JSONL based on source_repo metadata.


Complete and commit beads-mcp type safety improvements

Uncommitted type safety work in beads-mcp needs review and completion


bd-hv01: Race condition with concurrent snapshot operations

## Problem
Snapshot files have no locking. Multiple processes can call captureLeftSnapshot simultaneously:

1. Process A: export → begins snapshot
2. Process B: export → begins snapshot
3. Process A: writes partial left.jsonl
4. Process B: overwrites with its left.jsonl
5. Process A: completes merge with wrong snapshot

## Impact
- Data corruption in multi-process scenarios
- Daemon + manual sync race
- Multiple git clones on same filesystem

## Fix
Use atomic file operations with process-specific temp files:
```go
func captureLeftSnapshot(jsonlPath string) error {
    _, leftPath := getSnapshotPaths(jsonlPath)
    tempPath := fmt.Sprintf("%s.%d.tmp", leftPath, os.Getpid())
    if err := copyFileSnapshot(jsonlPath, tempPath); err != nil {
        return err
    }
    return os.Rename(tempPath, leftPath) // Atomic on POSIX
}
```

## Files Affected
- cmd/bd/deletion_tracking.go:24-29 (captureLeftSnapshot)
- cmd/bd/deletion_tracking.go:31-36 (updateBaseSnapshot)


Deletions pruning doesn't include results in JSON output

## Problem

When `bd compact --json` runs with deletions pruning, the prune results are silently discarded:

```go
// Only report if there were deletions to prune
if result.PrunedCount > 0 {
    if jsonOutput {
        // JSON output will be included in the main response
        return  // <-- BUG: results are NOT included anywhere
    }
    ...
}
```

## Location
`cmd/bd/compact.go:925-929`

## Impact
- JSON consumers don't know deletions were pruned
- No way to audit pruning via automation

## Fix
Return prune results and include in JSON output structure:

```json
{
  "success": true,
  "compacted": {...},
  "deletions_pruned": {
    "count": 5,
    "retention_days": 7
  }
}
```


Proposal workflow (propose/withdraw/accept)

Implement commands and state machine for moving issues between personal planning repos and canonical upstream repos, enabling contributors to propose work without polluting PRs.


Create npm package structure for bd-wasm

Set up npm package for distribution:
- Create package.json with bd-wasm name
- Bundle bd.wasm + wasm_exec.js
- Create CLI wrapper (bin/bd) that invokes WASM
- Add installation scripts if needed
- Configure package for Claude Code Web sandbox compatibility


Consider central serialization package for JSON handling

Multiple parts of the codebase handle JSON serialization of issues with slightly different approaches. Consider creating a centralized serialization package to ensure consistency.

Current serialization locations:
- `cmd/bd/export.go` - JSONL export (issues to file)
- `cmd/bd/import.go` - JSONL import (file to issues)
- `internal/rpc/protocol.go` - RPC JSON marshaling
- `internal/storage/memory/memory.go` - In-memory marshaling

Potential benefits:
- Single source of truth for JSON format
- Consistent field naming
- Easier to add new fields
- Centralized validation

Note: This is marked **optional** because:
- Current serialization mostly works
- May not provide enough benefit to justify refactor
- Risk of breaking compatibility

Decision point: Evaluate if benefits outweigh refactoring cost

Impact: TBD based on investigation - may defer to future work


Add validation and documentation for metadata key format

## Issues

### 1. No validation that keySuffix doesn't contain separator
```go
if keySuffix != "" {
    hashKey += ":" + keySuffix  // ❌ What if keySuffix contains ":"?
}
```

Add validation:
```go
if strings.Contains(keySuffix, ":") {
    return fmt.Errorf("keySuffix cannot contain ':' separator")
}
```

### 2. Inconsistent keySuffix semantics
Empty string means "global", non-empty means "scoped". Better to always pass explicit key:
```go
const (
    SingleRepoKey = "."
    // Multi-repo uses source_repo identifier
)
```

### 3. Metadata key format not documented
Need to document:
- `last_import_hash` - single-repo mode
- `last_import_hash:<repo_key>` - multi-repo mode
- `last_import_time` - when last import occurred
- `last_import_mtime` - fast-path optimization

Add to internal/storage/sqlite/schema.go or docs/


Investigate performance issues in VS Code Copilot (Windows)

Beads unusable in Windows 11 VS Code Copilot chat with Sonnet 4.5.
Summary event happens every 3-4 turns, taking 3 minutes.
Copilot summarizes after ~125k tokens despite model supporting 1M.
Large context size of beads might be triggering aggressive summarization.
Need workaround or optimization for context size.


bd-hv01: Missing cleanup of .merged temp file on failure

Problem: deletion_tracking.go:49 creates tmpMerged file but does not clean up on failure, causing disk space leak and potential interference with subsequent syncs.

Fix: Add defer os.Remove(tmpMerged) after creating temp file path.

Files: cmd/bd/deletion_tracking.go:38-89


bd sync fails with orphaned issues and duplicate ID conflict

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

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

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

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

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

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

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


GH#243: Recurring stale daemon.lock causes 5s delays

User reports daemon.lock keeps becoming stale after running Claude with beads.

Symptom:
- bd ready takes 5 seconds (exact)
- daemon.lock exists but socket is missing
- bd daemons killall temporarily fixes it
- Problem recurs after using beads with AI agents

User on v0.22.0, Macbook M2, 132 issues (89 closed)

Hypothesis: Daemon is crashing or exiting uncleanly during agent sessions, leaving stale lock file.

Need to:
1. Add crash logging to daemon to understand why it's exiting
2. Improve cleanup on daemon exit (ensure lock is always removed)
3. Add automatic stale lock detection/cleanup
4. Consider making daemon more resilient to crashes

Oracle analysis complete. Converting to epic with 5 focused sub-issues:
1. RPC fast-fail with socket stat + short timeouts (P0)
2. Standardize daemon detection with lock probe (P1)
3. Crash recovery improvements (P2)
4. Self-heal stale artifacts (P2)
5. Diagnostics and debugging (P3)


bd ready taking 5 seconds with 132 issues (89 closed)

User reports bd ready is annoyingly slow on M2 Mac - 5 seconds for 132 issues (89 closed). Started noticing after hash-based IDs update. Need to investigate performance regression. Reported in GH #243.

Root cause identified: Not a query performance issue, but stale daemon locks causing 5s timeout delays.

Fixed in bd-ndyz (closed) via 5 sub-issues:
- bd-expt: Fast-fail socket checks (200ms timeout)
- bd-wgu4: Lock probe before RPC attempts
- bd-1mzt: Self-heal stale artifacts
- bd-vcg5: Panic recovery + socket cleanup
- bd-j7e2: RPC diagnostics (BD_RPC_DEBUG)

All fixes merged. Ready for v0.22.2 release.


Add cancellation and timeout tests

Add comprehensive tests for context cancellation and timeout behavior.

## Context
Part of context propagation work. Validates that bd-rtp and bd-yb8 work correctly.

## Test Coverage Needed

### 1. Cancellation Tests
- [ ] Import operation cancelled mid-stream
- [ ] Export operation cancelled mid-stream
- [ ] Database query cancelled during long operation
- [ ] No corruption after cancellation
- [ ] Proper cleanup (defers execute, connections closed)

### 2. Timeout Tests
- [ ] Operations respect context deadlines
- [ ] Appropriate error messages on timeout
- [ ] State remains consistent after timeout

### 3. Signal Handling Tests
- [ ] SIGINT (Ctrl+C) triggers cancellation
- [ ] SIGTERM triggers graceful shutdown
- [ ] Multiple signals handled correctly

## Implementation Approach
```go
func TestImportCancellation(t *testing.T) {
    ctx, cancel := context.WithCancel(context.Background())

    // Start import in goroutine
    go func() {
        err := runImport(ctx, largeFile)
        assert.Error(err, context.Canceled)
    }()

    // Cancel after short delay
    time.Sleep(100 * time.Millisecond)
    cancel()

    // Verify database integrity
    assertDatabaseConsistent(t, store)
}
```

## Files to Create/Update
- cmd/bd/import_test.go - cancellation tests
- cmd/bd/export_test.go - cancellation tests
- internal/storage/sqlite/*_test.go - context timeout tests

## Acceptance Criteria
- [ ] All critical operations have cancellation tests
- [ ] Tests verify database integrity after cancellation
- [ ] Signal handling tested (if feasible)
- [ ] Test coverage >80% for context paths


deletions.jsonl recreated locally after tombstone migration

After running bd migrate-tombstones and removing deletions.jsonl from git, the file gets recreated locally on subsequent bd commands.

**Steps to reproduce:**
1. Run bd migrate-tombstones (converts to inline tombstones)
2. Remove deletions.jsonl from git and filesystem
3. Run bd sync or bd stats
4. deletions.jsonl reappears

**Expected behavior:**
After migration, deletions.jsonl should not be recreated. All tombstone data should come from inline tombstones in issues.jsonl.

**Workaround:** Add deletions.jsonl to .gitignore to prevent re-tracking. File still gets created but won't pollute the repo.


bd migrate --update-repo-id triggers auto-import that resurrects deleted issues

**Bug:** Running `bd migrate --update-repo-id` can resurrect previously deleted issues from git history.

## What Happened

User deleted 490 closed issues:
- Deletion committed successfully (06d655a) with JSONL at 48 lines
- Database had 48 issues after deletion
- User ran `bd migrate --update-repo-id` to fix legacy database
- Migration triggered daemon auto-import
- JSONL had been restored to 538 issues (from commit 6cd3a32 - before deletion)
- Auto-import loaded the old JSONL over the cleaned database
- Result: 490 deleted issues resurrected

## Root Cause

The auto-import logic in `cmd/bd/sync.go:130-136`:
```go
if isJSONLNewer(jsonlPath) {
    fmt.Println("→ JSONL is newer than database, importing first...")
    if err := importFromJSONL(ctx, jsonlPath, renameOnImport); err != nil {
```

This checks if JSONL mtime is newer than database and auto-imports. The problem:
1. Git operations (pull, merge, checkout) can restore old JSONL files
2. Restored file has recent mtime (time of git operation)
3. Auto-import sees "newer" JSONL and imports it
4. Old data overwrites current database state

## Timeline

- 19:59: Commit 6cd3a32 restored JSONL to 538 issues from d99222d
- 20:22: Commit 3520321 (bd sync)
- 20:23: Commit 06d655a deleted 490 issues → JSONL now 48 lines
- 20:23: User ran `bd migrate --update-repo-id`
- Migration completed, daemon started
- Daemon saw JSONL (restored earlier to 538) was "newer" than database
- Auto-import resurrected 490 deleted issues

## Impact

- **Critical data loss bug** - user deletions can be undone silently
- Affects any workflow that uses git branches, merges, or checkouts
- Auto-import has no safety checks against importing older data
- Users have no warning that old data will overwrite current state

## Fix Options

1. **Content-based staleness** (not mtime-based)
   - Compare JSONL content hash vs database content hash
   - Only import if content actually changed
   - Prevents re-importing old data with new mtime

2. **Database timestamp check**
   - Store "last export timestamp" in database metadata
   - Only import JSONL if it's newer than last export
   - Prevents importing old JSONL after git operations

3. **User confirmation**
   - Before auto-import, show diff of what will change
   - Require confirmation for large changes (>10% issues affected)
   - Safety valve for destructive imports

4. **Explicit sync mode**
   - Disable auto-import entirely
   - Require explicit `bd sync` or `bd import` commands
   - Trade convenience for safety

## Recommended Solution

Combination of #1 and #2:
- Add `last_export_timestamp` to database metadata
- Check JSONL mtime > last_export_timestamp before importing
- Add content hash check as additional safety
- Show warning if importing would delete >10 issues

This preserves auto-import convenience while preventing data loss.

## Files Involved

- `cmd/bd/sync.go:130-136` - Auto-import logic
- `cmd/bd/daemon_sync.go` - Daemon export/import cycle
- `internal/autoimport/autoimport.go` - Staleness detection

## Reproduction Steps

1. Create and delete some issues, commit to git
2. Checkout an earlier commit (before deletion)
3. Checkout back to current commit
4. JSONL file now has recent mtime but old content
5. Run any bd command that triggers auto-import
6. Deleted issues are resurrected


Fix transaction conflict in TryResurrectParent

Integration test TestImportWithDeletedParent fails with 'database is locked' error when resurrection happens inside CreateIssue.

Root cause: TryResurrectParent calls conn.Get() and insertIssue() which conflicts with existing transaction in CreateIssue.

Error: failed to create tombstone for parent bd-parent: failed to insert issue: sqlite3: database is locked

Solution: Refactor resurrection to accept optional transaction parameter, use existing transaction when available instead of creating new connection.

Impact: Blocks resurrection from working in CreateIssue flow, only works in EnsureIDs (which may not have active transaction).


Add integration test for cross-clone deletion propagation


bd-hv01: Multi-repo mode not tested with deletion tracking

Problem: Test suite has no coverage for multi-repo mode. ExportToMultiRepo creates multiple JSONL files but snapshot files are hardcoded to single JSONL location.

Impact: Deletion tracking likely silently broken for multi-repo users, could cause data loss.

Fix: Add test and update snapshot logic to handle multiple JSONL files.

Files: cmd/bd/deletion_tracking_test.go, cmd/bd/deletion_tracking.go, cmd/bd/daemon_sync.go:24-34


Clean up linter errors (914 total issues)

The codebase has 914 linter issues reported by golangci-lint. While many are documented as baseline in LINTING.md, we should clean these up systematically to improve code quality and maintainability.

Reduced from 56 to 41 issues locally, then to 0 issues.

**Fixed in commits:**
- c2c7eda: Fixed 15 actual errors (dupl, gosec, revive, staticcheck, unparam)
- 963181d: Configured exclusions to get to 0 issues locally

**Current status:**
- ✅ Local: golangci-lint reports 0 issues
- ❌ CI: Still failing (see [deleted:bd-cb64c226.1])

**Problem:**
Config v2 format or golangci-lint-action@v8 compatibility issue causing CI to fail despite local success.

**Next:** Debug [deleted:bd-cb64c226.1] to fix CI/local discrepancy


Add WaitGroup tracking to FileWatcher goroutines

FileWatcher spawns goroutines without WaitGroup tracking, causing race condition on shutdown.

Location: cmd/bd/daemon_watcher.go:123-182, 215-291

Problem:
- Goroutines spawned without sync.WaitGroup
- Close() cancels context but doesn't wait for goroutines to exit
- Race condition: goroutine may access fw.debouncer during Close() cleanup
- No guarantee goroutine stopped before fw.watcher.Close() is called

Solution:
- Add sync.WaitGroup field to FileWatcher
- Track goroutines with wg.Add(1) and defer wg.Done()
- Call wg.Wait() in Close() before cleanup

Impact: Race condition on daemon shutdown; potential panic

Effort: 2 hours


Git history fallback missing timeout - could hang on large repos

## Problem

The git commands in `checkGitHistoryForDeletions` have no timeout. On large repos with extensive history, `git log --all -S` or `git log --all -G` can take a very long time (minutes).

## Location
`internal/importer/importer.go:899` and `:930`

## Impact
- Import could hang indefinitely
- User has no feedback that git search is running
- No way to cancel except killing the process

## Fix
Add context with timeout to git commands:

```go
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, "git", ...)
```

Also consider adding a `--since` flag to bound the git history search.


test hash length


bd init UX Improvements

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


Fix default .beads/.gitignore to ignore merge artifacts (GH #274)

Updated the default .gitignore template created by `bd init` to properly ignore merge artifacts and fix overly broad patterns.

Changes:
- Added `*.db?*` pattern for database files with query strings
- Added explicit patterns for merge artifacts: beads.{base,left,right}.{jsonl,meta.json}
- Changed `!*.jsonl` to `!issues.jsonl` to avoid including merge artifact JSONL files

This fixes GitHub issue #274 reported by rscorer.


Fix TestTwoCloneCollision to compare content not timestamps

The test at beads_twoclone_test.go:204-207 currently compares full JSON output including timestamps, causing false negative failures.

Current behavior:
- Both clones converge to identical semantic content
- Clone A: test-2="Issue from clone A", test-1="Issue from clone B"
- Clone B: test-1="Issue from clone B", test-2="Issue from clone A"
- Titles match IDs correctly, no data corruption
- Only timestamps differ (expected and acceptable)

Fix needed:
- Replace exact JSON comparison with content-aware comparison
- Normalize or ignore timestamp fields when asserting convergence
- Test should PASS after this fix

This blocks completion of bd-71107098.


GH#146: No color showing in terminal for some users

User reports color not working in macOS (Taho 26.0.1) with iTerm 3.6.4 and Terminal.app, despite color working elsewhere in terminal. Python rich and printf escape codes work.

Need to investigate:
- Is NO_COLOR env var set?
- Terminal type detection?
- fatih/color library configuration
- Does bd list show colors? bd ready? bd init?
- What's the output of: echo $TERM, echo $NO_COLOR


GH#483: Pre-commit hook fails unnecessarily when .beads removed

Pre-commit hook fails on bd sync when .beads directory exists but user is on branch without beads. Should exit gracefully. See GitHub issue #483.


Add database maintenance commands section to QUICKSTART.md

**Problem:**
Users don't discover `bd compact` or `bd cleanup` commands until their database grows large. These maintenance commands aren't mentioned in quickstart documentation.

Related to issue #349 item #4.

**Current state:**
docs/QUICKSTART.md ends at line 217 with "See README.md for full documentation" but has no mention of maintenance operations.

**Proposed addition:**
Add a "Database Maintenance" section after line 140 (before "Advanced: Agent Mail" section) covering:
- When database grows (many closed issues)
- How to view compaction statistics
- How to compact old issues
- How to delete closed issues
- Warning about permanence

**Example content:**
```markdown
## Database Maintenance

As your project accumulates closed issues, the database grows. Use these commands to manage size:

```bash
# View compaction statistics
bd compact --stats

# Preview compaction candidates (30+ days closed)
bd compact --analyze --json

# Apply agent-generated summary
bd compact --apply --id bd-42 --summary summary.txt

# Immediately delete closed issues (use with caution!)
bd cleanup --force
```

**When to compact:**
- Database file > 10MB with many old closed issues
- After major project milestones
- Before archiving a project phase
```

**Files to modify:**
- docs/QUICKSTART.md (add section after line 140)


Add --json flags to all bd commands for agent-friendly output


bd sync should not stage gitignored snapshot files

## Problem

`gitCommitBeadsDir` in `cmd/bd/sync.go` runs `git add .beads/` which stages all files in the directory, including snapshot files that are listed in `.beads/.gitignore`.

If a snapshot file (e.g., `beads.left.meta.json`) was ever committed before being added to `.gitignore`, git continues to track it. This causes merge conflicts when multiple polecats run `bd sync` concurrently, since each one modifies and commits these temporary files.

## Root Cause

Line ~568 in sync.go:
```go
addCmd := exec.CommandContext(ctx, "git", "add", beadsDir)
```

This stages everything in `.beads/`, but `.gitignore` only prevents *untracked* files from being added - it doesn't affect already-tracked files.

## Suggested Fix

Option A: After `git add .beads/`, run `git reset` on snapshot files:
```go
exec.Command("git", "reset", "HEAD", ".beads/beads.*.jsonl", ".beads/*.meta.json")
```

Option B: Stage only specific files instead of the whole directory:
```go
exec.Command("git", "add", ".beads/issues.jsonl", ".beads/deletions.jsonl", ".beads/metadata.json")
```

Option C: Detect and untrack snapshot files if they're tracked:
```go
// Check if file is tracked: git ls-files --error-unmatch <file>
// If tracked, run: git rm --cached <file>
```

Option B is probably cleanest - explicitly add only the files that should be committed.


Issue 1 to reopen


Add fsnotify dependency to go.mod


bd sync --squash: batch multiple syncs into single commit

For solo developers who don't need real-time multi-agent coordination, add a --squash option to bd sync that accumulates changes and commits them in a single commit rather than one commit per sync.

This addresses the git history pollution concern (many 'bd sync: timestamp' commits) while preserving the default behavior needed for orchestration.

**Proposed behavior:**
- `bd sync --squash` accumulates pending exports without committing
- Commits accumulated changes on session end or explicit `bd sync` (without --squash)
- Default behavior unchanged (immediate commits for orchestration)

**Use case:** Solo developers who want cleaner git history but don't need real-time coordination between agents.

**Related:** PR #411 (docs: reduce bd sync commit pollution)
**See also:** Multi-repo support as alternative solution (docs/MULTI_REPO_AGENTS.md)


bd message: Implement full message reading functionality

The `bd message read` command is incomplete and doesn't actually fetch or display message content.

**Location:** cmd/bd/message.go:413-441

**Current Behavior:**
- Only marks message as read
- Prints placeholder text
- Doesn't fetch message body

**Expected:**
- Fetch full message from Agent Mail resource API
- Display sender, subject, timestamp, body
- Consider markdown rendering

**Blocker:** Core feature for message system MVP


Fix G304 potential file inclusion in cmd/bd/tips.go:259

Linting issue: G304: Potential file inclusion via variable (gosec) at cmd/bd/tips.go:259:18. Error: if data, err := os.ReadFile(settingsPath); err == nil {


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


Test issue 1


Vendor beads-merge Go code into internal/merge/

Copy beads-merge source code from @neongreen's repo into bd codebase.

**Tasks**:
- Create `internal/merge/` package
- Copy merge algorithm code
- Add attribution header to all files
- Update imports to use bd's internal types
- Add LICENSE/ATTRIBUTION file crediting @neongreen
- Keep original algorithm intact

**Source**: https://github.com/neongreen/mono/tree/main/beads-merge


Polecats get stuck on interactive shell prompts (cp/mv/rm -i)

During swarm operations, polecats frequently get stuck waiting for interactive prompts from shell commands like:
- cp prompting 'overwrite file? (y/n)'
- mv prompting 'overwrite file? (y/n)'
- rm prompting 'remove file?'

This happens because macOS aliases or shell configs may have -i flags set by default.

Root cause: Claude Code runs commands that trigger interactive confirmation prompts, but cannot respond to them, causing the agent to hang indefinitely.

Observed in: Multiple polecats during GH issues swarm (Dec 2024)
- Derrick, Roustabout, Prospector, Warboy all got stuck on y/n prompts

Suggested fixes:
1. AGENTS.md should instruct agents to always use -f flag with cp/mv/rm
2. Polecat startup could set shell aliases to use non-interactive versions
3. bd prime hook could include guidance about non-interactive commands
4. Consider detecting stuck prompts and auto-recovering


Updated title


Extract daemon config functions to daemon_config.go


Fix N-way collision convergence

Epic to fix the N-way collision convergence problem documented in n-way-collision-convergence.md.

## Problem Summary
The current collision resolution implementation works correctly for 2-way collisions but does not converge for 3-way (and by extension N-way) collisions. TestThreeCloneCollision demonstrates this with reproducible failures.

## Root Causes Identified
1. Pairwise resolution doesn't scale - each clone makes local decisions without global context
2. DetectCollisions modifies state during detection (line 83-86 in collision.go)
3. No remapping history - can't track transitive remap chains (test-1 → test-2 → test-3)
4. Import-time resolution is too late - happens after git merge

## Solution Architecture
Replace pairwise resolution with deterministic global N-way resolution using:
- Content-addressable identity (content hashing)
- Global collision resolution (sort all versions by hash)
- Read-only detection phase (separate from modification)
- Idempotent imports (content-first matching)

## Success Criteria
- TestThreeCloneCollision passes without skipping
- All clones converge to identical content after final pull
- No data loss (all issues present in all clones)
- Works for N workers (test with 5+ clones)
- Idempotent imports (importing same JSONL multiple times is safe)

## Implementation Phases
See child issues for detailed breakdown of each phase.


Improve test coverage for internal/compact (18.2% → 70%)

The compact package has only 18.2% test coverage. This is a core package handling issue compaction and should have at least 70% coverage.

Key functions needing tests:
- runCompactSingle (0%)
- runCompactAll (0%)
- runCompactRPC (0%)
- runCompactStatsRPC (0%)
- runCompactAnalyze (0%)
- runCompactApply (0%)
- pruneDeletionsManifest (0%)

Current coverage: 18.2%
Target coverage: 70%


Audit and consolidate collision test coverage

The codebase has 2,019 LOC of collision detection tests across 3 files. Run coverage analysis to identify redundant test cases and consolidate.

Test files:
- `cmd/bd/import_collision_test.go` - 974 LOC
- `cmd/bd/autoimport_collision_test.go` - 750 LOC
- `cmd/bd/import_collision_regression_test.go` - 295 LOC

Total: 2,019 LOC of collision tests

Analysis steps:
1. Run coverage analysis
2. Identify redundant tests
3. Document findings

Consolidation strategy:
- Keep regression tests for critical bugs
- Merge overlapping table-driven tests
- Remove redundant edge case tests covered elsewhere
- Ensure all collision scenarios still tested

Expected outcome: Reduce to ~1,200 LOC (save ~800 lines) while maintaining coverage

Impact: Faster test runs, easier maintenance, clearer test intent


Support P-prefix priority format (P0-P4) in create and update commands


RPC fast-fail: stat socket before dial, cap timeouts to 200ms

Eliminate 5s delay when daemon socket is missing by:
1. Add os.Stat(socketPath) check before dialing in TryConnect
2. Return (nil, nil) immediately if socket doesn't exist
3. Set default dial timeout to 200ms in TryConnect
4. Keep TryConnectWithTimeout for explicit health/status checks (1-2s)

This prevents clients from waiting through full timeout when no daemon is running.


bd find-duplicates - AI-powered duplicate detection

Find semantically duplicate issues.

Approaches:
1. Mechanical: Exact title/description matching
2. Embeddings: Cosine similarity (cheap, scalable)
3. AI: LLM-based semantic comparison (expensive, accurate)

Uses embeddings by default for >100 issues.

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


Re: Thread Test 2

Great! Thread is working well.


Test child issue


Consider extracting error handling helpers

Evaluate creating FatalError() and WarnError() helpers as suggested in ERROR_HANDLING.md to reduce boilerplate and enforce consistency. Prototype in a few files first to validate the approach.


Fix TestFallbackToDirectModeEnablesFlush deadlock causing 10min test timeout

## Problem

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

## Root Cause

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

## Stack Trace Evidence

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

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

## Impact

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

## Related

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

## Fix Approaches

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

## Files

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

## Acceptance

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


Document bd edit command and verify MCP exclusion

Follow-up from PR #152:
1. Add "bd edit" to AGENTS.md with "Humans only" note
2. Verify MCP server doesn't expose bd edit command
3. Consider adding test for command registration


Propagate context through command handlers

Thread context from signal-aware parent through all command handlers.

## Context
Part of context propagation work. Builds on bd-rtp (signal-aware contexts).

## Current State
Many command handlers create their own context.Background() locally instead of receiving context from parent.

## Implementation
1. Add context parameter to command handler functions
2. Pass context from cobra command Run/RunE closures
3. Update all storage operations to use propagated context

## Example Pattern
```go
// Before
Run: func(cmd *cobra.Command, args []string) {
    ctx := context.Background()
    store.GetIssues(ctx, ...)
}

// After
Run: func(cmd *cobra.Command, args []string) {
    // ctx comes from parent signal-aware context
    store.GetIssues(ctx, ...)
}
```

## Files to Update
- All cmd/bd/*.go command handlers
- Ensure context flows from main -> cobra -> handlers -> storage

## Benefits
- Commands respect cancellation signals
- Consistent context handling
- Enables timeouts and deadlines

## Acceptance Criteria
- [ ] All command handlers use propagated context
- [ ] No new context.Background() calls in command handlers
- [ ] Context flows from signal handler to storage layer


Performance Testing Framework

Add comprehensive performance testing for beads focusing on optimization guidance and validating 10K+ database scale. Uses standard Go tooling, follows existing patterns, minimal complexity.

Components:
- Benchmark suite for critical operations at 10K-20K scale
- Fixture generator for realistic test data (epic hierarchies, cross-links)
- User diagnostics via bd doctor --perf
- Always-on profiling integration

Goals:
- Identify bottlenecks for optimization work
- Validate performance at 10K+ issue scale
- Enable users to collect diagnostics for bug reports
- Support both SQLite and JSONL import paths


Deletions not propagated across multi-workspace sync

## Problem

When working with multiple beads workspaces (clones) sharing the same git remote, deleted issues keep coming back.

## Reproduction

1. Clone A deletes issue `bd-xyz` via `bd delete bd-xyz --force`
2. Clone A daemon syncs and pushes to GitHub
3. Clone B still has `bd-xyz` in its database
4. Clone B daemon exports and pushes its JSONL
5. Clone A pulls and imports → `bd-xyz` comes back!

## Root Cause

**No deletion tracking mechanism.** The system has no way to distinguish between:
- "Issue doesn't exist in JSONL because it was deleted"
- "Issue doesn't exist in JSONL because the export is stale"

Import treats missing issues as "not in this export" rather than "explicitly deleted."

## Solution Options

1. **Tombstone records** - Keep deleted issues in JSONL with `"status":"deleted"` or `"deleted_at"` field
2. **Deletion log** - Separate `.beads/deletions.jsonl` file tracking all deleted IDs
3. **Three-way merge** - Import compares: DB state, old JSONL, new JSONL
4. **Manual conflict resolution** - Detect resurrection and prompt user

## Related

- Similar to resurrection logic for orphaned children (bd-cc4f)
- beads-merge tool handles this better with 3-way merge


Implement filesystem shims for WASM

WASM needs JS shims for filesystem access. Child of epic bd-44d0.

## Tasks
- [ ] Implement file read/write shims
- [ ] Map WASM syscalls to Node.js fs API
- [ ] Handle .beads/ directory discovery
- [ ] Test with real JSONL files
- [ ] Support both absolute and relative paths

## Technical Notes
- Use Node.js fs module via syscall/js
- Consider MEMFS for in-memory option


Add `bd merge` command wrapping 3-way merge logic

Implement CLI command to invoke beads-merge functionality.

**Interface**:
```bash
bd merge <output> <base> <left> <right>
bd merge --debug <output> <base> <left> <right>
```

**Behavior**:
- Exit code 0 on clean merge
- Exit code 1 if conflicts (write conflict markers)
- Support --debug flag for verbose output
- Match beads-merge's existing behavior

**File**: `cmd/bd/merge.go`


Create tip system infrastructure for contextual hints

Implement a tip/hint system that shows helpful contextual messages after successful commands. This is different from the existing error-path "Hint:" messages - tips appear on success paths to educate users about features they might not know about.


Add BEADS_DAEMON_MODE flag handling

Add environment variable BEADS_DAEMON_MODE (values: poll, events). Default to 'poll' for Phase 1. Wire into daemon startup to select runEventLoop vs runEventDrivenLoop.