Auto-import fails in git workflows due to mtime issues

The auto-import mechanism (autoImportIfNewer) relies on file modification time comparison between JSONL and DB. This breaks in git workflows because git does not preserve original file modification times - pulled files get fresh mtimes based on checkout time.

Root causes:
1. Git checkout sets mtime to 'now', not original commit time
2. Auto-import compares JSONL mtime vs DB mtime (line 181 in main.go)
3. If DB was recently modified (agents working), mtime check fails
4. Auto-import silently returns without feedback
5. Agents continue with stale database state

This caused issues in VC project where 3 parallel agents:
- Pulled updated .beads/issues.jsonl from git
- Auto-import didn't trigger (JSONL appeared older than DB)
- Agents couldn't find their assigned issues
- Agents exported from wrong database, corrupting JSONL

Recommended approach: Checksum-based sync (option 3 from original design)

## Solution: Hash-based content comparison

Replace mtime comparison with JSONL content hash comparison:

1. **Compute JSONL hash on startup**:
   - SHA256 hash of .beads/issues.jsonl contents
   - Fast enough for typical repos (<1MB = ~20ms)
   - Only computed once per command invocation

2. **Store last import hash in DB**:
   - Add metadata table if not exists: CREATE TABLE IF NOT EXISTS metadata (key TEXT PRIMARY KEY, value TEXT)
   - Store hash after successful import: INSERT OR REPLACE INTO metadata (key, value) VALUES ('last_import_hash', '<hash>')
   - Query on startup: SELECT value FROM metadata WHERE key = 'last_import_hash'

3. **Compare hashes instead of mtimes**:
   - If JSONL hash != stored hash: auto-import (content changed)
   - If JSONL hash == stored hash: skip import (no changes)
   - If no stored hash: fall back to mtime comparison (backward compat)

4. **Update autoImportIfNewer() in cmd/bd/main.go**:
   - Lines 155-279 currently use mtime comparison (line 181)
   - Replace with hash comparison
   - Keep mtime as fallback for old DBs without metadata table

## Implementation Details

### New storage interface method:
```go
// In internal/storage/storage.go
type Storage interface {
    // ... existing methods ...
    GetMetadata(ctx context.Context, key string) (string, error)
    SetMetadata(ctx context.Context, key, value string) error
}
```

### Migration:
```go
// In internal/storage/sqlite/sqlite.go init
CREATE TABLE IF NOT EXISTS metadata (
    key TEXT PRIMARY KEY,
    value TEXT NOT NULL
);
```

### Updated autoImportIfNewer():
```go
func autoImportIfNewer() {
    jsonlPath := findJSONLPath()

    // Check if JSONL exists
    jsonlData, err := os.ReadFile(jsonlPath)
    if err != nil {
        return // No JSONL, skip
    }

    // Compute current hash
    hasher := sha256.New()
    hasher.Write(jsonlData)
    currentHash := hex.EncodeToString(hasher.Sum(nil))

    // Get last import hash from DB
    ctx := context.Background()
    lastHash, err := store.GetMetadata(ctx, "last_import_hash")
    if err != nil {
        // No metadata support (old DB) - fall back to mtime comparison
        autoImportIfNewerByMtime()
        return
    }

    // Compare hashes
    if currentHash == lastHash {
        return // No changes, skip import
    }

    // Content changed - import
    if err := importJSONLSilent(jsonlPath, jsonlData); err != nil {
        return // Import failed, skip
    }

    // Store new hash
    _ = store.SetMetadata(ctx, "last_import_hash", currentHash)
}
```

## Benefits

- **Git-proof**: Works regardless of file timestamps
- **Universal**: Works with git, Dropbox, rsync, manual edits
- **Backward compatible**: Falls back to mtime for old DBs
- **Efficient**: SHA256 is fast (~20ms for 1MB)
- **Accurate**: Only imports when content actually changed
- **No user action**: Fully automatic, invisible

## Performance Optimization

For very large repos (>10MB JSONL):
- Only hash if mtime changed (combine both checks)
- Use incremental hashing if metadata table tracks line count
- Consider sampling hash (first 1MB + last 1MB)

But start simple - full hash is fast enough for 99% of use cases.

## Rollout Plan

1. Add metadata table + Get/SetMetadata methods (backward compatible)
2. Update autoImportIfNewer() with hash logic + mtime fallback
3. Test with old and new DBs
4. Ship in next minor version (v0.10.0)
5. Document in CHANGELOG as "more reliable auto-import"
6. Git hooks remain optional but unnecessary for most users

- Auto-import works correctly after git pull
- Agents in parallel workflows see consistent database state
- Clear feedback when import is needed
- Performance acceptable for large databases
- Works in both git and non-git workflows
- Documentation updated with multi-agent best practices


parallel_test_10


race_test_5


race_test_2


race_test_7


race_test_14


Implement reserved database name _.db

Auto-detection now skips .beads/_.db to prevent pollution when beads dogfoods itself. This allows beads to use its own issue tracker without interfering with other projects using beads in the same directory tree. Implementation includes filtering in findDatabase(), stopping directory walk when .beads/ is found, and documentation in README.md and CLAUDE.md.


race_test_9


race_test_8


race_test_11


race_test_19


race_test_16


race_test_18


race_test_20


race_test_6


GH-3: Debug zsh killed error on bd init

User reports 'zsh: killed bd init' when running bd init or just bd command. Likely a crash or signal. Need to reproduce and investigate cause.


race_test_12


Auto-flush JSONL on CRUD operations with 5-second debounce

Implemented automatic write-through from SQLite to JSONL with 5-second debouncing. After any CRUD operation (create, update, close, dep add/remove), changes are scheduled to flush to JSONL after 5 seconds of inactivity. On process exit, any pending changes are flushed immediately. This prevents .db and .jsonl from getting out of sync, solving the workflow gap where agents forget to run 'bd export'. Can be disabled with --no-auto-flush flag. Addresses bd-33.


race_test_15


race_test_13


race_test_10


final_test_10


parallel_test_3


parallel_test_2


parallel_test_5


parallel_test_6


GH-11: Add Docker support for hosted/shared instance

Request for Docker container hosting to use beads across multiple dev machines. Would need to consider: centralized database (PostgreSQL?), authentication, concurrent access, API server, etc. This is a significant architectural change from the current local-first model.


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.


Handle missing JSONL directory in findJSONLPath

findJSONLPath() assumes the database directory exists. If someone runs bd init to create a new database but the .beads directory doesn't exist yet, the glob operations might fail silently. Add os.MkdirAll(dbDir, 0755) to ensure directory exists before globbing. Located in cmd/bd/main.go:188-201.


parallel_test_7


parallel_test_1


verification_


parallel_test_4


Add label management commands to CLI

Currently labels can only be managed programmatically via the storage API. Add CLI commands to add, remove, and filter by labels.

Can add/remove/list labels via CLI, can filter issues by label, labels persist to JSONL


Audit and document all inconsistent issues in database

Before we add the constraint, we need to know what data is inconsistent.

Steps:
1. Query database for status/closed_at mismatches
2. Document each case (how many, which issues, patterns)
3. Decide on cleanup strategy (trust status vs trust closed_at)
4. Create SQL script for cleanup

Output: Document with counts and cleanup SQL ready to review.

This unblocks the migration work.


Epic: Fix status/closed_at inconsistency (bd-224 solution)

Implement hybrid solution to enforce status/closed_at invariant:
- Database CHECK constraint
- Smart UpdateIssue logic
- Import enforcement
- Reopen command

This is a data integrity issue that affects statistics and will cause problems for agent swarms. The hybrid approach provides defense-in-depth.

See ULTRATHINK_BD224.md for full analysis and rationale.

Parent of all implementation tasks for this fix.


parallel_test_9


race_test_4


Refactor duplicate flush logic in PersistentPostRun

PersistentPostRun contains a complete copy of the flush logic instead of calling flushToJSONL(). This violates DRY principle and makes maintenance harder. Refactor to use flushToJSONL() with a force parameter to bypass isDirty check, or extract shared logic into a helper function. Located in cmd/bd/main.go:104-138.


Add migration scripts for GitHub Issues

Create scripts to import from GitHub Issues API or exported JSON


Consider batching API for bulk issue creation (recovered from bd-222)

Current CreateIssue acquires a dedicated connection for each call. For bulk imports or agent workflows creating many issues, a batched API could improve performance:

CreateIssues(ctx, issues []*Issue, actor string) error

This would:
- Acquire one connection
- Use one IMMEDIATE transaction
- Insert all issues atomically
- Reduce connection overhead

Not urgent - current approach is correct and fast enough for typical use.

**Recovered from:** bd-390 (lost in auto-import bug, see LOST_ISSUES_RECOVERY.md)


Add integration tests for auto-import collision detection

The auto-import collision detection fix (bd-552) needs comprehensive integration tests.

## Test Scenarios Needed

### 1. Basic Collision Detection
```go
TestAutoImportWithCollisions
- Setup: Create issue in DB with status=closed
- Simulate: Git pull with JSONL containing same issue with status=open
- Verify: Local changes preserved, warning printed, issue still closed
```

### 2. Multiple Collisions
```go
TestAutoImportWithMultipleCollisions
- Setup: 5 issues with local modifications
- Simulate: JSONL with 3 colliding, 2 matching
- Verify: 3 skipped, 2 updated, appropriate warnings
```

### 3. No Collisions (Happy Path)
```go
TestAutoImportWithoutCollisions
- Setup: Clean database
- Simulate: JSONL with new issues + exact matches
- Verify: All imported successfully, no warnings
```

### 4. All Collisions
```go
TestAutoImportAllCollisions
- Setup: Every issue has local modifications
- Simulate: JSONL with conflicting versions
- Verify: All skipped, warning lists all issues
```

### 5. Collision Detection Failure
```go
TestAutoImportCollisionDetectionError
- Setup: Mock DetectCollisions() to return error
- Verify: Import skipped entirely (safe failure mode)
```

### 6. Hash-Based Skip
```go
TestAutoImportHashUnchanged
- Setup: JSONL hash matches last_import_hash
- Verify: No collision detection runs, fast path
```

### 7. Fallback for Non-SQLite
```go
TestAutoImportWithoutCollisionDetection
- Setup: Mock non-SQLite storage backend
- Verify: Falls back to old behavior, no collision detection
```

## Test Infrastructure Needed

1. **Helper functions:**
   - createTestDBWithIssues()
   - writeJSONLFile()
   - captureStderrWarnings()
   - simulateGitPull()

2. **Fixtures:**
   - sample-collisions.jsonl
   - sample-exact-matches.jsonl
   - sample-mixed.jsonl

3. **Assertions:**
   - assertIssueState(id, expectedStatus)
   - assertWarningContains(text)
   - assertCollisionCount(n)

## Acceptance Criteria
- All 7 test scenarios implemented
- Tests pass consistently
- Code coverage >80% for autoImportIfNewer()
- Tests run in <5 seconds total
- Clear test names and documentation


Concurrent bd commands collide on shared .tmp filename

When multiple bd commands run concurrently (e.g., different agents), they all write to .beads/issues.jsonl.tmp causing race conditions. One command's rename succeeds, consuming the .tmp file, then other commands fail with 'no such file or directory'. Solution: use unique temp filenames with PID or random suffix (e.g., issues.jsonl.tmp.12345).


