Recurring dirty state after merge conflicts - bd sync keeps failing

## Problem

`bd sync` consistently fails with merge conflicts in `.beads/beads.jsonl`, creating a loop:
1. User runs `bd sync`
2. Git merge conflict occurs
3. User resolves with `git checkout --theirs` (takes remote)
4. Daemon auto-exports database state (which has local changes)
5. JSONL becomes dirty again immediately
6. Repeat

This has been happening for **weeks** and is extremely frustrating.

## Root Cause

The recommended conflict resolution (`git checkout --theirs`) throws away local database state (comments, dependencies, closed issues). The daemon then immediately re-exports, creating a dirty state.

## Current Workaround

Manual `bd export -o .beads/beads.jsonl && git add && git commit && git push` after every failed sync.

## Example Session

```bash
$ bd sync
CONFLICT (content): Merge conflict in .beads/beads.jsonl

$ git checkout --theirs .beads/beads.jsonl && bd import && git add && git commit && git push
# Pushed successfully

$ git status
modified: .beads/beads.jsonl  # DIRTY AGAIN!
```

## Lost Data in Recent Session

- bd-ry1u closure (lost in merge)
- Comments on bd-08fd, bd-23a8, bd-6049, bd-87a0 (lost)
- Dependencies that existed only in local DB

## Potential Solutions

1. **Use beads-merge tool** - Implement proper 3-way JSONL merge (bd-bzfy)
2. **Smarter conflict resolution** - Detect when `--theirs` will lose data, warn user
3. **Sync validation** - Check if JSONL == DB after merge, re-export if needed
4. **Daemon awareness** - Pause auto-export during merge resolution
5. **Transaction log** - Replay local changes after merge instead of losing them

## Related Issues

- bd-bzfy (beads-merge integration)
- Possibly related to daemon auto-export behavior

## Solution Implemented

Fixed the recurring dirty state after merge conflicts by adding **sync validation** before re-exporting.

### Root Cause
Lines 217-237 in `sync.go` unconditionally re-exported DB to JSONL after every import, even when they were already in sync. This created an infinite loop:
1. User runs `bd sync` which pulls and imports remote JSONL
2. Sync unconditionally re-exports DB (which has local changes)
3. JSONL becomes dirty immediately
4. Repeat

### Fix
Added `dbNeedsExport()` function in `integrity.go` that checks:
- If JSONL exists
- If DB modification time is newer than JSONL
- If DB and JSONL issue counts match

Now `bd sync` only re-exports if DB actually has changes that differ from JSONL.

### Changes
- Added `dbNeedsExport()` in `cmd/bd/integrity.go` (lines 228-271)
- Updated `sync.go` lines 217-251 to check before re-exporting
- Added comprehensive tests in `cmd/bd/sync_merge_test.go`

### Testing
All tests pass including 4 new tests:
- `TestDBNeedsExport_InSync` - Verifies no export when synced
- `TestDBNeedsExport_DBNewer` - Detects DB modifications
- `TestDBNeedsExport_CountMismatch` - Catches divergence
- `TestDBNeedsExport_NoJSONL` - Handles missing JSONL

This prevents the weeks-long frustration of merge conflicts causing infinite dirty loops.


Auto-close or warn about epics when all children complete


Agent 0 Batch 8 Issue 2


Agent 5 Issue 13

Created by agent 5


Agent 2 Issue 10


Agent 4 Issue 22

Created by agent 4


Implement daemon architecture for concurrent access

Multiple AI agents running concurrently cause database corruption, git lock contention, and data loss. Implement a daemon-based architecture where bd daemon owns SQLite (single writer) and all bd commands become RPC clients when daemon is running. Batches git operations to prevent index.lock contention. Maintains backward compatibility with graceful fallback to direct mode. See DAEMON_DESIGN.md for full details.


Agent 0 Issue 59


Agent 6 Issue 63

Created by agent 6


Agent 3 Issue 79

Created by agent 3


Agent 0 Issue 78

Created by agent 0


Global daemon should warn/reject --auto-commit and --auto-push

When user runs 'bd daemon --global --auto-commit', it's unclear which repo the daemon will commit to (especially after fixing bd-99 where global daemon won't open a DB).

Options:
1. Warn and ignore the flags in global mode
2. Error out with clear message

Line 87-91 already checks autoPush, but should skip check entirely for global mode. Add user-friendly messaging about flag incompatibility.


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


bd message: Add HTTP client timeout to prevent hangs

HTTP client in `sendAgentMailRequest` uses default http.Post with no timeout.

**Location:** cmd/bd/message.go:181

**Problem:**
- Can hang indefinitely if server is unresponsive
- No way to cancel stuck requests
- Poor UX in flaky networks

**Fix:**
```go
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Post(url, "application/json", bytes.NewReader(reqBody))
```

**Impact:** Production reliability and security issue


VCS-agnostic sync support

Make bd sync work with multiple VCS types (git, jujutsu, mercurial, sapling) by detecting VCS per repo and using appropriate sync commands, supporting mixed-VCS multi-repo configs.


Agent 7 Issue 50

Created by agent 7


Agent 4 Issue 95

Created by agent 4


Agent 3 Batch 28 Issue 1


Agent 1 Issue 52


Agent 1 Issue 62


Agent 6 Issue 13


Agent 3 Batch 6 Issue 1


Agent 3 Issue 5

Created by agent 3


Agent 6 Issue 54

Created by agent 6


Agent 6 Issue 37


Agent 7 Issue 10

Created by agent 7


Agent 3 New Issue 5


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.


Makefile Integration for Benchmarks

Add a single 'bench' target to the Makefile for running performance benchmarks.

Target:
.PHONY: bench

bench:
	go test -bench=. -benchtime=3s -tags=bench \
		-cpuprofile=cpu.prof -memprofile=mem.prof \
		./internal/storage/sqlite/
	@echo ""
	@echo "Profiles generated. View flamegraph:"
	@echo "  go tool pprof -http=:8080 cpu.prof"

Features:
- Single simple target, no complexity
- Always generates CPU and memory profiles
- Clear output on how to view results
- 3 second benchmark time for reliable results
- Uses bench build tag for heavy benchmarks

Usage:
  make bench                              # Run all benchmarks
  go test -bench=BenchmarkGetReadyWork... # Run specific benchmark


Agent 5 Issue 61


Agent 6 Issue 12

Created by agent 6


Agent 6 Issue 30


Agent 5 Issue 23

Created by agent 5


Agent 9 Issue 5


Agent 2 Issue 83


Agent 1 Issue 52

Created by agent 1


Agent 3 Batch 30 Issue 1


Agent 3 Batch 46 Issue 1


Agent 2 Issue 73


Agent 6 Issue 59


Agent 1 New Issue 45


Agent 6 Issue 3


bd prime: AI context loading and Claude Code integration

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


Phase 2: Add client auto-detection in bd commands

Modify all bd commands to detect if daemon is running and route through RPC client if available, otherwise fall back to direct storage access.

Changes needed:
- Update cmd/bd/main.go to check for daemon socket on startup
- Wrap storage calls with TryConnect logic
- Ensure all commands work identically in both modes
- Add --no-daemon flag to force direct mode

This maintains backward compatibility while enabling daemon mode.


Agent 3 Issue 51


Test issue


Agent 2 Issue 8


Agent 8 Issue 32


Agent 1 Batch 27 Issue 1


Agent 7 Issue 60

Created by agent 7


Unit tests for FileWatcher

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


Add WebSocket support

## Feature Request

[Describe the desired feature]

## Motivation

[Why is this feature needed? What problem does it solve?]

## Use Cases

1. **Use Case 1**: [description]
2. **Use Case 2**: [description]

## Proposed Solution

[High-level approach to implementing this feature]

## Alternatives Considered

- **Alternative 1**: [description and why not chosen]
- **Alternative 2**: [description and why not chosen]


Agent 0 Issue 60


Agent 1 Issue 78


Agent 2 Issue 9


Issue 2


Agent 3 Issue 93


Agent 0 Issue 47


Agent 2 Issue 61

Created by agent 2


Agent 4 Issue 30

Created by agent 4


Extract Daemon struct and config into internal/daemonrunner

Create internal/daemonrunner with Config struct and Daemon struct. Move daemon runtime logic from cmd/bd/daemon.go Run function into Daemon.Start/Stop methods.

Refactoring complete! Created internal/daemonrunner package with:
- Config struct (config.go)
- Daemon struct with Start/Stop methods (daemon.go)
- RPC server lifecycle (rpc.go)
- Sync loop implementation (sync.go)
- Git operations (git.go)
- Process management (process.go, flock_*.go)
- Logger setup (logger.go)
- Platform-specific signal handling (signals_*.go)
- Database fingerprint validation (fingerprint.go)

Build succeeds and most daemon tests pass. Import functionality still delegated to cmd/bd (marked with TODO(bd-b5a3)).


Agent 5 Issue 24

