title: Investigate jujutsu integration for beads

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

status: open

issue_type: task


title: Add "bd daemons" command for multi-daemon management

description: Add a new "bd daemons" command with subcommands to manage daemon processes across all beads repositories/worktrees. Should show all running daemons with metadata (version, workspace, uptime, last sync), allow stopping/restarting individual daemons, auto-clean stale processes, view logs, and show exclusive lock status.

design:
Subcommands:
- list: Show all running daemons with metadata (workspace, PID, version, socket path, uptime, last activity, exclusive lock status)
- stop <path|pid>: Gracefully stop a specific daemon
- restart <path|pid>: Stop and restart daemon
- killall: Emergency stop all daemons
- health: Verify each daemon responds to ping
- logs <path>: View daemon logs

Features:
- Auto-clean stale sockets/dead processes
- Discovery: Scan for .beads/bd.sock files + running processes
- Communication: Use existing socket protocol, add GET /status endpoint for metadata

issue_type: epic


title: Update AGENTS.md and README.md with "bd daemons" documentation

description: Document the new "bd daemons" command and all subcommands in AGENTS.md and README.md. Include examples and troubleshooting guidance.


title: Implement "bd daemons logs" subcommand

description: Add command to view daemon logs for a specific workspace. Requires daemon logging to file (may need separate issue for log infrastructure).


title: Test database naming


title: Final validation test


title: Update LINTING.md with current baseline

description: After cleanup, document the remaining acceptable baseline in LINTING.md so we can track regression.


title: Delete skipped tests for "old buggy behavior"

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

Test functions to remove:

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

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

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

Impact: Removes ~150 LOC of permanently skipped tests

acceptance_criteria:
- Delete the 3 test functions entirely (~150 LOC total)
- Update test file comments to reference GH#120 fix if needed
- All remaining tests pass: `go test ./...`
- No reduction in meaningful test coverage (these test fixed bugs)

labels: cleanup dead-code phase-1 test-cleanup

type: parent-child

created_by: daemon


title: Remove unreachable RPC methods

description:
Several RPC server and client methods are unreachable and should be removed:

Server methods (internal/rpc/server.go):
- `Server.GetLastImportTime` (line 2116)
- `Server.SetLastImportTime` (line 2123)
- `Server.findJSONLPath` (line 2255)

Client methods (internal/rpc/client.go):
- `Client.Import` (line 311) - RPC import not used (daemon uses autoimport)

Evidence:
```bash
go run golang.org/x/tools/cmd/deadcode@latest -test ./...
```

Impact: Removes ~80 LOC of unused RPC code

acceptance_criteria:
- Remove the 4 unreachable methods (~80 LOC total)
- Verify no callers: `grep -r "GetLastImportTime\|SetLastImportTime\|findJSONLPath" .`
- All tests pass: `go test ./internal/rpc/...`
- Daemon functionality works: test daemon start/stop/operations

labels: cleanup dead-code phase-1 rpc


title: Remove unreachable utility functions

description:
Several small utility functions are unreachable:

Files to clean:
1. `internal/storage/sqlite/hash.go` - `computeIssueContentHash` (line 17)
   - Check if entire file can be deleted if only contains this function

2. `internal/config/config.go` - `FileUsed` (line 151)
   - Delete unused config helper

3. `cmd/bd/git_sync_test.go` - `verifyIssueOpen` (line 300)
   - Delete dead test helper

4. `internal/compact/haiku.go` - `HaikuClient.SummarizeTier2` (line 81)
   - Tier 2 summarization not implemented
   - Options: implement feature OR delete method

Impact: Removes 50-100 LOC depending on decisions

acceptance_criteria:
- Remove unreachable functions
- If entire files can be deleted (like hash.go), delete them
- For SummarizeTier2: decide to implement or delete, document decision
- All tests pass: `go test ./...`
- Verify no callers exist for each function

labels: cleanup dead-code phase-1


title: Extract SQLite migrations into separate files

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

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

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

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

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

acceptance_criteria:
- All 11 migrations extracted to separate files
- Migration version tracking in database
- Migrations run in order on fresh database
- Existing databases upgrade correctly
- All tests pass: `go test ./internal/storage/sqlite/...`
- Database initialization time unchanged or improved
- Add migration rollback capability (optional, nice-to-have)

labels: database phase-2 refactor


title: Clean up linter errors (914 total issues)

description: The codebase has 914 linter issues reported by golangci-lint. While many are documented as baseline in LINTING.md, we should clean these up systematically to improve code quality and maintainability.

