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

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

## What Happened

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

## Root Cause

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

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

## Timeline

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

## Impact

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

## Fix Options

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

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

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

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

## Recommended Solution

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

This preserves auto-import convenience while preventing data loss.

## Files Involved

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

## Reproduction Steps

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


Optimize CLI test suite - eliminate redundant git init calls

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


Performance Testing Framework

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

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

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


Add tests for performFlush error handling in FlushManager

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

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

In cmd/bd/flush_manager.go:269, the performFlush method is flagged by unparam as always returning nil, indicating the error return value is never used.

Add tests to determine:
- Whether performFlush can actually return errors in failure scenarios
- If error return is needed, add tests for error cases (disk full, permission denied, etc.)
- If error return is not needed, refactor to remove unused return value
- Test full export vs incremental export error handling

This ensures proper error handling in the flush mechanism and removes dead code if the return value is unnecessary.

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

- Tests written for uncovered functionality
- Test coverage verified
- All tests passing


Fix `bd sync` failure due to daemon auto-export timestamp skew

`bd sync` fails with false-positive "JSONL is newer than database" after daemon auto-export.
Root Cause: Daemon exports local changes to JSONL, updating its timestamp. `bd sync` sees JSONL.mtime > DB.mtime and assumes external changes, blocking export.
Proposed Fixes:
1. `bd sync` auto-imports if timestamp matches but content differs (or just auto-imports).
2. Content-based comparison instead of timestamp only.


Add test coverage for internal/validation package

Validation package has 1 test file. Critical for data integrity. Target: 80% coverage

- At least 4 test files
- Package coverage >= 80%
- Tests cover all validation rules


Add .beads/README.md during 'bd init' for project documentation and promotion

When 'bd init' is run, automatically generate a .beads/README.md file that:

1. Briefly explains what Beads is (AI-native issue tracking that lives in your repo)
2. Links to the main repository: https://github.com/steveyegge/beads
3. Provides a quick reference of essential commands:
   - bd create: Create new issues
   - bd list: View all issues
   - bd update: Modify issue status/details
   - bd show: View issue details
   - bd sync: Sync with git remote
4. Highlights key benefits for AI coding agents and developers
5. Encourages developers to try it out

The README should be enthusiastic and compelling to get open source contributors excited about using Beads for their AI-assisted development workflows.


Extract computeJSONLHash helper to eliminate code duplication

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

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


Investigate performance issues in VS Code Copilot (Windows)

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


Speed up main_test.go - tests hang indefinitely due to nil rootCtx

## Problem

main_test.go tests are hanging indefinitely, making them unusable and blocking development.

## Root Cause

Tests that call flushToJSONL() or autoImportIfNewer() hang forever because:
- rootCtx is nil in test environment (defined in cmd/bd/main.go:67)
- flushToJSONL() uses rootCtx for DB operations (autoflush.go:503)
- When rootCtx is nil, DB calls timeout/hang indefinitely

## Affected Tests (10+)

- TestAutoFlushOnExit
- TestAutoFlushJSONLContent
- TestAutoFlushErrorHandling
- TestAutoImportIfNewer
- TestAutoImportDisabled
- TestAutoImportWithUpdate
- TestAutoImportNoUpdate
- TestAutoImportMergeConflict
- TestAutoImportConflictMarkerFalsePositive
- TestAutoImportClosedAtInvariant

## Proof

```bash
# Before fix: TestAutoFlushJSONLContent HANGS (killed after 2+ min)
# After adding rootCtx init: Completes in 0.04s
# Speedup: ∞ → 0.04s
```

## Solution

See MAIN_TEST_OPTIMIZATION_PLAN.md for complete fix plan.

**Quick Fix (5 min)**: Add to each hanging test:
```go
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
oldRootCtx := rootCtx
rootCtx = ctx
defer func() { rootCtx = oldRootCtx }()
```

**Full Optimization (40 min total)**:
1. Fix rootCtx (5 min) - unblocks tests
2. Reduce sleep durations 10x (2 min) - saves ~280ms
3. Use in-memory DBs (10 min) - saves ~1s
4. Share test fixtures (15 min) - saves ~1.2s
5. Fix skipped debounce test (5 min)