Created by agent 5


Agent 3 Issue 11

Created by agent 3


Agent 3 Issue 64

Created by agent 3


Agent 2 Batch 41 Issue 1


Implement label CLI commands (add, remove, list)

Add CLI commands for label management:
- bd label add <issue> <label...>
- bd label remove <issue> <label...>
- bd label list <issue>
- bd labels (list all labels in use)

Backend already has AddLabel/GetLabels/RemoveLabel methods in storage layer.


Agent 2 Issue 21

Created by agent 2


Agent 9 Issue 9


Agent 8 Issue 93


Agent 6 Issue 14


Agent 5 Issue 88


Parent's blocker should block children in ready work calculation

GitHub issue #19: If epic1 blocks epic2, children of epic2 should also be considered blocked when calculating ready work. Currently epic2's children show as ready even though their parent is blocked. This breaks the natural hierarchy of dependencies and can cause agents to work on tasks out of order.

Expected: ready work calculation should traverse up parent-child hierarchy and check if any ancestor has blocking dependencies.

See: https://github.com/anthropics/claude-code/issues/19


Fix CI failures (Coverage, Windows, Nix)

CI is failing, apparently on files not touched in the PRs - #327 , #305 .

Diagnosis summary:
1. Coverage gate in .github/workflows/ci.yml fails if total statements <50%; current PR run shows 46.9%.
2. The new hook plumbing in TestInstallHooks() asserts POSIX executable bits, which fails on Windows.
3. SQLite resurrection tests keep connections open, preventing TempDir cleanup on Windows.
4. The nix run step launches bd without creating .beads, causing failure.


Agent 3 Issue 25


Agent 5 Issue 10

Created by agent 5


Agent 1 Issue 9

Created by agent 1


Agent 2 Issue 52

Created by agent 2


Agent 3 Issue 43


Clean up MCP integration for daemon reliability

Ensure MCP server properly handles daemon lifecycle, connection failures, and recovery scenarios.


Implement `bd prime` command for AI context loading

Create a `bd prime` command that outputs AI-optimized markdown containing essential Beads workflow context. This provides an alternative to the MCP server for token-conscious users and enables context recovery after compaction/clearing.


Agent 4 Issue 67


Agent 9 Issue 13


Agent 1 Batch 40 Issue 2


Agent 5 Issue 46

Created by agent 5


Single-user issue


Agent 2 Batch 18 Issue 2


Agent 9 Issue 99


Agent 2 Issue 1

Created by agent 2


Agent 8 Issue 14


Agent 3 Issue 44

Created by agent 3


Agent 4 Issue 79


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

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

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

This fixes GitHub issue #274 reported by rscorer.


Timeout Test


Agent 5 Issue 72

Created by agent 5


Agent 6 Issue 82

Created by agent 6


Agent 1 Issue 3


Agent 2 Batch 30 Issue 2


Agent 9 Issue 86


Add daemon health checks and monitoring

Add health check endpoint that validates daemon state, storage connections, and resource usage.


Agent 4 Issue 35

Created by agent 4


Add BEADS_DAEMON_MODE flag handling

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


Agent 1 Issue 29

Created by agent 1


Agent 3 Issue 95


Agent 6 Issue 2


Fix MCP server None/null crashes in list/ready/stats


Agent 2 Issue 83

Created by agent 2


Agent 0 Issue 8


Agent 3 Issue 35


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)


Add log rotation for daemon.log

daemon.log grows forever without rotation. With sync every 5 minutes:
- ~105k log entries per year
- No size limit
- No cleanup
- Eventually fills disk

Need automatic log rotation with:
- Size-based rotation (default: 10MB)
- Age-based cleanup (default: 7 days)
- Compression of old logs
- Configurable retention

Location: cmd/bd/daemon.go:455


Agent 7 Issue 38

Created by agent 7


Agent 2 Batch 43 Issue 2


Test RPC mutation event


Agent 7 Issue 66

Created by agent 7


Agent 0 New Issue 10


Update bash-agent example with Agent Mail integration

Add Agent Mail integration to examples/bash-agent/agent.sh using curl for HTTP calls.

Acceptance Criteria:
- Health check function using curl
- Reserve issue before claiming
- Send notifications on status change
- Release on completion
- Graceful degradation if curl fails
- No bash errors when Agent Mail unavailable

File: examples/bash-agent/agent.sh


Agent 7 Issue 40


Agent 2 Issue 69

Created by agent 2


Bias ready work towards recent issues before oldest-first

Currently 'bd ready' shows oldest issues first (by created_at). This can bury recently discovered work that might be more relevant. Propose a hybrid approach: show issues from the past 1-2 days first (sorted by priority), then fall back to oldest-first for older issues. This keeps fresh discoveries visible while still surfacing old forgotten work.


Agent 3 Issue 34

Created by agent 3


Prevent test pollution in production database

The bd-vxdr cleanup revealed test issues were created during manual testing in the production workspace (Nov 2-4, template feature development).

**Root cause:** Manual testing with `./bd create "Test issue"` pollutes the production .beads database.

**Prevention strategies:**
1. Use TEST_DB environment variable for manual testing
2. Add warning when creating issues with "Test" prefix
3. Improve developer docs about testing workflow
4. Consider adding `bd test-mode` command for isolated testing

**Implementation completed:**

1. ✅ Added warning when creating issues with "Test" prefix in production database
   - Shows yellow warning with ⚠ symbol
   - Suggests using BEADS_DB for isolated testing
   - Warning appears in create.go after title validation

2. ✅ Documented BEADS_DB testing workflow in AGENTS.md
   - Added "Testing Workflow" section in Development Guidelines
   - Includes manual testing examples with BEADS_DB
   - Includes automated testing examples with t.TempDir()
   - Clear warning about not polluting production database

3. ⚠ Decided against bd test-mode command
   - BEADS_DB already provides simple, flexible isolation
   - Additional command would add complexity without much benefit
   - Current approach follows Unix philosophy (env vars for config)

**Files modified:**
- cmd/bd/create.go - Added Test prefix warning
- AGENTS.md - Added Testing Workflow section

**Testing:**
- Verified warning appears when creating "Test" prefix issues
- Verified BEADS_DB isolation works correctly
- Built successfully with `go build`


Agent 2 Issue 88


Agent 3 Issue 55

Created by agent 3


Agent 7 Issue 39

Created by agent 7


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.


Agent 8 Issue 39


Agent 6 Issue 65

Created by agent 6


Agent 6 Issue 88


Agent 9 Issue 21


Agent 7 Issue 46


Agent 4 Issue 81


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.


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

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

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

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

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


Improve timestamp comparison readability in import

The timestamp comparison logic uses double-negative which can be confusing:

Current code:
if !incoming.UpdatedAt.After(existing.UpdatedAt) {
    // skip update
}

More readable:
if incoming.UpdatedAt.After(existing.UpdatedAt) {
    // perform update
} else {
    // skip (local is newer)
}

This is a minor refactor for code clarity.

Related: bd-1022
Files: internal/importer/importer.go:411, 488


Agent 8 Issue 82


Agent 5 Issue 79


Agent 4 Issue 41


Agent 4 Issue 93


Agent 9 Issue 58


Agent 2 Batch 29 Issue 2


Agent 4 Issue 60


Agent 0 Issue 50

Created by agent 0


Add storage cache eviction policy to daemon

Daemon caches DB connections forever in storageCache map (server.go:29). For users with 50+ repos, this causes memory leaks and file descriptor exhaustion.

Need LRU cache with:
- Max size limit (default: 50 repos)
- TTL-based eviction (default: 30min idle)
- Periodic cleanup goroutine

Location: internal/rpc/server.go:29-40


Agent 1 Issue 82


Agent 6 Issue 95

Created by agent 6


Agent 7 Issue 29

Created by agent 7


Agent 7 Issue 14

Created by agent 7


Test update


Direct mode test2


Agent 2 Issue 70

Created by agent 2


Agent 1 Issue 18


Agent 1 Issue 76

Created by agent 1


Agent 7 Issue 82


Create Python adapter library

Create beads_mail_adapter.py library that wraps Agent Mail HTTP calls with health checks and graceful degradation.

Acceptance Criteria:
- AgentMailAdapter class with health check on init
- enabled flag auto-disables if server unreachable
- All methods wrapped in try/catch (non-blocking failures)
- Methods: reserve_issue(), release_issue(), notify(), check_inbox()
- Environment-based configuration (AGENT_MAIL_URL, AGENT_MAIL_TOKEN)
- Unit tests for enabled/disabled modes

File: lib/beads_mail_adapter.py


Agent 3 Issue 89


Agent 6 Issue 29


Agent 2 Issue 24


Agent 3 Issue 18

Created by agent 3


Agent 5 Issue 77

Created by agent 5


Add telemetry and observability to daemon

Daemon has no metrics or observability. Cannot monitor:
- Request latency (p50, p95, p99)
- Cache hit/miss rates
- Active connections
- Error rates
- Resource usage over time

