Fix variable shadowing in GetNextChildID resurrection code

## Problem
Minor variable shadowing in hash_ids.go:

```go
// Line 33: err declared here
err := s.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM issues WHERE id = ?`, parentID).Scan(&count)

// Line 38: err reused/shadowed here
resurrected, err := s.TryResurrectParent(ctx, parentID)
```

While Go allows this, it can be confusing and some linters flag it.

## Solution
Use different variable name:

```go
resurrected, resurrectErr := s.TryResurrectParent(ctx, parentID)
if resurrectErr != nil {
    return "", fmt.Errorf("failed to resurrect parent %s: %w", parentID, resurrectErr)
}
```

## Files
- internal/storage/sqlite/hash_ids.go:38

## Priority
Low - code works correctly, just a style improvement


Add unit tests for config modes

Test all four orphan_handling modes: strict (fails), resurrect (creates tombstone), skip (logs warning), allow (imports orphan). Verify error messages and logging output for each mode.


Remove obsolete stale.go command (executor tables never implemented)


Add embedding generation for duplicate detection

Use embeddings for scalable duplicate detection.

Model: text-embedding-3-small (OpenAI) or all-MiniLM-L6-v2 (local)
Storage: SQLite vector extension or in-memory
Cost: ~/bin/bash.0002 per 100 issues

Much cheaper than LLM comparisons for large databases.

Files: internal/embeddings/ (new package)


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%


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


Add migration registry and simplify New()

Create migrations.go with Migration type and registry. Change New() to: openDB -> initSchema -> RunMigrations(db). This removes 8+ separate migrate functions from New().


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.


Add performance benchmarks for multi-repo hydration

The contributor-workflow-analysis.md asserts sub-second queries (line 702) and describes smart caching via file mtime tracking (Decision #4, lines 584-618), but doesn't provide concrete performance benchmarks.

VC's requirement (from VC feedback section):
- Executor polls GetReadyWork() every 5-10 seconds
- Queries must be sub-second (ideally <100ms)
- Smart caching must avoid re-parsing JSONLs on every query

Suggested performance targets to validate:
- File stat overhead: <1ms per repo
- Hydration (when needed): <500ms for typical JSONL (<25k)
- Query (from cache): <10ms
- Total GetReadyWork(): <100ms (VC's requirement)

Also test at scale:
- N=1 repo (baseline)
- N=3 repos (typical)
- N=10 repos (edge case)

These benchmarks are critical for library consumers like VC that run automated polling loops.


Auto-routing and maintainer detection

Implement intelligent routing to automatically send new issues to correct repo based on user's maintainer vs contributor status, with discovered issues inheriting parent's source_repo.


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 Epic 2

## Overview

[Describe the high-level goal and scope of this epic]

## Success Criteria

- [ ] Criteria 1
- [ ] Criteria 2
- [ ] Criteria 3

## Background

[Provide context and motivation]

## Scope

**In Scope:**
- Item 1
- Item 2

**Out of Scope:**
- Item 1
- Item 2


Phase 2: Implement sandbox auto-detection for GH #353

Implement automatic sandbox detection to improve UX for users in sandboxed environments (e.g., Codex).

**Tasks:**
1. Implement sandbox detection heuristic using syscall.Kill permission checks
2. Auto-enable --sandbox mode when sandbox is detected
3. Display informative message when sandbox is detected

**Implementation:**
- Add isSandboxed() function in cmd/bd/main.go
- Auto-set sandboxMode = true in PersistentPreRun when detected
- Show: 'ℹ  Sandbox detected, using direct mode'

**References:**
- docs/GH353_INVESTIGATION.md (Solution 3, lines 120-160)
- Depends on: Phase 1 (bd-???)

**Acceptance Criteria:**
- Codex users don't need to manually specify --sandbox
- No false positives in normal environments
- Clear messaging when auto-detection triggers


Integration Layer Implementation

Build the adapter layer that makes Agent Mail optional and non-intrusive.

Progress: bd-m9th (Python adapter library) completed with full test coverage. Next: bd-fzbg (update python-agent example with Agent Mail integration).


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


Fix GH #227: bd edit broken pipe errors

bd edit command gets "broken pipe" errors when using daemon mode because editing can take minutes and the daemon connection times out.

Solution: Force bd edit to always use direct mode (--no-daemon) since it's human-only and interactive.

Fixed by checking cmd.Name() == "edit" in main.go PersistentPreRun and setting noDaemon = true.


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

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

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

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

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

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

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


Delete skipped tests for "old buggy behavior"

Three test functions are permanently skipped with comments indicating they test behavior that was fixed in GH#120. These tests will never run again and should be deleted.

Test functions to remove:

1. `cmd/bd/import_collision_test.go:228`
   ```go
   t.Skip("Test expects old buggy behavior - needs rewrite for GH#120 fix")
   ```

2. `cmd/bd/import_collision_test.go:505`
   ```go
   t.Skip("Test expects old buggy behavior - needs rewrite for GH#120 fix")
   ```

3. `internal/storage/sqlite/collision_test.go:919`
   ```go
   t.Skip("Test expects old buggy behavior - needs rewrite for GH#120 fix")
   ```

Impact: Removes ~150 LOC of permanently skipped tests


Code review follow-up for bd-dvd and bd-ymj fixes

Track improvements and issues identified during code review of parent resurrection (bd-dvd) and export metadata (bd-ymj) bug fixes.

## Context
Code review identified several areas for improvement:
- Code duplication in metadata updates
- Missing multi-repo support
- Test coverage gaps
- Potential race conditions

## Related Issues
Original bugs fixed: bd-dvd, bd-ymj

## Goals
- Eliminate code duplication
- Add multi-repo support where needed
- Improve test coverage
- Address edge cases


Rapid 3


bd preflight: PR readiness checks for contributors

## Vision

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

## Problem Statement

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

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

## Why beads?

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

## Proposed Interface

### Tier 1: Checklist Mode (v1)

    $ bd preflight
    PR Readiness Checklist:

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

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

### Tier 2: Check Mode (v2)

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

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

### Tier 3: Fix Mode (v3)

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

## Checks to Implement

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

## Future: Configuration

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

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

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

## Implementation Phases

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

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

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

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

## Dependencies

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

## Success Metrics

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


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.


Fix Windows-specific test failures in CI

Several tests are failing on Windows but passing on Linux:

**Failing tests:**
- TestFindDatabasePathEnvVar
- TestHashIDs_MultiCloneConverge
- TestHashIDs_IdenticalContentDedup
- TestDatabaseReinitialization (all 5 subtests):
  - fresh_clone_auto_import
  - database_removal_scenario
  - legacy_filename_support
  - precedence_test
  - init_safety_check
- TestFindBeadsDir_NotFound
- TestMetricsSnapshot/uptime (in internal/rpc)

**CI Run:** https://github.com/steveyegge/beads/actions/runs/19015638968

These are likely path separator or filesystem behavior differences between Windows and Linux.

Fixed all Windows path issues:
1. TestFindDatabasePathEnvVar - expects canonicalized paths ✅
2. TestHashIDs tests - use platform-specific bd.exe command ✅
3. TestMetricsSnapshot/uptime - enforce minimum 1 second uptime ✅
4. TestFindBeadsDir_NotFound - allow finding .beads in parent dirs ✅
5. TestDatabaseReinitialization - fix git path conversion on Windows (git returns /c/Users/... but filepath needs C:\Users\...) ✅

CI run in progress to verify all fixes.


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.


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?


Switch from modernc.org/sqlite to ncruces/go-sqlite3 for WASM support

modernc.org/sqlite depends on modernc.org/libc which has no js/wasm support (platform-specific syscalls). Need to switch to ncruces/go-sqlite3 which wraps a WASM build of SQLite using wazero runtime.

Key differences:
- ncruces/go-sqlite3: Uses WASM build of SQLite + wazero runtime
- modernc.org/sqlite: Pure Go translation, requires libc for syscalls

This is a prerequisite for bd-62a0 (WASM build infrastructure).


Latency test


Manual task


Remove Daemon Storage Cache

The daemon's multi-repo storage cache is the root cause of stale data bugs. Since global daemon is deprecated, we only ever serve one repository, making the cache unnecessary complexity. This epic removes the cache entirely for simpler, more reliable direct storage access.


Add external_ref change tracking and auditing

Currently we don't track when external_ref is added, removed, or changed. This would be useful for debugging and auditing.

Proposed features:
- Log event when external_ref changes
- Track in events table with old/new values
- Add query to find issues where external_ref changed
- Add metrics: issues with external_ref vs without

Use cases:
- Debugging import issues
- Understanding which issues are externally managed
- Auditing external system linkage

Related: bd-1022


Multi-clone collision resolution testing and documentation

Epic to track improvements to multi-clone collision resolution based on ultrathinking analysis of-3d844c58 and [deleted:bd-71107098].

Current state:
- 2-clone collision resolution is SOUND and working correctly
- Hash-based deterministic collision resolution works
- Test fails due to timestamp comparison, not actual logic issues

Work needed:
1. Fix TestTwoCloneCollision to compare content not timestamps
2. Add TestThreeCloneCollision for regression protection
3. Document 3-clone ID non-determinism as known behavior


Add 'bd status' command for issue database overview

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

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

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

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

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


Add sync resilience guardrails for forced pushes and prefix mismatches

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


Fix validatePreExport to use content hash instead of mtime

validatePreExport() in integrity.go:70 still uses isJSONLNewer() (mtime-based), creating inconsistent behavior. Auto-import correctly uses hasJSONLChanged() (hash-based) but export validation still uses the old mtime approach. This can cause false positive blocks after git operations.

Fix: Replace isJSONLNewer() call with hasJSONLChanged() in validatePreExport().

Impact: Without this fix, the bd-khnb solution is incomplete - we prevent resurrection but still have export blocking issues.


Batch test 4


Registry has cross-process race condition

The global daemon registry (~/.beads/registry.json) can be corrupted when multiple daemons from different workspaces write simultaneously.

**Root cause:**
- Registry uses an in-process mutex but no file-level locking
- Register() and Unregister() release the mutex between read and write
- Multiple daemon processes can interleave their read-modify-write cycles

**Evidence:**
Found registry.json with double closing bracket: `]]` instead of `]`

**Fix options:**
1. Use file locking (flock/fcntl) around the entire read-modify-write cycle
2. Use atomic write pattern (write to temp file, rename)
3. Both (belt and suspenders)

**Files:**
- internal/daemon/registry.go:46-64 (readEntries)
- internal/daemon/registry.go:67-87 (writeEntries)
- internal/daemon/registry.go:90-108 (Register - the race window)


Fresh clone: bd should suggest 'bd init' when no database exists

On a fresh clone of a repo using beads, running `bd stats` or `bd list` gives a cryptic error:

```
Error: failed to open database: post-migration validation failed: migration invariants failed:
  - required_config_present: required config key missing: issue_prefix (database has 2 issues)