Benchmark cycle detection performance on large dependency graphs

Measure performance impact of cross-type cycle checking on large graphs (1000+ issues). Determine if optimization needed before enabling full prevention. Test various graph structures (sparse, dense, deeply nested).

Create benchmark test with synthetic dependency graphs of varying sizes and structures. Measure AddDependency performance with type-specific vs all-types cycle checking. Document findings and optimization recommendations if needed.

- Benchmark test covers 100, 1000, 5000 issue graphs
- Tests sparse and dense dependency patterns
- Measures time for both checking approaches
- Documents results in comments or DESIGN.md
- Recommends optimization if >100ms impact found


stress_test_6


Add performance benchmarks document

Document actual performance metrics with hyperfine tests


Low priority chore


Phase 1: Implement bd sync command

Create bd sync command to manually synchronize with git remote in a single operation.

Command should:
1. Export pending changes to JSONL
2. Commit changes to git
3. Pull from remote (with conflict resolution)
4. Push local commits to remote

Wraps the entire sync workflow in one command for better UX.

- bd sync command implemented
- Exports dirty changes to JSONL automatically
- Commits to git with descriptive message
- Pulls and handles merge conflicts
- Auto-imports updated JSONL
- Pushes to remote
- Error handling for git failures
- Tests cover success and failure scenarios
- Documentation in README.md

---


Code review: Auto-import collision detection fix (bd-228)

Request thorough code review of the auto-import collision detection implementation.

## Files Changed
- cmd/bd/main.go: autoImportIfNewer() and new autoImportWithoutCollisionDetection()

## Review Focus Areas

### 1. Correctness
- Does collision detection properly identify conflicts?
- Are colliding issues correctly filtered from import?
- Is the fallback function correct for non-SQLite backends?

### 2. Edge Cases
- What happens if DetectCollisions() fails?
- What if all issues are collisions?
- What if JSONL is malformed?
- Race conditions with concurrent auto-imports?

### 3. User Experience
- Is the warning message clear and actionable?
- Should we log to a file instead of/in addition to stderr?
- Should there be a --auto-resolve flag?

### 4. Performance
- Does collision detection add significant latency?
- Will this work with 1000+ issues?
- Any unnecessary N+1 queries?

### 5. Testing Gaps
- Do we need integration tests for collision scenarios?
- Should we test the warning output?
- Test autoImportWithoutCollisionDetection() fallback?

## Questions for Reviewer
1. Should auto-import be more aggressive (auto-resolve) or more conservative (fail)?
2. Should we add a counter for how many times collisions occurred?
3. Should there be a config option to disable collision detection?
4. Is the warning too verbose for typical workflows?

## Current Behavior
- Skips colliding issues (preserves local)
- Prints warning to stderr
- Suggests manual resolution command
- Continues with non-colliding issues

Review complete. Found 3 P0 bugs and 1 P1 bug. Created bd-663, bd-664, bd-665, bd-666. Full analysis in REVIEW_BD379.md


Add visibility for auto-flush failures

flushToJSONL() writes warnings to stderr when flush fails, but calling code has no way to know if flush succeeded or failed. This means a command could return success even though JSONL is now out of sync. Consider maintaining a 'last flush status' variable or counter for failed flushes, and warn user after multiple consecutive failures (e.g., 3+). Located in cmd/bd/main.go:227-307.


Document git-based restoration feature

Update COMPACTION.md and README.md with restoration workflow and examples


Add tests for git-based restoration

Test compaction stores commit hash, restore command works, handles missing git repo gracefully


parallel_test_8


Implement bd restore command

Create new restore command that checks out git commit from compacted_at_commit, reads issue from JSONL, and displays full history


Add compacted_at_commit field to Issue type

Add optional compacted_at_commit string field to store git commit hash when issue is compacted


Git-based restoration for compacted issues

Store git commit hash at compaction time to enable restoration of full issue history from version control. When issues are compacted, record the current git commit hash so users can restore the original uncompacted issue from git history.


Phase 1: Implement bd daemon command

Create bd daemon command to run background sync process with configurable interval.

Daemon should:
- Run in background (fork or systemd service)
- Poll for changes at configurable interval (default: 5 minutes)
- Export dirty issues to JSONL
- Auto-commit if --auto-commit flag set
- Auto-push if --auto-push flag set
- Pull remote changes periodically
- Auto-import when remote changes detected
- Log sync activity

Command flags:
- --interval=5m (sync check interval)
- --auto-commit (automatically commit changes)
- --auto-push (automatically push commits)
- --stop (stop running daemon)
- --status (show daemon status)

- bd daemon command implemented
- Background process management working
- Configurable sync interval
- Auto-commit functionality
- Auto-push functionality
- PID file for process management
- Graceful shutdown on SIGTERM
- Logging to file or syslog
- Status command shows daemon state
- Tests for daemon lifecycle
- Documentation in README.md

---


Record git commit hash during compaction

Update compact command to capture current git HEAD commit hash and store in compacted_at_commit field


Reach 1.0 release milestone

Stabilize API, finalize documentation, comprehensive testing


Code review follow-up: Post-PR #8 merge improvements

Follow-up tasks from the ultrathink code review of PR #8 merge (bd-62).

**Context:** PR #8 successfully merged atomic counter + dirty tracking. Core functionality is solid but several improvements identified.

**Critical (P0-P1):**
- bd-64: Fix SyncAllCounters performance bottleneck (P0)
- bd-65: Add migration for issue_counters table (P1)
- bd-66: Make import counter sync failure fatal (P1)

**Nice to have (P2-P3):**
- bd-67: Update test comments (P2)
- bd-68: Add performance benchmarks (P2)
- bd-69: Add metrics/logging (P3)
- bd-70: Add EXPLAIN QUERY PLAN tests (P3)

**Overall assessment:** 4/5 stars - Excellent implementation with one critical performance issue. After bd-64 is fixed, this becomes 5/5.

**Review document:** Available if needed

Status update: All P0-P1 critical tasks completed! bd-64 (performance), bd-65 (migration), bd-66 (fatal error), bd-67 (comments) are all done. Atomic counter implementation is now production-ready. Remaining tasks are P2-P3 enhancements.


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.


P2: Consider batching API for bulk issue creation

Current CreateIssue acquires a dedicated connection for each call. For bulk imports or agent workflows creating many issues, a batched API could improve performance:

CreateIssues(ctx, issues []*Issue, actor string) error

This would:
- Acquire one connection
- Use one IMMEDIATE transaction
- Insert all issues atomically
- Reduce connection overhead

Not urgent - current approach is correct and fast enough for typical use.


Data model allows inconsistent status/closed_at states

Issue bd-89 demonstrates a data model inconsistency: an issue can have status='open' but also have a closed_at timestamp set. This creates a liminal state that violates the expected invariant that closed_at should only be set when status='closed'.

Root causes:
1. Import (bd import) updates status field independently from closed_at field
2. UpdateIssue allows status changes without managing closed_at
3. No database constraint enforcing the invariant
4. Export includes both fields independently in JSONL

Current behavior:
- bd close: Sets status='closed' AND closed_at (correct)
- bd update --status open: Sets status='open' but leaves closed_at unchanged (creates inconsistency)
- bd import: Can import inconsistent data from JSONL

Impact:
- 'bd ready' shows issues that appear closed (have closed_at)
- Confusing for users and downstream tools
- Stats may be inaccurate

Potential solutions:
A) Add CHECK constraint: (status = 'closed') = (closed_at IS NOT NULL)
B) Update import/update logic to enforce invariant in application code
C) Add a 'reopened' event that explicitly clears closed_at
D) Remove closed_at field entirely (calculate from events or use status only)

See bd-89 for concrete example.


Epic: Add intelligent database compaction with Claude Haiku

Implement multi-tier database compaction using Claude Haiku to semantically compress old, closed issues. This keeps the database lightweight and agent-friendly while preserving essential context.

Goals:
- 70-95% space reduction for eligible issues
- Full restore capability via snapshots
- Opt-in with dry-run safety
- ~$1 per 1,000 issues compacted

- Schema migration with snapshots table
- Haiku integration for summarization
- Two-tier compaction (30d, 90d)
- CLI with dry-run, restore, stats
- Full test coverage
- Documentation complete


Critical: Auto-import was skipping collisions instead of remapping them

The auto-import mechanism was SKIPPING colliding issues instead of automatically remapping them to new IDs. This caused work from other workers/devices to be LOST during git pull operations.

Root cause: Lines 283-326 in main.go were filtering out colliding issues instead of calling RemapCollisions() to resolve them.

Impact: Multi-device workflows would silently lose issues when two devices created issues with the same ID.

- Auto-import detects collisions
- Calls ScoreCollisions + RemapCollisions automatically
- Shows remapping notification to user
- No work is lost from other workers
- All tests pass


GH-11: Add Docker support for hosted/shared instance

Request for Docker container hosting to use beads across multiple dev machines. Would need to consider: centralized database (PostgreSQL?), authentication, concurrent access, API server, etc. This is a significant architectural change from the current local-first model.


Epic: Fix status/closed_at inconsistency (bd-224 solution)

Implement hybrid solution to enforce status/closed_at invariant:
- Database CHECK constraint
- Smart UpdateIssue logic
- Import enforcement
- Reopen command

This is a data integrity issue that affects statistics and will cause problems for agent swarms. The hybrid approach provides defense-in-depth.

See ULTRATHINK_BD224.md for full analysis and rationale.

Parent of all implementation tasks for this fix.


Audit and document all inconsistent issues in database

Before we add the constraint, we need to know what data is inconsistent.

Steps:
1. Query database for status/closed_at mismatches
2. Document each case (how many, which issues, patterns)
3. Decide on cleanup strategy (trust status vs trust closed_at)
4. Create SQL script for cleanup

Output: Document with counts and cleanup SQL ready to review.

This unblocks the migration work.


Reach 1.0 release milestone


Low priority chore


Make auto-flush debounce duration configurable

flushDebounce is hardcoded to 5 seconds. Make it configurable via environment variable BEADS_FLUSH_DEBOUNCE (e.g., '500ms', '10s'). Current 5-second value is reasonable for interactive use, but CI/automated scenarios might want faster flush. Add getDebounceDuration() helper function. Located in cmd/bd/main.go:31.


Request thorough code review of the auto-import collision detection implementation.

## Files Changed
- cmd/bd/main.go: autoImportIfNewer() and new autoImportWithoutCollisionDetection()

## Review Focus Areas

### 1. Correctness
- Does collision detection properly identify conflicts?
- Are colliding issues correctly filtered from import?
- Is the fallback function correct for non-SQLite backends?

### 2. Edge Cases
- What happens if DetectCollisions() fails?
- What if all issues are collisions?
- What if JSONL is malformed?
- Race conditions with concurrent auto-imports?

### 3. User Experience
- Is the warning message clear and actionable?
- Should we log to a file instead of/in addition to stderr?
- Should there be a --auto-resolve flag?

### 4. Performance
- Does collision detection add significant latency?
- Will this work with 1000+ issues?
- Any unnecessary N+1 queries?

### 5. Testing Gaps
- Do we need integration tests for collision scenarios?
- Should we test the warning output?
- Test autoImportWithoutCollisionDetection() fallback?

