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


GH-1: Fix bd dep tree graph display issues

Tree display has several issues: 1) Epic items may not expand all sub-items, 2) Subitems repeat multiple times at same level, 3) Items with multiple blockers appear multiple times. The tree visualization doesn't properly handle graph structures with multiple dependencies.


GH-2: Evaluate optional Turso backend for collaboration

RFC proposal for optional Turso/libSQL backend to enable: database branching, near-real-time sync between agents/humans, native vector search, browser-ready persistence (WASM/OPFS), and concurrent writes. Would be opt-in, keeping current JSONL+SQLite as default. Requires storage driver interface.


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.


GH-4: Consider system-wide/multi-repo beads usage

User wants to use beads across multiple repositories and for sysadmin tasks. Currently beads is project-scoped (.beads/ directory). Explore options for system-wide issue tracking that spans multiple repos. Related question: how does beads compare to membank MCP?


GH-6: Fix race condition in parallel issue creation

Creating multiple issues rapidly in parallel causes 'UNIQUE constraint failed: issues.id' error. The ID generation has a race condition. Reproducible with: for i in {26..35}; do ./bd create parallel_ 2>&1 & done


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-302) while handling distributed creation gracefully.


GH-7: Package available in AUR (beads-git)

Community member created AUR package for Arch Linux: https://aur.archlinux.org/packages/beads-git. This is informational - no action needed, but good to track for release process and documentation.


GH-9: Support markdown files in bd create

Request to support markdown files as input to bd create, which would parse the markdown and split it into multiple issues. Use case: developers keep feature drafts in markdown files in version control, then want to convert them into issues. Example: bd create -f feature-draft.md


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-18: Add --deps flag to bd create for one-command issue creation

Request to add dependency specification to bd create command instead of requiring separate 'bd dep add' command. Proposed syntax: bd create 'Fix bug' --deps discovered-from=bd-524. This would be especially useful for aider integration and reducing command verbosity.


parallel_test_1


parallel_test_4


parallel_test_7


parallel_test_6


parallel_test_5


parallel_test_2


Test issue with explicit ID


Root issue for dep tree test


Dependency A


Dependency B


Shared dependency C


Epic test


Task A under epic


Task B under epic


Sub-task under A


Sub-task under B


Another explicit ID