## Expected Results

- Tests go from hanging → <5s total runtime
- Keep critical integration test coverage
- Tests caught real bugs: bd-270, bd-206, bd-160

## Files

- Analysis: MAIN_TEST_REFACTOR_NOTES.md
- Solution: MAIN_TEST_OPTIMIZATION_PLAN.md
- Tests: cmd/bd/main_test.go (18 tests, 1322 LOC)

## Priority

P1 - Blocking development, tests unusable in current state


Add security tests for file path validation in clean command

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

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

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

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

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

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


Fix Homebrew warning about Ruby file location

Homebrew warning: Found Ruby file outside steveyegge/beads tap formula directory.
Warning points to: /opt/homebrew/Library/Taps/steveyegge/homebrew-beads/bd.rb
It should likely be inside a Formula/ directory or similar structure expected by Homebrew taps.


Enhance `bd doctor` to verify Claude Code integration

Add checks to `bd doctor` that verify Claude Code integration is properly set up when .claude/ directory or Claude environment is detected.

## Checks to Add

### 1. Claude Environment Detection
```go
if isClaudeDetected() {
    fmt.Println("✓ Claude Code environment detected")

    // Check what's installed
    hasMCP := isMCPServerInstalled()
    hasHooks := isClaudeSetupComplete()

    if hasMCP && hasHooks {
        fmt.Println("✓ MCP server installed")
        fmt.Println("✓ Claude hooks installed (provides workflow reminders)")
    } else if !hasMCP && hasHooks {
        fmt.Println("✓ Claude hooks installed (provides CLI reference)")
        fmt.Println("  Note: MCP server not detected (using CLI mode)")
    } else if hasMCP && !hasHooks {
        fmt.Println("✓ MCP server installed")
        fmt.Println("⚠ Claude hooks not installed")
        fmt.Println("  Hooks prevent agent from forgetting bd workflow after compaction")
        fmt.Println("  Run: bd setup claude")
    } else {
        fmt.Println("⚠ Claude integration not set up")
        fmt.Println("  Run: bd setup claude")
        // TODO (future): Recommend CLI approach once proven in production
        // fmt.Println("  Tip: CLI mode (without MCP) uses fewer tokens")
    }
}

func isMCPServerInstalled() bool {
    // Check ~/.claude/settings.json for beads MCP server
    home, _ := os.UserHomeDir()
    settingsPath := filepath.Join(home, ".claude/settings.json")

    data, err := os.ReadFile(settingsPath)
    if err != nil {
        return false
    }

    var settings map[string]interface{}
    if err := json.Unmarshal(data, &settings); err != nil {
        return false
    }

    // Check mcpServers section for beads
    mcpServers, ok := settings["mcpServers"].(map[string]interface{})
    if !ok {
        return false
    }

    // Look for beads server (any key containing "beads")
    for key := range mcpServers {
        if strings.Contains(strings.ToLower(key), "beads") {
            return true
        }
    }

    return false
}
```

### 2. Hook Installation Verification (MCP-Aware)

```go
func checkClaudeHooks() {
    home, _ := os.UserHomeDir()
    globalSettings := filepath.Join(home, ".claude/settings.json")
    projectSettings := ".claude/settings.local.json"

    globalHooks := hasBeadsHooks(globalSettings)
    projectHooks := hasBeadsHooks(projectSettings)

    if globalHooks {
        fmt.Println("✓ Global hooks installed")
    } else if projectHooks {
        fmt.Println("✓ Project hooks installed")
    } else {
        fmt.Println("⚠ No hooks installed")
        fmt.Println("  Run: bd setup claude")
        return
    }

    // Check if hooks will work
    if !commandExists("bd") {
        fmt.Println("⚠ 'bd' command not in PATH")
        fmt.Println("  Hooks won't work - ensure bd is installed globally")
    }
}

func hasBeadsHooks(settingsPath string) bool {
    data, err := os.ReadFile(settingsPath)
    if err != nil {
        return false
    }

    var settings map[string]interface{}
    if err := json.Unmarshal(data, &settings); err != nil {
        return false
    }

    hooks, ok := settings["hooks"].(map[string]interface{})
    if !ok {
        return false
    }

    // Check SessionStart and PreCompact for "bd prime"
    for _, event := range []string{"SessionStart", "PreCompact"} {
        eventHooks, ok := hooks[event].([]interface{})
        if !ok {
            continue
        }

        for _, hook := range eventHooks {
            hookMap, _ := hook.(map[string]interface{})
            commands, _ := hookMap["hooks"].([]interface{})
            for _, cmd := range commands {
                cmdMap, _ := cmd.(map[string]interface{})
                if cmdMap["command"] == "bd prime" {
                    return true
                }
            }
        }
    }

    return false
}
```