## Questions for Reviewer
1. Should auto-import be more aggressive (auto-resolve) or more conservative (fail)?
2. Should we add a counter for how many times collisions occurred?
3. Should there be a config option to disable collision detection?
4. Is the warning too verbose for typical workflows?

## Current Behavior
- Skips colliding issues (preserves local)
- Prints warning to stderr
- Suggests manual resolution command
- Continues with non-colliding issues


Add integration tests for auto-import collision detection

The auto-import collision detection fix (bd-376) needs comprehensive integration tests.

## Test Scenarios Needed

### 1. Basic Collision Detection
```go
TestAutoImportWithCollisions
- Setup: Create issue in DB with status=closed
- Simulate: Git pull with JSONL containing same issue with status=open
- Verify: Local changes preserved, warning printed, issue still closed
```

### 2. Multiple Collisions
```go
TestAutoImportWithMultipleCollisions
- Setup: 5 issues with local modifications
- Simulate: JSONL with 3 colliding, 2 matching
- Verify: 3 skipped, 2 updated, appropriate warnings
```

### 3. No Collisions (Happy Path)
```go
TestAutoImportWithoutCollisions
- Setup: Clean database
- Simulate: JSONL with new issues + exact matches
- Verify: All imported successfully, no warnings
```

### 4. All Collisions
```go
TestAutoImportAllCollisions
- Setup: Every issue has local modifications
- Simulate: JSONL with conflicting versions
- Verify: All skipped, warning lists all issues
```

### 5. Collision Detection Failure
```go
TestAutoImportCollisionDetectionError
- Setup: Mock DetectCollisions() to return error
- Verify: Import skipped entirely (safe failure mode)
```

### 6. Hash-Based Skip
```go
TestAutoImportHashUnchanged
- Setup: JSONL hash matches last_import_hash
- Verify: No collision detection runs, fast path
```

### 7. Fallback for Non-SQLite
```go
TestAutoImportWithoutCollisionDetection
- Setup: Mock non-SQLite storage backend
- Verify: Falls back to old behavior, no collision detection
```

## Test Infrastructure Needed

1. **Helper functions:**
   - createTestDBWithIssues()
   - writeJSONLFile()
   - captureStderrWarnings()
   - simulateGitPull()

2. **Fixtures:**
   - sample-collisions.jsonl
   - sample-exact-matches.jsonl
   - sample-mixed.jsonl

3. **Assertions:**
   - assertIssueState(id, expectedStatus)
   - assertWarningContains(text)
   - assertCollisionCount(n)

## Acceptance Criteria
- All 7 test scenarios implemented
- Tests pass consistently
- Code coverage >80% for autoImportIfNewer()
- Tests run in <5 seconds total
- Clear test names and documentation


Consider batching API for bulk issue creation (recovered from bd-222)

Current CreateIssue acquires a dedicated connection for each call. For bulk imports or agent workflows creating many issues, a batched API could improve performance:

CreateIssues(ctx, issues []*Issue, actor string) error

This would:
- Acquire one connection
- Use one IMMEDIATE transaction
- Insert all issues atomically
- Reduce connection overhead

Not urgent - current approach is correct and fast enough for typical use.

**Recovered from:** bd-360 (lost in auto-import bug, see LOST_ISSUES_RECOVERY.md)


Use safer placeholder pattern in replaceIDReferences

Currently uses bd-324 which could theoretically collide with user text. Use a truly unique placeholder like null bytes: \x00REMAP\x00_0_\x00 which are unlikely to appear in normal text. Located in collision.go:324. Very low probability issue but worth fixing for completeness.


Store git commit hash at compaction time to enable restoration of full issue history from version control. When issues are compacted, record the current git commit hash so users can restore the original uncompacted issue from git history.


Add optional compacted_at_commit string field to store git commit hash when issue is compacted


GH-3: Debug zsh killed error on bd init

User reports 'zsh: killed bd init' when running bd init or just bd command. Likely a crash or signal. Need to reproduce and investigate cause.

Awaiting user feedback - cannot reproduce locally, waiting for user to provide more details about environment and error message


Implement bd restore command

Create new restore command that checks out git commit from compacted_at_commit, reads issue from JSONL, and displays full history


Add tests for git-based restoration

Test compaction stores commit hash, restore command works, handles missing git repo gracefully


Document git-based restoration feature

Update COMPACTION.md and README.md with restoration workflow and examples


Add godoc comments for auto-flush functions

Add comprehensive godoc comments for findJSONLPath(), markDirtyAndScheduleFlush(), and flushToJSONL() explaining behavior, concurrency considerations, and error handling. Include notes about debouncing behavior (timer resets on each write, flush occurs 5s after LAST operation) and flush-on-exit guarantees. Located in cmd/bd/main.go:188-307.


Add performance benchmarks document

Document actual performance metrics with hyperfine tests


parallel_test_2


parallel_test_5


parallel_test_6


parallel_test_4


parallel_test_1


Add migration scripts for GitHub Issues

Create scripts to import from GitHub Issues API or exported JSON


Issue bd-89 demonstrates a data model inconsistency: an issue can have status='open' but also have a closed_at timestamp set. This creates a liminal state that violates the expected invariant that closed_at should only be set when status='closed'.

Root causes:
1. Import (bd import) updates status field independently from closed_at field
2. UpdateIssue allows status changes without managing closed_at
3. No database constraint enforcing the invariant
4. Export includes both fields independently in JSONL

Current behavior:
- bd close: Sets status='closed' AND closed_at (correct)
- bd update --status open: Sets status='open' but leaves closed_at unchanged (creates inconsistency)
- bd import: Can import inconsistent data from JSONL

Impact:
- 'bd ready' shows issues that appear closed (have closed_at)
- Confusing for users and downstream tools
- Stats may be inaccurate

Potential solutions:
A) Add CHECK constraint: (status = 'closed') = (closed_at IS NOT NULL)
B) Update import/update logic to enforce invariant in application code
C) Add a 'reopened' event that explicitly clears closed_at
D) Remove closed_at field entirely (calculate from events or use status only)

See bd-89 for concrete example.


parallel_test_4

Sections:
1. Introduction
2. Authentication
3. REST Endpoints
4. WebSocket Protocol
5. Error Handling
6. Rate Limits
7. Examples

For each endpoint:
- URL and method
- Request parameters
- Request body schema
- Response schema
- Error responses
- Code example

- API.md created with all sections
- All endpoints documented
- Request/response schemas
- WebSocket protocol docs
- Error codes and handling
- Rate limit documentation
- Examples in multiple languages
- Links from README.md

---


Add test coverage for auto-flush feature

Add comprehensive tests for auto-flush functionality:\n- Test that markDirtyAndScheduleFlush() is called after CRUD operations\n- Test debounce timing (rapid operations result in single flush)\n- Test --no-auto-flush flag disables feature\n- Test flush on program exit\n- Test concurrent operations don't cause races\n- Test error scenarios (disk full, permission denied, etc.)\n- Test import command triggers auto-flush\n\nCurrent implementation has no test coverage for the auto-flush feature. Located in cmd/bd/main_test.go (to be created).


Add command to rename issue prefix

Allow users to rename their issue prefix (e.g., from 'knowledge-work-' to 'kw-'). Must update the prefix in all issues and update all text references across all issue fields (title, description, design, acceptance criteria, notes) using regexp. Should validate new prefix format and prevent collisions with existing IDs.

Prefix validation rules:
- Max length: 8 characters
- Allowed characters: lowercase letters, numbers, hyphens
- Must start with a letter
- Must end with a hyphen (e.g., 'kw-', 'work-')
- Cannot be empty or just a hyphen

Implementation:
- Add validation function for prefix format
- Update all issue IDs in the database
- Use regexp to find and replace all text references across all fields
- Atomic operation (rollback on failure)


Auto-import fails in git workflows due to mtime issues

The auto-import mechanism (autoImportIfNewer) relies on file modification time comparison between JSONL and DB. This breaks in git workflows because git does not preserve original file modification times - pulled files get fresh mtimes based on checkout time.

Root causes:
1. Git checkout sets mtime to 'now', not original commit time
2. Auto-import compares JSONL mtime vs DB mtime (line 181 in main.go)
3. If DB was recently modified (agents working), mtime check fails
4. Auto-import silently returns without feedback
5. Agents continue with stale database state

This caused issues in VC project where 3 parallel agents:
- Pulled updated .beads/issues.jsonl from git
- Auto-import didn't trigger (JSONL appeared older than DB)
- Agents couldn't find their assigned issues
- Agents exported from wrong database, corrupting JSONL


Git-based restoration for compacted issues


Implement hybrid solution to enforce status/closed_at invariant:
- Database CHECK constraint
- Smart UpdateIssue logic
- Import enforcement
- Reopen command

This is a data integrity issue that affects statistics and will cause problems for agent swarms. The hybrid approach provides defense-in-depth.

See ULTRATHINK_BD224.md for full analysis and rationale.

Parent of all implementation tasks for this fix.


Before we add the constraint, we need to know what data is inconsistent.

Steps:
1. Query database for status/closed_at mismatches
2. Document each case (how many, which issues, patterns)
3. Decide on cleanup strategy (trust status vs trust closed_at)
4. Create SQL script for cleanup

Output: Document with counts and cleanup SQL ready to review.

This unblocks the migration work.


Reach 1.0 release milestone

Stabilize API, finalize documentation, comprehensive testing


Code review follow-up: Post-PR #8 merge improvements

Follow-up tasks from the ultrathink code review of PR #8 merge (bd-62).

**Context:** PR #8 successfully merged atomic counter + dirty tracking. Core functionality is solid but several improvements identified.

**Critical (P0-P1):**
- bd-64: Fix SyncAllCounters performance bottleneck (P0)
- bd-65: Add migration for issue_counters table (P1)
- bd-66: Make import counter sync failure fatal (P1)

**Nice to have (P2-P3):**
- bd-67: Update test comments (P2)
- bd-68: Add performance benchmarks (P2)
- bd-69: Add metrics/logging (P3)
- bd-70: Add EXPLAIN QUERY PLAN tests (P3)

**Overall assessment:** 4/5 stars - Excellent implementation with one critical performance issue. After bd-64 is fixed, this becomes 5/5.

**Review document:** Available if needed


Code review: Auto-import collision detection fix (bd-228)

Request thorough code review of the auto-import collision detection implementation.

## Files Changed
- cmd/bd/main.go: autoImportIfNewer() and new autoImportWithoutCollisionDetection()

## Review Focus Areas

### 1. Correctness
- Does collision detection properly identify conflicts?
- Are colliding issues correctly filtered from import?
- Is the fallback function correct for non-SQLite backends?

### 2. Edge Cases
- What happens if DetectCollisions() fails?
- What if all issues are collisions?
- What if JSONL is malformed?
- Race conditions with concurrent auto-imports?

### 3. User Experience
- Is the warning message clear and actionable?
- Should we log to a file instead of/in addition to stderr?
- Should there be a --auto-resolve flag?

### 4. Performance
- Does collision detection add significant latency?
- Will this work with 1000+ issues?
- Any unnecessary N+1 queries?

### 5. Testing Gaps
- Do we need integration tests for collision scenarios?
- Should we test the warning output?
- Test autoImportWithoutCollisionDetection() fallback?