design:
Break down by linter category, prioritizing high-impact issues:
1. dupl (7) - Code duplication
2. goconst (12) - Repeated strings
3. gocyclo (11) - High complexity functions
4. revive (78) - Style issues
5. gosec (102) - Security warnings
6. errcheck (683) - Unchecked errors (many in tests)

acceptance_criteria: All linter categories reduced to acceptable levels, with remaining baseline documented in LINTING.md

notes:
Reduced from 56 to 41 issues locally, then to 0 issues.

**Fixed in commits:**
- c2c7eda: Fixed 15 actual errors (dupl, gosec, revive, staticcheck, unparam)
- 963181d: Configured exclusions to get to 0 issues locally

**Current status:**
- ✅ Local: golangci-lint reports 0 issues
- ❌ CI: Still failing (see [deleted:bd-50])

**Problem:**
Config v2 format or golangci-lint-action@v8 compatibility issue causing CI to fail despite local success.

**Next:** Debug [deleted:bd-50] to fix CI/local discrepancy


title: Extract normalizeLabels to shared utility package

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

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

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

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

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

acceptance_criteria:
- Create `internal/util/strings.go` with `NormalizeLabels`
- Add comprehensive unit tests in `internal/util/strings_test.go`
- Update `internal/rpc/server.go` to import and use `util.NormalizeLabels`
- Update `cmd/bd/list.go` to import and use `util.NormalizeLabels`
- Remove duplicate implementations
- All tests pass: `go test ./...`
- Verify label normalization works: test `bd list --label` commands

labels: deduplication phase-3 refactor


title: Centralize BD_DEBUG logging into debug package

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

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

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

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

Impact: Removes 43 scattered checks, improves code clarity

acceptance_criteria:
- Create `internal/debug/debug.go` with `Enabled`, `Logf`, `Printf`
- Add unit tests in `internal/debug/debug_test.go` (test with/without BD_DEBUG)
- Replace all 43 instances of `os.Getenv("BD_DEBUG")` checks with `debug.Logf()`
- Verify debug output works: run with `BD_DEBUG=1 bd status`
- All tests pass: `go test ./...`
- No behavior change (output identical to before)

labels: deduplication logging phase-3 refactor

created_by: stevey


title: Consider central serialization package for JSON handling

description:
Multiple parts of the codebase handle JSON serialization of issues with slightly different approaches. Consider creating a centralized serialization package to ensure consistency.

Current serialization locations:
- `cmd/bd/export.go` - JSONL export (issues to file)
- `cmd/bd/import.go` - JSONL import (file to issues)
- `internal/rpc/protocol.go` - RPC JSON marshaling
- `internal/storage/memory/memory.go` - In-memory marshaling

Potential benefits:
- Single source of truth for JSON format
- Consistent field naming
- Easier to add new fields
- Centralized validation

Note: This is marked **optional** because:
- Current serialization mostly works
- May not provide enough benefit to justify refactor
- Risk of breaking compatibility

Decision point: Evaluate if benefits outweigh refactoring cost

Impact: TBD based on investigation - may defer to future work

acceptance_criteria:
- Create serialization package with documented JSON format
- Migrate export/import to use centralized serialization
- All existing JSONL files can be read with new code
- All tests pass: `go test ./...`
- Export/import round-trip works perfectly
- RPC protocol unchanged (or backwards compatible)

labels: deduplication optional phase-3 refactor serialization


title: Audit and consolidate collision test coverage

description:
The codebase has 2,019 LOC of collision detection tests across 3 files. Run coverage analysis to identify redundant test cases and consolidate.

Test files:
- `cmd/bd/import_collision_test.go` - 974 LOC
- `cmd/bd/autoimport_collision_test.go` - 750 LOC
- `cmd/bd/import_collision_regression_test.go` - 295 LOC

Total: 2,019 LOC of collision tests

Analysis steps:
1. Run coverage analysis
2. Identify redundant tests
3. Document findings

Consolidation strategy:
- Keep regression tests for critical bugs
- Merge overlapping table-driven tests
- Remove redundant edge case tests covered elsewhere
- Ensure all collision scenarios still tested

Expected outcome: Reduce to ~1,200 LOC (save ~800 lines) while maintaining coverage

Impact: Faster test runs, easier maintenance, clearer test intent

acceptance_criteria:
- Coverage analysis completed and documented
- Redundant tests identified (~800 LOC estimated)
- Consolidated test suite maintains or improves coverage
- All remaining tests pass: `go test ./cmd/bd/...`
- Test run time unchanged or faster
- Document which tests were removed and why
- Coverage percentage maintained: `go test -cover ./cmd/bd/` shows same %


title: Update documentation after code health cleanup