Needed for:
- Performance debugging
- Capacity planning
- Production monitoring
- SLA tracking

Location: internal/rpc/server.go


Agent 1 Issue 78

Created by agent 1


Update documentation for bd prime and Claude integration

Update AGENTS.md, README.md, and QUICKSTART.md to document the new `bd prime` command, `bd setup claude` command, and tip system.


Agent 3 Batch 23 Issue 1


Agent 8 Issue 41


Agent 7 Issue 96

Created by agent 7


Agent 4 Issue 21

Created by agent 4


Agent 1 Issue 64


Agent 4 Issue 63


Agent 3 Issue 50

Created by agent 3


Add tests for extracted modules

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


Agent 0 Issue 93

Created by agent 0


Agent 1 Issue 34

Created by agent 1


Agent 6 Issue 61


Agent 0 Batch 30 Issue 1


Centralize BD_DEBUG logging into debug package

The codebase has 43 scattered instances of `if os.Getenv("BD_DEBUG") != ""` debug checks across 6 files. Centralize into a debug logging package.

Current locations:
- `cmd/bd/main.go` - 15 checks
- `cmd/bd/autoflush.go` - 6 checks
- `cmd/bd/nodb.go` - 4 checks
- `internal/rpc/server.go` - 2 checks
- `internal/rpc/client.go` - 5 checks
- `cmd/bd/daemon_autostart.go` - 11 checks

Target structure:
```
internal/debug/
└── debug.go
```

Benefits:
- Centralized debug logging
- Easier to add structured logging later
- Testable (can mock debug output)
- Consistent debug message format

Impact: Removes 43 scattered checks, improves code clarity


Split internal/rpc/server.go into focused modules

The file `internal/rpc/server.go` is 2,273 lines with 50+ methods, making it difficult to navigate and prone to merge conflicts. Split into 8 focused files with clear responsibilities.

Current structure: Single 2,273-line file with:
- Connection handling
- Request routing
- All 40+ RPC method implementations
- Storage caching
- Health checks & metrics
- Cleanup loops

Target structure:
```
internal/rpc/
├── server.go          # Core server, connection handling (~300 lines)
├── methods_issue.go   # Issue operations (~400 lines)
├── methods_deps.go    # Dependency operations (~200 lines)
├── methods_labels.go  # Label operations (~150 lines)
├── methods_ready.go   # Ready work queries (~150 lines)
├── methods_compact.go # Compaction operations (~200 lines)
├── methods_comments.go # Comment operations (~150 lines)
├── storage_cache.go   # Storage caching logic (~300 lines)
└── health.go          # Health & metrics (~200 lines)
```

Migration strategy:
1. Create new files with appropriate methods
2. Keep `server.go` as main file with core server logic
3. Test incrementally after each file split
4. Final verification with full test suite


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.


bd-hv01: Deletion tracking production readiness

Epic to track all improvements and fixes needed to make the deletion tracking implementation ([deleted:bd-hv01]) production-ready.

The core 3-way merge algorithm is sound, but there are critical issues around atomicity, error handling, and edge cases that need to be addressed before this can be safely used in production.

Critical path (P1):
- Non-atomic snapshot operations
- Brittle JSON string comparison
- Silent partial deletion failures
- Race conditions in concurrent scenarios

Follow-up work (P2-P3):
- Test coverage for edge cases and multi-repo mode
- Performance optimizations
- Code refactoring and observability

Related commit: 708a81c


Dependency A


Agent 5 Issue 27


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


Agent 2 Batch 4 Issue 1


Agent 7 Issue 23


Agent 8 Issue 55


Agent 8 Issue 36


bd validate - Comprehensive health check

Run all validation checks in one command.

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

Supports --fix-all for auto-repair.

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

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


Agent 3 Issue 26

Created by agent 3


Agent 9 Issue 81


Agent 0 Batch 16 Issue 2


Agent 1 Issue 86


Agent 0 Batch 30 Issue 2


Agent 1 Issue 45

Created by agent 1


Agent 1 Batch 17 Issue 1


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)


Agent 6 Issue 62

Created by agent 6


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


Agent 6 Issue 39


Agent 0 Issue 38


Agent 8 Issue 52


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


Agent 2 Issue 11


Agent 1 Batch 28 Issue 2


Agent 5 Issue 74


Agent 2 Issue 71


Agent 6 Issue 3

Created by agent 6


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

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

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

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

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

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

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


Investigate database pollution - unexpected issue count increases

Two repositories showing unexpected issue counts:
- ~/src/beads: 280 issues (expected ~209-220)
- ~/src/dave/beads: 895 issues (675 open, 149 closed)

This suggests database pollution - issues from one repository leaking into another. Need to investigate:
1. Run bd detect-pollution on both repos
2. Check for cross-repo contamination
3. Identify source of pollution (daemon? multi-repo config? import issues?)
4. Clean up polluted databases
5. Prevent future pollution


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


Base Issue 4


Agent 2 Issue 44

Created by agent 2


Add validation/warning for malformed issue IDs

getNextID silently ignores non-numeric ID suffixes (e.g., bd-foo). CAST returns NULL for invalid strings. Consider detecting and warning about malformed IDs in database. Location: internal/storage/sqlite/sqlite.go:79-82


Agent 3 Issue 98


Use external_ref as primary matching key for import updates

Enable re-syncing from external systems (Jira, GitHub, Linear) by using external_ref as the primary matching key during imports. Currently imports treat any content change as a collision, making it impossible to sync updates from external systems without creating duplicates.

See GH #142 for detailed proposal and implementation plan.

Key changes needed:
1. Add findByExternalRef() query function
2. Update DetectCollisions() to match by external_ref first
3. Update import_shared.go to update existing issues when external_ref matches
4. Add index on external_ref for performance
5. Preserve local issues (no external_ref) from being overwritten

This enables hybrid workflows: import external backlog, break down with local tasks, re-sync anytime.

## Code Review Complete ✅

**Overall Assessment**: EXCELLENT - Production ready

### Implementation Quality
- ✓ Clean architecture with proper interface extension
- ✓ Dual backend support (SQLite + Memory)
- ✓ Smart matching priority: external_ref → ID → content hash
- ✓ O(1) lookups with database index
- ✓ Timestamp-based conflict resolution
- ✓ Comprehensive test coverage (11 test cases)

### Follow-up Issues Filed
High Priority (P2):
- bd-897a: Add UNIQUE constraint on external_ref column
- bd-7315: Add validation for duplicate external_ref in batch imports

Medium Priority (P3):
- bd-f9a1: Add index usage verification test
- bd-3f6a: Add concurrent import race condition tests

Low Priority (P4):
- bd-e166: Improve timestamp comparison readability
- bd-9e23: Optimize Memory backend with index
- bd-537e: Add external_ref change tracking
- bd-df11: Add import metrics
- bd-9f4a: Document external_ref in content hash

### Key Features
✅ External systems (Jira, GitHub, Linear) can re-sync without duplicates
✅ Hybrid workflows: import external backlog, add local tasks, re-sync anytime
✅ Local issues protected from being overwritten
✅ Timestamp checking ensures only newer updates applied
✅ Performance optimized with database index

**Confidence Level**: 95% - Ship it! 🚀


Agent 0 Issue 52

Created by agent 0


Agent 6 Issue 60


Agent 4 Issue 82


Agent 2 Batch 47 Issue 1


Daemon doesn't clean up stale socket on startup

If daemon crashes, the socket file remains. Next startup fails with 'address already in use' even though no process is listening.

Fix: Before starting RPC server, check if socket exists and PID file shows dead process - remove stale socket.

Lines 520-532 in daemon.go create socket but don't clean up stale ones.


Agent 5 Issue 53

Created by agent 5


Agent 2 Issue 35


Install and test MCP Agent Mail locally

Install MCP Agent Mail on a single development machine and verify basic functionality.

Acceptance Criteria:
- Server installed via one-line installer
- Server running on port 8765
- Can register a project via HTTP
- Can register an agent identity
- Web UI accessible at /mail

Tested local installation. Server runs on port 8765, web UI works. MCP API tool execution has errors - needs debugging. See /tmp/bd-muls-report.md for details.


Sub-task under B


Agent 2 Batch 13 Issue 2


Agent 2 Issue 53

Created by agent 2


Agent 6 Issue 84


Agent 5 Issue 30


Agent 3 Batch 32 Issue 1


Add tests for internal/utils package

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


Agent 2 Issue 64


Agent 0 Issue 50


Agent 2 Issue 36

Created by agent 2


Agent 2 Batch 13 Issue 1


Add 'bd repos' command for multi-repo aggregation

When using daemon in multi-repo mode, users need commands to view/manage work across all active repositories.

Add 'bd repos' subcommand with:

1. bd repos list
   - Show all repositories daemon has cached
   - Display: path, prefix, issue count, last activity
   - Example output:
     ~/src/project1  [p1-]   45 issues  (active)
     ~/src/project2  [p2-]   12 issues  (2m ago)

2. bd repos ready --all
   - Aggregate ready work across all repos
   - Group by repo or show combined list
   - Support priority/assignee filters

3. bd repos stats
   - Combined statistics across all repos
   - Total issues, breakdown by status/priority
   - Per-repo breakdown

4. bd repos clear-cache
   - Close all cached storage connections
   - Useful for freeing resources

Implementation notes:
- Requires daemon to track active storage instances
- May need RPC protocol additions for multi-repo queries
- Should gracefully handle repos that no longer exist

Depends on: Global daemon flag (makes this more useful)

Context: This provides the UX layer on top of existing multi-repo support. The daemon can already serve multiple repos - this makes it easy to work with them.


Update docs with multi-repo patterns

Update AGENTS.md, README.md, QUICKSTART.md with multi-repo patterns. Document: config options, routing behavior, backward compatibility, troubleshooting, best practices.


Agent 4 Issue 78


Agent 5 Issue 92

Created by agent 5


Agent 6 Issue 29

Created by agent 6


Agent 2 Batch 38 Issue 2


Fix silent error handling in RPC response writing

Marshal and write errors silently ignored in writeResponse, can send partial JSON and hang clients.

Location: internal/rpc/server_lifecycle_conn.go:228-232

Problem:
- json.Marshal error ignored - cyclic reference sends corrupt JSON
- Write error ignored - connection closed, no indication to caller
- WriteByte error ignored - client hangs waiting for newline
- Flush error ignored - partial data buffered

Current code:
func (s *Server) writeResponse(writer *bufio.Writer, resp Response) {
    data, _ := json.Marshal(resp)  // Ignored!
    _, _ = writer.Write(data)      // Ignored!
    _ = writer.WriteByte('\n')     // Ignored!
    _ = writer.Flush()             // Ignored!
}

Solution: Return errors, handle in caller, close connection on error

Impact: Client hangs waiting for response; corrupt JSON sent

Effort: 1 hour


Validate issue prefix matches database prefix on create

Users can accidentally create issues with the wrong prefix (e.g., creating 'bd-95' in the vc tracker instead of the beads tracker).

This causes problems:
- Issues appear in wrong project
- Renumbering silently removes them (correct behavior, but surprising)
- Confusion about which tracker owns the issue

Evidence from vc project:
- bd-95 and bd-96 were created in ~/src/vc/vc/.beads/vc.db (should use vc- prefix)
- These were silently removed during renumbering (working as intended)
- But user didn't realize they were in wrong database until too late

Root cause: User was in vc directory but created issues with bd- prefix, probably because they were beads-related fixes.


Agent 5 Issue 20


Agent 4 Issue 47


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.


Agent 9 Issue 34


Agent 5 Issue 42

Created by agent 5


Integration Testing

Verify cache removal doesn't break any workflows


Consolidate ID generation and validation into ids.go

Extract ID logic into ids.go: ValidateIssueIDPrefix, GenerateIssueID, EnsureIDs. Move GetAdaptiveIDLength here. Unify single and bulk ID generation flows.


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

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

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


Agent 1 Issue 16

Created by agent 1


Agent 3 Issue 84


Agent 1 Issue 35


Agent 1 Issue 98

Created by agent 1


Agent 0 Issue 7


Agent 8 Issue 81


Agent 1 Issue 88


Agent 2 Issue 4

Created by agent 2


Agent 5 Issue 47


Agent 0 Batch 26 Issue 2


Version test issue


Base Issue 5


Add AI planning docs management guidance to bd onboard (GH-196)

Enhanced bd onboard command to provide guidance for managing AI-generated planning documents (Claude slop).

Addresses GitHub issue #196: https://github.com/steveyegge/beads/issues/196

Changes:
- Added Managing AI-Generated Planning Documents section to bd onboard
- Recommends using history/ directory for ephemeral planning files
- Updated AGENTS.md to demonstrate the pattern
- Added comprehensive tests

Commit: d46177d


Agent 7 Issue 42


Agent 1 Issue 48

Created by agent 1


Agent 1 Issue 68

Created by agent 1


Agent 0 Issue 26

Created by agent 0


Agent 6 Issue 28

Created by agent 6


Add monitoring and alerting

Observability for production Agent Mail server.

Acceptance Criteria:
- Health check endpoint (/health)
- Prometheus metrics export
- Grafana dashboard
- Alerts for server downtime
- Alerts for high error rate
- Log aggregation config

File: deployment/agent-mail/monitoring/


Agent 0 Batch 1 Issue 1


Agent 4 Issue 6

Created by agent 4


Agent 6 Issue 33


Agent 0 Issue 96

Created by agent 0


Agent 2 Issue 28

Created by agent 2


Agent 3 Issue 2

Created by agent 3


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.


Agent 6 Issue 69


Agent 0 Issue 86

Created by agent 0


Agent 2 Issue 26

Created by agent 2


Agent 4 Issue 62

Created by agent 4


Agent 9 Issue 29


Agent 1 Issue 54

Created by agent 1


Agent 2 Issue 64

Created by agent 2


Agent 3 Batch 30 Issue 2


Agent 7 Issue 12

Created by agent 7


Agent 0 Issue 46

Created by agent 0


Agent 2 Batch 33 Issue 2


Agent 0 Issue 25

Created by agent 0


Agent 2 Issue 35

Created by agent 2


Agent 8 Issue 53


Publish bd-wasm to npm

Package and publish WASM build to npm. Child of epic bd-44d0.

## Tasks
- [ ] Optimize WASM bundle (compression)
- [ ] Create README for npm package
- [ ] Set up npm publishing workflow
- [ ] Publish v0.1.0-alpha
- [ ] Test installation in clean environment
- [ ] Update beads AGENTS.md with installation instructions

## Package Name
bd-wasm (or @beads/wasm-cli)


Agent 5 Issue 80


Agent 3 Batch 25 Issue 1


Agent 6 Issue 91

Created by agent 6


Agent 0 Issue 16


Agent 3 Batch 33 Issue 2


Agent 3 Batch 22 Issue 2


Agent 5 Issue 51


Agent 0 New Issue 35


Agent 1 Issue 97

Created by agent 1


Agent 0 Batch 40 Issue 2


Agent 1 Issue 0


bd message: Add input validation for --importance flag

The --importance flag accepts any string without validation, leading to confusing server errors.

**Location:** cmd/bd/message.go:256-258

**Fix:**
- Add flag validation for: low, normal, high, urgent
- Add shell completion support
- Validate in runMessageSend before sending

**Impact:** Better UX, prevents confusing errors


Agent 8 Issue 56


Agent 7 Issue 70


Agent 4 Issue 35


Agent 4 Issue 85


Agent 6 Issue 47

Created by agent 6


Agent 2 Batch 4 Issue 2


Agent 0 Batch 32 Issue 1


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)


Update documentation for collision resolution

Update README.md with collision resolution section. Update CLAUDE.md with new workflow. Document --resolve-collisions and --dry-run flags. Add example scenarios showing branch merge workflows.


Agent 0 Issue 64


Agent 6 Issue 23

Created by agent 6


Agent 6 Issue 35


Agent 3 Issue 29

Created by agent 3


Agent 5 Issue 50


Agent 5 Issue 30

Created by agent 5


Need comprehensive daemon health check command (bd daemon doctor?)


Agent 3 Issue 77

Created by agent 3


Agent 0 Issue 27

Created by agent 0


Test child issue


Agent 3 Issue 79


Agent 0 Batch 21 Issue 2


Agent 1 Issue 37

Created by agent 1


Agent 5 Issue 78


bd delete fails to find closed issues

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

## Expected Behavior
Should delete the closed issues

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

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

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


npm package for bd with native binaries

Create an npm package that wraps native bd binaries for easy installation in Claude Code for Web and other Node.js environments.

## Problem
Claude Code for Web sandboxes are full Linux VMs with npm support, but cannot easily download binaries from GitHub releases due to network restrictions or tooling limitations.

## Solution
Publish bd as an npm package that:
- Downloads platform-specific native binaries during postinstall
- Provides a CLI wrapper that invokes the native binary
- Works seamlessly in Claude Code for Web SessionStart hooks
- Maintains full feature parity (uses native SQLite)

## Benefits vs WASM
- ✅ Full SQLite support (no custom VFS needed)
- ✅ All features work identically to native bd
- ✅ Better performance (native vs WASM overhead)
- ✅ ~4 hours effort vs ~2 days for WASM
- ✅ Minimal maintenance burden

## Success Criteria
- npm install @beads/bd works in Claude Code for Web
- All bd commands function identically to native binary
- SessionStart hook documented for auto-installation
- Package published to npm registry


Phase 4: Add atomic operations and stress testing

Implement atomic multi-operation support and test under concurrent load.