### 3. AGENTS.md/CLAUDE.md Reference Check
```go
// Check if documentation references bd prime
agentsContent := readFileIfExists("AGENTS.md")
claudeContent := readFileIfExists("CLAUDE.md")

if strings.Contains(agentsContent, "bd prime") || strings.Contains(claudeContent, "bd prime") {
    // Verify bd prime command exists in current version
    if !commandExists("prime") {
        fmt.Println("⚠ Documentation references 'bd prime' but command not found")
        fmt.Println("  Upgrade bd or remove references")
    } else {
        fmt.Println("✓ Documentation references match installed features")
    }
}
```

### 4. Context Priming Test
```go
// Verify bd prime actually works
cmd := exec.Command("bd", "prime")
output, err := cmd.CombinedOutput()

if err != nil {
    fmt.Println("⚠ 'bd prime' failed to execute")
    fmt.Println("  Error:", err)
} else if len(output) == 0 {
    fmt.Println("⚠ 'bd prime' produced no output")
    fmt.Println("  Expected workflow context markdown")
} else {
    // Check if output adapts to MCP mode
    hasMCP := isMCPServerInstalled()
    outputStr := string(output)

    if hasMCP && strings.Contains(outputStr, "mcp__plugin_beads_beads__") {
        fmt.Println("✓ bd prime detected MCP mode (workflow reminders)")
    } else if !hasMCP && strings.Contains(outputStr, "bd ready") {
        fmt.Println("✓ bd prime using CLI mode (full command reference)")
    } else {
        fmt.Println("⚠ bd prime output may not be adapting to environment")
    }
}
```

## Output Format Examples

### With MCP and Hooks
```
bd doctor

Database:
✓ Database found at .beads/beads.db
✓ Git hooks installed

Claude Code Integration:
✓ Claude Code environment detected
✓ MCP server installed
✓ Claude hooks installed (provides workflow reminders)
✓ bd prime detected MCP mode (workflow reminders)
✓ Documentation references match installed features

Sync Status:
✓ No sync issues detected
```

### Without MCP, With Hooks
```
bd doctor

Database:
✓ Database found at .beads/beads.db
✓ Git hooks installed

Claude Code Integration:
✓ Claude Code environment detected
✓ Claude hooks installed (provides CLI reference)
  Note: MCP server not detected (using CLI mode)
✓ bd prime using CLI mode (full command reference)

Sync Status:
✓ No sync issues detected
```

### No Integration
```
bd doctor

Database:
✓ Database found at .beads/beads.db
✓ Git hooks installed

Claude Code Integration:
✓ Claude Code environment detected
⚠ Claude integration not set up
  Run: bd setup claude

Sync Status:
✓ No sync issues detected
```

## Future Enhancement (Post-Production Validation)

Once CLI mode is proven in production, add recommendation:

```go
if isClaudeDetected() && !hasMCP && !hasHooks {
    fmt.Println("⚠ Claude integration not set up")
    fmt.Println("  Run: bd setup claude")
    fmt.Println("  Tip: CLI mode (without MCP) uses fewer tokens than MCP server")
    fmt.Println("       Both approaches work equally well - choose based on preference")
}
```

This recommendation should only be added after CLI mode with `bd prime` is validated in real-world usage.

- bd doctor checks for Claude environment
- Verifies hook installation if .claude/ exists
- Checks AGENTS.md/CLAUDE.md for bd prime references
- Detects version mismatches between docs and installed bd
- Provides actionable suggestions (bd setup claude, upgrade)
- Tests cover detection logic


Implement transaction retry logic for SQLITE_BUSY