description:
Update all documentation to reflect code structure changes after cleanup phases complete.

Documentation to update:
1. **AGENTS.md** - Update file structure references
2. **CONTRIBUTING.md** (if exists) - Update build/test instructions
3. **Code comments** - Update any outdated references
4. **Package documentation** - Update godoc for reorganized packages

New documentation to add:
1. **internal/util/README.md** - Document shared utilities
2. **internal/debug/README.md** - Document debug logging
3. **internal/rpc/README.md** - Document new file organization
4. **internal/storage/sqlite/migrations/README.md** - Migration system docs

Impact: Keeps documentation in sync with code

acceptance_criteria:
- All documentation references to deleted files removed
- New package READMEs written
- Code comments updated for reorganized code
- Migration guide for developers (if needed)
- Architecture diagrams updated (if they exist)


title: Run final validation and cleanup checks

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

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

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

Impact: Confirms all cleanup objectives achieved successfully

acceptance_criteria:
- Zero unreachable functions per deadcode analyzer
- All tests pass: `go test ./...`
- Test coverage maintained or improved
- Builds cleanly: `go build ./...`
- Linting shows improvements
- Integration tests all pass
- LOC reduction target achieved (~2,500 LOC)
- No unintended behavior changes
- Git commit messages document all changes


title: Code Health & Technical Debt Cleanup

description:
Comprehensive codebase cleanup to remove dead code, refactor monolithic files, deduplicate utilities, and improve maintainability. Based on ultrathink code health analysis conducted 2025-10-27.

Goals:
- Remove ~1,500 LOC of dead/unreachable code
- Split 2 monolithic files (server.go 2,273 LOC, sqlite.go 2,136 LOC) into focused modules
- Deduplicate scattered utility functions (normalizeLabels, BD_DEBUG checks)
- Consolidate test coverage (2,019 LOC of collision tests)
- Improve code navigation and reduce merge conflicts

Impact: Reduces codebase by ~6-8%, improves maintainability, faster CI/CD

Estimated Effort: 11 days across 4 phases

acceptance_criteria:
- All unreachable code identified by `deadcode` analyzer is removed
- RPC server split into <500 LOC files with clear responsibilities
- Duplicate utility functions centralized
- Test coverage maintained or improved
- All tests passing
- Documentation updated

labels: cleanup epic


title: Split internal/rpc/server.go into focused modules

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

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

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

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

acceptance_criteria:
- All 50 methods split into appropriate files
- Each file <500 LOC
- All methods remain on `*Server` receiver (no behavior change)
- All tests pass: `go test ./internal/rpc/...`
- Verify daemon works: start daemon, run operations, check health
- Update internal documentation if needed
- No change to public API


title: Fix auto-import creating duplicates instead of updating issues

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

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

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

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

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

acceptance_criteria:
- Auto-import does NOT create duplicates when pulling git changes
- Existing issues are updated in-place by ID match
- No ping-pong commits between agents
- Test: two agents updating same issue should NOT create duplicates
- bd duplicates shows 0 groups after fix

issue_type: bug


title: Investigate and upgrade to modernc.org/sqlite 1.39.1+

description:
We had to pin modernc.org/sqlite to v1.38.2 due to a FOREIGN KEY constraint regression in v1.39.1 (SQLite 3.50.4).

**Issue:** [deleted:bd-47], GH #144

**Symptom:** CloseIssue fails with "FOREIGN KEY constraint failed (787)" when called via MCP/daemon, but works fine via CLI.

**Root Cause:** Unknown - likely stricter FK enforcement in SQLite 3.50.4 or modernc.org wrapper changes.

**Workaround:** Pinned to v1.38.2 (SQLite 3.49.x)

**TODO:**
1. Monitor modernc.org/sqlite releases for fixes
2. Check SQLite 3.50.5+ changelogs for FK-related fixes
3. Investigate why daemon mode fails but CLI succeeds (connection reuse? transaction isolation?)
4. Consider filing upstream issue with reproducible test case
5. Upgrade when safe


title: GH#146: No color showing in terminal for some users

description:
User reports color not working in macOS (Taho 26.0.1) with iTerm 3.6.4 and Terminal.app, despite color working elsewhere in terminal. Python rich and printf escape codes work.

Need to investigate:
- Is NO_COLOR env var set?
- Terminal type detection?
- fatih/color library configuration
- Does bd list show colors? bd ready? bd init?
- What's the output of: echo $TERM, echo $NO_COLOR


title: Address gosec security warnings (102 issues)

description: Security linter warnings: file permissions (0755 should be 0750), G304 file inclusion via variable, G204 subprocess launches. Many are false positives but should be reviewed.