Features:
- Batch/transaction API for multi-step operations
- Request timeout and cancellation support
- Connection pooling optimization
- Stress tests with 4+ concurrent agents
- Performance benchmarks vs direct mode
- Documentation updates

Validates all acceptance criteria for bd-80.


Add compact --dry-run that shows size savings estimates

When running 'bd compact --dry-run', show estimated database size reduction in KB/MB and percentage, similar to what 'du -h' would show.

Example output:
  Tier 1 candidates: 15 issues
  Current size: 2.4 MB
  After compaction: ~1.7 MB (70% reduction, 0.7 MB saved)

This helps users understand impact before compacting.


Agent 7 Issue 86

Created by agent 7


Agent 4 Issue 31

Created by agent 4


Agent 8 Issue 20


Rapid 4


Agent 1 New Issue 35


Agent 3 Issue 32

Created by agent 3


Agent 9 Issue 64


Agent 0 Batch 0 Issue 2


Agent 8 Issue 48


Agent 9 Issue 46


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-0dcea000 (immediate fix)
- See beads_nway_test.go for failing N-way tests


Agent 4 New Issue 15


Base Issue 7


Agent 7 Issue 93

Created by agent 7


Agent 9 Issue 75


Implement --max-depth flag for bd dep tree

PR #87 adds the flag but doesn't wire it through. Need to:
1. Add flag definition in cmd/bd/dep.go
2. Pass maxDepth to store.GetDependencyTree()
3. Fix truncation warning to show actual depth used
4. Add tests for truncation behavior (TestGetDependencyTree_TruncationDepth, TestGetDependencyTree_DefaultDepth)
5. Update CLI docs/help

Default should remain 50. Keep using direct storage mode (no RPC needed for now).

Related: PR #87, bd-5


Remove Cache Configuration Docs

Remove documentation of deprecated cache env vars


Replace getStorageForRequest with Direct Access

Replace all getStorageForRequest(req) calls with s.storage


Add sync.branch configuration

Add configuration layer to support sync.branch setting via config file, environment variable, or CLI flag.

Tasks:
- Add sync.branch field to config schema
- Add BEADS_SYNC_BRANCH environment variable
- Add --branch flag to bd init
- Add bd config get/set sync.branch commands
- Validation (branch name format, conflicts)
- Config migration for existing users

Estimated effort: 1-2 days


Agent 0 Batch 20 Issue 1


Agent 1 Issue 9


Agent 5 Issue 10


Agent 0 Issue 29

Created by agent 0


Agent 0 Batch 20 Issue 2


Agent 4 Issue 76

Created by agent 4


Agent 1 Issue 20


Agent 2 Issue 80

Created by agent 2


Agent 7 Issue 51


Agent 7 Issue 61

Created by agent 7


Agent 4 Issue 66

Created by agent 4


Agent 2 Issue 46


Auto-detect scenarios and prompt users

Detect when user is in fork/contributor scenario and prompt with helpful suggestions. Check: git remote relationships, existing .beads config, repo ownership. Suggest appropriate wizard.


Agent 0 Issue 37


Agent 3 Issue 4

Created by agent 3


Improve error handling in dependency removal during remapping

In updateDependencyReferences(), RemoveDependency errors are caught and ignored with continue (line 392). Comment says 'if dependency doesn't exist' but this catches ALL errors including real failures. Should check error type with errors.Is(err, ErrDependencyNotFound) and only ignore not-found errors, returning other errors properly.


Agent 2 Issue 42

Created by agent 2


Phase 3: Implement daemon command with SQLite ownership

Create 'bd daemon' command that starts the RPC server and owns the SQLite database.

Implementation:
- Add cmd/bd/daemon.go with start/stop/status subcommands
- Daemon holds exclusive SQLite connection
- Integrates git sync loop (batch exports every 5 seconds)
- PID file management for daemon lifecycle
- Logging for daemon operations

Socket location: .beads/bd.sock per repository


Agent 4 Issue 89

Created by agent 4


Agent 4 Issue 14

Created by agent 4


Agent 9 Issue 8


Agent 5 New Issue 25


Verify MCP Server Compatibility

Ensure MCP server works with cache-free daemon


Agent 6 Issue 31


Agent 2 Issue 42


Agent 2 Issue 45


Agent 2 Issue 28


Optimize cmd/bd long-mode tests by switching to in-process testing

The long-mode CLI tests in cmd/bd are slow (1.4-4.4 seconds each) because they spawn full bd processes via exec.Command() for every operation.

Current approach:
- Each runBD() call spawns new bd process via exec.Command(testBD, args...)
- Each process initializes Go runtime, loads SQLite, parses CLI flags
- Tests run serially (create → update → show → close)
- Even with --no-daemon flag, there's significant process spawn overhead

Example timing from test run:
- TestCLI_PriorityFormats: 2.21s
- TestCLI_Show: 2.26s
- TestCLI_Ready: 2.29s
- TestCLI_Import: 4.42s

Optimization strategy:
1. Convert most tests to in-process testing (call bd functions directly)
2. Reuse test databases across related operations instead of fresh init each time
3. Keep a few exec.Command() tests that batch multiple operations to verify the CLI path works end-to-end

This should reduce test time from ~40s to ~5s for the affected tests.

Converted all CLI tests in cli_fast_test.go to use in-process testing via rootCmd.Execute(). Created runBDInProcess() helper that:
- Calls rootCmd.Execute() directly instead of spawning processes
- Uses mutex to serialize execution (rootCmd/viper not thread-safe)
- Properly cleans up global state (store, daemonClient)
- Returns only stdout to avoid JSON parsing issues with stderr warnings

Performance results:
- In-process tests: ~0.6s each (cached even faster)
- exec.Command tests: ~3.7s each
- Speedup: ~10x faster

Kept TestCLI_EndToEnd() that uses exec.Command for end-to-end validation of the actual binary.


Agent 5 Issue 84

Created by agent 5


Agent 3 Issue 11


Daemon stop has race condition with SIGKILL

When stopping daemon, sometimes see 'Warning: daemon did not stop after 5 seconds, sending SIGKILL' followed by 'Error killing process: os: process already finished'. Indicates timing issue where process exits between check and kill.


Add bd init --team wizard

Interactive wizard for team workflow setup. Guides user through: branch workflow configuration, shared repo setup, team member onboarding, examples of team collaboration patterns.


Agent 1 New Issue 15


Agent 2 Issue 99

Created by agent 2


Agent 8 Issue 16


Agent 8 Issue 24


Agent 6 Issue 13

Created by agent 6


Agent 4 Issue 0

Created by agent 4


Agent 3 Batch 35 Issue 1


Agent 7 Issue 56


Tests take too long - unacceptable for project size

## Problem

Running `go test ./internal/importer/... -v` takes an unacceptably long time (minutes). For a project this size, tests should complete in seconds.

## Impact

- Slows down development iteration
- AI agents waste time waiting for tests
- Blocks rapid bug fixes and validation
- Poor developer experience

## Investigation Needed

- Profile which tests are slow
- Check for unnecessary sleeps, timeouts, or integration tests
- Look for tests that could be parallelized
- Consider splitting unit vs integration tests

## Goal

Test suite for a single package should complete in <5 seconds, ideally <2 seconds.

## Optimizations Applied

1. **Added t.Parallel() to CLI tests** (13 tests) - allows concurrent execution
2. **Removed unnecessary 200ms sleep** in daemon_autoimport_test.go - Execute() forces auto-import synchronously
3. **Reduced filesystem settle wait** from 100ms → 50ms on non-Windows platforms
4. **Optimized debouncer test sleeps** (9 reductions):
   - Before debounce waits: 30ms → 20ms, 20ms → 10ms
   - After debounce waits: 40ms → 35ms, 30ms → 35ms, etc.
   - Thread safety test: 100ms → 70ms
   - Sequential cycles: 50ms → 40ms (3x)
   - Cancel tests: 70-80ms → 60ms

## Results

### cmd/bd package (main improvement target):
- **Before**: 5+ minutes (timeout)
- **After**: ~18-20 seconds
- **Speedup**: ~15-18x faster

### internal/importer package:
- **After**: <1 second (0.9s)

### Full test suite (with `-short` flag):
- Most packages complete in <2s
- Total runtime constrained by sequential integration tests

## Known Issues

- TestConcurrentExternalRefImports hangs due to :memory: connection pool issue (bd-b121)
- Some sync_branch tests may need sequential execution (git worktree conflicts)


Agent 3 Issue 87


Agent 0 Issue 1


Agent 2 Issue 81


Add tests for bd init --contributor wizard

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


Agent 0 Issue 75

Created by agent 0


Agent 0 Issue 15

Created by agent 0


Create plugin metadata files

Create .claude-plugin/plugin.json and marketplace.json.

Requirements:
- Name, description, version, author
- MCP server configuration bundling
- License and repository info
- Installation instructions


Agent 5 Issue 9