BEGIN IMMEDIATE fails immediately on SQLITE_BUSY instead of retrying with exponential backoff.

Location: internal/storage/sqlite/sqlite.go:223-225

Problem:
- Under concurrent write load, BEGIN IMMEDIATE can fail with SQLITE_BUSY
- Current implementation fails immediately instead of retrying
- Results in spurious failures under normal concurrent usage

Solution: Implement exponential backoff retry:
- Retry up to N times (e.g., 5)
- Backoff: 10ms, 20ms, 40ms, 80ms, 160ms
- Check for context cancellation between retries
- Only retry on SQLITE_BUSY/database locked errors

Impact: Spurious failures under concurrent write load

Effort: 3 hours


Clarify .beads/.gitattributes handling in Protected Branches docs

Protected Branches docs quick start leaves untracked `.beads` directory and `.gitattributes`.
Question: Are these changes meant to be checked into the protected branch?
Need to clarify if these should be ignored or committed, or if the instructions are missing a step.


Support --reason flag in daemon mode for reopen command

The reopen.go command has a TODO at line 61 to add reason as a comment once RPC supports AddComment. Currently --reason flag is ignored in daemon mode with a warning.


Add unit tests for hasJSONLChanged() function

The hasJSONLChanged() function has no unit tests. Should test:
- Normal case: hash matches
- Normal case: hash differs
- Edge case: empty file
- Edge case: missing metadata (first run)
- Edge case: corrupted metadata
- Edge case: file read errors
- Edge case: invalid hash format in metadata

Also add performance benchmarks for large JSONL files.


Implement signal-aware context in CLI commands

Replace context.Background() with signal.NotifyContext() to enable graceful cancellation.

## Context
Part of context propagation work (bd-350). Phase 1 infrastructure is complete - sqlite.New() now accepts context parameter.

## Implementation
Set up signal-aware context at the CLI entry points:

```go
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer cancel()
```

## Key Locations
- cmd/bd/main.go:438 (marked with TODO(bd-350))
- cmd/bd/daemon.go:317 (marked with TODO(bd-350))
- Other command entry points

## Benefits
- Ctrl+C cancels ongoing database operations
- Graceful shutdown on SIGTERM
- Better user experience during long operations

## Acceptance Criteria
- [ ] Ctrl+C during import cancels operation cleanly
- [ ] Ctrl+C during export cancels operation cleanly
- [ ] No database corruption on cancellation
- [ ] Proper cleanup on signal (defers execute)


Manual task


Add test coverage for internal/config package

Config package has 1 test file. Need comprehensive tests. Target: 70% coverage

- At least 3 test files
- Package coverage >= 70%


False positive detection by Kaspersky Antivirus (Trojan)

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


Document error handling strategy for metadata update failures

Multiple places silently ignore metadata update failures (sync.go:614-617, import.go:320-322) with non-fatal warnings. This is intentional (degrades gracefully to mtime-based approach) but not well documented.

Add comments explaining:
- Why these failures are non-fatal
- How system degrades gracefully
- What the fallback behavior is (mtime-based detection)


Add Claude setup tip with dynamic priority

Add a predefined tip that suggests running `bd setup claude` when Claude Code is detected but not configured. This tip should have higher priority (shown more frequently) until the setup is complete.

## Implementation

Add to tip registry in `cmd/bd/tips.go`:

```go
{
    ID: "claude_setup",
    Condition: func() bool {
        return isClaudeDetected() && !isClaudeSetupComplete()
    },
    Message: "Run 'bd setup claude' to enable automatic context recovery in Claude Code",
    Frequency: 24 * time.Hour,  // Daily minimum gap
    Priority: 100,              // Highest priority
    Probability: 0.6,           // 60% chance when eligible
}
```

## Detection Logic