## Questions for Reviewer
1. Should auto-import be more aggressive (auto-resolve) or more conservative (fail)?
2. Should we add a counter for how many times collisions occurred?
3. Should there be a config option to disable collision detection?
4. Is the warning too verbose for typical workflows?

## Current Behavior
- Skips colliding issues (preserves local)
- Prints warning to stderr
- Suggests manual resolution command
- Continues with non-colliding issues


The auto-import collision detection fix (bd-376) needs comprehensive integration tests.

## Test Scenarios Needed

### 1. Basic Collision Detection
```go
TestAutoImportWithCollisions
- Setup: Create issue in DB with status=closed
- Simulate: Git pull with JSONL containing same issue with status=open
- Verify: Local changes preserved, warning printed, issue still closed
```

### 2. Multiple Collisions
```go
TestAutoImportWithMultipleCollisions
- Setup: 5 issues with local modifications
- Simulate: JSONL with 3 colliding, 2 matching
- Verify: 3 skipped, 2 updated, appropriate warnings
```

### 3. No Collisions (Happy Path)
```go
TestAutoImportWithoutCollisions
- Setup: Clean database
- Simulate: JSONL with new issues + exact matches
- Verify: All imported successfully, no warnings
```

### 4. All Collisions
```go
TestAutoImportAllCollisions
- Setup: Every issue has local modifications
- Simulate: JSONL with conflicting versions
- Verify: All skipped, warning lists all issues
```

### 5. Collision Detection Failure
```go
TestAutoImportCollisionDetectionError
- Setup: Mock DetectCollisions() to return error
- Verify: Import skipped entirely (safe failure mode)
```

### 6. Hash-Based Skip
```go
TestAutoImportHashUnchanged
- Setup: JSONL hash matches last_import_hash
- Verify: No collision detection runs, fast path
```

### 7. Fallback for Non-SQLite
```go
TestAutoImportWithoutCollisionDetection
- Setup: Mock non-SQLite storage backend
- Verify: Falls back to old behavior, no collision detection
```

## Test Infrastructure Needed

1. **Helper functions:**
   - createTestDBWithIssues()
   - writeJSONLFile()
   - captureStderrWarnings()
   - simulateGitPull()

2. **Fixtures:**
   - sample-collisions.jsonl
   - sample-exact-matches.jsonl
   - sample-mixed.jsonl

3. **Assertions:**
   - assertIssueState(id, expectedStatus)
   - assertWarningContains(text)
   - assertCollisionCount(n)

## Acceptance Criteria
- All 7 test scenarios implemented
- Tests pass consistently
- Code coverage >80% for autoImportIfNewer()
- Tests run in <5 seconds total
- Clear test names and documentation


Current CreateIssue acquires a dedicated connection for each call. For bulk imports or agent workflows creating many issues, a batched API could improve performance:

CreateIssues(ctx, issues []*Issue, actor string) error

This would:
- Acquire one connection
- Use one IMMEDIATE transaction
- Insert all issues atomically
- Reduce connection overhead

Not urgent - current approach is correct and fast enough for typical use.

**Recovered from:** bd-360 (lost in auto-import bug, see LOST_ISSUES_RECOVERY.md)


Test auto-sync feature


GH-3: Debug zsh killed error on bd init

User reports 'zsh: killed bd init' when running bd init or just bd command. Likely a crash or signal. Need to reproduce and investigate cause.


Add compacted_at_commit field to Issue type

Add optional compacted_at_commit string field to store git commit hash when issue is compacted


Record git commit hash during compaction

Update compact command to capture current git HEAD commit hash and store in compacted_at_commit field


Implement bd restore command

Create new restore command that checks out git commit from compacted_at_commit, reads issue from JSONL, and displays full history


Add tests for git-based restoration

Test compaction stores commit hash, restore command works, handles missing git repo gracefully


Document git-based restoration feature

Update COMPACTION.md and README.md with restoration workflow and examples


Add performance benchmarks document

Document actual performance metrics with hyperfine tests


parallel_test_2


parallel_test_5


Regular auto-ID issue


parallel_test_6


parallel_test_4


parallel_test_1


Add migration scripts for GitHub Issues

Create scripts to import from GitHub Issues API or exported JSON


Request for Docker container hosting to use beads across multiple dev machines. Would need to consider: centralized database (PostgreSQL?), authentication, concurrent access, API server, etc. This is a significant architectural change from the current local-first model.


Currently uses bd-324 which could theoretically collide with user text. Use a truly unique placeholder like null bytes: \x00REMAP\x00_0_\x00 which are unlikely to appear in normal text. Located in collision.go:324. Very low probability issue but worth fixing for completeness.


Add tests for git-based restoration


The auto-import mechanism (autoImportIfNewer) relies on file modification time comparison between JSONL and DB. This breaks in git workflows because git does not preserve original file modification times - pulled files get fresh mtimes based on checkout time.

Root causes:
1. Git checkout sets mtime to 'now', not original commit time
2. Auto-import compares JSONL mtime vs DB mtime (line 181 in main.go)
3. If DB was recently modified (agents working), mtime check fails
4. Auto-import silently returns without feedback
5. Agents continue with stale database state

This caused issues in VC project where 3 parallel agents:
- Pulled updated .beads/issues.jsonl from git
- Auto-import didn't trigger (JSONL appeared older than DB)
- Agents couldn't find their assigned issues
- Agents exported from wrong database, corrupting JSONL


parallel_test_8


Test flush tracking


parallel_test_9


Fix: bd init --prefix test -q flag not recognized

The init command doesn't recognize the -q flag. When running 'bd init --prefix test -q', it fails silently or behaves unexpectedly. The flag should either be implemented for quiet mode or removed from documentation if not supported.


race_test_4


race_test_8


race_test_5


race_test_16


race_test_17


race_test_10


race_test_11


final_test_10


Test export cancels timer


final_test_7


stress_test_5


stress_test_6


stress_test_1


Add label management commands to CLI

Currently labels can only be managed programmatically via the storage API. Add CLI commands to add, remove, and filter by labels.


race_test_2


race_test_7


race_test_14


Test incremental export

Testing bd-39 implementation


race_test_9


race_test_8


race_test_19


race_test_16


race_test_18


race_test_20


race_test_6


race_test_3


race_test_12


Test incremental 2


race_test_15


race_test_13


race_test_10


race_test_17


verification_


final_review_test_


P2: Consider batching API for bulk issue creation

Current CreateIssue acquires a dedicated connection for each call. For bulk imports or agent workflows creating many issues, a batched API could improve performance:

CreateIssues(ctx, issues []*Issue, actor string) error

This would:
- Acquire one connection
- Use one IMMEDIATE transaction
- Insert all issues atomically
- Reduce connection overhead

Not urgent - current approach is correct and fast enough for typical use.


Data model allows inconsistent status/closed_at states

Issue bd-89 demonstrates a data model inconsistency: an issue can have status='open' but also have a closed_at timestamp set. This creates a liminal state that violates the expected invariant that closed_at should only be set when status='closed'.

Root causes:
1. Import (bd import) updates status field independently from closed_at field
2. UpdateIssue allows status changes without managing closed_at
3. No database constraint enforcing the invariant
4. Export includes both fields independently in JSONL

Current behavior:
- bd close: Sets status='closed' AND closed_at (correct)
- bd update --status open: Sets status='open' but leaves closed_at unchanged (creates inconsistency)
- bd import: Can import inconsistent data from JSONL

Impact:
- 'bd ready' shows issues that appear closed (have closed_at)
- Confusing for users and downstream tools
- Stats may be inaccurate

Potential solutions:
A) Add CHECK constraint: (status = 'closed') = (closed_at IS NOT NULL)
B) Update import/update logic to enforce invariant in application code
C) Add a 'reopened' event that explicitly clears closed_at
D) Remove closed_at field entirely (calculate from events or use status only)

See bd-89 for concrete example.


Epic: Fix status/closed_at inconsistency (bd-224 solution)

Implement hybrid solution to enforce status/closed_at invariant:
- Database CHECK constraint
- Smart UpdateIssue logic
- Import enforcement
- Reopen command

This is a data integrity issue that affects statistics and will cause problems for agent swarms. The hybrid approach provides defense-in-depth.

See ULTRATHINK_BD224.md for full analysis and rationale.

Parent of all implementation tasks for this fix.


Audit and document all inconsistent issues in database

Before we add the constraint, we need to know what data is inconsistent.

Steps:
1. Query database for status/closed_at mismatches
2. Document each case (how many, which issues, patterns)
3. Decide on cleanup strategy (trust status vs trust closed_at)
4. Create SQL script for cleanup

Output: Document with counts and cleanup SQL ready to review.

This unblocks the migration work.


Final test

Testing with new binary


Request thorough code review of the auto-import collision detection implementation.

## Files Changed
- cmd/bd/main.go: autoImportIfNewer() and new autoImportWithoutCollisionDetection()

## Review Focus Areas

### 1. Correctness
- Does collision detection properly identify conflicts?
- Are colliding issues correctly filtered from import?
- Is the fallback function correct for non-SQLite backends?

### 2. Edge Cases
- What happens if DetectCollisions() fails?
- What if all issues are collisions?
- What if JSONL is malformed?
- Race conditions with concurrent auto-imports?

### 3. User Experience
- Is the warning message clear and actionable?
- Should we log to a file instead of/in addition to stderr?
- Should there be a --auto-resolve flag?

### 4. Performance
- Does collision detection add significant latency?
- Will this work with 1000+ issues?
- Any unnecessary N+1 queries?

### 5. Testing Gaps
- Do we need integration tests for collision scenarios?
- Should we test the warning output?
- Test autoImportWithoutCollisionDetection() fallback?

## Questions for Reviewer
1. Should auto-import be more aggressive (auto-resolve) or more conservative (fail)?
2. Should we add a counter for how many times collisions occurred?
3. Should there be a config option to disable collision detection?
4. Is the warning too verbose for typical workflows?

## Current Behavior
- Skips colliding issues (preserves local)
- Prints warning to stderr
- Suggests manual resolution command
- Continues with non-colliding issues


The auto-import collision detection fix (bd-376) needs comprehensive integration tests.

## Test Scenarios Needed

### 1. Basic Collision Detection
```go
TestAutoImportWithCollisions
- Setup: Create issue in DB with status=closed
- Simulate: Git pull with JSONL containing same issue with status=open
- Verify: Local changes preserved, warning printed, issue still closed
```

### 2. Multiple Collisions
```go
TestAutoImportWithMultipleCollisions
- Setup: 5 issues with local modifications
- Simulate: JSONL with 3 colliding, 2 matching
- Verify: 3 skipped, 2 updated, appropriate warnings
```

### 3. No Collisions (Happy Path)
```go
TestAutoImportWithoutCollisions
- Setup: Clean database
- Simulate: JSONL with new issues + exact matches
- Verify: All imported successfully, no warnings
```

### 4. All Collisions
```go
TestAutoImportAllCollisions
- Setup: Every issue has local modifications
- Simulate: JSONL with conflicting versions
- Verify: All skipped, warning lists all issues
```

### 5. Collision Detection Failure
```go
TestAutoImportCollisionDetectionError
- Setup: Mock DetectCollisions() to return error
- Verify: Import skipped entirely (safe failure mode)
```