```

**Expected**: A helpful message like:
```
No database found. This appears to be a fresh clone.
Run 'bd init --prefix <prefix>' to hydrate from the committed JSONL file.
Found: .beads/beads.jsonl (38 issues)
```

**Why this matters**: The current UX is confusing for new contributors or fresh clones. The happy path should be obvious.


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)


Test FK constraint via close


TestHooksCheckGitHooks failing - version mismatch (0.23.0 vs 0.23.1)

The test is checking embedded hook versions and expecting 0.23.1, but got 0.23.0. This appears to be a version consistency issue that needs investigation.

Test output:
```
Hook pre-commit version mismatch: got 0.23.0, want 0.23.1
Hook post-merge version mismatch: got 0.23.0, want 0.23.1
Hook pre-push version mismatch: got 0.23.0, want 0.23.1
```

This is blocking the landing of GH #274 fix.


Client self-heal: remove stale pid when lock free + socket missing

When client detects:
- Socket is missing AND
- tryDaemonLock shows lock NOT held

Then automatically:
1. Remove stale daemon.pid file
2. Optionally auto-start daemon (behind BEADS_AUTO_START_DAEMON=1 env var)

This prevents stale artifacts from accumulating after daemon crashes.


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


Latency test manual


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 extracted modules

Create tests for migrations.go, hash_ids.go, batch_ops.go, and validators.go


YABB: Spurious issue updates during normal operations

Issue bd-627d was updated during config refactoring session without any actual changes to it. Only timestamps and content_hash changed.

Observed: Running various bd commands (list, create, etc.) caused bd-627d updated_at to change from 14:14 to 14:31.

Expected: Issues should only be updated when explicitly modified.

This causes:
- Dirty JSONL after every session
- False conflicts in git
- Confusing git history

Likely culprit: Daemon auto-import/export cycle or database migration touching all issues.

Investigated thoroughly - unable to reproduce. The import logic has IssueDataChanged() checks before calling UpdateIssue (importer/importer.go:458). All tests pass. May have been fixed by recent refactorings. Closing as cannot reproduce - please reopen with specific repro steps if it occurs again.


Batch test 2


Run final validation and cleanup checks

Final validation pass to ensure all cleanup objectives met and no regressions introduced.

Validation checklist:
1. Dead code verification: `go run golang.org/x/tools/cmd/deadcode@latest -test ./...`
2. Test coverage: `go test -cover ./...`
3. Build verification: `go build ./cmd/bd/`
4. Linting: `golangci-lint run`
5. Integration tests
6. Metrics verification
7. Git clean check

Final metrics to report:
- LOC removed: ~____
- Files deleted: ____
- Files created: ____
- Test coverage: ____%
- Build time: ____ (before/after)
- Test run time: ____ (before/after)

Impact: Confirms all cleanup objectives achieved successfully

## Validation Results (Oct 31, 2025)

**Dead Code:** ✅ Removed 5 unreachable functions (~200 LOC)
- computeIssueContentHash, shouldSkipExport (autoflush.go)
- addDependencyUnchecked, removeDependencyIfExists (dependencies.go)
- isUniqueConstraintError (util.go)

**Tests:** ✅ All pass
**Coverage:**
- Main package: 39.6%
- cmd/bd: 19.5%
- internal/daemon: 37.8%
- internal/storage/sqlite: 58.1%
- internal/rpc: 58.6%

**Build:** ✅ Clean (24.5 MB binary)
**Linting:** 247 issues (mostly errcheck on defer/Close statements)
**Integration Tests:** ✅ All pass
**Metrics:** 55,622 LOC across 200 Go files
**Git:** 3 files modified (dead code removal)


Hash-based IDs with aliasing system

Replace sequential auto-increment IDs (bd-1c63eb84, bd-9063acda) with content-hash based IDs (bd-af78e9a2) plus human-friendly aliases (#1, #2).

## Motivation
Current sequential IDs cause collision problems when multiple clones work offline:
- Non-deterministic convergence in N-way scenarios (bd-cbed9619.1, bd-e6d71828)
- Complex collision resolution logic (~2,100 LOC)
- UNIQUE constraint violations during import
- Requires coordination between workers

Hash-based IDs eliminate collisions entirely while aliases preserve human readability.

## Benefits
- ✅ Collision-free distributed ID generation
- ✅ Eliminates ~2,100 LOC of collision handling code
- ✅ Better git merge behavior (different IDs = different JSONL lines)
- ✅ True offline-first workflows
- ✅ Simpler auto-import (no remapping needed)
- ✅ Enables parallel CI/CD workers without coordination

## Design
- Canonical ID: bd-af78e9a2 (8-char SHA256 prefix of title+desc+timestamp+creator)
- Alias: #42 (auto-increment per workspace, mutable, display-only)
- CLI accepts both: bd show bd-af78e9a2 OR bd show #42
- JSONL stores hash IDs only (aliases reconstructed on import)
- Alias conflicts resolved via content-hash ordering (deterministic)

## Breaking Change
This is a v2.0 feature requiring migration. Provide bd migrate --hash-ids tool.

## Timeline
~9 weeks (Phase 1: Hash IDs 4w, Phase 2: Aliases 3w, Phase 3: Testing 2w)

## Dependencies
Should complete after bd-7c5915ae (cleanup validation) and before bd-710a4916 (CRDT).


Debug test 2


Cache getMultiRepoJSONLPaths() result during sync to avoid redundant calls

From bd-xo6b code review: getMultiRepoJSONLPaths() is called 3x per sync cycle.

**Current behavior:**
daemon_sync.go calls getMultiRepoJSONLPaths() three times per sync:
- Line 505: Snapshot capture before pull
- Line 575: Merge/prune after pull
- Line 613: Base snapshot update after import

**Cost per call:**
- Config lookup (likely cached, but still overhead)
- Path construction: O(N) where N = number of repos
- String allocations: (N + 1) × filepath.Join() calls

**Total per sync:** 3N path constructions + 3 config lookups + 3 slice allocations

**Impact:**
- For N=3 repos: Negligible (< 1ms)
- For N=10 repos: Still minimal
- For N=100+ repos: Wasteful

**Solution:**
Call once at sync start, reuse result:

```go
func createSyncFunc(...) func() {
    return func() {
        // ... existing setup ...

        // Call once at start
        multiRepoPaths := getMultiRepoJSONLPaths()

        // Snapshot capture
        if multiRepoPaths != nil {
            for _, path := range multiRepoPaths {
                if err := captureLeftSnapshot(path); err != nil { ... }
            }
        }

        // ... later ...

        // Merge/prune - reuse same paths
        if multiRepoPaths != nil {
            for _, path := range multiRepoPaths { ... }
        }

        // ... later ...

        // Base snapshot update - reuse same paths
        if multiRepoPaths != nil {
            for _, path := range multiRepoPaths { ... }
        }
    }
}
```

**Files:**
- cmd/bd/daemon_sync.go:449-636 (createSyncFunc)

**Note:** This is a performance optimization, not a correctness fix. Low priority unless multi-repo usage scales significantly.


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 tests for bd init --team wizard

Write integration tests for the team wizard:
- Test branch detection
- Test sync branch creation
- Test protected branch workflow
- Test auto-sync configuration


Implement postinstall script for binary download

Create npm/scripts/postinstall.js that downloads platform-specific binaries:

## Platform detection
- Detect os.platform() and os.arch()
- Map to GitHub release asset names:
  - linux-amd64 → bd-linux-amd64
  - linux-arm64 → bd-linux-arm64
  - darwin-amd64 → bd-darwin-amd64
  - darwin-arm64 → bd-darwin-arm64
  - win32-x64 → bd-windows-amd64.exe

## Download logic
- Fetch from GitHub releases: https://github.com/steveyegge/beads/releases/latest/download/${asset}
- Save to npm/bin/bd (or bd.exe on Windows)
- Set executable permissions (chmod +x)
- Handle errors gracefully with helpful messages

## Error handling
- Check for unsupported platforms
- Retry on network failures
- Provide manual download instructions if automated fails
- Skip download if binary already exists (for local development)


Export deduplication breaks when JSONL and export_hashes table diverge

## Problem

The export deduplication feature (timestamp-only skipping) breaks when the JSONL file and export_hashes table get out of sync, causing exports to skip issues that aren't actually in the file.

## Symptoms

- `bd export` reports "Skipped 128 issue(s) with timestamp-only changes"
- JSONL file only has 38 lines but DB has 149 issues
- export_hashes table has 149 entries
- Auto-import doesn't trigger (hash matches despite missing data)
- Two repos on same commit show different issue counts

## Root Cause

shouldSkipExport() in autoflush.go compares current issue hash with stored export_hashes entry. If they match, it skips export assuming the issue is already in the JSONL.

This assumption fails when:
1. Git operations (pull, reset, checkout) change JSONL without clearing export_hashes
2. Manual JSONL edits or corruption
3. Import operations that modify DB but don't update export_hashes
4. Partial exports that update export_hashes but don't complete

## Impact

- **Critical data loss risk**: Issues appear to be tracked but aren't persisted to git
- Breaks multi-repo sync (root cause of today's debugging session)
- Auto-import fails to detect staleness (hash matches despite missing data)
- Silent data corruption (no error messages, just missing issues)

## Reproduction

1. Have DB with 149 issues, all in export_hashes table
2. Truncate JSONL to 38 lines (simulate git reset or corruption)
3. Run `bd export` - it skips 128 issues
4. JSONL still has only 38 lines but export thinks it succeeded

## Current Workaround

```bash
sqlite3 .beads/beads.db "DELETE FROM export_hashes"
bd export -o .beads/beads.jsonl
```

## Proposed Solutions

**Option 1: Verify JSONL integrity before skipping**
- Count lines in JSONL, compare with export_hashes count
- If mismatch, clear export_hashes and force full export
- Safe but adds I/O overhead

**Option 2: Hash-based JSONL validation**
- Store hash of entire JSONL file in metadata
- Before export, check if JSONL hash matches
- If mismatch, clear export_hashes
- More efficient, detects any JSONL corruption

**Option 3: Disable timestamp-only deduplication**
- Remove the feature entirely
- Always export all issues
- Simplest and safest, but creates larger git commits

**Option 4: Clear export_hashes on git operations**
- Add post-merge hook to clear export_hashes
- Clear on any import operation
- Defensive approach but may over-clear

## Recommended Fix

Combination of Options 2 + 4:
1. Store JSONL file hash in metadata after export
2. Check hash before export, clear export_hashes if mismatch
3. Clear export_hashes on import operations
4. Add `bd validate` check for JSONL/export_hashes sync

## Files Involved

- cmd/bd/autoflush.go (shouldSkipExport)
- cmd/bd/export.go (export with deduplication)
- internal/storage/sqlite/metadata.go (export_hashes table)

## Recovery Session (2025-10-29 21:30)

### What Happened
- Created 14 new hash ID issues (bd-f8b764c9 through bd-f8b764c9.1)
- bd sync appeared to succeed
- Canonical repo (~/src/beads): 162 issues in DB + JSONL ✓
- Secondary repo (fred/beads): Only 145 issues vs 162 in canonical ✗
- Both repos on same git commit but different issue counts!

### Bug Manifestation During Recovery

1. **Initial state**: fred/beads had 145 issues, 145 lines in JSONL, 145 export_hashes entries

2. **After git reset --hard origin/main**:
   - JSONL: 162 lines (from git)
   - DB: 150 issues (auto-import partially worked)
   - Auto-import failed with UNIQUE constraint error

3. **After manual import --resolve-collisions**:
   - DB: 160 issues
   - JSONL: Still 162 lines
   - export_hashes: 159 entries

4. **After bd export**:
   - **JSONL reduced to 17 lines!** ← The bug in action
   - export_hashes: 159 entries (skipped exporting 142 issues)
   - Silent data loss - no error message

5. **After clearing export_hashes and re-export**:
   - JSONL: 159 lines (missing 3 issues still)
   - DB: 159 issues
   - Still diverged from canonical

### The Bug Loop
Once export_hashes and JSONL diverge:
- Export skips issues already in export_hashes
- But those issues aren't actually in JSONL
- This creates corrupt JSONL with missing issues
- Auto-import can't detect the problem (file hash matches what was exported)
- Data is lost with no error messages

### Recovery Solution
Couldn't break the loop with export alone. Had to:
1. Copy .beads/beads.db from canonical repo
2. Clear export_hashes
3. Full re-export
4. Finally converged to 162 issues

### Key Learnings

1. **The bug is worse than we thought**: It can create corrupt exports (17 lines instead of 162!)

2. **Auto-import can't save you**: Once export is corrupt, auto-import just imports the corrupt data

3. **Silent failure**: No warnings, no errors, just missing issues

4. **Git operations trigger it**: git reset, git pull, etc. change JSONL without clearing export_hashes

5. **Import operations populate export_hashes**: Even manual imports update export_hashes, setting up future export failures

### Immediate Action Required

**DISABLE EXPORT DEDUPLICATION NOW**

This feature is fundamentally broken and causes data loss. Should be disabled until properly fixed.

Quick fix options:
- Set environment variable to disable feature
- Comment out shouldSkipExport check
- Always clear export_hashes before export
- Add validation that DB count == JSONL line count before allowing export

### Long-term Fix

Need Option 2 + 4 from proposed solutions:
1. Store JSONL file hash after every successful export
2. Before export, verify JSONL hash matches expected
3. If mismatch, log WARNING and clear export_hashes
4. Clear export_hashes on every import operation
5. Add git post-merge hook to clear export_hashes
6. Add `bd validate` command to detect divergence


Add import.orphan_handling config option

Add configuration option to control orphan handling behavior: 'strict' (fail on missing parent, current behavior), 'resurrect' (auto-resurrect from JSONL, recommended default), 'skip' (skip orphaned issues with warning), 'allow' (import orphans without validation). Update CONFIG.md documentation.


Debug MCP Agent Mail tool execution errors

**EXTERNAL WORK**: Debug the standalone MCP Agent Mail server (separate from beads integration).

The Agent Mail server runs as an independent service at ~/src/mcp_agent_mail. This is NOT beads code - it's a separate GitHub project we're evaluating for optional coordination features.

Current Issue:
- MCP API endpoint returns errors when calling ensure_project tool
- Error: "Server encountered an unexpected error while executing tool"
- Core HTTP server works, web UI functional, but tool wrapper layer fails

Server Details:
- Location: ~/src/mcp_agent_mail (separate repo)
- Repository: https://github.com/Dicklesworthstone/mcp_agent_mail
- Runs on: http://127.0.0.1:8765
- Bearer token: In .env file

Investigation Steps:
1. Check tool execution logs for full stack trace
2. Verify Git storage initialization at ~/.mcp_agent_mail_git_mailbox_repo
3. Review database setup (storage.sqlite3)
4. Test with simpler MCP tools if available
5. Compare with working test cases in tests/

Why This Matters:
- Blocks [deleted:bd-6hji] (testing file reservations)
- Need working MCP API to validate Agent Mail benefits
- Proof of concept for lightweight beads integration later

Note: The actual beads integration (bd-wfmw) will be lightweight HTTP client code only.


Export doesn't update last_import_hash metadata causing perpetual 'JSONL content has changed' errors

After a successful export, the daemon doesn't update last_import_hash or last_import_mtime metadata. This causes hasJSONLChanged to return true on the next export attempt, blocking with 'refusing to export: JSONL content has changed since last import'.

Related to GH #334.

Scenario:
1. Daemon exports successfully → JSONL updated, hash changes
2. Metadata NOT updated (last_import_hash still points to old hash)
3. Next mutation triggers export
4. validatePreExport calls hasJSONLChanged
5. hasJSONLChanged sees current JSONL hash != last_import_hash
6. Export blocked with 'JSONL content has changed since last import'
7. Issue stuck: can't export, can't make progress

This creates a catch-22 where the system thinks JSONL changed externally when it was the daemon itself that changed it.

Current behavior:
- Import updates metadata (import.go:310-336)
- Export does NOT update metadata (daemon_sync.go:307)
- Result: metadata becomes stale after every export


Daemons don't exit when parent process dies, causing accumulation and race conditions

Multiple daemon processes accumulate over time because daemons don't automatically stop when their parent process (e.g., coding agent) is killed. This causes:

1. Race conditions: 8+ daemons watching same .beads/beads.db, each with own 30s debounce timer
2. Git conflicts: Multiple daemons racing to commit/push .beads/issues.jsonl
3. Resource waste: Orphaned daemons from sessions days/hours old still running

Example: User had 8 daemons from multiple sessions (12:37AM, 7:20PM, 7:22PM, 7:47PM, 9:19PM yesterday + 9:54AM, 10:55AM today).

Solutions to consider:
1. Track parent PID and exit when parent dies
2. Use single global daemon instead of per-session
3. Document manual cleanup: pkill -f "bd daemon"
4. Add daemon lifecycle management (auto-cleanup of stale daemons)

Implementation complete:

1. Added ParentPID field to DaemonLockInfo struct (stored in daemon.lock JSON)
2. Daemon now tracks parent PID via os.Getppid() at startup
3. Both event loops (polling and event-driven) check parent process every 10 seconds
4. Daemon gracefully exits if parent process dies (detected via isProcessRunning check)
5. Handles edge cases:
   - ParentPID=0: Older daemons without tracking (ignored)
   - ParentPID=1: Adopted by init means parent died (exits)
   - Otherwise checks if parent process is still running

The fix prevents daemon accumulation by ensuring orphaned daemons automatically exit within 10 seconds of parent death.


Add fsnotify dependency to go.mod


Extract SQLite migrations into separate files

The file `internal/storage/sqlite/sqlite.go` is 2,136 lines and contains 11 sequential migrations alongside core storage logic. Extract migrations into a versioned system.

Current issues:
- 11 migration functions mixed with core logic
- Hard to see migration history
- Sequential migrations slow database open
- No clear migration versioning

Migration functions to extract:
- `migrateDirtyIssuesTable()`
- `migrateIssueCountersTable()`
- `migrateExternalRefColumn()`
- `migrateCompositeIndexes()`
- `migrateClosedAtConstraint()`
- `migrateCompactionColumns()`
- `migrateSnapshotsTable()`
- `migrateCompactionConfig()`
- `migrateCompactedAtCommitColumn()`
- `migrateExportHashesTable()`
- Plus 1 more (11 total)

Target structure:
```
internal/storage/sqlite/
├── sqlite.go          # Core storage (~800 lines)
├── schema.go          # Table definitions (~200 lines)
├── migrations.go      # Migration orchestration (~200 lines)
└── migrations/        # Individual migrations
    ├── 001_initial_schema.go
    ├── 002_dirty_issues.go
    ├── 003_issue_counters.go
    [... through 011_export_hashes.go]