```go
func isClaudeDetected() bool {
    // Check environment variables
    if os.Getenv("CLAUDE_CODE") != "" || os.Getenv("ANTHROPIC_CLI") != "" {
        return true
    }
    // Check if .claude/ directory exists
    if _, err := os.Stat(filepath.Join(os.Getenv("HOME"), ".claude")); err == nil {
        return true
    }
    return false
}

func isClaudeSetupComplete() bool {
    // Check for global installation
    home, err := os.UserHomeDir()
    if err == nil {
        _, err1 := os.Stat(filepath.Join(home, ".claude/commands/prime_beads.md"))
        _, err2 := os.Stat(filepath.Join(home, ".claude/hooks/sessionstart"))
        if err1 == nil && err2 == nil {
            return true  // Global hooks installed
        }
    }

    // Check for project installation
    _, err1 := os.Stat(".claude/commands/prime_beads.md")
    _, err2 := os.Stat(".claude/hooks/sessionstart")
    return err1 == nil && err2 == nil
}
```

## Priority and Probability Behavior

**Why 60% probability?**
- Important message (priority 100) but not critical
- Daily frequency + 60% = shows ~4 times per week
- Avoids spam while staying visible
- Balances persistence with user experience

**Comparison with other probabilities:**
- 100% probability: Shows EVERY day (annoying)
- 80% probability: Shows ~6 days per week (too frequent)
- 60% probability: Shows ~4 days per week (balanced)
- 40% probability: Shows ~3 days per week (might be missed)

**Auto-stops when setup complete:**
- Condition becomes false after `bd setup claude`
- No manual dismissal needed
- Tip naturally disappears from rotation

- Claude setup tip added to registry
- isClaudeDetected() checks environment and filesystem
- isClaudeSetupComplete() verifies hook installation
- Tip shows daily until setup complete
- Tip stops showing after setup
- Unit tests for detection functions


Update documentation for bd prime and Claude integration

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

## Documentation Updates

### AGENTS.md
Add new section "Context Recovery":
```markdown
## Context Recovery

### The Problem
After context compaction or clearing conversation, AI agents may forget to use Beads and revert to markdown TODOs. Claude Code hooks solve this.

### bd prime Command
The `bd prime` command outputs essential Beads workflow context in AI-optimized markdown format (~1-2k tokens).

**When to use:**
- After context compaction
- After clearing conversation
- Starting new session
- When agent seems to forget bd workflow
- Manual context refresh

**Usage:**
```bash
bd prime              # Output workflow context
```

### Automatic Integration (Recommended)

Run `bd setup claude` to install hooks that auto-refresh bd context:
- **SessionStart hook**: Loads context in new sessions
- **PreCompact hook**: Refreshes context before compaction (survives better)
- **Works with MCP**: Hooks complement MCP server (not replace)
- **Works without MCP**: bd prime provides workflow via CLI

**Why hooks matter even with MCP:**
- MCP provides native tools, but agent may forget to use them
- Hooks keep "use bd, not markdown" fresh in context
- PreCompact refreshes workflow before compaction

### MCP Server vs bd prime

**Not an either/or choice** - they solve different problems:

| Aspect | MCP Server | bd prime | Both |
|--------|-----------|----------|------|
| **Purpose** | Native bd tools | Workflow context | Best of both |
| **Tokens** | 10.5k always loaded | ~1-2k when called | 10.5k + ~2k |
| **Tool access** | Function calls | CLI via Bash | Function calls |
| **Context memory** | Can fade after compaction | Hooks keep fresh | Hooks + tools |
| **Recommended** | Heavy usage | Token optimization | Best experience |

**Setup options:**
```bash
bd setup claude           # Install hooks (works with or without MCP)
bd setup claude --local   # Per-project only
bd setup claude --remove  # Remove hooks
```
```

### README.md
Add to "Getting Started" section:
```markdown
### AI Agent Integration

**Claude Code users:** Run `bd setup claude` to install automatic context recovery hooks.

Hooks work with both MCP server and CLI approaches, preventing agents from forgetting bd workflow after compaction.

**MCP vs bd prime:**
- **With MCP server**: Hooks keep agent using bd tools (prevents markdown TODO reversion)
- **Without MCP server**: Hooks provide workflow context via `bd prime` (~1-2k tokens)
```

### QUICKSTART.md
Add section on agent integration:
```markdown
## For AI Agents

**Context loading:**
```bash
bd prime    # Load workflow context (~1-2k tokens)
```

**Automatic setup (Claude Code):**
```bash
bd setup claude    # Install hooks for automatic context recovery
```

Hooks prevent agents from forgetting bd workflow after compaction.
```