Created by agent 5


Optimize auto-flush to use incremental updates

Every flush exports ALL issues and ALL dependencies, even if only one issue changed. For large projects (1000+ issues), this could be expensive. Current approach guarantees consistency, which is fine for MVP, but future optimization could track which issues changed and use incremental updates. Located in cmd/bd/main.go:255-276.


Debug test 2


Agent 4 Issue 10

Created by agent 4


Agent 3 Issue 56


Agent 2 New Issue 30


Agent 1 Issue 59

Created by agent 1


Add tests for daemon lifecycle module


Agent 3 Issue 59

Created by agent 3


Document or automate JSONL sync workflow for git collaboration

When using beads across multiple machines/environments via git, there's a workflow gap:

1. Machine A: Create issues → stored in .beads/project.db
2. Machine A: bd export -o .beads/issues.jsonl
3. Machine A: git add .beads/issues.jsonl && git commit && git push
4. Machine B: git pull
5. Machine B: ??? issues.jsonl exists but project.db is empty/stale

The missing step is: bd import --db .beads/project.db -i .beads/issues.jsonl

This needs to be either:
a) Documented clearly in workflow docs
b) Automated (e.g., git hook, or bd auto-imports if jsonl is newer than db)
c) Both

Real-world impact: User had Claude Code on GCP VM create vc issues from BOOTSTRAP.md. They were exported to issues.jsonl and committed. But on local machine, vc.db was empty until manual import was run.


Agent 7 Issue 1


Agent 0 Issue 93


Batch test 3


Agent 5 Issue 15

Created by agent 5


Agent 4 Issue 90

Created by agent 4


Agent 0 Issue 73


Design and implement slash commands

Create useful slash commands for beads workflow.

Proposed commands:
- /bd-ready - Show ready work
- /bd-create - Create new issue interactively
- /bd-show - Show issue details
- /bd-update - Update issue status
- /bd-close - Close issue
- /bd-workflow - Show full agent workflow guide

Each command should provide a good UX and leverage the MCP server tools.


Agent 6 Issue 51


Agent 9 Issue 31


Agent 5 Issue 65

Created by agent 5


False positive detection by Kaspersky Antivirus (Trojan)

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


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


Agent 5 Issue 4


Agent 2 Batch 14 Issue 1


Agent 9 Issue 30


Agent 3 Issue 28


Agent 9 Issue 87


Add version compatibility checks for daemon RPC protocol

Client (bd CLI) and daemon may be different versions after upgrade. This causes:
- Missing features (newer CLI, older daemon)
- Protocol mismatches (older CLI, newer daemon)
- Silent failures or confusing errors
- No guidance to restart daemon

Location: internal/rpc/protocol.go, internal/rpc/client.go


Agent 9 Issue 66


Add simple cleanup command to delete closed issues

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


Final validation test


Agent 8 Issue 26


Agent 9 Issue 95


Agent 1 Issue 24

Created by agent 1


Agent 7 Issue 15


Extract database search logic into helper function

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

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

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

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

Related to bd-e16b implementation.


Agent 8 Issue 90


Agent 6 Issue 8

Created by agent 6


Agent 0 Issue 84


Agent 3 Issue 67

Created by agent 3


Agent 4 Issue 5


Agent 7 Issue 35


Extract validation functions to validators.go

Move validatePriority, validateStatus, validateIssueType, validateTitle, validateEstimatedMinutes, validateFieldUpdate to validators.go


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.


Agent 4 Issue 38


Agent 8 Issue 75


Agent 9 Issue 18


Agent 5 Issue 54


Agent 7 Issue 32


Agent 6 Issue 19

Created by agent 6


Agent 7 Issue 11


Agent 4 Issue 53

Created by agent 4


Agent 0 Batch 27 Issue 1


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


Agent 2 Batch 44 Issue 1


Agent 4 Issue 2

Created by agent 4


Agent 1 Batch 46 Issue 1


Agent 5 Issue 96

Created by agent 5


Document versioning and release strategy

Create comprehensive versioning strategy for beads ecosystem.

Components to document:
1. bd CLI (Go binary) - main version number
2. Plugin (Claude Code) - tracks CLI version
3. MCP server (Python) - bundled with plugin
4. Release workflow - how to sync all three

Decisions to make:
- Should plugin.json auto-update from bd CLI version?
- Should we have a VERSION file at repo root?
- How to handle breaking changes across components?
- What's the update notification strategy?

References:
- plugin.json engines field now requires bd >=0.9.0
- /bd-version command added for checking compatibility
- PLUGIN.md now documents update workflow


Agent 0 Batch 11 Issue 2


Agent 6 Issue 74

Created by agent 6


Agent 5 Issue 44

Created by agent 5


Agent 7 Issue 73


Agent 4 Issue 43


Agent 4 Issue 24


Agent 6 Issue 17


Agent 2 Issue 22

Created by agent 2


Agent 1 Batch 35 Issue 2


Agent 4 New Issue 35


Agent 2 Issue 25


Agent 4 Issue 39


Agent 3 Batch 43 Issue 1


Agent 2 Issue 5

Created by agent 2


Agent 7 Issue 84

Created by agent 7


Agent 7 Issue 50


Agent 2 Issue 40


Migration and onboarding for multi-repo

Create migration tools, wizards, and documentation to help users adopt multi-repo workflow, with special focus on OSS contributor onboarding and team adoption scenarios.


Agent 3 Batch 10 Issue 2


Agent 3 Issue 43

Created by agent 3


Agent 0 New Issue 0


Agent 7 Issue 57

Created by agent 7


Agent 0 Issue 90

Created by agent 0


Agent 7 Issue 88

Created by agent 7


Fix row iterator resource leak in recursive dependency queries

Critical resource leak in findAllDependentsRecursive() where rows.Close() is called AFTER early return on error, never executing.

Location: internal/storage/sqlite/sqlite.go:1131-1136

Problem:
- rows.Close() placed after return statement
- On scan error, iterator never closed
- Can exhaust SQLite connections under moderate load

Fix: Move defer rows.Close() to execute on all code paths

Impact: Connection exhaustion during dependency traversal


Agent 9 Issue 84


Agent 0 Issue 38

Created by agent 0


Agent 2 Batch 1 Issue 1


Rapid 1


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


Agent 2 Issue 56


Agent 7 Issue 6

Created by agent 7


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

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

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

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


Implement alias conflict resolution

Handle alias conflicts when multiple clones assign same alias to different issues.

## Scenario
```
Clone A: Creates bd-a1b2c3d4, assigns alias #42
Clone B: Creates bd-e5f6a7b8, assigns alias #42
After sync: Conflict! Which issue gets #42?
```

## Resolution Strategy: Content-Hash Ordering
Deterministic, same result on all clones:
```go
func ResolveAliasConflicts(conflicts []AliasConflict) []AliasRemapping {
    for _, conflict := range conflicts {
        // Sort by hash ID (lexicographic)
        sort.Strings(conflict.IssueIDs)

        // Winner: lowest hash ID (arbitrary but deterministic)
        winner := conflict.IssueIDs[0]

        // Losers: reassign to next available aliases
        for _, loser := range conflict.IssueIDs[1:] {
            newAlias := getNextAlias()
            remappings = append(remappings, AliasRemapping{
                IssueID:  loser,
                OldAlias: conflict.Alias,
                NewAlias: newAlias,
            })
        }
    }
    return remappings
}
```

## Detection During Import
File: internal/importer/importer.go
```go
func handleAliasConflicts(imported []Issue, existing []Issue) error {
    // Build alias map from imported issues
    aliasMap := make(map[int][]string)  // alias → issue IDs

    for _, issue := range imported {
        aliasMap[issue.Alias] = append(aliasMap[issue.Alias], issue.ID)
    }

    // Check against existing aliases
    for alias, importedIDs := range aliasMap {
        existingID := storage.GetIssueIDByAlias(alias)
        if existingID != "" {
            // Conflict! Resolve it
            allIDs := append(importedIDs, existingID)
            conflicts = append(conflicts, AliasConflict{
                Alias:    alias,
                IssueIDs: allIDs,
            })
        }
    }

    // Resolve and apply
    remappings := ResolveAliasConflicts(conflicts)
    applyAliasRemappings(remappings)
}
```

## Alternative Strategies (For Future Consideration)

### Priority-Based
```go
// Higher priority keeps alias
if issueA.Priority < issueB.Priority {
    winner = issueA
}
```

### Timestamp-Based (Last-Write-Wins)
```go
// Newer issue keeps alias
if issueA.UpdatedAt.After(issueB.UpdatedAt) {
    winner = issueA
}
```

### Manual Resolution
```bash
bd resolve-aliases --manual
# Interactive prompt for each conflict
```

## User Notification
```bash
$ bd sync
✓ Synced 5 issues
⚠ Alias conflicts resolved:
  - Issue bd-e5f6a7b8: alias changed from #42 to #100
  - Issue bd-9a8b7c6d: alias changed from #15 to #101
```