```

Benefits:
- Clear migration history
- Each migration self-contained
- Easier to review migration changes in PRs
- Future migrations easier to add


bd prime: Detect ephemeral branches and adjust workflow output

When 'bd prime' runs on a branch with no upstream (ephemeral branch), it should output a different SESSION CLOSE PROTOCOL.

**Current output (wrong for ephemeral branches):**
```
[ ] 1. git status
[ ] 2. git add <files>
[ ] 3. bd sync
[ ] 4. git commit -m "..."
[ ] 5. bd sync
[ ] 6. git push
```

**Needed output for ephemeral branches:**
```
[ ] 1. git status
[ ] 2. git add <files>
[ ] 3. bd sync --from-main  (pull updates from main)
[ ] 4. git commit -m "..."
[ ] 5. (no push - branch is ephemeral)
```

**Detection:** `git rev-parse --abbrev-ref --symbolic-full-name @{u}` returns error code 128 if no upstream.

Also update Sync & Collaboration section to mention `bd sync --from-main` for ephemeral branches.

**Use case:** Gastown polecats work on ephemeral local branches that are never pushed. Their code gets merged to main via local merge, and beads changes stay local (communicated via gm mail to Overseer).


Implement clone-scoped ID allocation to prevent N-way collisions

## Problem
Current ID allocation uses per-clone atomic counters (issue_counters table) that sync based on local database state. In N-way collision scenarios:
- Clone B sees {test-1} locally, allocates test-2
- Clone D sees {test-1, test-2, test-3} locally, allocates test-4
- When same content gets assigned test-2 and test-4, convergence fails

Root cause: Each clone independently allocates IDs without global coordination, leading to overlapping assignments for the same content.

## Solution
Add clone UUID to ID allocation to make every ID globally unique:

**Current format:** `test-1`, `test-2`, `test-3`
**New format:** `test-1-a7b3`, `test-2-a7b3`, `test-3-c4d9`

Where suffix is first 4 chars of clone UUID.

## Implementation

### 1. Add clone_identity table
```sql
CREATE TABLE clone_identity (
    clone_uuid TEXT PRIMARY KEY,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
```

### 2. Modify getNextIDForPrefix()
```go
func (s *SQLiteStorage) getNextIDForPrefix(ctx context.Context, prefix string) (string, error) {
    cloneUUID := s.getOrCreateCloneUUID(ctx)
    shortUUID := cloneUUID[:4]

    nextNum := s.getNextCounterForPrefix(ctx, prefix)
    return fmt.Sprintf("%s-%d-%s", prefix, nextNum, shortUUID), nil
}
```

### 3. Update ID parsing logic
All places that parse IDs (utils.ExtractIssueNumber, etc.) need to handle new format.

### 4. Migration strategy
- Existing IDs remain unchanged (no suffix)
- New IDs get clone suffix automatically
- Display layer can hide suffix in UI: `bd-cb64c226.3-a7b3` → `#42`

## Benefits
- **Zero collision risk**: Same content in different clones gets different IDs
- **Maintains readability**: Still sequential numbering within clone
- **No coordination needed**: Works offline, no central authority
- **Scales to 100+ clones**: 4-char hex = 65,536 unique clones

## Concerns
- ID format change may break existing integrations
- Need migration path for existing databases
- Display logic needs update to hide/show suffixes appropriately

## Success Criteria
- 10+ clone collision test passes without failures
- Existing issues continue to work (backward compatibility)
- Documentation updated with new ID format
- Migration guide for v1.x → v2.x

## Timeline
Medium-term (v1.1-v1.2), 2-3 weeks implementation

## References
- Related to bd-e6d71828 (immediate fix)
- See beads_nway_test.go for failing N-way tests


Doctor should exclude merge artifacts from 'multiple JSONL' warning

Doctor command warns about 'multiple JSONL files' when .base.jsonl and .left.jsonl merge artifacts exist. These are expected during/after merge operations and should be excluded from the warning.


cmd/bd tests fail without -short flag (parallel test deadlock)


Extract normalizeLabels to shared utility package

The `normalizeLabels` function appears in multiple locations with identical implementation. Extract to a shared utility package.

Current locations:
- `internal/rpc/server.go:37` (53 lines) - full implementation
- `cmd/bd/list.go:50-52` - uses the server version (needs to use new shared version)

Function purpose:
- Trims whitespace from labels
- Removes empty strings
- Deduplicates labels
- Preserves order

Target structure:
```
internal/util/
├── strings.go         # String utilities
    └── NormalizeLabels([]string) []string
```

Impact: DRY principle, single source of truth, easier to test


bd doctor should recommend bd prime migration for existing repos

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

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

## Desired behavior
bd doctor should detect and suggest:

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

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

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

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

## Example output

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

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

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


Rapid 4


Issue to close


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


Test parent issue


Create npm package structure with package.json

Set up initial npm package structure for @beads/bd:

## Files to create
- npm/package.json - Package metadata, dependencies, scripts
- npm/bin/bd - CLI wrapper script that invokes native binary
- npm/.gitignore - Ignore downloaded binaries
- npm/README.md - Installation and usage instructions

## package.json structure
- Name: @beads/bd (scoped package)
- Main: index.js (exports binary path)
- Bin: bin/bd (CLI entry point)
- Scripts: postinstall (download binary)
- Keywords: issue-tracker, cli, beads, bd
- License: MIT

## Bin wrapper
Simple Node.js script that:
- Spawns native binary with child_process.spawn
- Passes through all arguments and stdio
- Exits with binary's exit code


Benchmark Suite for Critical Operations

Extend existing benchmark suite with comprehensive benchmarks for critical operations at 10K-20K scale.

Existing benchmarks (keep these):
- cycle_bench_test.go - Cycle detection up to 5K issues (linear, tree, dense graphs)
- compact_bench_test.go - Compaction candidate queries (100 issues)
- internal/rpc/bench_test.go - Daemon vs direct mode comparison

New benchmarks to add in sqlite_bench_test.go (~10-12 total):
1. GetReadyWork - Simple, deep hierarchies, cross-linked (CRITICAL - not currently benchmarked)
2. SearchIssues - No filters, complex filters (CRITICAL - not currently benchmarked)
3. CreateIssue - Single issue creation
4. UpdateIssue - Status/priority/assignee changes
5. AddDependency - Extend to 10K/20K scale (currently only up to 5K)
6. JSONL Export - Full export performance
7. JSONL Import - Import performance

Scale levels:
- Large: 10K issues (5K open, 5K closed)
- XLarge: 20K issues (10K open, 10K closed)

Implementation:
- NEW FILE: internal/storage/sqlite/sqlite_bench_test.go
- Keep existing cycle_bench_test.go and compact_bench_test.go unchanged
- Build tag: //go:build bench
- Standard testing.B benchmarks
- b.ReportAllocs() for memory tracking
- Test both SQLite and JSONL-imported databases

Always generates CPU and memory profiles for analysis.


Set up WASM build pipeline

Configure Go→WASM compilation pipeline. Child of epic bd-44d0.

## Tasks
- [ ] Create build-wasm.sh script (GOOS=js GOARCH=wasm)
- [ ] Test basic WASM module loading in Node.js
- [ ] Set up wasm_exec.js wrapper
- [ ] Add WASM build to CI/CD
- [ ] Document build process

## Validation
- bd.wasm compiles successfully
- Can load in Node.js without errors
- Bundle size <10MB


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.


Improve cmd/bd test coverage from 21% to 40% (multi-session effort)

Current coverage: 21.0% of statements in cmd/bd
Target: 40%
This is a multi-session incremental effort.

Focus areas:
- Command handler tests (create, update, close, list, etc.)
- Flag validation and error cases
- JSON output formatting
- Edge cases and error handling

Track progress with 'go test -cover ./cmd/bd'

Coverage improved from 21% to 27.4% (package) and 42.9% (total function coverage).

Added tests for:
- compact.go test coverage (eligibility checks, dry run scenarios)
- epic.go test coverage (epic status, children tracking, eligibility for closure)

New test files created:
- epic_test.go (3 test functions covering epic functionality)

Enhanced compact_test.go:
- TestRunCompactSingleDryRun
- TestRunCompactAllDryRun

Total function coverage now at 42.9%, exceeding the 40% target.


Batch test 5


Refactor path canonicalization into helper function

The path canonicalization logic (filepath.Abs + EvalSymlinks) is duplicated in 3 places:
- beads.go:131-137 (BEADS_DIR handling)
- cmd/bd/main.go:446-451 (--no-db cleanup)
- cmd/bd/nodb.go:26-31 (--no-db initialization)

Refactoring suggestion:
Extract to a helper function like:
  func canonicalizePath(path string) string

This would:
- Reduce code duplication
- Make the logic easier to maintain
- Ensure consistent behavior across all path handling

Related to bd-e16b implementation.


Replace getStorageForRequest with Direct Access

Replace all getStorageForRequest(req) calls with s.storage


Daemon fails to auto-import after git pull updates JSONL

After git pull updates .beads/issues.jsonl, daemon doesn't automatically re-import changes, causing stale data to be shown until next sync cycle (up to 5 minutes).

Reproduction:
1. Repo A: Close issues, export, commit, push
2. Repo B: git pull (successfully updates .beads/issues.jsonl)
3. bd show <issue> shows OLD status from daemon's SQLite db
4. JSONL on disk has correct new status

Root cause: Daemon sync cycle runs on timer (5min). When user manually runs git pull, daemon doesn't detect JSONL was updated externally and continues serving stale data from SQLite.

Impact:
- High for AI agents using beads in git workflows
- Breaks fundamental git-as-source-of-truth model
- Confusing UX: git log shows commit, bd shows old state
- Data consistency issues between JSONL and daemon

See WYVERN_SYNC_ISSUE.md for full analysis.

**Fixed in v0.21.2!**

The daemon auto-import is fully implemented:
- internal/autoimport package handles staleness detection
- internal/importer package provides shared import logic (used by both CLI and daemon)
- daemon's checkAndAutoImportIfStale() calls autoimport.AutoImportIfNewer()
- importFunc uses importer.ImportIssues() with auto-rename enabled
- All tests passing

The critical data corruption bug is FIXED:
✅ After git pull, daemon detects JSONL is newer (mtime check)
✅ Daemon auto-imports before serving requests
✅ No stale data served
✅ No data loss in multi-agent workflows

Verification needed: Run two-repo test to confirm end-to-end behavior.


Add orphan detection migration

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


Auto-import runs during --no-auto-import operations via stats/ready commands

Even when using --no-auto-import flag, certain commands like 'bd stats' and 'bd ready' still trigger auto-import internally, which can cause the git history backfill bug to corrupt data.

Example:
  bd stats --no-auto-import
  # Still prints 'Purged bd-xxx (recovered from git history...)'

The flag should completely disable auto-import for the command, but it appears some code paths still trigger it.

Workaround: Use --allow-stale instead, or --sandbox mode.


Add label-based filtering to bd ready command

Allow filtering ready work by labels to help organize work by sprint, week, or category.

Example usage:
  bd ready --label week1-2
  bd ready --label frontend,high-priority

This helps teams organize work into batches and makes it easier for agents to focus on specific categories of work.

Implementation notes:
- Add --label flag to ready command
- Support comma-separated labels (AND logic)
- Should work with existing ready work logic (unblocked issues)


Daemon crashes silently on RPC query after startup

The daemon fails to handle 'show' RPC commands when:
1) JSONL is newer than database (needs import)
2) git pull fails due to uncommitted changes