- AGENTS.md has Context Recovery section
- README.md mentions bd setup claude
- QUICKSTART.md mentions bd prime
- Examples show when to use bd prime vs MCP
- Clear comparison of trade-offs


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

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

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

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

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

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


Document CLI + hooks as recommended approach over MCP

Update documentation to position CLI + bd prime hooks as the primary recommended approach over MCP server, explaining why minimizing context matters even with large context windows (compute cost, energy, environment, latency).

## Goals

Position CLI + `bd prime` hooks as the **primary recommended approach** for AI agent integration, with MCP server as a legacy/fallback option.

Explore **hybrid mode** - if certain commands benefit from MCP (UX/DX advantages like no approval prompts), minimize MCP surface area to only those commands.

This requires production validation first - only update docs after CLI mode is proven reliable.

## Why Minimize Context (Even With Large Windows)

**Context window size ≠ free resource**

Large context windows (100k+, 200k+) don't mean we should fill them wastefully. Every token in context has real costs:

### Compute Cost
- **Processing overhead**: Larger context = more GPU/CPU cycles per request
- **Memory usage**: 10.5k tokens consume significant RAM/VRAM
- **Scaling impact**: Multiplied across all users, all sessions, all requests

### Energy & Environment
- **Electricity**: More compute = more power consumption
- **Carbon footprint**: Data centers running on grid power (not all renewable)
- **Sustainability**: Unnecessary token usage contributes to AI's environmental impact
- **Responsibility**: Efficient tools are better for the planet

### User Experience
- **Latency**: Larger context = slower processing (noticeable at 10k+ tokens)
- **Cost**: Many AI services charge per token (input + output)
- **Rate limits**: Context counts against API quotas

### Engineering Excellence
- **Efficiency**: Good engineering minimizes resource usage
- **Scalability**: Efficient tools scale better
- **Best practices**: Optimize for the common case

**The comparison:**

| Approach | Standing Context | Efficiency | User Cost | Environmental Impact |
|----------|-----------------|------------|-----------|---------------------|
| **CLI + hooks** | ~1-2k tokens | 80-90% reduction | Lower | Sustainable ✓ |
| **MCP minimal** | ~2-4k tokens | 60-80% reduction | Medium | Better ✓ |
| **MCP full** | ~10.5k tokens | Baseline | Higher | Wasteful ✗ |

**Functional equivalence:**
- CLI via Bash tool works just as well as MCP native calls
- Same features, same reliability
- No downside except initial learning curve

## Hybrid Mode: Minimal MCP Surface Area

**Philosophy:** MCP server doesn't have to expose everything.

If certain commands have legitimate UX/DX benefits from MCP (e.g., no approval prompts, cleaner syntax), we can expose ONLY those commands via MCP while using CLI for everything else.

### Potential MCP-Only Candidates (TBD)

Commands that might benefit from MCP native calls:
- `ready` - frequently checked, no side effects, approval prompt annoying
- `show` - read-only, frequently used, approval slows workflow
- `list` - read-only, no risk, approval adds friction

Commands that work fine via CLI:
- `create` - complex parameters, benefits from explicit confirmation
- `update` - state changes, good to see command explicitly
- `close` - state changes, explicit is better
- `dep` - relationships, good to see what's being linked
- `sync` - git operations, definitely want visibility

### Token Budget

**Full MCP** (current): ~10.5k tokens
- All ~20+ bd commands exposed
- All parameter schemas
- All descriptions and examples

**Minimal MCP** (proposed): ~2-4k tokens
- 3-5 high-frequency read commands only
- Simplified schemas
- Minimal descriptions
- Everything else via CLI

**Pure CLI**: ~1-2k tokens (only on SessionStart/PreCompact)
- No MCP tools loaded
- All commands via Bash

### Investigation Required

Before implementing hybrid mode, validate:

1. **Do MCP calls actually skip approval prompts?**
   - Test with Claude Code approval settings
   - Compare MCP tool calls vs Bash tool calls
   - Measure UX difference in real usage

2. **What's the actual token breakdown per command?**
   - Measure individual command schemas
   - Calculate token savings for minimal vs full

3. **Is approval prompt the only benefit?**
   - Are there other UX advantages to MCP?
   - Does native syntax actually improve experience?
   - User testing with both approaches