### 6. Hash-Based Skip
```go
TestAutoImportHashUnchanged
- Setup: JSONL hash matches last_import_hash
- Verify: No collision detection runs, fast path
```

### 7. Fallback for Non-SQLite
```go
TestAutoImportWithoutCollisionDetection
- Setup: Mock non-SQLite storage backend
- Verify: Falls back to old behavior, no collision detection
```

## Test Infrastructure Needed

1. **Helper functions:**
   - createTestDBWithIssues()
   - writeJSONLFile()
   - captureStderrWarnings()
   - simulateGitPull()

2. **Fixtures:**
   - sample-collisions.jsonl
   - sample-exact-matches.jsonl
   - sample-mixed.jsonl

3. **Assertions:**
   - assertIssueState(id, expectedStatus)
   - assertWarningContains(text)
   - assertCollisionCount(n)

## Acceptance Criteria
- All 7 test scenarios implemented
- Tests pass consistently
- Code coverage >80% for autoImportIfNewer()
- Tests run in <5 seconds total
- Clear test names and documentation


Current CreateIssue acquires a dedicated connection for each call. For bulk imports or agent workflows creating many issues, a batched API could improve performance:

CreateIssues(ctx, issues []*Issue, actor string) error

This would:
- Acquire one connection
- Use one IMMEDIATE transaction
- Insert all issues atomically
- Reduce connection overhead

Not urgent - current approach is correct and fast enough for typical use.

**Recovered from:** bd-360 (lost in auto-import bug, see LOST_ISSUES_RECOVERY.md)


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.


Store git commit hash at compaction time to enable restoration of full issue history from version control. When issues are compacted, record the current git commit hash so users can restore the original uncompacted issue from git history.


Add optional compacted_at_commit string field to store git commit hash when issue is compacted


Update compact command to capture current git HEAD commit hash and store in compacted_at_commit field


Implement bd restore command

Create new restore command that checks out git commit from compacted_at_commit, reads issue from JSONL, and displays full history


parallel_test_3


Test issue

Testing prefix


Test label dirty tracking


parallel_test_10


parallel_test_6


parallel_test_2


parallel_test_5


Concurrent bd commands collide on shared .tmp filename

When multiple bd commands run concurrently (e.g., different agents), they all write to .beads/issues.jsonl.tmp causing race conditions. One command's rename succeeds, consuming the .tmp file, then other commands fail with 'no such file or directory'. Solution: use unique temp filenames with PID or random suffix (e.g., issues.jsonl.tmp.12345).


parallel_test_4


parallel_test_1


GH-11: Add Docker support for hosted/shared instance

Request for Docker container hosting to use beads across multiple dev machines. Would need to consider: centralized database (PostgreSQL?), authentication, concurrent access, API server, etc. This is a significant architectural change from the current local-first model.


Test hash-based import


GH-3: Debug zsh killed error on bd init

User reports 'zsh: killed bd init' when running bd init or just bd command. Likely a crash or signal. Need to reproduce and investigate cause.


The auto-import mechanism (autoImportIfNewer) relies on file modification time comparison between JSONL and DB. This breaks in git workflows because git does not preserve original file modification times - pulled files get fresh mtimes based on checkout time.

Root causes:
1. Git checkout sets mtime to 'now', not original commit time
2. Auto-import compares JSONL mtime vs DB mtime (line 181 in main.go)
3. If DB was recently modified (agents working), mtime check fails
4. Auto-import silently returns without feedback
5. Agents continue with stale database state

This caused issues in VC project where 3 parallel agents:
- Pulled updated .beads/issues.jsonl from git
- Auto-import didn't trigger (JSONL appeared older than DB)
- Agents couldn't find their assigned issues
- Agents exported from wrong database, corrupting JSONL


parallel_test_3

Review GH-11 requirements:
- User wants to use beads across multiple dev machines
- Docker hosting was proposed solution

New approach:
- Phase 1: Pure git-based sync (no Docker needed)
- Phase 2: Optional Docker gateway for convenience
- Addresses use case without mandating server

Update GH-11 with:
- Link to DESIGN-GIT-SYNC.md
- Explain git-based approach
- Note optional Docker gateway
- Ask for feedback

Close issue when sync features implemented.

- GH-11 reviewed and understood
- Comment added explaining sync approach
- Link to design document
- User feedback requested
- Issue closed when Phase 1-2 complete
- Documentation cross-referenced


Add performance benchmarks document

Document actual performance metrics with hyperfine tests


Federation allows:
- Multiple gateways sync with each other
- Each gateway has authoritative set of issues
- Peer-to-peer sync protocol
- Conflict resolution across gateways

Architecture:
- Gateway-to-gateway API
- Peer discovery
- Sync protocol between gateways
- Distributed conflict resolution

Complex feature - only if team usage emerges.

- Gateway federation protocol designed
- Peer discovery mechanism
- Gateway-to-gateway sync
- Distributed conflict resolution
- Tests for federation
- Documentation in FEDERATION.md

---


parallel_test_1

Add new section: "Multi-Device Sync"

Content:
- Brief overview of git-based sync
- Link to SYNC.md for details
- Link to GATEWAY.md for gateway mode
- Quick start examples
- Configuration options

Keep brief - detailed docs in separate files.

- README.md updated with sync section
- Links to SYNC.md and GATEWAY.md
- Quick start examples
- Configuration overview
- Maintains existing structure
- All links working

---


verification_

Sections:
1. Introduction
2. Quick Start
3. Manual Git Workflow
4. bd sync Command
5. bd daemon Background Sync
6. Git Hooks
7. Conflict Resolution
8. Best Practices
9. Troubleshooting
10. FAQ

Include examples, code snippets, and diagrams.

- SYNC.md created with all sections
- Quick start guide for new users
- Detailed workflow examples
- Conflict resolution guide
- Best practices section
- Troubleshooting with solutions
- FAQ with common questions
- Code examples working
- Links from README.md

---


Create Claude Code plugin for beads

Package beads as a Claude Code plugin for easy installation via /plugin command.

Context: GitHub issue #28 - https://github.com/steveyegge/beads/issues/28

Current state:
- MCP server exists in integrations/beads-mcp/
- No plugin packaging yet

Deliverables:
1. .claude-plugin/plugin.json with metadata
2. .claude-plugin/marketplace.json for distribution
3. Custom slash commands (/bd-ready, /bd-create, /bd-show, etc.)
4. Bundle MCP server configuration
5. Optional: Pre-configured hooks for auto-sync
6. Documentation for installation and usage

Benefits:
- Makes beads instantly discoverable in Claude Code ecosystem
- Single-command installation vs. manual setup
- Bundled cohesive experience
- Lowers adoption barrier significantly

References:
- https://www.anthropic.com/news/claude-code-plugins
- https://docs.claude.com/en/docs/claude-code/plugins


parallel_test_7


GH-3: Debug zsh killed error on bd init


Document git-based restoration feature

Update COMPACTION.md and README.md with restoration workflow and examples


Reach 1.0 release milestone

Stabilize API, finalize documentation, comprehensive testing


Follow-up tasks from the ultrathink code review of PR #8 merge (bd-62).

**Context:** PR #8 successfully merged atomic counter + dirty tracking. Core functionality is solid but several improvements identified.

**Critical (P0-P1):**
- bd-64: Fix SyncAllCounters performance bottleneck (P0)
- bd-65: Add migration for issue_counters table (P1)
- bd-66: Make import counter sync failure fatal (P1)

**Nice to have (P2-P3):**
- bd-67: Update test comments (P2)
- bd-68: Add performance benchmarks (P2)
- bd-69: Add metrics/logging (P3)
- bd-70: Add EXPLAIN QUERY PLAN tests (P3)

**Overall assessment:** 4/5 stars - Excellent implementation with one critical performance issue. After bd-64 is fixed, this becomes 5/5.

**Review document:** Available if needed


Add performance benchmarks document


Add migration scripts for GitHub Issues

Create scripts to import from GitHub Issues API or exported JSON


Research Claude Code plugin structure and requirements

Study the plugin format, required files, and best practices.

Tasks:
- Review official plugin documentation
- Examine example plugins if available
- Document plugin.json schema
- Understand marketplace.json requirements
- Identify slash command format


Bug: Import creating duplicates instead of deduplicating

Git log shows "73 issues remapped" during merge, suggesting --resolve-collisions is creating duplicate issues with new IDs instead of recognizing they're identical and deduplicating them.

Evidence: bd-488, bd-367, bd-396 are all "Epic: Fix status/closed_at inconsistency" with identical content but different IDs and timestamps.

Root cause likely in DetectCollisions or --resolve-collisions logic - it's treating content-identical issues as collisions when they should be exact matches.

**Root Cause Found:**

DetectCollisions only checks if incoming issues exist in the DB. It doesn't deduplicate within the incoming JSONL batch itself.

When JSONL contains multiple issues with:
- Different IDs (bd-488, bd-367, bd-396)
- Identical content
- Different timestamps

DetectCollisions treats them all as "new issues" because none exist in DB, then --resolve-collisions leaves them as-is.

**Fix:**
Add deduplication step in DetectCollisions to detect content-identical issues within the incoming batch and consolidate them before processing.

**Fixed in commit [pending]**

Changes made:
1. Added `deduplicateIncomingIssues()` function in collision.go
2. Modified `DetectCollisions()` to call deduplication before processing
3. Deduplication consolidates content-identical issues, keeping smallest ID
4. Added comprehensive test suite in collision_dedup_test.go

Result: Importing JSONL with duplicates now correctly consolidates them instead of creating multiple copies.


Implement bd restore command


Low priority chore


Add integration tests for auto-import collision detection

The auto-import collision detection fix (bd-376) needs comprehensive integration tests.

## Test Scenarios Needed

### 1. Basic Collision Detection
```go
TestAutoImportWithCollisions
- Setup: Create issue in DB with status=closed
- Simulate: Git pull with JSONL containing same issue with status=open
- Verify: Local changes preserved, warning printed, issue still closed
```

### 2. Multiple Collisions
```go
TestAutoImportWithMultipleCollisions
- Setup: 5 issues with local modifications
- Simulate: JSONL with 3 colliding, 2 matching
- Verify: 3 skipped, 2 updated, appropriate warnings
```

### 3. No Collisions (Happy Path)
```go
TestAutoImportWithoutCollisions
- Setup: Clean database
- Simulate: JSONL with new issues + exact matches
- Verify: All imported successfully, no warnings
```

### 4. All Collisions
```go
TestAutoImportAllCollisions
- Setup: Every issue has local modifications
- Simulate: JSONL with conflicting versions
- Verify: All skipped, warning lists all issues
```

### 5. Collision Detection Failure
```go
TestAutoImportCollisionDetectionError
- Setup: Mock DetectCollisions() to return error
- Verify: Import skipped entirely (safe failure mode)
```

### 6. Hash-Based Skip
```go
TestAutoImportHashUnchanged
- Setup: JSONL hash matches last_import_hash
- Verify: No collision detection runs, fast path
```

### 7. Fallback for Non-SQLite
```go
TestAutoImportWithoutCollisionDetection
- Setup: Mock non-SQLite storage backend
- Verify: Falls back to old behavior, no collision detection
```

## Test Infrastructure Needed