## Files to Create/Modify
- internal/storage/sqlite/alias_conflicts.go (new)
- internal/importer/importer.go (detect conflicts)
- cmd/bd/sync.go (show conflict notifications)

## Testing
- Test two clones assign same alias to different issues
- Test conflict resolution is deterministic (same on all clones)
- Test loser gets new alias
- Test winner keeps original alias
- Test multiple conflicts resolved in one import


Another epic


Agent 8 Issue 7


Agent 6 Issue 28


Task B under epic


Issue with desc

This is a description


Agent 2 Issue 18

Created by agent 2


Agent 1 Issue 12


Agent 6 Issue 67

Created by agent 6


Agent 1 Issue 60

Created by agent 1


Agent 3 Issue 66

Created by agent 3


Agent 6 Issue 16


Increase test coverage for auto-flush and auto-import features

Critical features have 0% test coverage despite being core workflow functionality.

**Uncovered areas (0% coverage):**

Auto-flush/Auto-import (dirty tracking):
- MarkIssueDirty / MarkIssuesDirty
- GetDirtyIssues / GetDirtyIssueCount
- ClearDirtyIssues / ClearDirtyIssuesByID
- Auto-flush debouncing logic
- Auto-import hash comparison

Database/file discovery:
- FindDatabasePath (finds .beads/*.db in directory tree)
- FindJSONLPath (finds issues.jsonl)
- findDatabaseInTree helper

Label operations:
- AddLabel / RemoveLabel
- GetLabels / GetIssuesByLabel

Events/Comments:
- AddComment
- GetEvents
- GetStatistics

Metadata storage:
- SetMetadata / GetMetadata (used for import hash tracking)

CLI output formatting:
- outputJSON
- printCollisionReport / printRemappingReport
- createIssuesFromMarkdown

**Priority areas:**
1. Auto-flush/import (highest risk - core workflow)
2. Database discovery (second - affects all operations)
3. Labels/events (lower priority - less commonly used)

**Test approach:**
- Add unit tests for dirty tracking in sqlite package
- Add integration tests for auto-flush timing and debouncing
- Add tests for import hash detection and idempotency
- Add tests for database discovery edge cases (permissions, nested dirs)

**Target:** Get overall coverage from 60% → 75%, focus on cmd/bd (currently 24.1%)

**Note:** These features work well in practice (dogfooding proves it) but edge cases (disk full, permissions, concurrent access, race conditions) are untested.

Added comprehensive tests for CLI output functions (outputJSON, printCollisionReport, printRemappingReport).

Coverage analysis shows:
- outputJSON: 60% coverage (increased from 0%)
- printCollisionReport: 100% coverage (increased from 0%)
- printRemappingReport: 100% coverage (increased from 0%)
- cmd/bd overall: 19.7% (increased from 18.8%)

Other areas already have excellent coverage:
✅ Dirty tracking: TestMarkIssueDirty, TestMarkIssuesDirty, TestGetDirtyIssueCount, TestClearDirtyIssuesByID
✅ Auto-flush: TestAutoFlushDirtyMarking, TestAutoFlushDebounce, TestAutoFlushConcurrency
✅ Auto-import: TestAutoImportIfNewer, TestAutoImportHashUnchanged, TestAutoImportCollisions
✅ Database discovery: TestFindDatabasePathInTree, TestFindJSONLPath
✅ Labels: TestAddLabel, TestRemoveLabel, TestGetLabels, TestGetIssuesByLabel
✅ Metadata: TestSetAndGetMetadata, TestGetMetadataNotFound
✅ Events: TestAddComment, TestAddMultipleComments, TestGetEvents

The remaining low coverage in cmd/bd is primarily Cobra command setup and CLI wiring, which is difficult to unit test and best tested via integration/E2E tests.


Agent 1 Issue 58

Created by agent 1


Add .gitignore to prevent noisy untracked beads files

When using beads, git status shows several untracked files in .beads/ directory: .beads/.gitignore, .beads/db.sqlite, daemon.pid and daemon.lock files. These should be added to the project's .gitignore to prevent noise.


Agent 3 Batch 38 Issue 1


Agent 9 Issue 55


Agent 0 Batch 17 Issue 2


Agent 7 Issue 19


Agent 1 Issue 63


Agent 3 Issue 41


Agent 1 Issue 2

Created by agent 1


Agent 7 Issue 92

Created by agent 7


Agent 2 Issue 27


Agent 4 Issue 69


Remove unused global daemon infrastructure (internal/daemonrunner/)

The internal/daemonrunner/ package (1,468 LOC) contains the old global daemon implementation that is no longer used. We now use per-workspace daemons.

Deadcode analysis shows all these functions are unreachable:
- Daemon.Start, runGlobalDaemon, setupLock
- validateSingleDatabase, validateSchemaVersion
- registerDaemon, unregisterDaemon
- validateDatabaseFingerprint
- Full git client implementation (NewGitClient, HasUpstream, HasChanges, Commit, Push, Pull)
- Helper functions: isGitRepo, gitHasUpstream, gitHasChanges, gitCommit

The entire package appears unused since switching to per-workspace daemon architecture.

Files to remove:
- daemon.go (9,436 bytes)
- git.go (3,510 bytes)
- sync.go (6,401 bytes)
- fingerprint.go (2,076 bytes)
- process.go (3,332 bytes)
- rpc.go (994 bytes)
- config.go (486 bytes)
- logger.go (1,579 bytes)
- flock_*.go (platform-specific file locking)
- signals_*.go (platform-specific signal handling)
- All test files

Total cleanup: ~1,500 LOC


Agent 3 Batch 4 Issue 1


Agent 0 Batch 12 Issue 2


Agent 7 Issue 21

Created by agent 7


Agent 5 Issue 60

Created by agent 5


Agent 3 Issue 99

Created by agent 3


Agent 0 Issue 63


Agent 3 Issue 2


Agent 1 Batch 6 Issue 1


Agent 3 Issue 17


Agent 0 Issue 55

Created by agent 0


Agent 4 Issue 19

Created by agent 4


Agent 7 Issue 85


URGENT: main branch has failing tests (syncbranch migration error)

The main branch has failing tests that are blocking CI for all PRs.

## Problem
All syncbranch_test.go tests failing with:
"migration external_ref_column failed: failed to create index on external_ref: sqlite3: SQL logic error: no such table: main.issues"

## Evidence
- Last 5 CI runs on main: ALL FAILED
- Tests fail locally on current main (bd6dca5)
- Affects: TestGet, TestSet, TestUnset in internal/syncbranch

## Impact
- Blocking all PR merges
- CI shows red for all branches
- Can't trust test results

## Root Cause
Migration order issue - trying to create index on external_ref column before the issues table exists, or before the external_ref column is added to the issues table.

## Quick Fix Needed
Need to investigate migration order in internal/storage/sqlite/migrations.go and ensure:
1. issues table is created first
2. external_ref column is added to issues table
3. THEN index on external_ref is created

This is CRITICAL - main should never have breaking tests.


Benchmark git traffic reduction

Automated benchmark comparing git operations with/without Agent Mail.

Acceptance Criteria:
- Script that processes 50 issues
- Counts git operations (pull, commit, push)
- Generates comparison report
- Verifies ≥70% reduction
- Fails if regression detected

File: tests/benchmarks/git_traffic.py

Output: Without Agent Mail: 450 git operations, With Agent Mail: 135 git operations, Reduction: 70%

Implemented automated benchmark script with following features:
- Processes configurable number of issues (default 50)
- Compares git operations in two modes: git-only vs Agent Mail
- Generates detailed comparison report with statistics
- Exit code reflects pass/fail based on 70% reduction target
- Results: 98.5% reduction (200 ops → 3 ops) for 50 issues

Files created:
- tests/benchmarks/git_traffic.py (main benchmark script)
- tests/benchmarks/README.md (documentation)
- tests/benchmarks/git_traffic_50_issues.md (sample results)

The benchmark vastly exceeds the 70% target, showing 98.5% reduction.


Add integration tests for adapter library

Test suite for beads_mail_adapter.py covering all scenarios.

Acceptance Criteria:
- Test enabled mode (server available)
- Test disabled mode (server unavailable)
- Test graceful degradation (server dies mid-operation)
- Test reservation conflicts
- Test message sending/receiving
- Mock HTTP server for testing
- 90%+ code coverage

File: lib/test_beads_mail_adapter.py

Test suite completed with 29 comprehensive tests covering:
- Enabled mode (server available): 10 tests
- Disabled mode (server unavailable): 2 tests
- Graceful degradation: 4 tests
- Reservation conflicts: 2 tests
- Configuration: 5 tests
- Health check scenarios: 3 tests
- HTTP error handling: 3 tests

**Performance**: All tests run in 10ms (fast!)

**Coverage highlights**:
✅ Server health checks (ok, degraded, error, timeout)
✅ All API operations (reserve, release, notify, check_inbox, get_reservations)
✅ HTTP errors (404, 409 conflict, 500, 503)
✅ Network errors (timeout, connection refused)
✅ Malformed responses (bad JSON, empty body, plain text errors)
✅ Environment variable configuration
✅ Graceful degradation when server dies mid-operation
✅ Conflict handling with both JSON and plain text errors
✅ Dict wrapper responses ({"messages": [...]} and {"reservations": [...]})
✅ Custom TTL for reservations
✅ Default agent name fallback

No external dependencies, no slow integration tests, just fast unit tests with mocks.


Agent 2 Issue 75


Refactor import logic to eliminate duplication between manual and auto-import

The import logic is duplicated in two places:
1. cmd/bd/import.go (manual 'bd import' command)
2. cmd/bd/main.go:autoImportIfNewer() (auto-import after git pull)

Both have nearly identical code for:
- Reading and parsing JSONL
- Type-asserting store to *sqlite.SQLiteStorage (where we just fixed a bug twice)
- Opening direct SQLite connection when using daemon mode
- Detecting collisions with sqlite.DetectCollisions()
- Scoring and remapping collisions
- Importing issues, dependencies, and labels

**Problems:**
- Bugs must be fixed in two places (we just did this for daemon mode)
- Features must be implemented twice
- Tests must cover both code paths
- Harder to maintain and keep in sync
- Higher risk of divergence over time

**Proposed solution:**
Extract a shared function that handles the core import logic:

```go
// importIssues handles the core import logic used by both manual and auto-import
func importIssues(ctx context.Context, dbPath string, store storage.Storage,
                  issues []*types.Issue, opts ImportOptions) (*ImportResult, error) {
    // Handle SQLite store detection/creation for daemon mode
    // Detect collisions
    // Score and remap if needed
    // Import issues, dependencies, labels
    // Return result
}
```

Both import.go and autoImportIfNewer() would call this shared function with their specific options.

**Benefits:**
- Single source of truth for import logic
- Bugs fixed once
- Easier to test
- Easier to extend with new import features
- Less code overall


Agent 2 Issue 99


Agent 0 Issue 62

Created by agent 0


Agent 2 Issue 16

Created by agent 2


Add transaction + retry logic for N-way collision resolution

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

Failure at step 2 leaves database in inconsistent state.

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

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

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

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

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

## Progress Made

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

## Test Results

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

## Root Cause Analysis

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

## Next Steps

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

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


Agent 0 Issue 80

Created by agent 0


Direct mode real test


Agent 5 New Issue 10


Agent 2 New Issue 45


Agent 9 Issue 39


Agent 7 Issue 44


Agent 4 Issue 91

Created by agent 4


Agent 0 Issue 69

Created by agent 0


Agent 6 Issue 86

Created by agent 6


Agent 3 New Issue 30


Vendor and integrate beads-merge tool

Incorporate @neongreen's beads-merge 3-way merge tool into bd to solve:
- Multi-workspace deletion sync (bd-hv01)
- Git merge conflicts in JSONL
- Field-level intelligent merging

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

**Integration approach**: Vendor the Go code with attribution, pending @neongreen's approval (GitHub issue #240)

**Benefits**:
- Prevents deletion resurrection bug
- Smart dependency merging (union + dedup)
- Timestamp handling (max wins)
- Detects deleted-vs-modified conflicts
- Works as git merge driver

**Acceptance criteria**:
- beads-merge code vendored into bd codebase
- Available as `bd merge` command
- Git merge driver setup during `bd init`
- Tests verify 3-way merge logic
- Documentation updated
- @neongreen credited


Agent 5 Issue 19


Agent 1 Issue 25

Created by agent 1


Agent 1 Batch 28 Issue 1


Agent 5 Issue 44


Agent 0 Batch 38 Issue 1


Agent 2 Issue 66


Test numeric 1


Agent 2 Issue 15

Created by agent 2


Agent 7 Issue 55


Agent 3 Batch 7 Issue 2


Add comprehensive tests for sync branch daemon logic

The daemon sync branch functionality (bd-6545) was implemented but needs proper end-to-end testing.

Current implementation:
- daemon_sync_branch.go has syncBranchCommitAndPush() and syncBranchPull()
- daemon_sync.go has been updated to use these functions when sync.branch is configured
- All daemon tests pass, but no specific tests for sync branch behavior

Testing needed:
- Test that daemon commits to sync branch when sync.branch is configured
- Test that daemon commits to current branch when sync.branch is NOT configured (backward compatibility)
- Test that daemon pulls from sync branch and syncs JSONL back to main repo
- Test worktree creation and health checks during daemon operations
- Test error handling (missing branch, worktree corruption, etc.)

Key challenge: Tests need to run in the context of the git repo (getGitRoot() uses current working directory), so test setup needs to properly change directory or mock the git root detection.

Reference existing daemon tests in daemon_test.go and daemon_autoimport_test.go for patterns.


Agent 8 Issue 15


Write plugin documentation

Create comprehensive documentation for the plugin.

Contents:
- Installation instructions
- Available commands
- MCP tools reference
- Configuration options
- Examples and workflows
- Troubleshooting guide


Add MCP server functions for repair commands

Expose new repair commands via MCP server for agent access:

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

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

See repair_commands.md lines 803-884 for design.


Agent 2 Issue 73

Created by agent 2


Test graceful degradation when server unavailable

Verify that agents continue working normally when Agent Mail server is stopped or unreachable.

Acceptance Criteria:
- Agent detects server unavailable on startup
- Logs "falling back to Beads-only mode"
- All bd commands work normally
- Agent can claim issues (no reservations, like today)
- Git sync operates as normal
- No errors or crashes

Success Metric: Zero functional difference when Agent Mail unavailable


Agent 0 Issue 6


Agent 0 Batch 36 Issue 2


Agent 1 New Issue 40


Refactor parseMarkdownFile to reduce cyclomatic complexity

The parseMarkdownFile function in cmd/bd/markdown.go has a cyclomatic complexity of 38, which exceeds the recommended threshold of 30. This makes the function harder to understand, test, and maintain.


Unit tests for Debouncer

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


Agent 8 Issue 3


Agent 5 Issue 72


Agent 5 Issue 82


Agent 7 Issue 83


Agent 3 Batch 11 Issue 2


Agent 2 Issue 32


Agent 3 Issue 98

Created by agent 3


Agent 0 Issue 90


Agent 3 Issue 7

Created by agent 3


Agent 6 Issue 15


Agent 2 Issue 93

Created by agent 2


Agent 8 Issue 94


Agent 7 Issue 67

Created by agent 7


Agent 2 Batch 33 Issue 1


Agent 9 Issue 33


Agent 5 Issue 73


Agent 2 Issue 46

Created by agent 2


Agent 0 Issue 5


Agent 0 Batch 3 Issue 1


Agent 1 Batch 16 Issue 2


File change test issue


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 completed:
- LOC: 52,372 lines total
- Dead code: 4 functions in import_shared.go (tracked in bd-6fe4622f)
- Build: ✓ Successful
- Test coverage: ~20-82% across packages
- Test failure: TestTwoCloneCollision (timeout issue)
- Linting: errcheck warnings present (defer close, fmt errors)
- Test time: ~20s

Issues found:
1. bd-6fe4622f: Remove unreachable import functions (renameImportedIssuePrefixes, etc)
2. TestTwoCloneCollision: Daemon killall timeout causing test failure
3. Linting: errcheck violations need fixing


Agent 8 Issue 50


Agent 5 New Issue 5


Agent 3 Issue 5


Agent 3 Issue 13


Agent 2 Issue 89

Created by agent 2


Agent 1 Issue 31


Remove Storage Cache from Server Struct

Eliminate cache fields and use s.storage directly


Testing event-driven mode!


Updated title


Reach 1.0 release milestone

Stabilize API, finalize documentation, comprehensive testing


Agent 4 Issue 13


Agent 3 Issue 61


Agent 4 Issue 44

Created by agent 4


Agent 4 Issue 8

Created by agent 4


Consolidate export/import/commit/push into sync.go

Create internal/daemonrunner/sync.go with Syncer type. Add ExportOnce, ImportOnce, CommitAndMaybePush methods. Replace createExportFunc/createAutoImportFunc with thin closures calling Syncer.


bd message: Implement full message reading functionality

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

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

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

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

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


Agent 5 Issue 35


Agent 5 Issue 7


Agent 4 New Issue 25


Agent 1 Issue 14


Agent 0 Issue 18

Created by agent 0


Base Issue 2


Add import metrics for external_ref matching statistics

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

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

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

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

Related: bd-1022


Agent 7 Issue 26

Created by agent 7


Agent 5 Issue 94

Created by agent 5


Agent 6 Issue 80


Agent 6 Issue 34

Created by agent 6


Agent 3 Issue 50


Agent 0 Issue 79