4. **Can we dynamically load MCP tools?**
   - Only load MCP when certain commands needed?
   - Hot-swap between CLI and MCP?
   - Probably not - MCP loads at startup

### Hybrid Mode Documentation (If Validated)

```markdown
## Choosing Your Integration Approach

Beads supports three AI agent integration approaches:

### CLI + Hooks (Recommended - Most Efficient)

**Setup:** `bd setup claude`

Uses Claude Code hooks to inject workflow context via `bd prime` command. Agent uses bd via Bash tool.

**Tokens:** ~1-2k (on SessionStart/PreCompact only)

**Pros:**
- Maximum efficiency (80-90% reduction vs full MCP)
- Lowest compute/energy usage
- Same functionality as MCP

**Cons:**
- Bash tool calls may require approval prompts
- Slightly more verbose in conversation

### Minimal MCP + Hooks (Balanced)

**Setup:** Install minimal MCP server (read-only commands) + `bd setup claude`

Exposes only high-frequency read commands via MCP (ready, show, list). Everything else via CLI.

**Tokens:** ~2-4k MCP + ~1-2k hooks

**Pros:**
- 60-80% reduction vs full MCP
- No approval prompts for common queries
- Cleaner syntax for frequent operations
- Still efficient

**Cons:**
- Requires MCP server (additional setup)
- Mixed interface (some MCP, some CLI)

### Full MCP + Hooks (Legacy)

**Setup:** Install full MCP server + `bd setup claude`

**Tokens:** ~10.5k MCP + hooks

**Pros:**
- All commands as native function calls
- Consistent interface

**Cons:**
- Highest token usage (worst for compute/energy/cost)
- Slowest processing
- Less sustainable

### Recommendation

1. **Start with CLI + hooks** - most efficient, works great
2. **Try minimal MCP** if approval prompts become annoying
3. **Avoid full MCP** - wasteful with no significant benefit
```

## Production Validation Checklist

Before making these documentation changes, validate CLI approach works reliably:

### Phase 1: Pure CLI Validation
- [ ] `bd prime` implemented and tested
- [ ] Hooks installed and working in Claude Code
- [ ] Real-world usage by at least 2-3 developers for 1+ weeks
- [ ] No significant usability issues reported
- [ ] Agent successfully uses bd via Bash tool
- [ ] Document which commands (if any) have approval prompt issues

### Phase 2: Hybrid Mode Investigation (Optional)
- [ ] Test if MCP calls skip approval prompts vs Bash calls
- [ ] Measure token cost per MCP command
- [ ] Identify minimal set of commands worth exposing via MCP
- [ ] Build minimal MCP server variant
- [ ] Validate token savings (should be 60-80% vs full MCP)
- [ ] User testing shows actual UX improvement

### Phase 3: Documentation Update
- [ ] Update based on validation results
- [ ] Include measured token counts (not estimates)
- [ ] Provide clear migration paths
- [ ] Update `bd doctor` recommendations

## Migration Guide (Optional)

For users currently using MCP:

```markdown
### Migrating from Full MCP to CLI + Hooks

Already using full MCP server? You can switch to the more efficient CLI approach:

1. Install hooks: `bd setup claude`
2. Test it works (hooks inject context, agent uses Bash tool)
3. Remove MCP server from `~/.claude/settings.json`
4. Restart Claude Code

You'll get the same functionality with 80-90% less token usage.

### Migrating to Minimal MCP (If Available)

If you find approval prompts annoying for certain commands:

1. Replace full MCP with minimal MCP in `~/.claude/settings.json`
2. Restart Claude Code
3. Verify high-frequency commands (ready, show, list) work via MCP
4. Everything else automatically uses CLI

You'll get 60-80% token reduction vs full MCP while keeping the UX benefits.
```

## Files to Update

- `README.md` - Add recommendation in AI Integration section
- `AGENTS.md` - Add "Choosing Your Integration Approach" section early
- `QUICKSTART.md` - Update AI integration section
- `docs/` - Any other AI integration docs if they exist
- `mcp-server/` - Create minimal variant if hybrid validated

## Future: Update `bd init`