Symptoms:
- Daemon appears to run (ps shows process)
- 'bd list' and other commands work fine
- 'bd show <id>' returns "failed to read response: EOF"
- No panic or error logged in daemon.log

Root cause likely: auto-import deadlock or state corruption when import is blocked by git conflicts.

Workaround:
- Restart daemon after syncing git state (commit/push changes)
- OR use --no-daemon flag for all commands

The panic recovery added in server_lifecycle_conn.go:183 didn't catch any panics, confirming this isn't a panic-based crash.

Root cause found and fixed: Two bugs - (1) nil pointer check missing in handleShow causing panic, (2) double JSON encoding in show.go ID resolution. Both fixed. bd show now works with daemon.


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

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

Current coverage: 28.4%
Target coverage: 50%


bd init creates nested .beads directories

bd init sometimes creates .beads/.beads/ nested directories, which should never happen. This occurs fairly often and can cause confusion about which .beads directory is active. Need to add validation to detect if already inside a .beads directory and either error or use the parent .beads location.


Change default JSONL filename from beads.jsonl back to issues.jsonl throughout codebase


Improve compact/daemon/merge documentation and UX

Multiple documentation and UX issues encountered:
1. "bd compact --analyze" fails with misleading "requires SQLite storage" error when daemon is running. Needs --no-daemon or better error.
2. "bd merge" help text is outdated (refers to 3-way merge instead of issue merging).
3. Daemon mode purpose isn't clear to local-only users.
4. Compact/cleanup commands are hard to discover.