design: Review each gosec warning. Add exclusions for legitimate cases to .golangci.yml. Fix real security issues (overly permissive file modes).


title: Add optional post-merge git hook example for bd sync

description:
Create example git hook that auto-runs bd sync after git pull/merge.

Add to examples/git-hooks/:
- post-merge hook that checks if .beads/issues.jsonl changed
- If changed: run `bd sync` automatically
- Make it optional/documented (not auto-installed)

Benefits:
- Zero-friction sync after git pull
- Complements auto-detection as belt-and-suspenders

Note: post-merge hook already exists for pre-commit/post-merge. Extend it to support sync.


title: Enforce daemon singleton per workspace with file locking

description:
Agent in ~/src/wyvern discovered 4 simultaneous daemon processes running, causing infinite directory recursion (.beads/.beads/.beads/...). Each daemon used relative paths and created nested .beads/ directories.

Root cause: No singleton enforcement. Multiple `bd daemon` processes can start in same workspace.

Expected: One daemon per workspace (each workspace = separate .beads/ dir with bd.sock)
Actual: Multiple daemons can run simultaneously in same workspace

Note: Separate git clones = separate workspaces = separate daemons (correct). Git worktrees share .beads/ and have known limitations (documented, use --no-daemon).

design:
Use flock (file locking) on daemon socket or database file to enforce singleton:

1. On daemon start, attempt exclusive lock on .beads/bd.sock or .beads/daemon.lock
2. If lock held by another process, refuse to start (exit with clear error)
3. Hold lock for lifetime of daemon process
4. Release lock on daemon shutdown

Alternative: Use PID file with stale detection (check if PID is still running)

Implementation location: Daemon startup code in cmd/bd/ or internal/daemon/

acceptance_criteria:
1. Starting second daemon process in same workspace fails with clear error
2. Test: Start daemon, attempt second start, verify failure
3. Killing daemon releases lock, allowing new daemon to start
4. No infinite .beads/ directory recursion possible
5. Works correctly with auto-start mechanism


title: Daemon fails to auto-import after git pull updates JSONL

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

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

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

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

See WYVERN_SYNC_ISSUE.md for full analysis.

design:
Three possible solutions:

Option 1: Auto-detect and re-import (recommended)
- Before serving any bd command, check if .beads/issues.jsonl mtime > last import time
- If newer, auto-import before processing request
- Fast check, minimal overhead

Option 2: File watcher in daemon
- Daemon watches .beads/issues.jsonl for mtime changes
- Auto-imports when file changes
- More complex, requires file watching infrastructure

Option 3: Explicit sync command
- User runs `bd sync` after git pull
- Manual, error-prone, defeats automation

Recommended: Option 1 (auto-detect) + Option 3 (explicit sync) as fallback.

acceptance_criteria:
1. After git pull updates .beads/issues.jsonl, next bd command sees fresh data
2. No manual import or daemon restart required
3. Performance impact < 10ms per command (mtime check is fast)
4. Works in both daemon and non-daemon modes
5. Test: Two repo clones, update in one, pull in other, verify immediate sync

notes:
**Current Status (2025-10-26):**

✅ **Completed (bd-128):**
- Created internal/autoimport package with staleness detection
- Daemon can detect when JSONL is newer than last import
- Infrastructure exists to call import logic

❌ **Remaining Work:**
The daemon's importFunc in server.go (line 2096-2102) is a stub that just logs a notice. It needs to actually import the issues.

**Problem:**
- importIssuesCore is in cmd/bd package, not accessible from internal/rpc
- daemon's handleImport() returns 'not yet implemented' error

**Two approaches:**
1. Move importIssuesCore to internal/import package (shares with daemon)
2. Use storage layer directly in daemon (create/update issues via Storage interface)

**Blocker:**
This is the critical bug causing data corruption:
- Agent A pushes changes
- Agent B does git pull
- Agent B's daemon serves stale SQLite data
- Agent B exports stale data back to JSONL, overwriting Agent A's changes
- Agent B pushes, losing Agent A's work

**Next Steps:**
1. Choose approach (probably #1 - move importIssuesCore to internal/import)
2. Implement real importFunc in daemon's checkAndAutoImportIfStale()
3. Test with two-repo scenario (push from A, pull in B, verify B sees changes)
4. Ensure no data corruption in multi-agent workflows


title: Document bd edit command and verify MCP exclusion

description:
Follow-up from PR #152:
1. Add "bd edit" to AGENTS.md with "Humans only" note
2. Verify MCP server doesn't expose bd edit command
3. Consider adding test for command registration