1. **Helper functions:**
   - createTestDBWithIssues()
   - writeJSONLFile()
   - captureStderrWarnings()
   - simulateGitPull()

2. **Fixtures:**
   - sample-collisions.jsonl
   - sample-exact-matches.jsonl
   - sample-mixed.jsonl

3. **Assertions:**
   - assertIssueState(id, expectedStatus)
   - assertWarningContains(text)
   - assertCollisionCount(n)

## Acceptance Criteria
- All 7 test scenarios implemented
- Tests pass consistently
- Code coverage >80% for autoImportIfNewer()
- Tests run in <5 seconds total
- Clear test names and documentation


Consider batching API for bulk issue creation (recovered from bd-222)

Current CreateIssue acquires a dedicated connection for each call. For bulk imports or agent workflows creating many issues, a batched API could improve performance:

CreateIssues(ctx, issues []*Issue, actor string) error

This would:
- Acquire one connection
- Use one IMMEDIATE transaction
- Insert all issues atomically
- Reduce connection overhead

Not urgent - current approach is correct and fast enough for typical use.

**Recovered from:** bd-390 (lost in auto-import bug, see LOST_ISSUES_RECOVERY.md)

Investigation complete. Successfully recovered 3 valuable issues (bd-536, bd-233) from 22 total lost. Historical context preserved in bd-89 notes. Auto-import collision detection (bd-552) now prevents future data loss.


Ultrathink: Choose solution for status/closed_at inconsistency (bd-224)

Deep analysis of solution options for bd-538 data model inconsistency issue.

Context:
- Target users: individual devs and small teams
- Future: swarms of agent workers
- Brand-new codebase with few users (can break things)
- Issue: status='open' with closed_at!=NULL creates liminal state

Options to evaluate:
A) Database CHECK constraint
B) Application-level enforcement
C) Add explicit reopened event
D) Remove closed_at field entirely

Analysis framework:
1. Simplicity (mental model for devs)
2. Robustness (hard to break, especially for agent swarms)
3. Migration cost (schema changes, data cleanup)
4. Future extensibility
5. Performance
6. Consistency guarantees

Need to determine best approach given tradeoffs.

Analysis complete. Recommendation: Hybrid approach with DB CHECK constraint + smart UpdateIssue + import enforcement + reopen command.

Key insights:
- DB constraint provides defense-in-depth perfect for agent swarms
- Statistics calculation currently uses 'closed_at IS NOT NULL' which is BROKEN by inconsistent data
- UpdateIssue and Import don't manage the invariant
- EventReopened exists but is unused

See ULTRATHINK_BD224.md for full analysis.


Label management fully implemented with comprehensive test coverage (10 test cases). Provides foundation for better data organization and filtering.


Investigate data recovery for issues overwritten by auto-import bug

Auto-import bug (bd-376) has been silently overwriting local changes since the feature was introduced. We need to investigate if any important data was lost and if it can be recovered.

## Data Sources for Recovery

1. **Git history of .beads/issues.jsonl** - May contain older versions with lost changes
2. **Events table** - Has full audit trail with old_value/new_value
3. **Commit messages** - May reference work that was done but later overwritten

## Investigation Steps

1. Check events table for patterns:
   - Look for auto-import events that changed status from closed → open
   - Look for manual updates followed by auto-import reverts
   - Identify issues with suspicious event sequences

2. Git archaeology:
   - Check git log for .beads/issues.jsonl
   - Look for commits where status=closed was changed to status=open
   - Compare git history vs current database state

3. Document lost work:
   - Create list of issues that were closed but reverted to open
   - Identify any critical work that was marked done but lost
   - Check if any issue references are now broken

## Recovery Actions

If significant data loss is found:
- Create script to reconstruct lost state from events table
- Manually review and restore critical closed issues
- Document lessons learned for preventing future data loss

## Example Query

```sql
-- Find issues where status was manually changed then reverted by auto-import
SELECT
  issue_id,
  GROUP_CONCAT(event_type || ':' || actor, ' -> ') as event_chain
FROM events
WHERE issue_id IN (
  SELECT DISTINCT issue_id
  FROM events
  WHERE actor = 'auto-import' AND event_type = 'status_changed'
)
GROUP BY issue_id
HAVING event_chain LIKE '%stevey%auto-import%';
```

Investigation complete. Successfully recovered 3 valuable issues (bd-367, bd-233) from 22 total lost. Historical context preserved in bd-89 notes. Auto-import collision detection (bd-376) now prevents future data loss.


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


Reach 1.0 release milestone


parallel_test_7

Add validation in AddDependency that checks if dep.Type == DepParentChild and validates the direction is correct (parent depends on child, not child on parent). Reject with clear error message if direction is backwards.

- Child→parent dependencies are rejected with clear error
- Parent→child dependencies work as expected
- Error message explains correct direction
- Tests cover both valid and invalid cases


Add migration scripts for GitHub Issues


parallel_test_9


Build collision resolution tooling for distributed branch workflows

When branches diverge and both create issues, auto-incrementing IDs collide on merge. Build excellent tooling to detect collisions during import, auto-renumber issues with fewer dependencies, update all references in descriptions and dependency links, and provide clear user feedback. Goal: keep beautiful brevity of numeric IDs (bd-551) while handling distributed creation gracefully.


Add compacted_at_commit field to Issue type

Add optional compacted_at_commit string field to store git commit hash when issue is compacted


Low priority chore

Benchmarks for:
1. JSONL import/export performance
2. Collision detection performance
3. Three-way merge performance
4. Gateway API latency
5. WebSocket throughput
6. Concurrent client handling
7. Large database operations (10k+ issues)

Use Go benchmarking and profiling tools.

Track performance over time in CI.

- Benchmark suite implemented
- All key operations benchmarked
- CPU and memory profiling
- Large database benchmarks
- Concurrent operation benchmarks
- CI integration for regression detection
- Performance baseline documented
- Documentation in PERFORMANCE.md

---


Code review follow-up: Post-PR #8 merge improvements

All tasks completed: bd-64 (performance), bd-65 (migration), bd-66 (version sync), bd-67 (version script), bd-69 (CI coverage), bd-70 (test coverage). Code review follow-up complete.


Implement bd restore command

Create new restore command that checks out git commit from compacted_at_commit, reads issue from JSONL, and displays full history


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.


Git-based restoration for compacted issues

Store git commit hash at compaction time to enable restoration of full issue history from version control. When issues are compacted, record the current git commit hash so users can restore the original uncompacted issue from git history.


Record git commit hash during compaction

Update compact command to capture current git HEAD commit hash and store in compacted_at_commit field


Critical: Auto-import silently overwrites local changes without collision detection

## Problem

Auto-import is silently overwriting local changes when pulling from Git, without using collision detection or warning the user.

## Evidence

Issue bd-89 timeline:
1. 08:07:31 - Manual update: set status=in_progress
2. 08:13:08 - Manual close: set status=closed, closed_at set
3. 08:33:25 - **Auto-import silently overwrote to status=open** (discarded local change)
4. 08:57:59 - Auto-import overwrote again
5. 08:58:30 - Auto-import overwrote again

Query shows 10+ issues with multiple auto-import overwrites, including bd-513, bd-199, bd-358-206.

## Root Cause Hypothesis

When two clones both modify issue N:
- Clone A: modifies bd-89, exports to JSONL, commits
- Clone B: also modifies bd-89 differently, pulls from A
- Auto-import uses UpdateIssue which **unconditionally overwrites**
- Clone B's local changes are silently lost
- No collision detection warning
- References and work context corrupted

## Impact

- **Data loss**: Local changes silently discarded
- **Corruption**: Issue state doesn't reflect actual work done
- **Lost references**: Comments/deps referencing lost state are now wrong
- **Trust**: Can't trust the database reflects reality
- **Agent swarms**: Catastrophic for parallel workers

## Expected Behavior

Auto-import should:
1. Detect collisions (same ID, different content)
2. Warn user or fail import
3. Offer resolution options (--resolve-collisions)
4. Never silently overwrite without user confirmation

## Current Behavior

Auto-import silently overwrites using UpdateIssue, no collision detection.

## Reproduction

1. Clone A: bd update bd-1 --status in_progress
2. Clone A: git add .beads/issues.jsonl && git commit && git push
3. Clone B: bd update bd-1 --status closed
4. Clone B: git pull (triggers auto-import)
5. Result: bd-1 status silently reverted to in_progress, closed state lost

## Files Involved

- cmd/bd/main.go: auto-import trigger logic
- cmd/bd/import.go: import without collision detection
- internal/storage/sqlite/sqlite.go: UpdateIssue unconditionally overwrites


When branches diverge and both create issues, auto-incrementing IDs collide on merge. Build excellent tooling to detect collisions during import, auto-renumber issues with fewer dependencies, update all references in descriptions and dependency links, and provide clear user feedback. Goal: keep beautiful brevity of numeric IDs (bd-374) while handling distributed creation gracefully.


Add tests for git-based restoration

Test compaction stores commit hash, restore command works, handles missing git repo gracefully


parallel_test_8


Fix: bd init --prefix test -q flag not recognized

The init command doesn't recognize the -q flag. When running 'bd init --prefix test -q', it fails silently or behaves unexpectedly. The flag should either be implemented for quiet mode or removed from documentation if not supported.


race_test_4


Write plugin documentation

Create comprehensive documentation for the plugin.

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


race_test_8


race_test_5


race_test_16


race_test_17


race_test_10


race_test_11


final_test_10


When multiple bd commands run concurrently (e.g., different agents), they all write to .beads/issues.jsonl.tmp causing race conditions. One command's rename succeeds, consuming the .tmp file, then other commands fail with 'no such file or directory'. Solution: use unique temp filenames with PID or random suffix (e.g., issues.jsonl.tmp.12345).


stress_test_5


stress_test_6


Test plugin installation and functionality

Verify the plugin works end-to-end.

Test cases:
- Fresh installation via /plugin command
- All slash commands work correctly
- MCP server tools are accessible
- Configuration options work
- Documentation is accurate
- Works in both terminal and VS Code


stress_test_1


Add label management commands to CLI

Currently labels can only be managed programmatically via the storage API. Add CLI commands to add, remove, and filter by labels.


race_test_2


race_test_7


race_test_14


race_test_9


race_test_8


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


race_test_19


race_test_16


race_test_18


race_test_20


race_test_6


race_test_3


race_test_12


race_test_15


race_test_13


race_test_10


Add composite index on dependencies(depends_on_id, type)

The hierarchical blocking query does:
JOIN dependencies d ON d.depends_on_id = bt.issue_id
WHERE d.type = 'parent-child'

Currently we only have idx_dependencies_depends_on (line 41 in schema.go), which covers depends_on_id but not the type filter.

**Impact:**
- Query has to scan ALL dependencies for a given depends_on_id, then filter by type
- With 10k+ issues and many dependencies, this could cause slowdowns
- The blocker propagation happens recursively, amplifying the cost

**Solution:**
Add composite index: CREATE INDEX idx_dependencies_depends_on_type ON dependencies(depends_on_id, type)

**Testing:**
Run EXPLAIN QUERY PLAN on GetReadyWork query before/after to verify index usage.


race_test_17


verification_


final_review_test_


P2: Consider batching API for bulk issue creation

Current CreateIssue acquires a dedicated connection for each call. For bulk imports or agent workflows creating many issues, a batched API could improve performance:

CreateIssues(ctx, issues []*Issue, actor string) error

This would:
- Acquire one connection
- Use one IMMEDIATE transaction
- Insert all issues atomically
- Reduce connection overhead

Not urgent - current approach is correct and fast enough for typical use.


Data model allows inconsistent status/closed_at states

Issue bd-89 demonstrates a data model inconsistency: an issue can have status='open' but also have a closed_at timestamp set. This creates a liminal state that violates the expected invariant that closed_at should only be set when status='closed'.

Root causes:
1. Import (bd import) updates status field independently from closed_at field
2. UpdateIssue allows status changes without managing closed_at
3. No database constraint enforcing the invariant
4. Export includes both fields independently in JSONL

Current behavior:
- bd close: Sets status='closed' AND closed_at (correct)
- bd update --status open: Sets status='open' but leaves closed_at unchanged (creates inconsistency)
- bd import: Can import inconsistent data from JSONL

Impact:
- 'bd ready' shows issues that appear closed (have closed_at)
- Confusing for users and downstream tools
- Stats may be inaccurate

Potential solutions:
A) Add CHECK constraint: (status = 'closed') = (closed_at IS NOT NULL)
B) Update import/update logic to enforce invariant in application code
C) Add a 'reopened' event that explicitly clears closed_at
D) Remove closed_at field entirely (calculate from events or use status only)

See bd-89 for concrete example.


Deep analysis of solution options for bd-654 data model inconsistency issue.

Context:
- Target users: individual devs and small teams
- Future: swarms of agent workers
- Brand-new codebase with few users (can break things)
- Issue: status='open' with closed_at!=NULL creates liminal state

Options to evaluate:
A) Database CHECK constraint
B) Application-level enforcement
C) Add explicit reopened event
D) Remove closed_at field entirely

Analysis framework:
1. Simplicity (mental model for devs)
2. Robustness (hard to break, especially for agent swarms)
3. Migration cost (schema changes, data cleanup)
4. Future extensibility
5. Performance
6. Consistency guarantees

Need to determine best approach given tradeoffs.


Epic: Fix status/closed_at inconsistency (bd-224 solution)

Implement hybrid solution to enforce status/closed_at invariant:
- Database CHECK constraint
- Smart UpdateIssue logic
- Import enforcement
- Reopen command

This is a data integrity issue that affects statistics and will cause problems for agent swarms. The hybrid approach provides defense-in-depth.

See ULTRATHINK_BD224.md for full analysis and rationale.

Parent of all implementation tasks for this fix.


Audit and document all inconsistent issues in database

Before we add the constraint, we need to know what data is inconsistent.

Steps:
1. Query database for status/closed_at mismatches
2. Document each case (how many, which issues, patterns)
3. Decide on cleanup strategy (trust status vs trust closed_at)
4. Create SQL script for cleanup

Output: Document with counts and cleanup SQL ready to review.

This unblocks the migration work.


Code review: Auto-import collision detection fix (bd-228)

Request thorough code review of the auto-import collision detection implementation.

## Files Changed
- cmd/bd/main.go: autoImportIfNewer() and new autoImportWithoutCollisionDetection()

## Review Focus Areas

### 1. Correctness
- Does collision detection properly identify conflicts?
- Are colliding issues correctly filtered from import?
- Is the fallback function correct for non-SQLite backends?

### 2. Edge Cases
- What happens if DetectCollisions() fails?
- What if all issues are collisions?
- What if JSONL is malformed?
- Race conditions with concurrent auto-imports?

### 3. User Experience
- Is the warning message clear and actionable?
- Should we log to a file instead of/in addition to stderr?
- Should there be a --auto-resolve flag?

### 4. Performance
- Does collision detection add significant latency?
- Will this work with 1000+ issues?
- Any unnecessary N+1 queries?

### 5. Testing Gaps
- Do we need integration tests for collision scenarios?
- Should we test the warning output?
- Test autoImportWithoutCollisionDetection() fallback?

## Questions for Reviewer
1. Should auto-import be more aggressive (auto-resolve) or more conservative (fail)?
2. Should we add a counter for how many times collisions occurred?
3. Should there be a config option to disable collision detection?
4. Is the warning too verbose for typical workflows?

## Current Behavior
- Skips colliding issues (preserves local)
- Prints warning to stderr
- Suggests manual resolution command
- Continues with non-colliding issues


Add integration tests for auto-import collision detection

The auto-import collision detection fix (bd-376) needs comprehensive integration tests.

## Test Scenarios Needed

### 1. Basic Collision Detection
```go
TestAutoImportWithCollisions
- Setup: Create issue in DB with status=closed
- Simulate: Git pull with JSONL containing same issue with status=open
- Verify: Local changes preserved, warning printed, issue still closed
```

### 2. Multiple Collisions
```go
TestAutoImportWithMultipleCollisions
- Setup: 5 issues with local modifications
- Simulate: JSONL with 3 colliding, 2 matching
- Verify: 3 skipped, 2 updated, appropriate warnings
```

### 3. No Collisions (Happy Path)
```go
TestAutoImportWithoutCollisions
- Setup: Clean database
- Simulate: JSONL with new issues + exact matches
- Verify: All imported successfully, no warnings
```

### 4. All Collisions
```go
TestAutoImportAllCollisions
- Setup: Every issue has local modifications
- Simulate: JSONL with conflicting versions
- Verify: All skipped, warning lists all issues
```

### 5. Collision Detection Failure
```go
TestAutoImportCollisionDetectionError
- Setup: Mock DetectCollisions() to return error
- Verify: Import skipped entirely (safe failure mode)
```

### 6. Hash-Based Skip
```go
TestAutoImportHashUnchanged
- Setup: JSONL hash matches last_import_hash
- Verify: No collision detection runs, fast path
```

### 7. Fallback for Non-SQLite
```go
TestAutoImportWithoutCollisionDetection
- Setup: Mock non-SQLite storage backend
- Verify: Falls back to old behavior, no collision detection
```

## Test Infrastructure Needed

1. **Helper functions:**
   - createTestDBWithIssues()
   - writeJSONLFile()
   - captureStderrWarnings()
   - simulateGitPull()

2. **Fixtures:**
   - sample-collisions.jsonl
   - sample-exact-matches.jsonl
   - sample-mixed.jsonl

3. **Assertions:**
   - assertIssueState(id, expectedStatus)
   - assertWarningContains(text)
   - assertCollisionCount(n)

## Acceptance Criteria
- All 7 test scenarios implemented
- Tests pass consistently
- Code coverage >80% for autoImportIfNewer()
- Tests run in <5 seconds total
- Clear test names and documentation


Add migration scripts for GitHub Issues

Create scripts to import from GitHub Issues API or exported JSON


Update ready_issues VIEW to use hierarchical blocking

The ready_issues VIEW (schema.go:97-108) uses the OLD blocking logic that doesn't propagate through parent-child hierarchies.

**Problem:**
- GetReadyWork() function now uses recursive CTE with propagation
- But the ready_issues VIEW still uses simple NOT EXISTS check
- Any code using the VIEW will get DIFFERENT results than GetReadyWork()
- This creates inconsistency and confusion

**Impact:**
- Unknown if the VIEW is actually used anywhere in the codebase
- If it is used, it's returning incorrect results (showing children as ready when parent is blocked)

**Solution:**
Either:
1. Update VIEW to match GetReadyWork logic (complex CTE in a view)
2. Drop the VIEW entirely if unused
3. Make VIEW call GetReadyWork as a function (if SQLite supports it)

**Investigation needed:**
Grep for 'ready_issues' to see if the view is actually used.

**Investigation results:**
Grepped the codebase - the ready_issues VIEW appears in:
- schema.go (definition)
- WORKFLOW.md, DESIGN.md (documentation)
- No actual Go code queries it directly

**Conclusion:** The VIEW is defined but appears UNUSED by actual code. GetReadyWork() function is used instead.

**Recommended solution:** Drop the VIEW entirely to avoid confusion. It serves no purpose if unused and creates a maintenance burden (needs to stay in sync with GetReadyWork logic).

**Alternative:** If we want to keep it for direct SQL access, update the VIEW definition to match the new recursive CTE logic.


Consider batching API for bulk issue creation (recovered from bd-222)

Current CreateIssue acquires a dedicated connection for each call. For bulk imports or agent workflows creating many issues, a batched API could improve performance:

CreateIssues(ctx, issues []*Issue, actor string) error

This would:
- Acquire one connection
- Use one IMMEDIATE transaction
- Insert all issues atomically
- Reduce connection overhead

Not urgent - current approach is correct and fast enough for typical use.

**Recovered from:** bd-360 (lost in auto-import bug, see LOST_ISSUES_RECOVERY.md)


Support ID space partitioning for parallel worker agents

Enable external orchestrators (like AI worker swarms) to control issue ID assignment. Add --id flag to 'bd create' for explicit ID specification. Optionally support 'bd config set next_id N' to set the starting point for auto-increment. Storage layer already supports pre-assigned IDs (sqlite.go:52-71), just need CLI wiring. This keeps beads simple while letting orchestrators implement their own ID partitioning strategies to minimize merge conflicts. Complementary to bd-660's collision resolution.


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.


Use safer placeholder pattern in replaceIDReferences

Currently uses bd-324 which could theoretically collide with user text. Use a truly unique placeholder like null bytes: \x00REMAP\x00_0_\x00 which are unlikely to appear in normal text. Located in collision.go:324. Very low probability issue but worth fixing for completeness.


Git-based restoration for compacted issues

Store git commit hash at compaction time to enable restoration of full issue history from version control. When issues are compacted, record the current git commit hash so users can restore the original uncompacted issue from git history.


Add compacted_at_commit field to Issue type

Add optional compacted_at_commit string field to store git commit hash when issue is compacted


Record git commit hash during compaction

Update compact command to capture current git HEAD commit hash and store in compacted_at_commit field


Implement bd restore command

Create new restore command that checks out git commit from compacted_at_commit, reads issue from JSONL, and displays full history


parallel_test_3


parallel_test_10


Add test for deep hierarchy blocking (50+ levels)

Current tests verify 2-level depth (grandparent → parent → child). The depth limit is hardcoded to 50 in the recursive CTE, but we don't test edge cases near that limit.

**Test cases needed:**
1. Verify 50-level deep hierarchy works correctly
2. Verify depth limit prevents runaway recursion
3. Measure performance impact of deep hierarchies
4. Consider if 50 is the right limit (why not 100? why not 20?)

**Rationale:**
- Most hierarchies are 2-5 levels deep
- But pathological cases (malicious or accidental) could create 50+ level nesting
- Need to ensure graceful degradation, not catastrophic failure

**Implementation:**
Add TestDeepHierarchyBlocking to ready_test.go


final_test_7


parallel_test_7


parallel_test_2


parallel_test_5


parallel_test_6


parallel_test_4


parallel_test_1


GH-11: Add Docker support for hosted/shared instance

Request for Docker container hosting to use beads across multiple dev machines. Would need to consider: centralized database (PostgreSQL?), authentication, concurrent access, API server, etc. This is a significant architectural change from the current local-first model.


GH-3: Debug zsh killed error on bd init

User reports 'zsh: killed bd init' when running bd init or just bd command. Likely a crash or signal. Need to reproduce and investigate cause.