Once validated, update `bd init` to:
- Default to recommending `bd setup claude` (hooks only)
- Mention minimal MCP as option for UX improvement
- Detect existing full MCP and suggest migration
- Provide token usage estimates for each approach

## MCP Server Architecture Note

**Key insight:** MCP server doesn't have to expose all bd functionality.

Current design exposes ~20+ commands (all bd subcommands). This is over-engineered.

**Better design:**
- **Minimal MCP**: 3-5 read-only commands (~2-4k tokens)
- **CLI**: Everything else via Bash tool
- **Hooks**: Context injection via `bd prime`

This achieves best of both worlds:
- Low token usage (efficient)
- No approval prompts for common queries (UX)
- Explicit visibility for state changes (safety)

If validation shows NO meaningful benefit to MCP (even minimal), skip hybrid mode entirely and recommend pure CLI.

- Documentation explains CLI + hooks as recommended approach
- Explains why context size matters (compute/energy/cost/latency)
- Token comparison table shows 80-90% reduction
- Migration guide for existing MCP users
- Only deployed AFTER production validation
- Clear that both approaches are supported


Fix validatePreExport to use content hash instead of mtime

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

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

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


Refactor create_test.go to use shared DB setup

Convert TestCreate_* functions to use test suites with shared database setup.

Example transformation:
- Before: 10 separate tests, each with newTestStore()
- After: 1 TestCreate() with 10 t.Run() subtests sharing one DB

Estimated speedup: 10x faster (1 DB setup instead of 10)


Propagate context through command handlers

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

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

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

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

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

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

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

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

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


Refactor AGENTS.md to use external references

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


Add bd doctor --fix flag to automatically repair issues

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

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

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


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

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

Related to GH #334.

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

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

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

Add metadata updates after successful export in daemon_sync.go:

After line 307 in createExportFunc (after 'Exported to JSONL' log):
- Compute current JSONL hash
- Update last_import_hash metadata
- Update last_import_mtime metadata
- Same for createSyncFunc at line 497

This mirrors what import.go does at lines 310-336.

The logic is: after export, DB and JSONL are in sync, so metadata should reflect the current JSONL state to prevent false positives in hasJSONLChanged.

1. After daemon export, last_import_hash matches current JSONL hash
2. After daemon export, last_import_mtime matches current JSONL mtime
3. Subsequent exports don't fail with 'JSONL content has changed'
4. Import still correctly detects external JSONL changes
5. All existing export/import tests pass


Add command injection prevention tests for git rm in merge command

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

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

In cmd/bd/merge.go:121, exec.Command is called with variable fullPath, flagged by gosec G204 for potential command injection.

Add tests covering:
- File paths with shell metacharacters (;, |, &, $, etc.)
- Paths with spaces and special characters
- Paths attempting command injection (e.g., 'file; rm -rf /')
- Validation that fullPath is properly sanitized
- Only valid git-tracked files can be removed

This is a critical security path preventing arbitrary command execution.

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


Channel isolation test - beads

Resetting stale in_progress status from old executor run (13 days old)


Create deployment scripts for GCP

Automated provisioning scripts for GCP Compute Engine deployment.

Acceptance Criteria:
- Terraform/gcloud scripts
- Static IP allocation
- Firewall rules
- NGINX reverse proxy config
- TLS setup (Let's Encrypt)
- Systemd service file

File: deployment/agent-mail/gcp/


Performance Testing Documentation

Create docs/performance-testing.md documenting the performance testing framework.

Sections:
1. Overview - What the framework does, goals
2. Running Benchmarks
   - make bench command
   - Running specific benchmarks
   - Interpreting output (ns/op, allocs/op)
3. Profiling and Analysis
   - Viewing CPU profiles with pprof
   - Reading flamegraphs
   - Memory profiling
   - Finding hotspots
4. User Diagnostics
   - bd doctor --perf usage
   - Sharing profiles with bug reports
   - Understanding the report output
5. Comparing Performance
   - Using benchstat for before/after comparisons
   - Detecting regressions
6. Tips for Optimization
   - Common patterns
   - When to profile vs benchmark

Style:
- Concise, practical examples
- Screenshots/examples of pprof output
- Clear command-line examples
- Focus on workflow, not theory