Proposed fixes:
- Fix compact+daemon interaction or error message.
- Update "bd merge" help text.
- Add "when to use daemon" section to docs.
- Add maintenance section to quickstart.


Import fails on same-content-different-ID instead of treating as update

## Problem

The importer still has rename detection (importer.go:482-500) that triggers when same content hash has different IDs. With hash IDs, this shouldn't happen, but when it does (test data, bugs, legacy data), the import fails:

```
failed to handle rename bd-ce75 -> bd-5a90: rename collision handling removed - should not occur with hash IDs
```

## Current Behavior

1. Importer finds same content hash with different IDs
2. Calls handleRename() (line 490)
3. handleRename() errors out (line 294): "rename collision handling removed"
4. Import fails

## Expected Behavior

Same content hash + different IDs should be treated as an **update**, not a rename:
- Keep existing ID (already in database)
- Update fields if incoming has newer timestamp
- Discard incoming ID (it's wrong - hash should have generated same ID)

## Impact

- Import fails on legitimate edge cases (test data, data corruption)
- Cryptic error message
- Blocks sync operations

## Fix

In handleRename() or import loop, instead of erroring:
```go
// Same content, different ID - treat as update
if incoming.UpdatedAt.After(existing.UpdatedAt) {
    existing.Status = incoming.Status
    // ... copy other fields
    s.UpdateIssue(ctx, existing)
}
result.Updated++
```

## Files
- internal/importer/importer.go:271-294 (handleRename)
- internal/importer/importer.go:482-500 (rename detection)

## Repro
Import JSONL with bd-ce75 and bd-5a90 (both "Test parent issue" but different content hashes).


Refactor AGENTS.md to use external references

Suggestion to use external references (e.g., "ALWAYS REFER TO ./beads/prompt.md") instead of including all instructions directly within AGENTS.md.
Reasons:
1. Agents can follow external references.
2. Prevents context pollution/stuffing in AGENTS.md as more tools append instructions.


bd show --json doesn't include dependency type field

Fix GitHub issue #202. The JSON output from bd show and bd list commands should include the dependency type field (and optionally created_at, created_by) to match internal storage format and enable better tooling integration.

PR #203 updated with cleaner implementation: https://github.com/steveyegge/beads/pull/203

## Final Implementation

Cleanest possible approach - no internal helper methods needed:

**Design:**
- `GetDependenciesWithMetadata()` / `GetDependentsWithMetadata()` - canonical implementations with full SQL query
- `GetDependencies()` / `GetDependents()` - thin wrappers that strip metadata for backward compat
- `scanIssuesWithDependencyType()` - shared helper for scanning rows with dependency type

**Benefits:**
- Single source of truth - the `...WithMetadata()` methods ARE the implementation
- Eliminated ~139 lines of duplicated SQL and scanning code
- All tests passing (14 dependency-related tests)
- Backward compatible
- dependency_type field appears correctly in JSON output

**Note on scan helpers:**
The duplication between `scanIssues()` and `scanIssuesWithDependencyType()` is necessary because they handle different SQL result shapes (16 vs 17 columns). This is justified as they serve fundamentally different purposes based on query structure.


bd resolve-conflicts - Git merge conflict resolver

Automatically resolve JSONL merge conflicts.

Modes:
- Mechanical: ID remapping (no AI)
- AI-assisted: Smart merge/keep decisions
- Interactive: Review each conflict

Handles <<<<<<< conflict markers in .beads/beads.jsonl

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


Proposal: Custom status states via config

Proposal to add 'custom status states' via `bd config`.
Users could define an optional issue status enum (e.g., awaiting_review, review_in_progress) in the config.
This would enable multi-step pipelines to process issues where each step correlates to a specific status.

Examples:
- awaiting_verification
- awaiting_docs
- awaiting_testing


bd doctor should check for multiple DBs, multiple JSONLs, daemon health


sync tries to create worktree in .git file

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


Fix auto-import creating duplicates instead of updating issues

ROOT CAUSE: server_export_import_auto.go line 221 uses ResolveCollisions: true for ALL auto-imports. This is wrong.

Problem:
- ResolveCollisions is for branch merges (different issues with same ID)
- Auto-import should UPDATE existing issues, not create duplicates
- Every git pull creates NEW duplicate issues with different IDs
- Two agents ping-pong creating endless duplicates

Evidence:
- 31 duplicate groups found (bd duplicates)
- bd-236-246 are duplicates of bd-224-235
- Both agents keep pulling and creating more duplicates
- JSONL file grows endlessly with duplicates

The Fix:
Change checkAndAutoImportIfStale in server_export_import_auto.go:
- Remove ResolveCollisions: true (line 221)
- Use normal import logic that updates existing issues by ID
- Only use ResolveCollisions for explicit bd import --resolve-collisions

Impact: Critical - makes beads unusable for multi-agent workflows


CRDT-based architecture for guaranteed convergence (v2.0)

## Vision
Redesign beads around Conflict-Free Replicated Data Types (CRDTs) to provide mathematical guarantees for N-way collision resolution at arbitrary scale.

## Current Limitations
- Content-hash based collision resolution fails at 5+ clones
- Non-deterministic convergence in multi-round scenarios
- UNIQUE constraint violations during rename operations
- No formal proof of convergence properties

## CRDT Benefits
- Provably convergent (Strong Eventual Consistency)
- Commutative/Associative/Idempotent operations
- No coordination required between clones
- Scales to 100+ concurrent workers
- Well-understood mathematical foundations

## Proposed Architecture

### 1. UUID-Based IDs
Replace sequential IDs with UUIDs:
- Current: bd-1c63eb84, bd-9063acda, bd-4d80b7b1
- CRDT: bd-a1b2c3d4-e5f6-7890-abcd-ef1234567890
- Human aliases maintained separately: #42 maps to UUID

### 2. Last-Write-Wins (LWW) Elements
Each field becomes an LWW register:
- title: (timestamp, clone_id, value)
- status: (timestamp, clone_id, value)
- Deterministic conflict resolution via Lamport timestamp + clone_id tiebreaker

### 3. Operation Log
Track all operations as CRDT ops:
- CREATE(uuid, timestamp, clone_id, fields)
- UPDATE(uuid, field, timestamp, clone_id, value)
- DELETE(uuid, timestamp, clone_id) - tombstone, not hard delete

### 4. Sync as Merge
Syncing becomes merging two CRDT states:
- No merge conflicts possible
- Deterministic merge function
- Guaranteed convergence

## Implementation Phases

### Phase 1: Research & Design (4 weeks)
- Study existing CRDT implementations (Automerge, Yjs, Loro)
- Design schema for CRDT-based issue tracking
- Prototype LWW-based Issue CRDT
- Benchmark performance vs current system

### Phase 2: Parallel Implementation (6 weeks)
- Implement CRDT storage layer alongside SQLite
- Build conversion tools: SQLite ↔ CRDT
- Maintain backward compatibility with v1.x format
- Migration path for existing databases

### Phase 3: Testing & Validation (4 weeks)
- Formal verification of convergence properties
- Stress testing with 100+ clone scenario
- Performance profiling and optimization
- Documentation and examples

### Phase 4: Migration & Rollout (4 weeks)
- Release v2.0-beta with CRDT backend
- Gradual migration from v1.x
- Monitoring and bug fixes
- Final v2.0 release

## Risks & Mitigations

**Risk 1: Performance overhead**
- Mitigation: Benchmark early, optimize hot paths
- CRDTs can be slower than append-only logs
- May need compaction strategy

**Risk 2: Storage bloat**
- Mitigation: Implement operation log compaction
- Tombstone garbage collection for deleted issues
- Periodic snapshots to reduce log size

**Risk 3: Breaking changes**
- Mitigation: Maintain v1.x compatibility layer
- Gradual migration tools
- Dual-mode operation during transition

**Risk 4: Complexity**
- Mitigation: Use battle-tested CRDT libraries
- Comprehensive documentation
- Clear migration guide

## Success Criteria
- 100-clone collision test passes without failures
- Formal proof of convergence properties
- Performance within 2x of current system
- Zero manual conflict resolution required
- Backward compatible with v1.x databases

## Timeline
18-20 weeks total (4-5 months)

## References
- Automerge: https://automerge.org
- Yjs: https://docs.yjs.dev
- Loro: https://loro.dev
- CRDT theory: Shapiro et al, A comprehensive study of CRDTs
- Related issues: bd-0dcea000, bd-4d7fca8a, bd-6221bdcd


Consolidate check-health DB opens into single connection

The --check-health flag opens the database 3 separate times (once per quick check). Consolidate into a single DB open for better performance, especially on slower filesystems.


Fix autoimport tests for content-hash collision scoring

## Overview
Three autoimport tests are failing after bd-cbed9619.4 because they expect behavior based on the old reference-counting collision resolution, but the system now uses deterministic content-hash scoring.

## Failing Tests
1. `TestAutoImportMultipleCollisionsRemapped` - expects local versions preserved
2. `TestAutoImportAllCollisionsRemapped` - expects local versions preserved
3. `TestAutoImportCollisionRemapMultipleFields` - expects specific collision resolution behavior

## Root Cause
These tests were written when ScoreCollisions used reference counting to determine which version to keep. Now it uses content-hash comparison (introduced in commit 2e87329), which produces different but deterministic results.

## Example
Old behavior: Issue with more references would be kept
New behavior: Issue with lexicographically lower content hash is kept

## Solution
Update each test to:
1. Verify the new content-hash based behavior is correct
2. Check that the remapped issue (not necessarily local/remote) has the expected content
3. Ensure dependencies are preserved on the correct remapped issue

## Acceptance Criteria
- All three autoimport tests pass
- Tests verify content-hash determinism (same collision always resolves the same way)
- Tests check dependency preservation on remapped issues
- Test documentation explains content-hash scoring expectations

## Files to Modify
- `cmd/bd/autoimport_collision_test.go`

## Testing
Run: `go test ./cmd/bd -run "TestAutoImport.*Collision" -v`


bd repair-deps - Orphaned dependency cleaner

Find and fix orphaned dependency references.

Implementation:
- Scan all issues for dependencies pointing to non-existent issues
- Report orphaned refs
- Auto-fix with --fix flag
- Interactive mode with --interactive

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


Add schema compatibility probe to prevent silent migration failures

Issue #262 revealed a serious bug: migrations may fail silently, causing UNIQUE constraint errors later.

Root cause:
- sqlite.New() runs migrations once on open
- checkVersionMismatch() prints 'database will be upgraded automatically' but only updates metadata
- If migrations fail or daemon runs older version, queries expecting new columns fail with 'no such column'
- Import logic misinterprets this as 'not found' and tries INSERT on existing ID
- Result: UNIQUE constraint failed: issues.id

Fix strategy (minimal):
1. Add schema probe in sqlite.New() after RunMigrations
   - SELECT all expected columns from all tables with LIMIT 0
   - If fails, retry RunMigrations and probe again
   - If still fails, return fatal error with clear message
2. Fix checkVersionMismatch to not claim 'will upgrade' unless probe passes
3. Only update bd_version after successful migration probe
4. Add schema verification before import operations
5. Map 'no such column' errors to clear actionable message

Related: #262


Add test coverage for internal/git package


Measure git operation reduction

Quantify the reduction in git operations (pulls, commits, pushes) when using Agent Mail for coordination.

Acceptance Criteria:
- Baseline: count git ops for 10 issues without Agent Mail
- With Agent Mail: count git ops for 10 issues
- Document reduction percentage
- Verify 70-80% reduction claim
- Measure impact on .git directory size growth

Success Metric: ≥70% reduction in git operations


Create examples for common workflows

Add examples/ subdirectories: OSS contributor workflow, team branch workflow, multi-phase development, multiple personas (architect/implementer). Each with README and sample configs.


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.


Document bd-wasm installation and usage

Create documentation for bd-wasm:
- Update README with npm installation instructions
- Add troubleshooting section for WASM-specific issues
- Document known limitations vs native bd
- Add examples for Claude Code Web sandbox usage
- Update INSTALLING.md with bd-wasm option


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.


Remove obsolete renumber.go command (hash IDs eliminated need)


Add close_reason field to CLI schema and documentation

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

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

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

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


Extract batch operations to batch_ops.go

Move validateBatchIssues, generateBatchIDs, bulkInsertIssues, bulkRecordEvents, bulkMarkDirty, CreateIssues to batch_ops.go


Daemon crashes silently when multiple .db files exist in .beads/

When daemon detects multiple .db files (after filtering out .backup and vc.db files), it writes error details to .beads/daemon-error file before exiting.

The error file is checked when:
1. Daemon discovery fails to connect (internal/daemon/discovery.go)
2. Auto-start fails to yield a running daemon (cmd/bd/main.go)
3. Daemon list shows 'daemon not responding' error

This makes the error immediately visible to users without requiring them to check daemon logs.

File created:  cmd/bd/daemon.go (writes daemon-error on multiple .db detection)
Files modified:
- internal/daemon/discovery.go (reads daemon-error and surfaces in DaemonInfo.Error)
- cmd/bd/main.go (displays daemon-error when auto-start fails)

Testing: Create multiple .db files in .beads/, start daemon, verify error file created and shown in bd daemons list

Root cause: Daemon exits with os.Exit(1) when multiple .db files detected (daemon.go:1381), but error only goes to daemon log file. User sees 'daemon not responding' without knowing why.

Current detection:
- daemon.go filters out .backup and vc.db files
- bd doctor detects multiple databases
- Error message tells user to run 'bd init' or manually remove

Problem: Error is not user-visible unless they check daemon logs.

Proposed fix options:
1. Surface the error in 'bd info' and 'bd daemons list' output
2. Add a hint in error messages to run 'bd doctor' when daemon fails
3. Make daemon write error to a .beads/daemon-error file that gets checked
4. Improve 'bd doctor' to run automatically when daemon is unhealthy


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


Add edge case tests for GetNextChildID parent resurrection

## Current Coverage
TestGetNextChildID_ResurrectParent tests happy path only:
- Parent exists in JSONL
- Resurrection succeeds

## Missing Test Cases

### 1. Parent Not in JSONL
- Parent doesn't exist in DB
- Parent doesn't exist in JSONL either
- Should return: "could not be resurrected from JSONL history"

### 2. JSONL File Missing
- Parent doesn't exist in DB
- No issues.jsonl file exists
- Should handle gracefully

### 3. Malformed JSONL
- Parent doesn't exist in DB
- JSONL file exists but has invalid JSON
- Should skip bad lines, continue searching

### 4. Concurrent Resurrections
- Multiple goroutines call GetNextChildID with same parent
- Only one should resurrect, others should succeed
- Test for race conditions

### 5. Deeply Nested Missing Parents
- Creating bd-abc.1.2.1 where:
  - bd-abc exists
  - bd-abc.1 missing (should fail with current fix)
- Related to bd-ar2.4

### 6. Content Hash Mismatch
- Parent in JSONL has different content_hash than expected
- Should still resurrect (content_hash is for integrity, not identity)

## Files
- internal/storage/sqlite/child_id_test.go

## Implementation
```go
func TestGetNextChildID_ResurrectParent_NotInJSONL(t *testing.T) { ... }
func TestGetNextChildID_ResurrectParent_NoJSONL(t *testing.T) { ... }
func TestGetNextChildID_ResurrectParent_MalformedJSONL(t *testing.T) { ... }
func TestGetNextChildID_ResurrectParent_Concurrent(t *testing.T) { ... }
```


deletions.jsonl gets corrupted with full issue objects instead of deletion records

## Bug Description

The deletions.jsonl file was found to contain full issue objects (like issues.jsonl) instead of deletion records.

### Expected Format (DeletionRecord)
```json
{"id":"bd-xxx","timestamp":"2025-...","actor":"user","reason":"deleted"}
```

### Actual Content Found
```json
{"id":"bd-03r","title":"Document deletions manifest...","description":"...","status":"closed",...}
```

## Impact
- bd sync sanitization step reads deletions.jsonl and removes any matching IDs from issues.jsonl
- With 60 full issue objects in deletions.jsonl, ALL 60 issues were incorrectly removed during sync
- This caused complete data loss of the issue database

## Root Cause (suspected)
Something wrote issues.jsonl content to deletions.jsonl. Possible causes:
- Export writing to wrong file
- File path confusion during sync
- Race condition between export and deletion tracking

## Related Issues
- bd-0b2: --no-git-history flag (just fixed)
- bd-4pv: export outputs only 1 issue after corruption
- bd-4t7: auto-import runs during --no-auto-import

## Reproduction
Unknown - discovered during bd sync session on 2025-11-26

## Fix
Need to investigate what code path could write issue objects to deletions.jsonl


Integration Testing

Verify cache removal doesn't break any workflows


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


Investigate jujutsu integration for beads

Research and document how beads could integrate with jujutsu (jj), the next-generation VCS. Key areas to explore:
- How jj's operation model differs from git (immutable operations, working-copy-as-commit)
- JSONL sync strategy with jj's conflict resolution model
- Daemon compatibility with jj's more frequent rewrites
- Whether auto-import/export needs changes for jj workflows
- Example configurations and documentation updates needed


Test child issue


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.


Final validation test


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


Migration guide documentation

Write comprehensive migration guide covering: OSS contributor workflow, team workflow, multi-phase development, multiple personas. Include step-by-step instructions, troubleshooting, and backward compatibility notes.


Documentation

Complete documentation for Agent Mail integration to enable adoption.


Add test coverage for internal/autoimport package


Clean up merge conflict artifacts in .beads directory

After resolving merge conflicts in .beads/beads.jsonl, leftover artifacts remain as untracked files:
- .beads/beads.base.jsonl
- .beads/beads.left.jsonl

These appear to be temporary files created during merge conflict resolution.

Options to fix:
1. Add these patterns to .beads/.gitignore automatically
2. Clean up these files after successful merge resolution
3. Document that users should delete them manually
4. Add a check in 'bd sync' or 'bd doctor' to detect and remove stale merge artifacts

Preferred solution: Add *.base.jsonl and *.left.jsonl patterns to .beads/.gitignore during 'bd init', and optionally clean them up automatically after successful import.


Fix TestMigrateHashIDs test failure

Test failure in cmd/bd/migrate_hash_ids_test.go:100 - New ID bd-09970281 for bd-1 is not a hash ID. This test is validating the hash ID migration but the generated ID doesn't match the expected format.


Auto-compact deletions during bd sync

Parent: bd-imj

## Task
Optionally prune deletions manifest during sync when threshold exceeded.

**Note: Opt-in feature** - disabled by default to avoid sync latency.

## Implementation

In `bd sync`:
```go
func (s *Syncer) Sync() error {
    // ... existing sync logic ...

    // Auto-compact only if enabled
    if s.config.GetBool("deletions.auto_compact", false) {
        deletionCount := deletions.Count(".beads/deletions.jsonl")
        threshold := s.config.GetInt("deletions.auto_compact_threshold", 1000)

        if deletionCount > threshold {
            retentionDays := s.config.GetInt("deletions.retention_days", 7)
            if err := s.compactor.PruneDeletions(retentionDays); err != nil {
                log.Warnf("Failed to auto-compact deletions: %v", err)
                // Non-fatal, continue sync
            }
        }
    }

    // ... rest of sync ...
}
```

## Configuration
```yaml
deletions:
  retention_days: 7
  auto_compact: false              # Opt-in, disabled by default
  auto_compact_threshold: 1000     # Trigger when > N entries (if enabled)
```

## Acceptance Criteria
- [ ] Auto-compact disabled by default
- [ ] Enabled via config `deletions.auto_compact: true`
- [ ] Sync checks deletion count only when enabled
- [ ] Auto-prunes when threshold exceeded
- [ ] Failure is non-fatal (logged warning)
- [ ] Test: no compaction when disabled
- [ ] Test: compaction triggers when enabled and threshold exceeded


Research Go WASM compilation and modernc.org/sqlite WASM support

Investigate technical requirements for compiling bd to WASM:
- Verify modernc.org/sqlite has working js/wasm support
- Identify Go stdlib limitations in WASM (syscalls, file I/O, etc.)
- Research wasm_exec.js runtime and Node.js integration
- Document any API differences between native and WASM builds


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

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


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

Linting issue: checkHooksQuick - path is unused (unparam) at cmd/bd/doctor.go:541:22. Error: func checkHooksQuick(path string) string {


[CRITICAL] Sync validation false positive - legitimate deletions trigger 'data loss detected'

Sync preflight validation incorrectly detects 'data loss' when legitimate deletions occur. This blocks all syncs and is the highest priority fix.


Document Claude Code for Web SessionStart hook

Create documentation for using bd in Claude Code for Web:

## Documentation locations
- README.md - Add Claude Code for Web section
- Create docs/CLAUDE_CODE_WEB.md with detailed instructions

## SessionStart hook example
```json
{
  "sessionStart": {
    "script": "npm install -g @beads/bd && bd init --quiet --prefix bd || true"
  }
}
```

## Documentation should cover
- How to configure SessionStart hook in .claude/settings.json
- Verification: Check bd is installed (bd --version)
- Basic workflow in Claude Code for Web
- Troubleshooting common issues
- Note about network restrictions and why npm approach works

## Examples
- Creating issues in web sandbox
- Syncing with git in web environment
- Using MCP server (if applicable)


Add tests for daemon config module


Show dependency status in bd show output

When bd show displays dependencies and dependents, include their status (open/closed/in_progress/blocked) for quick progress tracking. Improves context rebuilding and planning.


CLI tests (cli_fast_test.go) are slow and should be integration tests

The TestCLI_* tests in cmd/bd/cli_fast_test.go are taking 4-5 seconds each (40+ seconds total), making them the slowest part of the fast test suite.

Current timings:
- TestCLI_Import: 4.73s
- TestCLI_Blocked: 4.33s
- TestCLI_DepTree: 4.15s
- TestCLI_Close: 3.59s
- TestCLI_DepAdd: 3.50s
- etc.

These tests compile the bd binary once in init(), but then execute it multiple times per test with filesystem operations. Despite being named "fast", they're actually end-to-end CLI integration tests.

Options:
1. Tag with //go:build integration (move to integration suite)
2. Optimize: Use in-memory databases, reduce exec calls, better parallelization
3. Keep as-is but understand they're the baseline for "fast" tests

Total test suite currently: 13.8s (cmd/bd alone is 12.8s, and most of that is these CLI tests)

Fixed by reusing existing bd binary from repo root instead of rebuilding.

Before: 15+ minutes (rebuilding binary for every test package)
After: ~12 seconds (reuses pre-built binary)

The init() function now checks for ../../bd first before falling back to building. This means `go build && go test` is now fast.


bd status "Recent Activity" is misleading - should use git history

## Problem

`bd status` shows "Recent Activity (last 7 days)" but the numbers are wrong. It only looks at database timestamps, not git history. Says "141 issues closed in last 7 days" when thousands have actually come and go.

## Issues

1. Only queries database timestamps, not git history
2. 7 days is too long a window
3. Numbers don't reflect actual activity in JSONL git history

## Proposed Fix

Either:
- Query git history of `.beads/beads.jsonl` to get actual activity (last 24-48 hours)
- Remove "Recent Activity" section entirely if not useful
- Make time window configurable and default to 24h

## Example Output (Current)
```
Recent Activity (last 7 days):
Issues Created:    174
Issues Closed:     141
Issues Updated:    37
```
This is misleading when thousands of issues have actually cycled through.


Create multi-agent race condition test

Automated test that runs 2+ agents simultaneously to verify collision prevention.

Acceptance Criteria:
- Script spawns 2 agents in parallel
- Both try to claim same issue
- Only one succeeds (via reservation)
- Other agent skips to different work
- Verify in JSONL that no duplicate claims
- Test with Agent Mail enabled/disabled

File: tests/integration/test_agent_race.py

Success Metric: Zero duplicate claims with Agent Mail, collisions without it


Add 'town report' command for aggregated swarm status

## Problem
Getting a full swarm status requires running 6+ commands:
- `town list <rig>` for each rig
- `town mail inbox` as Boss
- `bd list --status=open/in_progress` per rig

This is slow and error-prone for both humans and agents.

## Proposed Solution
Add `town report [RIG]` command that aggregates:
- All rigs with polecat states (running/stopped, awake/asleep)
- Boss inbox summary (unread count, recent senders)
- Aggregate issue counts per rig (open/in_progress/blocked)

Example output:
```
=== beads ===
Polecats: 5 (5 running, 0 stopped)
Issues: 20 open, 0 in_progress, 0 blocked

=== gastown ===
Polecats: 6 (4 running, 2 stopped)
Issues: 0 open, 0 in_progress, 0 blocked

=== Boss Mail ===
Unread: 10 | Total: 22
Recent: rictus (21:19), scrotus (21:14), immortanjoe (21:14)
```

## Acceptance Criteria
- [ ] `town report` shows all rigs
- [ ] `town report <rig>` shows single rig detail
- [ ] Output is concise and scannable
- [ ] Completes in <2 seconds


GH#518: Document bd setup command

bd setup (e.g. bd setup cursor) is undocumented. Users only find it by grepping source. Need proper docs. See: https://github.com/steveyegge/beads/issues/518


GH#519: bd sync fails when sync.branch equals current branch

bd sync tries to create worktree when sync.branch matches current checkout, fails with 'already used by worktree'. Should detect and commit directly. See: https://github.com/steveyegge/beads/issues/519


Extract daemon lifecycle functions to daemon_lifecycle.go


Add security tests for file path validation in clean command

Test coverage gap identified by automated analysis (vc-217).

**Original Issue:** [deleted:bd-da96-baseline-lint]

In cmd/bd/clean.go:118, os.Open(gitignorePath) is flagged by gosec G304 for potential file inclusion via variable without validation.

Add tests covering:
- Path traversal attempts (../../etc/passwd)
- Absolute paths outside project directory
- Symlink following behavior
- Non-existent file handling
- Validation that only .gitignore files in valid locations are opened

This is a security-sensitive code path that needs validation to prevent unauthorized file access.

_This issue was automatically created by AI test coverage analysis._


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


bd resolve-conflicts - Git merge conflict resolver

Automatically resolve JSONL merge conflicts.

Modes:
- Mechanical: ID remapping (no AI)
- AI-assisted: Smart merge/keep decisions
- Interactive: Review each conflict

Handles <<<<<<< conflict markers in .beads/beads.jsonl

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


Add tests for internal/autoimport package

Currently 0.0% coverage. Need tests for auto-import functionality that detects and imports updated JSONL files.


Optimize CLI test suite - eliminate redundant git init calls

Current: Each of 13 CLI tests calls git init (31s total). Solution: Use single test binary built once in init(), skip git operations where possible, or use mock filesystem.


Complete remaining sync branch daemon tests

4 remaining test scenarios in daemon_sync_branch_test.go need completion:

⚠ MINOR FIXES (apply same pattern as TestSyncBranchCommitAndPush_Success):
1. TestSyncBranchCommitAndPush_NoChanges
   - Reorder: call initMainBranch() BEFORE creating JSONL
   - Pattern: init branch → create issue → export JSONL → test

2. TestSyncBranchCommitAndPush_WorktreeHealthCheck
   - Same reordering needed
   - Verify worktree corruption detection and auto-repair

🔧 MORE WORK NEEDED (remote branch setup):
3. TestSyncBranchPull_Success
   - Issue: remote doesn't have sync branch after push
   - Need to verify branch is pushed to remote correctly
   - Then test pull from clone2

4. TestSyncBranchIntegration_EndToEnd
   - Full workflow: Agent A commits → Agent B pulls → Agent B commits → Agent A pulls
   - Same remote branch issue

Pattern to apply (from TestSyncBranchCommitAndPush_Success):
- Call initMainBranch(t, dir) BEFORE creating issues/JSONL
- This ensures sync branch worktree has changes to commit

Acceptance:
- All 7 tests pass
- go test -v -run TestSyncBranch ./cmd/bd/ succeeds


Ready issue


Use -short flag in CI for PR checks

Update CI configuration to use -short flag for PR checks, run full tests nightly.

The slow tests already support testing.Short() and will be skipped.

Expected savings: ~20 seconds for PR checks (fast tests only)

Implementation:
- Update .github/workflows/ci.yml to add -short flag for PR tests
- Create/update nightly workflow for full test runs
- Update README/docs about test strategy

File: .github/workflows/ci.yml:30


Enhance dep tree to show full dependency graph

When running `bd dep tree <issue-id>`, the current output only shows the issue itself without its dependency relationships.

## Current Behavior

```
$ bd dep tree gt-0iqq
🌲 Dependency tree for gt-0iqq:

→ gt-0iqq: Implement Boss (global overseer) [P2] (open)
```

This doesn't show any of the dependency structure.

## Desired Behavior

Show the full dependency DAG rooted at the given issue. For example:

```
$ bd dep tree gt-0iqq
🌲 Dependency tree for gt-0iqq:

gt-0iqq: Implement Boss (global overseer) [P2] (open)
├── gt-0xh4: Boss session management [P2] (open) [READY]
│   ├── gt-le7c: Boss mail identity [P2] (open)
│   │   ├── gt-r8fe: Boss human escalation queue [P2] (open)
│   │   └── gt-vdak: Boss dispatch loop [P2] (open)
│   │       └── gt-kgy6: Boss resource management [P2] (open)
│   │           └── gt-93iv: Boss wake daemon [P2] (open)
│   └── gt-vdak: (shown above)
```

## Suggested Options

- `--direction=down|up|both` - Show dependents (what this blocks), dependencies (what blocks this), or both
- `--status=open` - Filter to only show issues with a given status
- `--depth=N` - Limit tree depth
- Handle DAG cycles gracefully (show "(shown above)" or similar for already-displayed nodes)

## Use Case

When reorganizing a set of related issues (like I just did with the Boss implementation), being able to visualize the full dependency graph helps verify the structure is correct before syncing.