Auto-import fails in git workflows due to mtime issues

The auto-import mechanism (autoImportIfNewer) relies on file modification time comparison between JSONL and DB. This breaks in git workflows because git does not preserve original file modification times - pulled files get fresh mtimes based on checkout time.

Root causes:
1. Git checkout sets mtime to 'now', not original commit time
2. Auto-import compares JSONL mtime vs DB mtime (line 181 in main.go)
3. If DB was recently modified (agents working), mtime check fails
4. Auto-import silently returns without feedback
5. Agents continue with stale database state

This caused issues in VC project where 3 parallel agents:
- Pulled updated .beads/issues.jsonl from git
- Auto-import didn't trigger (JSONL appeared older than DB)
- Agents couldn't find their assigned issues
- Agents exported from wrong database, corrupting JSONL


Document hierarchical blocking behavior in README

The fix for bd-58 changes user-visible behavior: children of blocked epics are now automatically blocked.

**What needs documenting:**
1. README.md dependency section should explain blocking propagation
2. Clarify that 'blocks' + 'parent-child' together create transitive blocking
3. Note that 'related' and 'discovered-from' do NOT propagate blocking
4. Add example showing epic → child blocking propagation

**Example to add:**
```bash
# If epic is blocked, children are too
bd create "Epic 1" -t epic -p 1
bd create "Task 1" -t task -p 1
bd dep add task-1 epic-1 --type parent-child

# Block the epic
bd create "Blocker" -t task -p 0
bd dep add epic-1 blocker-1 --type blocks

# Now both epic-1 AND task-1 are blocked
bd ready  # Neither will show up
```


Implement bd restore command


Add EXPLAIN QUERY PLAN tests for ready work query

Verify that the hierarchical blocking query uses proper indexes and doesn't do full table scans.

**Queries to analyze:**
1. The recursive CTE (both base case and recursive case)
2. The final SELECT with NOT EXISTS
3. Impact of various filters (status, priority, assignee)

**Implementation:**
Add test function that:
- Runs EXPLAIN QUERY PLAN on GetReadyWork query
- Parses output to verify no SCAN TABLE operations
- Documents expected query plan in comments
- Fails if query plan degrades

**Benefits:**
- Catch performance regressions in tests
- Document expected query behavior
- Ensure indexes are being used

Related to: bd-59 (composite index on depends_on_id, type)


GH-3: Debug zsh killed error on bd init


Reach 1.0 release milestone

Stabilize API, finalize documentation, comprehensive testing


Code review follow-up: Post-PR #8 merge improvements

Follow-up tasks from the ultrathink code review of PR #8 merge (bd-62).

**Context:** PR #8 successfully merged atomic counter + dirty tracking. Core functionality is solid but several improvements identified.

**Critical (P0-P1):**
- bd-64: Fix SyncAllCounters performance bottleneck (P0)
- bd-65: Add migration for issue_counters table (P1)
- bd-66: Make import counter sync failure fatal (P1)

**Nice to have (P2-P3):**
- bd-67: Update test comments (P2)
- bd-68: Add performance benchmarks (P2)
- bd-69: Add metrics/logging (P3)
- bd-70: Add EXPLAIN QUERY PLAN tests (P3)

**Overall assessment:** 4/5 stars - Excellent implementation with one critical performance issue. After bd-64 is fixed, this becomes 5/5.

**Review document:** Available if needed


Add performance benchmarks document

Document actual performance metrics with hyperfine tests


Add migration scripts for GitHub Issues

Create scripts to import from GitHub Issues API or exported JSON


Bug: Import creating duplicates instead of deduplicating

Git log shows "73 issues remapped" during merge, suggesting --resolve-collisions is creating duplicate issues with new IDs instead of recognizing they're identical and deduplicating them.

Evidence: bd-596, bd-367, bd-396 are all "Epic: Fix status/closed_at inconsistency" with identical content but different IDs and timestamps.

Root cause likely in DetectCollisions or --resolve-collisions logic - it's treating content-identical issues as collisions when they should be exact matches.

**Root Cause Found:**

DetectCollisions only checks if incoming issues exist in the DB. It doesn't deduplicate within the incoming JSONL batch itself.

When JSONL contains multiple issues with:
- Different IDs (bd-596, bd-367, bd-396)
- Identical content
- Different timestamps

DetectCollisions treats them all as "new issues" because none exist in DB, then --resolve-collisions leaves them as-is.

**Fix:**
Add deduplication step in DetectCollisions to detect content-identical issues within the incoming batch and consolidate them before processing.


Document git-based restoration feature

Update COMPACTION.md and README.md with restoration workflow and examples


Verify and test Claude Code plugin

Address remaining items from code review:

Critical:
1. Test plugin installation locally with /plugin marketplace add
2. Verify ${CLAUDE_PLUGIN_ROOT} variable works correctly
3. Test each slash command works
4. Test @task-agent execution
5. Verify MCP server connects properly

Documentation:
1. Clarify 'one-command installation' vs prerequisites
2. Add note about plugin development status
3. Verify all paths and examples work

Nice-to-have:
1. Add icon for marketplace (optional)
2. Add categories field to plugin.json
3. Add engines field for version compatibility

PLUGIN IMPLEMENTATION COMPLETE - READY FOR TESTING

What was built:
✅ .claude-plugin/plugin.json - Plugin metadata with MCP server config
✅ .claude-plugin/marketplace.json - Marketplace configuration
✅ 9 slash commands in .claude-plugin/commands/:
   - bd-ready, bd-create, bd-show, bd-update, bd-close
   - bd-workflow, bd-init, bd-stats, bd-version
✅ Task agent in .claude-plugin/agents/task-agent.md
✅ PLUGIN.md - Comprehensive documentation
✅ README.md - Updated with plugin section
✅ Version sync - All components at 0.9.2

Testing Instructions for Next Agent:
=====================================

1. INSTALL PLUGIN FROM GITHUB:
   /plugin marketplace add steveyegge/beads
   /plugin install beads

2. RESTART CLAUDE CODE (required for MCP server)

3. TEST SLASH COMMANDS:
   /bd-version        # Check versions (should show 0.9.2)
   /bd-workflow       # Show workflow guide
   /bd-stats          # Show project stats
   /bd-ready          # Find ready work
   /bd-create "Test plugin" task 2
   /bd-show bd-<id>

4. TEST MCP SERVER:
   /mcp               # Verify 'beads' server appears

5. TEST TASK AGENT:
   @task-agent        # If supported in Claude Code

6. VERIFY:
   - All commands work without errors
   - MCP tools are accessible
   - Version checking works
   - Documentation is accurate

Expected Issues:
- May need to adjust MCP server path variable (${CLAUDE_PLUGIN_ROOT})
- Task agent syntax might differ
- Some commands may need refinement based on actual usage

If testing fails, check:
- bd CLI is in PATH: which bd
- uv is installed: which uv
- MCP server logs in Claude Code
- PLUGIN.md troubleshooting section

Commit references:
- 9f38375: feat: Add Claude Code plugin for beads
- d25fc53: feat: Add version compatibility checking
- c0f1044: fix: Sync all component versions to 0.9.2
- a5c71f0: feat: Add version bump script
- a612b92: docs: Add version management to CLAUDE.md


The auto-import collision detection fix (bd-376) needs comprehensive integration tests.

## Test Scenarios Needed

### 1. Basic Collision Detection
```go
TestAutoImportWithCollisions
- Setup: Create issue in DB with status=closed
- Simulate: Git pull with JSONL containing same issue with status=open
- Verify: Local changes preserved, warning printed, issue still closed
```

### 2. Multiple Collisions
```go
TestAutoImportWithMultipleCollisions
- Setup: 5 issues with local modifications
- Simulate: JSONL with 3 colliding, 2 matching
- Verify: 3 skipped, 2 updated, appropriate warnings
```

### 3. No Collisions (Happy Path)
```go
TestAutoImportWithoutCollisions
- Setup: Clean database
- Simulate: JSONL with new issues + exact matches
- Verify: All imported successfully, no warnings
```

### 4. All Collisions
```go
TestAutoImportAllCollisions
- Setup: Every issue has local modifications
- Simulate: JSONL with conflicting versions
- Verify: All skipped, warning lists all issues
```

### 5. Collision Detection Failure
```go
TestAutoImportCollisionDetectionError
- Setup: Mock DetectCollisions() to return error
- Verify: Import skipped entirely (safe failure mode)
```

### 6. Hash-Based Skip
```go
TestAutoImportHashUnchanged
- Setup: JSONL hash matches last_import_hash
- Verify: No collision detection runs, fast path
```

### 7. Fallback for Non-SQLite
```go
TestAutoImportWithoutCollisionDetection
- Setup: Mock non-SQLite storage backend
- Verify: Falls back to old behavior, no collision detection
```

## Test Infrastructure Needed

1. **Helper functions:**
   - createTestDBWithIssues()
   - writeJSONLFile()
   - captureStderrWarnings()
   - simulateGitPull()

2. **Fixtures:**
   - sample-collisions.jsonl
   - sample-exact-matches.jsonl
   - sample-mixed.jsonl

3. **Assertions:**
   - assertIssueState(id, expectedStatus)
   - assertWarningContains(text)
   - assertCollisionCount(n)

## Acceptance Criteria
- All 7 test scenarios implemented
- Tests pass consistently
- Code coverage >80% for autoImportIfNewer()
- Tests run in <5 seconds total
- Clear test names and documentation


Low priority chore


parallel_test_4


Add migration scripts for GitHub Issues


Investigate data recovery for issues overwritten by auto-import bug

Auto-import bug (bd-376) has been silently overwriting local changes since the feature was introduced. We need to investigate if any important data was lost and if it can be recovered.

## Data Sources for Recovery

1. **Git history of .beads/issues.jsonl** - May contain older versions with lost changes
2. **Events table** - Has full audit trail with old_value/new_value
3. **Commit messages** - May reference work that was done but later overwritten

## Investigation Steps

1. Check events table for patterns:
   - Look for auto-import events that changed status from closed → open
   - Look for manual updates followed by auto-import reverts
   - Identify issues with suspicious event sequences

2. Git archaeology:
   - Check git log for .beads/issues.jsonl
   - Look for commits where status=closed was changed to status=open
   - Compare git history vs current database state

3. Document lost work:
   - Create list of issues that were closed but reverted to open
   - Identify any critical work that was marked done but lost
   - Check if any issue references are now broken

## Recovery Actions

If significant data loss is found:
- Create script to reconstruct lost state from events table
- Manually review and restore critical closed issues
- Document lessons learned for preventing future data loss

## Example Query

```sql
-- Find issues where status was manually changed then reverted by auto-import
SELECT
  issue_id,
  GROUP_CONCAT(event_type || ':' || actor, ' -> ') as event_chain
FROM events
WHERE issue_id IN (
  SELECT DISTINCT issue_id
  FROM events
  WHERE actor = 'auto-import' AND event_type = 'status_changed'
)
GROUP BY issue_id
HAVING event_chain LIKE '%stevey%auto-import%';
```


Add performance benchmarks document


Store git commit hash at compaction time to enable restoration of full issue history from version control. When issues are compacted, record the current git commit hash so users can restore the original uncompacted issue from git history.