File epic bd-130: Fix database reinitialization data loss bug

Critical P0 bug with 5 child issues:
- bd-131: Fix checkGitForIssues() filename detection (beads.jsonl vs issues.jsonl)
- bd-132: Immediate export after import to prevent daemon race
- bd-133: Init safety check - error if DB empty but git has issues
- bd-134: Daemon startup import for auto-recovery
- bd-135: Integration tests for all scenarios

Total estimate: 5-7 hours
See DATABASE_REINIT_BUG.md for complete analysis

Amp-Thread-ID: https://ampcode.com/threads/T-57e73277-9112-42fd-a3c1-a1d1f5a22c8b
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Steve Yegge
2025-10-24 14:13:10 -07:00
parent 7eb8fa37da
commit 3b15b5b259

View File

@@ -33,6 +33,12 @@
{"id":"bd-128","title":"Issue counter gets out of sync with actual issues","description":"The issue counter in issue_counters table frequently desyncs from actual max issue ID, causing:\n- Import from JSONL leaves counter at old high value\n- Test pollution increments counter but cleanup doesn't decrement it\n- Delete issues doesn't update counter\n- Only fix is 'rm -rf .beads' which is destructive\n\nExamples from today's session:\n- Had 48 issues but counter at 7714 after test pollution\n- Import from git didn't reset counter\n- Next new issue would be bd-7715 instead of bd-11\n\nProposed fixes:\n1. Auto-recalculate counter from max(issue_id) on import\n2. Add 'bd fix-counter' command\n3. Make counter lazy (always compute from DB, don't store)\n4. Import should reset counter to match imported data\n\nRelated:-254 (test isolation), bd-12 (init timestamp bug)","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-10-24T13:35:23.109915-07:00","updated_at":"2025-10-24T13:51:54.444058-07:00","closed_at":"2025-10-21T23:13:04.249149-07:00"}
{"id":"bd-129","title":"Counter not synced after import on existing DB with populated issue_counters table","description":"The counter sync fix in counter_sync_test.go only syncs during initial migration when issue_counters table is empty (migrateIssueCountersTable checks count==0). For existing databases with stale counters:\n\n- Import doesn't resync the counter\n- Delete doesn't update counter \n- Renumber doesn't fix counter\n- Counter remains stuck at old high value\n\nExample from today:\n- Had 49 issues after clean import\n- Counter stuck at 4106 from previous test pollution\n- Next issue would be bd-4107 instead of bd-12\n- Even after renumber, counter stayed at 4106\n\nRoot cause: Migration only syncs if table is empty (line 182 in sqlite.go). Once populated, never resyncs.\n\nFix needed: \n1. Sync counter after import operations (not just empty table)\n2. Add counter resync after renumber\n3. Daemon caches counter value - needs to reload after external changes\n\nRelated: bd-11 (original counter sync fix), bd-7 (daemon cache staleness)","notes":"## Investigation Results\n\nAfter thorough code review, all the fixes mentioned in the issue description have ALREADY been implemented:\n\n### ✅ Fixes Already in Place:\n\n1. **Import DOES resync counters**\n - `cmd/bd/import_shared.go:253` calls `SyncAllCounters()` after batch import\n - Verified with new test `TestCounterSyncAfterImport`\n\n2. **Delete DOES update counters**\n - `internal/storage/sqlite/sqlite.go:1424` calls `SyncAllCounters()` after deletion\n - Both single delete and batch delete sync properly\n - Verified with existing tests: `TestCounterSyncAfterDelete`, `TestCounterSyncAfterBatchDelete`\n\n3. **Renumber DOES fix counters**\n - `cmd/bd/renumber.go:298-304` calls `ResetCounter()` then `SyncAllCounters()`\n - Forces counter to actual max ID (not just MAX with stale value)\n\n4. **Daemon cache DOES detect external changes**\n - `internal/rpc/server.go:1466-1487` checks file mtime and evicts stale cache\n - When DB file changes externally, cached storage is evicted and reopened\n\n### Tests Added:\n\n- `TestCounterSyncAfterImport`: Confirms import syncs counters from stale value (4106) to actual max (49)\n- `TestCounterNotSyncedWithoutExplicitSync`: Documents what would happen without the fix (bd-4107 instead of bd-12)\n\n### Conclusion:\n\nThe issue described in bd-12 has been **fully resolved**. All operations (import, delete, renumber) now properly sync counters. The daemon correctly detects external DB changes via file modification time.\n\nThe root cause (migration only syncing empty tables) was fixed by adding explicit `SyncAllCounters()` calls after import, delete, and renumber operations.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-10-24T13:35:23.110118-07:00","updated_at":"2025-10-24T13:51:54.444298-07:00","closed_at":"2025-10-22T00:03:46.697918-07:00"}
{"id":"bd-13","title":"Auto-flush writes test pollution and session work to git-tracked issues.jsonl","description":"Auto-flush exports ALL issues from DB to issues.jsonl every 5 seconds, including:\n- Test issues (bd-4053 through bd-4059 were version test junk)\n- Issues created during debugging sessions\n- Test pollution from stress tests\n- Temporary diagnostic issues\n\nThis pollutes the git-tracked issues.jsonl with garbage that shouldn't be committed.\n\nExample from today:\n- Git had 49 clean issues\n- Our DB grew to 100+ with test junk and session work\n- Auto-flush wrote all 100+ to issues.jsonl\n- Git status showed modified issues.jsonl with 50+ unwanted issues\n\nImpact:\n- Pollutes git history with test/debug garbage\n- Makes code review difficult (noise in diffs)\n- Can't distinguish real work from session artifacts\n- Other team members pull polluted issues\n\nSolutions to consider:\n1. Disable auto-flush by default (require explicit --enable-auto-flush)\n2. Add .beadsignore to exclude issue ID patterns\n3. Make auto-flush only export 'real' issues (exclude test-*)\n4. Require manual 'bd sync' for git commit\n5. Auto-flush to separate file (.beads/session.jsonl vs issues.jsonl)\n\nRelated: bd-117 (test pollution), isolation_test.go (test DB separation)","design":"## Analysis\n\nConfirmed the issue exists - bd-118 through bd-19 are test pollution in the git-tracked issues.jsonl.\n\n### Solution Evaluation:\n\n**Option 1: Disable auto-flush by default** ❌\n- Breaks the auto-sync workflow that users rely on\n- Requires manual intervention which defeats the purpose\n- Not recommended\n\n**Option 2: Add .beadsignore** ⚠️\n- Complex to implement (pattern matching, configuration)\n- Doesn't solve root cause: test issues in production DB\n- Better to prevent pollution at source\n\n**Option 3: Filter on export** ❌\n- Doesn't solve root cause\n- Test issues still pollute production DB\n- Complicates export logic\n\n**Option 4: Manual 'bd sync'** ❌\n- Same issues as Option 1\n- Breaks automated workflow\n\n**Option 5: Separate session file** ❌\n- Splits issue tracking across files\n- Confusing for users\n- Import/export complexity\n\n### RECOMMENDED SOLUTION:\n\n**Fix the root cause: Tests should NEVER touch the production database**\n\nThe real problem is that Go tests ARE properly isolated (they use temp DBs), but someone must be manually creating test issues in the production DB during development/debugging.\n\n**Best fix:**\n1. Document that production DB is for real work only\n2. Add a convenience command: `bd test-create` that uses a separate test database\n3. Clean up the existing test pollution: bd-118 through bd-19\n4. Consider adding a git pre-commit hook to warn about suspicious issues\n\nThis preserves auto-flush (which is valuable) while preventing pollution at the source.","notes":"## Resolution\n\n**Root Cause Identified:**\nThe issue was NOT a bug in auto-flush, but rather test pollution in the production database from manual testing/debugging. Go tests are properly isolated using temp directories.\n\n**Actions Taken:**\n1. Cleaned up test pollution: deleted bd-118 through bd-19 (all \"Version test issue\" entries)\n2. Verified auto-flush is working correctly - it exports the database as designed\n3. Confirmed Go test isolation works properly (uses temp dirs, not production DB)\n\n**Prevention Strategy:**\n- Production database (.beads/) should only contain real work issues\n- Manual testing should use throwaway databases or test scripts\n- Go tests already use isolated temp databases\n- Auto-flush is working as intended and should remain enabled\n\n**Conclusion:**\nThis was user error, not a system bug. The auto-flush mechanism is correct - it should export ALL database contents. The problem was polluting the production database with test issues in the first place.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-10-21T23:54:57.369511-07:00","updated_at":"2025-10-24T13:51:54.437155-07:00","closed_at":"2025-10-22T00:05:29.864829-07:00"}
{"id":"bd-130","title":"Fix database reinitialization data loss bug","description":"**Critical P0 bug**: Silent data loss when .beads/ directory is removed and daemon auto-starts.\n\n**Root Cause:**\nTwo bugs working together:\n1. autoimport.go:76 hardcodes \"issues.jsonl\" but git tracks \"beads.jsonl\"\n2. Daemon creates wrong JSONL file without checking git HEAD\n\n**Impact:**\n- Silent data loss (0 issues after removing .beads/)\n- No error, no warning\n- Core workflow broken (init + auto-import)\n- Multi-workspace scenarios broken\n\n**Solution:**\n4 fixes + integration tests (5-7 hours total)\n\nSee DATABASE_REINIT_BUG.md for complete analysis.","acceptance_criteria":"All child issues completed, integration tests pass, no silent data loss in any scenario","status":"open","priority":0,"issue_type":"epic","created_at":"2025-10-24T14:04:09.563926-07:00","updated_at":"2025-10-24T14:04:09.563926-07:00"}
{"id":"bd-131","title":"Fix A: checkGitForIssues() filename detection","description":"Update autoimport.go:70-96 to try beads.jsonl then issues.jsonl instead of hardcoding issues.jsonl.\n\n**Current Code** (autoimport.go:76):\n```go\nrelPath, err := filepath.Rel(gitRoot, filepath.Join(beadsDir, \"issues.jsonl\"))\n```\n\n**Fixed Code**:\n```go\n// Try canonical JSONL filenames in precedence order\nrelBeads, err := filepath.Rel(gitRoot, beadsDir)\nif err != nil {\n return 0, \"\"\n}\n\ncandidates := []string{\n filepath.Join(relBeads, \"beads.jsonl\"),\n filepath.Join(relBeads, \"issues.jsonl\"),\n}\n\nfor _, relPath := range candidates {\n cmd := exec.Command(\"git\", \"show\", fmt.Sprintf(\"HEAD:%s\", relPath))\n output, err := cmd.Output()\n if err == nil \u0026\u0026 len(output) \u003e 0 {\n lines := bytes.Count(output, []byte(\"\\n\"))\n return lines, relPath\n }\n}\n\nreturn 0, \"\"\n```\n\n**Testing:**\n- Test detects beads.jsonl in git\n- Test detects issues.jsonl in git (legacy)\n- Test precedence: beads.jsonl preferred over issues.jsonl","design":"Precedence: beads.jsonl \u003e issues.jsonl. Ignore non-canonical names (archive.jsonl, backup.jsonl).","status":"open","priority":0,"issue_type":"task","created_at":"2025-10-24T14:04:28.17985-07:00","updated_at":"2025-10-24T14:04:28.17985-07:00","dependencies":[{"issue_id":"bd-131","depends_on_id":"bd-130","type":"parent-child","created_at":"2025-10-24T14:04:28.180741-07:00","created_by":"daemon"}]}
{"id":"bd-132","title":"Fix B-Alt: Immediate export after import in bd init","description":"In cmd/bd/init.go after successful importFromGit(), immediately call exportToJSONL() to prevent daemon race condition.\n\n**Problem:**\nAfter `rm -rf .beads/`, there's a race:\n1. bd init imports from git's beads.jsonl\n2. Import schedules auto-flush (5-second debounce)\n3. Daemon auto-starts before flush completes\n4. Daemon calls findJSONLPath() → no local file yet → creates wrong issues.jsonl\n\n**Solution:**\nImmediate export (no debounce) wins the race.\n\n**Code Location:** cmd/bd/init.go after line 148\n\n**Add After Import Success**:\n```go\nif err := importFromGit(ctx, initDBPath, store, jsonlPath); err != nil {\n // error handling\n} else {\n // CRITICAL: Immediately export to local to prevent daemon race\n localPath := filepath.Join(\".beads\", filepath.Base(jsonlPath))\n if err := exportToJSONL(ctx, store, localPath); err != nil {\n fmt.Fprintf(os.Stderr, \"Warning: failed to export after import: %v\\n\", err)\n }\n fmt.Fprintf(os.Stderr, \"✓ Successfully imported %d issues from git.\\n\\n\", issueCount)\n}\n```\n\n**Testing:**\n- Test local JSONL created immediately after init\n- Test filename matches git (beads.jsonl not issues.jsonl)\n- Test race condition: start daemon immediately after init in background","design":"Simpler than making findJSONLPath() git-aware. Immediate export prevents all races.","status":"open","priority":0,"issue_type":"task","created_at":"2025-10-24T14:04:28.186783-07:00","updated_at":"2025-10-24T14:04:28.186783-07:00","dependencies":[{"issue_id":"bd-132","depends_on_id":"bd-130","type":"parent-child","created_at":"2025-10-24T14:04:28.187373-07:00","created_by":"daemon"}]}
{"id":"bd-133","title":"Fix C: Init safety check - error if DB empty but git has issues","description":"Add post-init verification in cmd/bd/init.go to prevent silent data loss.\n\n**Code Location:** cmd/bd/init.go after line 150 (after import attempt)\n\n**Add Safety Check**:\n```go\n// Safety check: verify import succeeded\nstats, err := store.GetStatistics(ctx)\nif err == nil \u0026\u0026 stats.TotalIssues == 0 {\n // DB empty after init - check if git has issues we failed to import\n recheck, recheckPath := checkGitForIssues()\n if recheck \u003e 0 {\n fmt.Fprintf(os.Stderr, \"\\n❌ ERROR: Database empty but git has %d issues!\\n\", recheck)\n fmt.Fprintf(os.Stderr, \"Auto-import failed. Manual recovery:\\n\")\n fmt.Fprintf(os.Stderr, \" git show HEAD:%s | bd import -i /dev/stdin\\n\", recheckPath)\n fmt.Fprintf(os.Stderr, \"Or:\\n\")\n fmt.Fprintf(os.Stderr, \" bd import -i %s\\n\", recheckPath)\n os.Exit(1)\n }\n}\n```\n\n**Impact:**\nPrevents silent data loss by failing loudly with recovery instructions.\n\n**Testing:**\n- Test fails when import fails but git has issues\n- Test shows clear error message with recovery commands\n- Test exits with non-zero status\n- Test normal path (successful import) still works","design":"Fail loudly instead of silently. Critical safety net that catches import failures.","status":"open","priority":0,"issue_type":"task","created_at":"2025-10-24T14:04:47.112254-07:00","updated_at":"2025-10-24T14:04:47.112254-07:00","dependencies":[{"issue_id":"bd-133","depends_on_id":"bd-130","type":"parent-child","created_at":"2025-10-24T14:04:47.112994-07:00","created_by":"daemon"},{"issue_id":"bd-133","depends_on_id":"bd-131","type":"blocks","created_at":"2025-10-24T14:04:47.113372-07:00","created_by":"daemon"}]}
{"id":"bd-134","title":"Fix D: Daemon startup import when DB empty but git has issues","description":"Add empty-DB check on daemon startup for auto-recovery.\n\n**Code Location:** cmd/bd/daemon.go after DB open (around line 914)\n\n**Add After Database Open**:\n```go\n// Check for empty DB with issues in git\nctx := context.Background()\nstats, err := store.GetStatistics(ctx)\nif err == nil \u0026\u0026 stats.TotalIssues == 0 {\n issueCount, jsonlPath := checkGitForIssues()\n if issueCount \u003e 0 {\n log(fmt.Sprintf(\"Empty database but git has %d issues, importing...\", issueCount))\n if err := importFromGit(ctx, dbPath, store, jsonlPath); err != nil {\n log(fmt.Sprintf(\"Warning: startup import failed: %v\", err))\n } else {\n log(fmt.Sprintf(\"Successfully imported %d issues from git\", issueCount))\n }\n }\n}\n```\n\n**Impact:**\nDaemon auto-recovers from empty DB on startup.\n\n**Testing:**\n- Test daemon imports on startup when DB empty\n- Test daemon logs success/failure\n- Test daemon continues if import fails\n- Test daemon skips import if DB has issues","design":"Nice-to-have recovery. Less critical if Fix A+B+C work correctly.","status":"open","priority":1,"issue_type":"task","created_at":"2025-10-24T14:04:47.121748-07:00","updated_at":"2025-10-24T14:04:47.121748-07:00","dependencies":[{"issue_id":"bd-134","depends_on_id":"bd-130","type":"parent-child","created_at":"2025-10-24T14:04:47.122344-07:00","created_by":"daemon"},{"issue_id":"bd-134","depends_on_id":"bd-131","type":"blocks","created_at":"2025-10-24T14:04:47.122698-07:00","created_by":"daemon"}]}
{"id":"bd-135","title":"Integration tests for database reinitialization scenarios","description":"Comprehensive test suite covering all database reinitialization scenarios.\n\n**Test Cases:**\n\n1. **Fresh Clone Scenario**\n - Clone repo, run bd init, verify auto-import from beads.jsonl\n - Verify local beads.jsonl created immediately\n - Verify issue count matches git\n\n2. **Database Removal Scenario (Primary Bug)**\n - rm -rf .beads/, run bd init\n - Verify detects git-tracked JSONL and imports\n - Verify creates correct filename (beads.jsonl not issues.jsonl)\n - Verify stats show \u003e0 issues\n\n3. **Race Condition Scenario (Daemon Startup)**\n - rm -rf .beads/, bd init in background, immediately trigger daemon\n - Verify daemon does NOT create issues.jsonl\n - Verify uses beads.jsonl from git\n\n4. **Legacy Filename Support (issues.jsonl)**\n - Git has .beads/issues.jsonl (not beads.jsonl)\n - rm -rf .beads/, run bd init\n - Verify imports correctly\n - Verify creates local issues.jsonl (matches git)\n\n5. **Init Safety Check Scenario**\n - Simulate import failure\n - Verify bd init errors with clear message\n - Verify non-zero exit status\n\n6. **Daemon Restart Scenario**\n - Stop daemon, rm database, restart daemon\n - Verify daemon imports from git on startup\n\n7. **Precedence Test**\n - Git has both beads.jsonl and issues.jsonl\n - Verify beads.jsonl preferred\n\n**Test Framework:** Go tests in cmd/bd/*_test.go\n\n**Estimated Effort:** 1-2 hours","acceptance_criteria":"All 7 test scenarios pass, coverage \u003e90% for modified code","status":"open","priority":0,"issue_type":"task","created_at":"2025-10-24T14:05:00.418697-07:00","updated_at":"2025-10-24T14:05:00.418697-07:00","dependencies":[{"issue_id":"bd-135","depends_on_id":"bd-130","type":"parent-child","created_at":"2025-10-24T14:05:00.41981-07:00","created_by":"daemon"},{"issue_id":"bd-135","depends_on_id":"bd-131","type":"blocks","created_at":"2025-10-24T14:05:00.42024-07:00","created_by":"daemon"},{"issue_id":"bd-135","depends_on_id":"bd-132","type":"blocks","created_at":"2025-10-24T14:05:00.420701-07:00","created_by":"daemon"},{"issue_id":"bd-135","depends_on_id":"bd-133","type":"blocks","created_at":"2025-10-24T14:05:00.421064-07:00","created_by":"daemon"},{"issue_id":"bd-135","depends_on_id":"bd-134","type":"blocks","created_at":"2025-10-24T14:05:00.421415-07:00","created_by":"daemon"}]}
{"id":"bd-14","title":"Auto-flush writes test pollution and session work to git-tracked issues.jsonl","description":"Auto-flush exports ALL issues from DB to issues.jsonl every 5 seconds, including:\n- Test issues (bd-4053 through bd-4059 were version test junk)\n- Issues created during debugging sessions\n- Test pollution from stress tests\n- Temporary diagnostic issues\n\nThis pollutes the git-tracked issues.jsonl with garbage that shouldn't be committed.\n\nExample from today:\n- Git had 49 clean issues\n- Our DB grew to 100+ with test junk and session work\n- Auto-flush wrote all 100+ to issues.jsonl\n- Git status showed modified issues.jsonl with 50+ unwanted issues\n\nImpact:\n- Pollutes git history with test/debug garbage\n- Makes code review difficult (noise in diffs)\n- Can't distinguish real work from session artifacts\n- Other team members pull polluted issues\n\nSolutions to consider:\n1. Disable auto-flush by default (require explicit --enable-auto-flush)\n2. Add .beadsignore to exclude issue ID patterns\n3. Make auto-flush only export 'real' issues (exclude test-*)\n4. Require manual 'bd sync' for git commit\n5. Auto-flush to separate file (.beads/session.jsonl vs issues.jsonl)\n\nRelated: bd-117 (test pollution), isolation_test.go (test DB separation)","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-10-22T00:05:10.788996-07:00","updated_at":"2025-10-24T13:51:54.437366-07:00","closed_at":"2025-10-22T01:05:59.459797-07:00"}
{"id":"bd-15","title":"Make merge command idempotent for safe retry after partial failures","description":"The merge command currently performs 3 operations without an outer transaction:\n1. Migrate dependencies from source → target\n2. Update text references across all issues\n3. Close source issues\n\nIf merge fails mid-operation (network issue, daemon crash, etc.), a retry will fail or produce incorrect results because some operations already succeeded.\n\n**Goal:** Make merge idempotent so retrying after partial failure is safe and completes the remaining work.\n\n**Idempotency checks needed:**\n- Skip dependency migration if target already has the dependency\n- Skip text reference updates if already updated\n- Skip closing source issues if already closed\n- Report which operations were skipped vs performed\n\n**Example output:**\n```\n✓ Merged 2 issue(s) into bd-63\n - Dependencies: 3 migrated, 2 already existed\n - Text references: 5 updated, 0 already correct\n - Source issues: 1 closed, 1 already closed\n```\n\n**Related:** bd-115 originally requested transaction support, but idempotency is a better solution for this use case since individual operations are already atomic.","design":"Current merge code already has some idempotency:\n- Dependency migration checks `alreadyExists` before adding (line ~145-151 in merge.go)\n- Text reference updates are naturally idempotent (replacing bd-X with bd-Y twice has same result)\n\nMissing idempotency:\n- CloseIssue fails if source already closed\n- Error messages don't distinguish \"already done\" from \"real failure\"\n\nImplementation:\n1. Check source issue status before closing - skip if already closed\n2. Track which operations succeeded/skipped\n3. Return detailed results for user visibility\n4. Consider adding --dry-run output showing what would be done vs skipped","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-10-22T00:47:43.165434-07:00","updated_at":"2025-10-24T13:51:54.437619-07:00","closed_at":"2025-10-22T11:56:36.526276-07:00"}
{"id":"bd-16","title":"Global daemon should warn/reject --auto-commit and --auto-push","description":"When user runs 'bd daemon --global --auto-commit', it's unclear which repo the daemon will commit to (especially after fixing bd-62 where global daemon won't open a DB).\n\nOptions:\n1. Warn and ignore the flags in global mode\n2. Error out with clear message\n\nLine 87-91 already checks autoPush, but should skip check entirely for global mode. Add user-friendly messaging about flag incompatibility.","status":"closed","priority":3,"issue_type":"feature","created_at":"2025-10-22T00:47:43.165645-07:00","updated_at":"2025-10-24T13:51:54.437812-07:00","closed_at":"2025-10-17T23:04:30.223432-07:00"}
@@ -40,14 +46,14 @@
{"id":"bd-18","title":"Make beads reusable as a Go library for external projects like vc","description":"Currently beads is only usable as a CLI tool. We want to use beads as a library in other Go projects like ~/src/vc so they can programmatically manage issues without shelling out to the bd CLI.\n\nGoals:\n- Export public API from internal packages\n- Document Go package usage\n- Provide examples of programmatic usage\n- Ensure vc can import and use beads storage layer directly\n\nUse case: The vc project needs issue tracking and wants to use beads as an embedded library rather than as a separate CLI tool.","notes":"UnderlyingDB() method implemented and tested. Core functionality complete. Still needs documentation updates (bd-26) and lifecycle safety enhancements (bd-25).","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-10-22T12:27:30.35968-07:00","updated_at":"2025-10-24T13:51:54.438187-07:00","closed_at":"2025-10-22T19:46:09.362533-07:00"}
{"id":"bd-19","title":"Beads Library Integration","description":"Migrate from custom SQLite implementation to using Beads as a library dependency. This eliminates ~3000 lines of duplicated code, reduces schema drift risk, and automatically inherits new Beads features.\n\n**Key Benefits:**\n- Remove 3000+ lines of duplicated SQLite code\n- Eliminate schema drift between bd and vc CLIs\n- Inherit Beads improvements automatically\n- Stronger type safety with Beads error types\n- Faster development velocity for new features\n- Clean separation: Beads stays general-purpose, VC extends via wrapper\n\n**Architecture Principle:**\nBeads remains 100% standalone with NO VC dependencies. VC imports Beads (VC → Beads dependency) and wraps it with VC-specific storage methods. Both share the same database but maintain separate table namespaces.\n\n**Current Pain Points:**\n1. Code Duplication: Issue CRUD, dependency graphs, labels, status transitions all reimplemented\n2. Schema Drift Risk: VC schema manually defined, could diverge from Beads\n3. Lost Features: Can't leverage Beads query optimizer or advanced features without process spawning\n4. Atomic Operations: Hand-rolled 100+ line transaction management\n5. Maintenance Burden: Every Beads feature must be manually replicated\n\n**Concrete Example:**\nWhen Beads adds a new field (e.g., estimated_hours):\n- Current: 4-6 hours of manual work (update types, 6+ SQL queries, migration, testing)\n- With Library: 5 minutes (go get -u github.com/steveyegge/beads)\n\n**Phased Approach:**\n1. Phase 1: Add Beads dependency (non-breaking, feature flag)\n2. Phase 2: Implement VCStorage wrapper (embeds beads.Storage)\n3. Phase 3: Migration script for existing databases\n4. Phase 4: Gradual cutover, deprecate SQLite code\n\n**Related Analysis:**\nSee BEADS_INTEGRATION_ANALYSIS.md for detailed current state analysis and BEADS_LIBRARY_INTEGRATION_EPIC.md for full design document (both to be archived after issue creation).\n\n**Estimated Effort:** 3-4 sprints\n**Priority:** P2 (Medium-High - architectural improvement, high ROI)","notes":"Phase 1 (bd-20) complete! Beads can now be used as a Go library. VC can import github.com/steveyegge/beads and use beads.Storage directly instead of spawning CLI processes. No custom tables needed - VC uses pure Beads primitives.","status":"closed","priority":2,"issue_type":"epic","created_at":"2025-10-22T14:04:08.692803-07:00","updated_at":"2025-10-24T13:51:54.438399-07:00","closed_at":"2025-10-22T14:40:10.225406-07:00"}
{"id":"bd-2","title":"Refactor parseMarkdownFile to reduce cyclomatic complexity","description":"The parseMarkdownFile function in cmd/bd/markdown.go has a cyclomatic complexity of 38, which exceeds the recommended threshold of 30. This makes the function harder to understand, test, and maintain.","design":"Split the function into smaller, focused units:\n\n1. parseMarkdownFile(filepath) - Main entry point, handles file I/O\n2. parseMarkdownContent(scanner) - Core parsing logic\n3. processIssueSection(issue, section, content) - Handle section finalization (current switch statement)\n4. parseLabels(content) []string - Extract labels from content\n5. parseDependencies(content) []string - Extract dependencies from content\n6. parsePriority(content) int - Parse and validate priority\n\nBenefits:\n- Each function has a single responsibility\n- Easier to test individual components\n- Lower cognitive load when reading code\n- Better encapsulation of parsing logic","acceptance_criteria":"- parseMarkdownFile complexity \u003c 15\n- New helper functions each have complexity \u003c 10\n- All existing tests still pass\n- No change in functionality or behavior\n- Code coverage maintained or improved","status":"closed","priority":3,"issue_type":"task","created_at":"2025-10-21T23:53:44.31362-07:00","updated_at":"2025-10-24T13:51:54.376654-07:00","closed_at":"2025-10-14T14:37:17.463352-07:00"}
{"id":"bd-20","title":"Phase 1: Add Beads Dependency (Non-Breaking)","description":"Introduce Beads library alongside existing SQLite code without breaking production.\n\n**Goal:** Add Beads as optional dependency with feature flag, establish compatibility baseline.\n\n**Key Tasks:**\n1. Add github.com/steveyegge/beads to go.mod\n2. Create compatibility test suite comparing VC SQLite vs Beads schema\n3. Identify schema differences and document migration requirements\n4. Create internal/storage/beads/adapter.go implementing Storage interface\n5. Add feature flag: VC_USE_BEADS_LIBRARY=true (disabled by default)\n\n**Acceptance Criteria:**\n- Beads library imported successfully\n- Compatibility tests pass identifying all schema differences\n- Both implementations coexist without conflicts\n- No production impact (feature flag disabled by default)\n- Documentation of schema differences and migration needs\n\n**Technical Details:**\n- Use feature flag to allow A/B testing\n- Compatibility tests must cover: issues, dependencies, labels, status transitions, ID generation\n- Adapter must implement full Storage interface\n- Zero changes to existing production code paths\n\n**Blockers:** None - can start immediately\n\n**Estimated Effort:** 1 sprint","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-22T14:04:20.24179-07:00","updated_at":"2025-10-24T13:51:54.397621-07:00","closed_at":"2025-10-22T14:36:08.066041-07:00","dependencies":[{"issue_id":"bd-20","depends_on_id":"bd-19","type":"parent-child","created_at":"2025-10-24T13:17:40.322877-07:00","created_by":"renumber"}]}
{"id":"bd-21","title":"Phase 2: Implement VCStorage Wrapper","description":"Create VCStorage wrapper that embeds beads.Storage and adds VC-specific operations.\n\n**Goal:** Build clean abstraction layer where VC extends Beads without modifying Beads library.\n\n**Architecture:**\n- VCStorage embeds beads.Storage (delegates core operations)\n- VCStorage adds VC-specific methods (executor instances, events)\n- Same database, separate table namespaces (Beads tables + VC tables)\n- Zero changes to Beads library code\n\n**Key Tasks:**\n1. Create VCStorage struct that embeds beads.Storage\n2. Implement VC-specific methods: CreateExecutorInstance(), GetStaleExecutors(), LogEvent(), UpdateExecutionState()\n3. Create VC table schemas (executor_instances, issue_execution_state, agent_events)\n4. Verify type compatibility between VC types.Issue and Beads Issue\n5. Create MockVCStorage for testing\n6. Write unit tests for VC-specific methods\n7. Write integration tests (end-to-end with Beads)\n8. Benchmark performance vs current SQLite\n9. Verify NO changes needed to Beads library\n\n**Acceptance Criteria:**\n- VCStorage successfully wraps Beads storage (embedding works)\n- VC-specific tables created and accessible via foreign keys to Beads tables\n- VC-specific methods work (executor instances, events)\n- Core operations delegate to Beads correctly\n- Tests pass with \u003e90% coverage\n- Performance benchmark shows no regression\n- Beads library remains unmodified and standalone\n\n**Technical Details:**\n- Use beadsStore.DB() to get underlying database connection\n- Create VC tables with FOREIGN KEY references to Beads issues table\n- Schema separation: Beads owns (issues, dependencies, labels), VC owns (executor_instances, agent_events)\n- Testing: Embed MockBeadsStorage in MockVCStorage\n\n**Dependencies:**\n- Blocked by Phase 1 (need Beads library imported)\n\n**Estimated Effort:** 1.5 sprints","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-22T14:04:36.674165-07:00","updated_at":"2025-10-24T13:51:54.398392-07:00","closed_at":"2025-10-22T21:37:48.747033-07:00","dependencies":[{"issue_id":"bd-21","depends_on_id":"bd-19","type":"parent-child","created_at":"2025-10-24T13:17:40.321936-07:00","created_by":"renumber"},{"issue_id":"bd-21","depends_on_id":"bd-20","type":"blocks","created_at":"2025-10-24T13:17:40.322171-07:00","created_by":"renumber"}]}
{"id":"bd-22","title":"Phase 3: Migration Path \u0026 Database Schema Alignment","description":"Enable existing .beads/vc.db files to work with Beads library through automated migration.\n\n**Goal:** Provide safe, tested migration path from SQLite implementation to Beads library.\n\n**Key Tasks:**\n1. Run compatibility tests against production databases\n2. Identify schema differences (columns, indexes, constraints)\n3. Document required migrations\n4. Create migration CLI command: 'vc migrate --from sqlite --to beads'\n5. Add dry-run mode for preview\n6. Add backup/restore capability\n7. Implement rollback mechanism\n8. Add auto-detection of schema version on startup\n9. Add auto-migrate with user prompt\n\n**Acceptance Criteria:**\n- Existing databases migrate successfully\n- Data integrity preserved (zero data loss verified via checksums)\n- Rollback works if migration fails\n- Migration tested on real production VC databases\n- Dry-run mode shows exactly what will change\n- Backup created before migration\n- Feature flag: VC_FORCE_SQLITE=true provides escape hatch\n\n**Technical Details:**\n- Compare current SQLite schema with Beads schema\n- Handle version detection (read schema_version or detect from structure)\n- Migration should be idempotent (safe to run multiple times)\n- Backup strategy: Copy .beads/vc.db to .beads/vc.db.backup-\u003ctimestamp\u003e\n- Verify foreign key integrity after migration\n\n**Safety Measures:**\n- Require executor shutdown before migration (check for running executors)\n- Atomic migration (BEGIN IMMEDIATE transaction)\n- Comprehensive pre/post migration validation\n- Clear error messages with recovery instructions\n\n**Dependencies:**\n- Blocked by Phase 2 (need VCStorage implementation)\n\n**Estimated Effort:** 0.5 sprint","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-22T14:04:51.320435-07:00","updated_at":"2025-10-24T13:51:54.399455-07:00","closed_at":"2025-10-22T21:37:48.748273-07:00","dependencies":[{"issue_id":"bd-22","depends_on_id":"bd-19","type":"parent-child","created_at":"2025-10-24T13:17:40.323317-07:00","created_by":"renumber"},{"issue_id":"bd-22","depends_on_id":"bd-21","type":"blocks","created_at":"2025-10-24T13:17:40.323527-07:00","created_by":"renumber"}]}
{"id":"bd-23","title":"Phase 4: Gradual Cutover \u0026 Production Rollout","description":"Replace SQLite implementation with Beads library in production and remove legacy code.\n\n**Goal:** Complete transition to Beads library, deprecate and remove custom SQLite implementation.\n\n**Key Tasks:**\n1. Run VC executor with Beads library in CI\n2. Dogfood: Use Beads library for VC's own development\n3. Monitor for regressions and performance issues\n4. Flip feature flag: VC_USE_BEADS_LIBRARY=true by default\n5. Monitor production logs for errors\n6. Collect user feedback\n7. Add deprecation notice to CLAUDE.md\n8. Provide migration guide for users\n9. Remove legacy code: internal/storage/sqlite/sqlite.go (~1500 lines)\n10. Remove migration framework: internal/storage/migrations/\n11. Remove manual transaction management code\n12. Update all documentation\n\n**Acceptance Criteria:**\n- Beads library enabled by default in production\n- Zero production incidents related to migration\n- Performance meets or exceeds SQLite implementation\n- All tests passing with Beads library\n- Legacy SQLite code removed\n- Documentation updated\n- Celebration documented 🎉\n\n**Rollout Strategy:**\n1. Week 1: Enable for CI/testing environments\n2. Week 2: Dogfood on VC development\n3. Week 3: Enable for 50% of production (canary)\n4. Week 4: Enable for 100% of production\n5. Week 5: Remove legacy code\n\n**Monitoring:**\n- Track error rates before/after cutover\n- Monitor database query performance\n- Track issue creation/update latency\n- Monitor executor claim performance\n\n**Rollback Plan:**\n- Keep VC_FORCE_SQLITE=true escape hatch for 2 weeks post-cutover\n- Keep legacy code for 1 sprint after cutover\n- Document rollback procedure\n\n**Success Metrics:**\n- Zero data loss\n- No performance regression (\u003c 5% latency increase acceptable)\n- Reduced maintenance burden (code LOC reduction)\n- Positive developer feedback\n\n**Dependencies:**\n- Blocked by Phase 3 (need migration tooling)\n\n**Estimated Effort:** 1 sprint","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-22T14:05:07.755107-07:00","updated_at":"2025-10-24T13:51:54.401936-07:00","closed_at":"2025-10-22T21:37:48.748919-07:00","dependencies":[{"issue_id":"bd-23","depends_on_id":"bd-19","type":"parent-child","created_at":"2025-10-24T13:17:40.324637-07:00","created_by":"renumber"},{"issue_id":"bd-23","depends_on_id":"bd-22","type":"blocks","created_at":"2025-10-24T13:17:40.324851-07:00","created_by":"renumber"}]}
{"id":"bd-24","title":"Example library-created issue","description":"This issue was created programmatically using Beads as a library","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-22T14:34:44.081801-07:00","updated_at":"2025-10-24T13:51:54.402932-07:00","closed_at":"2025-10-22T14:34:44.084241-07:00","labels":["library-usage"],"dependencies":[{"issue_id":"bd-24","depends_on_id":"bd-4","type":"discovered-from","created_at":"2025-10-24T13:31:30.930671-07:00","created_by":"import"},{"issue_id":"bd-24","depends_on_id":"bd-125","type":"discovered-from","created_at":"2025-10-24T13:45:29.804656-07:00","created_by":"stevey"}],"comments":[{"id":7,"issue_id":"bd-24","author":"library-example","text":"This is a programmatic comment","created_at":"2025-10-22T21:34:44Z"}]}
{"id":"bd-20","title":"Phase 1: Add Beads Dependency (Non-Breaking)","description":"Introduce Beads library alongside existing SQLite code without breaking production.\n\n**Goal:** Add Beads as optional dependency with feature flag, establish compatibility baseline.\n\n**Key Tasks:**\n1. Add github.com/steveyegge/beads to go.mod\n2. Create compatibility test suite comparing VC SQLite vs Beads schema\n3. Identify schema differences and document migration requirements\n4. Create internal/storage/beads/adapter.go implementing Storage interface\n5. Add feature flag: VC_USE_BEADS_LIBRARY=true (disabled by default)\n\n**Acceptance Criteria:**\n- Beads library imported successfully\n- Compatibility tests pass identifying all schema differences\n- Both implementations coexist without conflicts\n- No production impact (feature flag disabled by default)\n- Documentation of schema differences and migration needs\n\n**Technical Details:**\n- Use feature flag to allow A/B testing\n- Compatibility tests must cover: issues, dependencies, labels, status transitions, ID generation\n- Adapter must implement full Storage interface\n- Zero changes to existing production code paths\n\n**Blockers:** None - can start immediately\n\n**Estimated Effort:** 1 sprint","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-22T14:04:20.24179-07:00","updated_at":"2025-10-24T13:51:54.397621-07:00","closed_at":"2025-10-22T14:36:08.066041-07:00","dependencies":[{"issue_id":"bd-20","depends_on_id":"bd-19","type":"parent-child","created_at":"2025-10-24T14:02:30.986474-07:00","created_by":"import"}]}
{"id":"bd-21","title":"Phase 2: Implement VCStorage Wrapper","description":"Create VCStorage wrapper that embeds beads.Storage and adds VC-specific operations.\n\n**Goal:** Build clean abstraction layer where VC extends Beads without modifying Beads library.\n\n**Architecture:**\n- VCStorage embeds beads.Storage (delegates core operations)\n- VCStorage adds VC-specific methods (executor instances, events)\n- Same database, separate table namespaces (Beads tables + VC tables)\n- Zero changes to Beads library code\n\n**Key Tasks:**\n1. Create VCStorage struct that embeds beads.Storage\n2. Implement VC-specific methods: CreateExecutorInstance(), GetStaleExecutors(), LogEvent(), UpdateExecutionState()\n3. Create VC table schemas (executor_instances, issue_execution_state, agent_events)\n4. Verify type compatibility between VC types.Issue and Beads Issue\n5. Create MockVCStorage for testing\n6. Write unit tests for VC-specific methods\n7. Write integration tests (end-to-end with Beads)\n8. Benchmark performance vs current SQLite\n9. Verify NO changes needed to Beads library\n\n**Acceptance Criteria:**\n- VCStorage successfully wraps Beads storage (embedding works)\n- VC-specific tables created and accessible via foreign keys to Beads tables\n- VC-specific methods work (executor instances, events)\n- Core operations delegate to Beads correctly\n- Tests pass with \u003e90% coverage\n- Performance benchmark shows no regression\n- Beads library remains unmodified and standalone\n\n**Technical Details:**\n- Use beadsStore.DB() to get underlying database connection\n- Create VC tables with FOREIGN KEY references to Beads issues table\n- Schema separation: Beads owns (issues, dependencies, labels), VC owns (executor_instances, agent_events)\n- Testing: Embed MockBeadsStorage in MockVCStorage\n\n**Dependencies:**\n- Blocked by Phase 1 (need Beads library imported)\n\n**Estimated Effort:** 1.5 sprints","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-22T14:04:36.674165-07:00","updated_at":"2025-10-24T13:51:54.398392-07:00","closed_at":"2025-10-22T21:37:48.747033-07:00","dependencies":[{"issue_id":"bd-21","depends_on_id":"bd-19","type":"parent-child","created_at":"2025-10-24T14:02:30.986899-07:00","created_by":"import"},{"issue_id":"bd-21","depends_on_id":"bd-20","type":"blocks","created_at":"2025-10-24T14:02:30.987251-07:00","created_by":"import"}]}
{"id":"bd-22","title":"Phase 3: Migration Path \u0026 Database Schema Alignment","description":"Enable existing .beads/vc.db files to work with Beads library through automated migration.\n\n**Goal:** Provide safe, tested migration path from SQLite implementation to Beads library.\n\n**Key Tasks:**\n1. Run compatibility tests against production databases\n2. Identify schema differences (columns, indexes, constraints)\n3. Document required migrations\n4. Create migration CLI command: 'vc migrate --from sqlite --to beads'\n5. Add dry-run mode for preview\n6. Add backup/restore capability\n7. Implement rollback mechanism\n8. Add auto-detection of schema version on startup\n9. Add auto-migrate with user prompt\n\n**Acceptance Criteria:**\n- Existing databases migrate successfully\n- Data integrity preserved (zero data loss verified via checksums)\n- Rollback works if migration fails\n- Migration tested on real production VC databases\n- Dry-run mode shows exactly what will change\n- Backup created before migration\n- Feature flag: VC_FORCE_SQLITE=true provides escape hatch\n\n**Technical Details:**\n- Compare current SQLite schema with Beads schema\n- Handle version detection (read schema_version or detect from structure)\n- Migration should be idempotent (safe to run multiple times)\n- Backup strategy: Copy .beads/vc.db to .beads/vc.db.backup-\u003ctimestamp\u003e\n- Verify foreign key integrity after migration\n\n**Safety Measures:**\n- Require executor shutdown before migration (check for running executors)\n- Atomic migration (BEGIN IMMEDIATE transaction)\n- Comprehensive pre/post migration validation\n- Clear error messages with recovery instructions\n\n**Dependencies:**\n- Blocked by Phase 2 (need VCStorage implementation)\n\n**Estimated Effort:** 0.5 sprint","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-22T14:04:51.320435-07:00","updated_at":"2025-10-24T13:51:54.399455-07:00","closed_at":"2025-10-22T21:37:48.748273-07:00","dependencies":[{"issue_id":"bd-22","depends_on_id":"bd-19","type":"parent-child","created_at":"2025-10-24T14:02:30.987566-07:00","created_by":"import"},{"issue_id":"bd-22","depends_on_id":"bd-21","type":"blocks","created_at":"2025-10-24T14:02:30.987902-07:00","created_by":"import"}]}
{"id":"bd-23","title":"Phase 4: Gradual Cutover \u0026 Production Rollout","description":"Replace SQLite implementation with Beads library in production and remove legacy code.\n\n**Goal:** Complete transition to Beads library, deprecate and remove custom SQLite implementation.\n\n**Key Tasks:**\n1. Run VC executor with Beads library in CI\n2. Dogfood: Use Beads library for VC's own development\n3. Monitor for regressions and performance issues\n4. Flip feature flag: VC_USE_BEADS_LIBRARY=true by default\n5. Monitor production logs for errors\n6. Collect user feedback\n7. Add deprecation notice to CLAUDE.md\n8. Provide migration guide for users\n9. Remove legacy code: internal/storage/sqlite/sqlite.go (~1500 lines)\n10. Remove migration framework: internal/storage/migrations/\n11. Remove manual transaction management code\n12. Update all documentation\n\n**Acceptance Criteria:**\n- Beads library enabled by default in production\n- Zero production incidents related to migration\n- Performance meets or exceeds SQLite implementation\n- All tests passing with Beads library\n- Legacy SQLite code removed\n- Documentation updated\n- Celebration documented 🎉\n\n**Rollout Strategy:**\n1. Week 1: Enable for CI/testing environments\n2. Week 2: Dogfood on VC development\n3. Week 3: Enable for 50% of production (canary)\n4. Week 4: Enable for 100% of production\n5. Week 5: Remove legacy code\n\n**Monitoring:**\n- Track error rates before/after cutover\n- Monitor database query performance\n- Track issue creation/update latency\n- Monitor executor claim performance\n\n**Rollback Plan:**\n- Keep VC_FORCE_SQLITE=true escape hatch for 2 weeks post-cutover\n- Keep legacy code for 1 sprint after cutover\n- Document rollback procedure\n\n**Success Metrics:**\n- Zero data loss\n- No performance regression (\u003c 5% latency increase acceptable)\n- Reduced maintenance burden (code LOC reduction)\n- Positive developer feedback\n\n**Dependencies:**\n- Blocked by Phase 3 (need migration tooling)\n\n**Estimated Effort:** 1 sprint","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-22T14:05:07.755107-07:00","updated_at":"2025-10-24T13:51:54.401936-07:00","closed_at":"2025-10-22T21:37:48.748919-07:00","dependencies":[{"issue_id":"bd-23","depends_on_id":"bd-19","type":"parent-child","created_at":"2025-10-24T14:02:30.988255-07:00","created_by":"import"},{"issue_id":"bd-23","depends_on_id":"bd-22","type":"blocks","created_at":"2025-10-24T14:02:30.988584-07:00","created_by":"import"}]}
{"id":"bd-24","title":"Example library-created issue","description":"This issue was created programmatically using Beads as a library","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-22T14:34:44.081801-07:00","updated_at":"2025-10-24T13:51:54.402932-07:00","closed_at":"2025-10-22T14:34:44.084241-07:00","labels":["library-usage"],"dependencies":[{"issue_id":"bd-24","depends_on_id":"bd-4","type":"discovered-from","created_at":"2025-10-24T14:02:30.988866-07:00","created_by":"import"},{"issue_id":"bd-24","depends_on_id":"bd-125","type":"discovered-from","created_at":"2025-10-24T14:02:30.989161-07:00","created_by":"import"}],"comments":[{"id":1,"issue_id":"bd-24","author":"library-example","text":"This is a programmatic comment","created_at":"2025-10-24T21:02:30Z"}]}
{"id":"bd-25","title":"Add lifecycle safety docs and tests for UnderlyingDB() method","description":"The new UnderlyingDB() method exposes the raw *sql.DB connection for extensions like VC to create their own tables. While database/sql is concurrency-safe, there are lifecycle and misuse risks that need documentation and testing.\n\n**What needs to be done:**\n\n1. **Enhanced documentation** - Expand UnderlyingDB() comments to warn:\n - Callers MUST NOT call Close() on returned DB\n - Do NOT change pool/driver settings (SetMaxOpenConns, SetConnMaxIdleTime)\n - Do NOT modify SQLite PRAGMAs (WAL mode, journal, etc.)\n - Expect errors after Storage.Close() - use contexts\n - Keep write transactions short to avoid blocking core storage\n\n2. **Add lifecycle tracking** - Implement closed flag:\n - Add atomic.Bool closed field to SQLiteStorage\n - Set flag in Close(), clear in New()\n - Optional: Add IsClosed() bool method\n\n3. **Add safety tests** (run with -race):\n - TestUnderlyingDB_ConcurrentAccess - N goroutines using UnderlyingDB() during normal storage ops\n - TestUnderlyingDB_AfterClose - Verify operations fail cleanly after storage closed\n - TestUnderlyingDB_CreateExtensionTables - Create VC table with FK to issues, verify FK enforcement\n - TestUnderlyingDB_LongTxDoesNotCorrupt - Ensure long read tx doesn't block writes indefinitely\n\n**Why this matters:**\nVC will use this to create tables in the same database. Need to ensure production-ready safety without over-engineering.\n\n**Estimated effort:** S+S+S = M total (1-3h)","design":"Oracle recommends \"simple path\": enhanced docs + minimal guardrails + focused tests. See oracle output for detailed rationale on concurrency safety, lifecycle risks, and when to consider advanced path (wrapping interface).","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-22T17:07:56.812983-07:00","updated_at":"2025-10-24T13:51:54.403868-07:00","closed_at":"2025-10-22T20:10:52.636372-07:00"}
{"id":"bd-26","title":"Update EXTENDING.md with UnderlyingDB() usage and best practices","description":"EXTENDING.md currently shows how to use direct sql.Open() to access the database, but doesn't mention the new UnderlyingDB() method that's the recommended way for extensions.\n\n**Update needed:**\n1. Add section showing UnderlyingDB() usage:\n ```go\n store, err := beads.NewSQLiteStorage(dbPath)\n db := store.UnderlyingDB()\n // Create extension tables using db\n ```\n\n2. Document when to use UnderlyingDB() vs direct sql.Open():\n - Use UnderlyingDB() when you want to share the storage connection\n - Use sql.Open() when you need independent connection management\n\n3. Add safety warnings (cross-reference from UnderlyingDB() docs):\n - Don't close the DB\n - Don't modify pool settings\n - Keep transactions short\n\n4. Update the VC example to show UnderlyingDB() pattern\n\n5. Explain beads.Storage.UnderlyingDB() in the API section","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-22T17:07:56.820056-07:00","updated_at":"2025-10-24T13:51:54.404615-07:00","closed_at":"2025-10-22T19:41:19.895847-07:00","dependencies":[{"issue_id":"bd-26","depends_on_id":"bd-18","type":"discovered-from","created_at":"2025-10-24T13:17:40.32522-07:00","created_by":"renumber"}]}
{"id":"bd-27","title":"Consider adding UnderlyingConn(ctx) for safer scoped DB access","description":"Currently UnderlyingDB() returns *sql.DB which is correct for most uses, but for extension migrations/DDL, a scoped connection might be safer.\n\n**Proposal:** Add optional UnderlyingConn(ctx) (*sql.Conn, error) method that:\n- Returns a scoped connection via s.db.Conn(ctx)\n- Encourages lifetime-bounded usage\n- Reduces temptation to tune global pool settings\n- Better for one-time DDL operations like CREATE TABLE\n\n**Implementation:**\n```go\n// UnderlyingConn returns a single connection from the pool for scoped use\n// Useful for migrations and DDL. Close the connection when done.\nfunc (s *SQLiteStorage) UnderlyingConn(ctx context.Context) (*sql.Conn, error) {\n return s.db.Conn(ctx)\n}\n```\n\n**Benefits:**\n- Safer for migrations (explicit scope)\n- Complements UnderlyingDB() for different use cases\n- Low implementation cost\n\n**Trade-off:** Adds another method to maintain, but Oracle considers this balanced compromise between safety and flexibility.\n\n**Decision:** This is optional - evaluate based on VC's actual usage patterns.","status":"closed","priority":3,"issue_type":"feature","created_at":"2025-10-22T17:07:56.832638-07:00","updated_at":"2025-10-24T13:51:54.405456-07:00","closed_at":"2025-10-22T22:02:18.479512-07:00","dependencies":[{"issue_id":"bd-27","depends_on_id":"bd-18","type":"related","created_at":"2025-10-24T13:17:40.325463-07:00","created_by":"renumber"}]}
{"id":"bd-26","title":"Update EXTENDING.md with UnderlyingDB() usage and best practices","description":"EXTENDING.md currently shows how to use direct sql.Open() to access the database, but doesn't mention the new UnderlyingDB() method that's the recommended way for extensions.\n\n**Update needed:**\n1. Add section showing UnderlyingDB() usage:\n ```go\n store, err := beads.NewSQLiteStorage(dbPath)\n db := store.UnderlyingDB()\n // Create extension tables using db\n ```\n\n2. Document when to use UnderlyingDB() vs direct sql.Open():\n - Use UnderlyingDB() when you want to share the storage connection\n - Use sql.Open() when you need independent connection management\n\n3. Add safety warnings (cross-reference from UnderlyingDB() docs):\n - Don't close the DB\n - Don't modify pool settings\n - Keep transactions short\n\n4. Update the VC example to show UnderlyingDB() pattern\n\n5. Explain beads.Storage.UnderlyingDB() in the API section","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-22T17:07:56.820056-07:00","updated_at":"2025-10-24T13:51:54.404615-07:00","closed_at":"2025-10-22T19:41:19.895847-07:00","dependencies":[{"issue_id":"bd-26","depends_on_id":"bd-18","type":"discovered-from","created_at":"2025-10-24T14:02:30.989454-07:00","created_by":"import"}]}
{"id":"bd-27","title":"Consider adding UnderlyingConn(ctx) for safer scoped DB access","description":"Currently UnderlyingDB() returns *sql.DB which is correct for most uses, but for extension migrations/DDL, a scoped connection might be safer.\n\n**Proposal:** Add optional UnderlyingConn(ctx) (*sql.Conn, error) method that:\n- Returns a scoped connection via s.db.Conn(ctx)\n- Encourages lifetime-bounded usage\n- Reduces temptation to tune global pool settings\n- Better for one-time DDL operations like CREATE TABLE\n\n**Implementation:**\n```go\n// UnderlyingConn returns a single connection from the pool for scoped use\n// Useful for migrations and DDL. Close the connection when done.\nfunc (s *SQLiteStorage) UnderlyingConn(ctx context.Context) (*sql.Conn, error) {\n return s.db.Conn(ctx)\n}\n```\n\n**Benefits:**\n- Safer for migrations (explicit scope)\n- Complements UnderlyingDB() for different use cases\n- Low implementation cost\n\n**Trade-off:** Adds another method to maintain, but Oracle considers this balanced compromise between safety and flexibility.\n\n**Decision:** This is optional - evaluate based on VC's actual usage patterns.","status":"closed","priority":3,"issue_type":"feature","created_at":"2025-10-22T17:07:56.832638-07:00","updated_at":"2025-10-24T13:51:54.405456-07:00","closed_at":"2025-10-22T22:02:18.479512-07:00","dependencies":[{"issue_id":"bd-27","depends_on_id":"bd-18","type":"related","created_at":"2025-10-24T14:02:30.98974-07:00","created_by":"import"}]}
{"id":"bd-28","title":"MCP close tool method signature error - takes 1 positional argument but 2 were given","description":"The close approval routing fix in beads-mcp v0.11.0 works correctly and successfully routes update(status=\"closed\") calls to close() tool. However, the close() tool has a Python method signature bug that prevents execution.\n\nImpact: All MCP-based close operations are broken. Workaround: Use bd CLI directly.\n\nError: BdDaemonClient.close() takes 1 positional argument but 2 were given\n\nRoot cause: BdDaemonClient.close() only accepts self, but MCP tool passes issue_id and reason.\n\nAdditional issue: CLI close has FOREIGN KEY constraint error when recording reason parameter.\n\nSee GitHub issue #107 for full details.","status":"closed","priority":0,"issue_type":"bug","created_at":"2025-10-22T17:25:34.67056-07:00","updated_at":"2025-10-24T13:51:54.406337-07:00","closed_at":"2025-10-22T17:36:55.463445-07:00"}
{"id":"bd-29","title":"Test close issue","description":"","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-22T17:27:56.89475-07:00","updated_at":"2025-10-24T13:51:54.407141-07:00","closed_at":"2025-10-22T17:28:00.795511-07:00"}
{"id":"bd-3","title":"Issue counter gets out of sync with actual issues","description":"The issue counter in issue_counters table frequently desyncs from actual max issue ID, causing:\n- Import from JSONL leaves counter at old high value\n- Test pollution increments counter but cleanup doesn't decrement it\n- Delete issues doesn't update counter\n- Only fix is 'rm -rf .beads' which is destructive\n\nExamples from today's session:\n- Had 48 issues but counter at 7714 after test pollution\n- Import from git didn't reset counter\n- Next new issue would be bd-7715 instead of bd-117\n\nProposed fixes:\n1. Auto-recalculate counter from max(issue_id) on import\n2. Add 'bd fix-counter' command\n3. Make counter lazy (always compute from DB, don't store)\n4. Import should reset counter to match imported data\n\nRelated: bd-112 (test isolation), bd-111 (init timestamp bug)","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-10-21T23:53:44.31362-07:00","updated_at":"2025-10-24T13:51:54.436493-07:00","closed_at":"2025-10-21T23:13:04.249149-07:00"}
@@ -62,11 +68,11 @@
{"id":"bd-38","title":"Add integration tests for worktree workflow with separate databases","description":"Created test_worktree_separate_dbs.py that verifies the recommended workflow: one beads database per worktree with daemon-less MCP mode (BEADS_USE_DAEMON=0). Tests confirm isolation, git syncing, MCP operations, and --no-daemon flag.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-23T13:05:57.195044-07:00","updated_at":"2025-10-24T13:51:54.412271-07:00","closed_at":"2025-10-23T13:06:05.258164-07:00"}
{"id":"bd-39","title":"Migrate to Viper for unified configuration management","description":"Consolidate all config, flags, and environment variables into a singleton using spf13/viper. Benefits: unified config precedence, auto-env binding, live reloading, better integration with Cobra. Current state: manual env var handling, global flags, and new bd config command (GH #124).","design":"Hybrid architecture:\n- Viper: Tool-level user preferences (global defaults for --json, --no-daemon, etc) from ~/.config/bd/config.yaml, env vars, flags\n- bd config: Project-level integration data (jira.url, linear.token) in database - version-controlled, team-shared\n\nThis separation is correct: tool settings are user-specific, project config is team-shared. Agents benefit from bd config's structured interface vs manual YAML editing.","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-10-23T16:44:43.537822-07:00","updated_at":"2025-10-24T13:51:54.412532-07:00","closed_at":"2025-10-23T17:06:05.007958-07:00"}
{"id":"bd-4","title":"Improve error handling in dependency removal during remapping","description":"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.","status":"closed","priority":3,"issue_type":"bug","created_at":"2025-10-21T23:53:44.31362-07:00","updated_at":"2025-10-24T13:51:54.378724-07:00","closed_at":"2025-10-18T09:41:18.209717-07:00"}
{"id":"bd-40","title":"Add viper dependency and initialize singleton","description":"go get github.com/spf13/viper, create viper instance in main, set up config file paths (.beads/config.yaml, ~/.config/bd/config.yaml)","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-23T16:44:48.863208-07:00","updated_at":"2025-10-24T13:51:54.412902-07:00","closed_at":"2025-10-23T17:02:13.818118-07:00","dependencies":[{"issue_id":"bd-40","depends_on_id":"bd-39","type":"parent-child","created_at":"2025-10-24T13:17:40.32675-07:00","created_by":"renumber"}]}
{"id":"bd-41","title":"Bind all global flags to viper","description":"Migrate --db, --actor, --json, --no-daemon, --no-auto-flush, --no-auto-import to viper with automatic env binding (BD_DB, BD_ACTOR, etc)","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-23T16:44:53.547159-07:00","updated_at":"2025-10-24T13:51:54.413183-07:00","closed_at":"2025-10-23T17:03:25.133499-07:00","dependencies":[{"issue_id":"bd-41","depends_on_id":"bd-39","type":"parent-child","created_at":"2025-10-24T13:17:40.325689-07:00","created_by":"renumber"}]}
{"id":"bd-42","title":"Replace manual env var handling with viper","description":"Remove os.Getenv() calls for BD_* variables, use viper.GetString() etc. Update CONFIG.md with viper precedence order","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-23T16:44:58.242662-07:00","updated_at":"2025-10-24T13:51:54.413481-07:00","closed_at":"2025-10-23T17:03:25.139194-07:00","dependencies":[{"issue_id":"bd-42","depends_on_id":"bd-39","type":"parent-child","created_at":"2025-10-24T13:17:40.323778-07:00","created_by":"renumber"}]}
{"id":"bd-43","title":"Keep bd config independent from Viper","description":"Update config.go to use viper for get/set/list operations. Consider whether to keep db-stored config or migrate fully to file-based","design":"Keep bd config independent from Viper. Two separate systems:\n- Viper: tool-level preferences (global flags, env vars, ~/.config/bd/config.yaml) - user settings like default --json, --no-daemon\n- bd config: project-level integration config (jira.url, linear.token) - stored in database, version-controlled, team-shared\n\nRationale: Agents need structured commands with validation/discoverability. Direct YAML editing is error-prone for agents. This separation is architecturally correct: tool settings vs project data.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-23T16:45:04.078518-07:00","updated_at":"2025-10-24T13:51:54.413743-07:00","closed_at":"2025-10-23T17:04:01.161921-07:00","dependencies":[{"issue_id":"bd-43","depends_on_id":"bd-39","type":"parent-child","created_at":"2025-10-24T13:17:40.328628-07:00","created_by":"renumber"}]}
{"id":"bd-44","title":"Add tests for viper configuration","description":"Test config precedence (defaults \u003c config file \u003c env vars \u003c flags), test config file discovery, test env binding","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-23T16:45:08.629325-07:00","updated_at":"2025-10-24T13:51:54.414044-07:00","closed_at":"2025-10-23T17:06:05.000166-07:00","dependencies":[{"issue_id":"bd-44","depends_on_id":"bd-39","type":"parent-child","created_at":"2025-10-24T13:17:40.326967-07:00","created_by":"renumber"}]}
{"id":"bd-40","title":"Add viper dependency and initialize singleton","description":"go get github.com/spf13/viper, create viper instance in main, set up config file paths (.beads/config.yaml, ~/.config/bd/config.yaml)","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-23T16:44:48.863208-07:00","updated_at":"2025-10-24T13:51:54.412902-07:00","closed_at":"2025-10-23T17:02:13.818118-07:00","dependencies":[{"issue_id":"bd-40","depends_on_id":"bd-39","type":"parent-child","created_at":"2025-10-24T14:02:30.990017-07:00","created_by":"import"}]}
{"id":"bd-41","title":"Bind all global flags to viper","description":"Migrate --db, --actor, --json, --no-daemon, --no-auto-flush, --no-auto-import to viper with automatic env binding (BD_DB, BD_ACTOR, etc)","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-23T16:44:53.547159-07:00","updated_at":"2025-10-24T13:51:54.413183-07:00","closed_at":"2025-10-23T17:03:25.133499-07:00","dependencies":[{"issue_id":"bd-41","depends_on_id":"bd-39","type":"parent-child","created_at":"2025-10-24T14:02:30.990305-07:00","created_by":"import"}]}
{"id":"bd-42","title":"Replace manual env var handling with viper","description":"Remove os.Getenv() calls for BD_* variables, use viper.GetString() etc. Update CONFIG.md with viper precedence order","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-23T16:44:58.242662-07:00","updated_at":"2025-10-24T13:51:54.413481-07:00","closed_at":"2025-10-23T17:03:25.139194-07:00","dependencies":[{"issue_id":"bd-42","depends_on_id":"bd-39","type":"parent-child","created_at":"2025-10-24T14:02:30.990577-07:00","created_by":"import"}]}
{"id":"bd-43","title":"Keep bd config independent from Viper","description":"Update config.go to use viper for get/set/list operations. Consider whether to keep db-stored config or migrate fully to file-based","design":"Keep bd config independent from Viper. Two separate systems:\n- Viper: tool-level preferences (global flags, env vars, ~/.config/bd/config.yaml) - user settings like default --json, --no-daemon\n- bd config: project-level integration config (jira.url, linear.token) - stored in database, version-controlled, team-shared\n\nRationale: Agents need structured commands with validation/discoverability. Direct YAML editing is error-prone for agents. This separation is architecturally correct: tool settings vs project data.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-23T16:45:04.078518-07:00","updated_at":"2025-10-24T13:51:54.413743-07:00","closed_at":"2025-10-23T17:04:01.161921-07:00","dependencies":[{"issue_id":"bd-43","depends_on_id":"bd-39","type":"parent-child","created_at":"2025-10-24T14:02:30.990842-07:00","created_by":"import"}]}
{"id":"bd-44","title":"Add tests for viper configuration","description":"Test config precedence (defaults \u003c config file \u003c env vars \u003c flags), test config file discovery, test env binding","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-23T16:45:08.629325-07:00","updated_at":"2025-10-24T13:51:54.414044-07:00","closed_at":"2025-10-23T17:06:05.000166-07:00","dependencies":[{"issue_id":"bd-44","depends_on_id":"bd-39","type":"parent-child","created_at":"2025-10-24T14:02:30.991107-07:00","created_by":"import"}]}
{"id":"bd-45","title":"Auto-import updates all issue timestamps causing perpetually dirty JSONL","description":"**Problem:**\nEvery bd command ends sessions with dirty .beads/beads.jsonl because auto-import unnecessarily updates `updated_at` timestamps on ALL issues, even when issue data is unchanged.\n\n**Root Cause:**\nAuto-import (autoImportIfNewer) imports issues and the import process updates `updated_at` timestamps on every issue, even when the issue data is identical. This causes:\n1. Import updates all 83 issues' timestamps (e.g., 2025-10-23T11:01:13.xxx)\n2. Export writes JSONL with new timestamps\n3. Git shows JSONL as modified with 200+ line changes\n4. User commits changes\n5. Next command triggers auto-import again\n6. Cycle repeats\n\n**Evidence:**\n```bash\ngit diff .beads/beads.jsonl | grep 'updated_at' | wc -l\n# Shows 200+ lines changed (every issue touched)\n\ngit diff .beads/beads.jsonl | grep 'updated_at' | sed 's/.*updated_at[^:]*:\\s*\"\\([^\"]*\\)\".*/\\1/' | sort -u\n# All timestamps identical, proving bulk update\n```\n\n**Expected Behavior:**\nImport should only update `updated_at` when issue data actually changes. Idempotent imports should not modify timestamps.\n\n**Impact:**\n- Frustrating UX: \"always conclude sessions with dirty beads.jsonl\"\n- Pollutes git history with timestamp-only changes\n- Makes actual changes hard to detect in git diff\n- Wastes CI/CD time on false changes\n\n**Related:**\n- bd-17 fixed import to preserve timestamps, but doesn't address this issue\n- May be related to how UpdateIssue works vs just inserting/replacing","status":"closed","priority":1,"issue_type":"bug","assignee":"amp","created_at":"2025-10-23T17:42:41.419893-07:00","updated_at":"2025-10-24T13:51:54.43892-07:00","closed_at":"2025-10-23T17:50:54.883229-07:00"}
{"id":"bd-46","title":"Add GoReleaser workflow for cross-platform binary releases","description":"GitHub issue #89 requests pre-compiled binaries for vendoring.\n\nCurrently users must have Go installed to use beads via 'go install'. Publishing release binaries would:\n- Enable vendoring (user's use case) \n- Support users without Go\n- Enable version pinning\n- Simplify CI/CD integration\n\nImplementation:\n1. Add .goreleaser.yml config\n2. Add .github/workflows/release.yml for tag pushes\n3. Build matrix: darwin (amd64/arm64), linux (amd64/arm64), windows (amd64)\n4. Generate checksums\n5. Create GitHub releases automatically\n6. Update install.sh to download from releases\n\nReference: https://github.com/steveyegge/beads/issues/89","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-10-23T18:58:30.701929-07:00","updated_at":"2025-10-24T13:51:54.414545-07:00","closed_at":"2025-10-23T19:02:16.463059-07:00"}
{"id":"bd-47","title":"Add transaction support for atomicity in merge operations","description":"The merge operation in cmd/bd/merge.go should use transactions to ensure atomicity. Currently marked as TODO at line 143.\n\nThis would prevent partial merges if an error occurs partway through the operation.","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-10-23T19:33:34.549858-07:00","updated_at":"2025-10-24T13:51:54.414813-07:00","closed_at":"2025-10-23T19:35:40.620329-07:00"}
@@ -76,19 +82,19 @@
{"id":"bd-50","title":"Auto-detect and kill old daemon versions","description":"When the client version doesn't match the daemon version, we get confusing behavior (auto-flush race conditions, stale data, etc.). The client should automatically detect version mismatches and handle them gracefully.\n\n**Current behavior:**\n- `bd version --daemon` shows mismatch but requires manual intervention\n- Old daemons keep running after binary upgrades\n- MCP server may connect to old daemon\n- Results in dirty working tree after commits, stale data\n\n**Proposed solution:**\n\nKey lifecycle points to check/restart daemon:\n1. **On first command after version mismatch**: Check daemon version, auto-restart if incompatible\n2. **On daemon start**: Check for existing daemons, kill old ones before starting\n3. **After brew upgrade/install**: Add post-install hook to kill old daemons\n4. **On `bd init`**: Ensure fresh daemon\n\n**Detection logic:**\n```go\n// PersistentPreRun: check daemon version\nif daemonVersion != clientVersion {\n log.Warn(\"Daemon version mismatch, restarting...\")\n killDaemon()\n startDaemon()\n}\n```\n\n**Considerations:**\n- Should we be aggressive (always kill mismatched) or conservative (warn first)?\n- What about multiple workspaces with different bd versions?\n- Should this be opt-in via config flag?\n- How to handle graceful shutdown vs force kill?\n\n**Related issues:**\n- Race condition with auto-flush (see bd-50)\n- Version mismatch confusion for users\n- Stale daemon after upgrades","notes":"## Implementation Summary\n\nImplemented automatic daemon version detection and restart in v0.16.0.\n\n### Changes Made\n\n**1. Auto-restart on version mismatch (main.go PersistentPreRun)**\n- Check daemon version during health check\n- If incompatible, automatically stop old daemon and start new one\n- Falls back to direct mode if restart fails\n- Transparent to users - no manual intervention needed\n\n**2. Auto-stop old daemon on startup (daemon.go)**\n- When starting daemon, check if existing daemon has compatible version\n- If versions are incompatible, auto-stop old daemon before starting new one\n- Prevents \"daemon already running\" errors after upgrades\n\n**3. Robust restart implementation**\n- Sets correct working directory so daemon finds right database\n- Cleans up stale socket files after force kill\n- Properly reaps child process to avoid zombies\n- Uses waitForSocketReadiness helper for reliable startup detection\n- 5-second readiness timeout\n\n### Key Features\n\n- **Automatic**: No user action required after upgrading bd\n- **Transparent**: Works with both MCP server and CLI\n- **Safe**: Falls back to direct mode if restart fails\n- **Tested**: All existing tests pass\n\n### Related\n- Addresses race conditions mentioned in bd-51\n- Uses semver compatibility checking from internal/rpc/server.go","status":"closed","priority":1,"issue_type":"feature","created_at":"2025-10-23T23:15:59.764705-07:00","updated_at":"2025-10-24T13:51:54.439179-07:00","closed_at":"2025-10-23T23:28:06.611221-07:00"}
{"id":"bd-51","title":"Race condition between git commit and auto-flush debounce","description":"When using MCP/daemon mode, operations trigger a 5-second debounced auto-flush to JSONL. This creates a race condition with git commits, leaving the working tree dirty.\n\n**Example scenario:**\n1. User closes issue via MCP → daemon schedules flush (5 sec delay)\n2. User commits code changes → JSONL appears clean\n3. Daemon flush fires → JSONL modified after commit\n4. Result: dirty working tree showing JSONL changes\n\n**Root cause:**\n- Auto-flush uses 5-second debounce to batch changes\n- Git commits happen immediately\n- No coordination between flush schedule and git operations\n\n**Possible solutions:**\n\n1. **Immediate flush before git operations**\n - Detect git commands (commit, status, push)\n - Force immediate flush if pending\n - Pros: Clean working tree guaranteed\n - Cons: Requires hooking git, may be slow\n\n2. **Commit includes pending flushes**\n - Add `bd sync` to commit workflow\n - Wait for flush to complete before committing\n - Pros: Simple, explicit\n - Cons: Requires user discipline\n\n3. **Git hooks integration**\n - pre-commit hook: `bd sync --wait`\n - Ensures JSONL is up-to-date before commit\n - Pros: Automatic, reliable\n - Cons: Requires hook installation\n\n4. **Reduce debounce delay**\n - Lower from 5s to 1s or 500ms\n - Pros: Faster sync, less likely to race\n - Cons: More frequent I/O, doesn't eliminate race\n\n5. **Lock-based coordination**\n - Daemon holds lock while flush pending\n - Git operations wait for lock\n - Pros: Guarantees ordering\n - Cons: Complex, may block operations\n\n**Recommended approach:**\nCombine #2 and #3:\n- Add `bd sync` command to explicitly flush\n- Provide git hooks in `examples/git-hooks/`\n- Document workflow in AGENTS.md\n- Keep 5s debounce for normal operations\n\n**Related:**\n- bd-50 (daemon version detection)","status":"open","priority":1,"issue_type":"bug","created_at":"2025-10-23T23:16:29.502191-07:00","updated_at":"2025-10-24T13:51:54.439387-07:00"}
{"id":"bd-52","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:\n1. dupl (7) - Code duplication\n2. goconst (12) - Repeated strings\n3. gocyclo (11) - High complexity functions\n4. revive (78) - Style issues\n5. gosec (102) - Security warnings\n6. 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.\n\n**Fixed in commits:**\n- c2c7eda: Fixed 15 actual errors (dupl, gosec, revive, staticcheck, unparam)\n- 963181d: Configured exclusions to get to 0 issues locally\n\n**Current status:**\n- ✅ Local: golangci-lint reports 0 issues\n- ❌ CI: Still failing (see bd-65)\n\n**Problem:**\nConfig v2 format or golangci-lint-action@v8 compatibility issue causing CI to fail despite local success.\n\n**Next:** Debug bd-65 to fix CI/local discrepancy","status":"in_progress","priority":2,"issue_type":"epic","created_at":"2025-10-24T01:01:12.997982-07:00","updated_at":"2025-10-24T13:51:54.439577-07:00"}
{"id":"bd-53","title":"Fix code duplication in label.go (dupl)","description":"Lines 72-120 duplicate lines 122-170 in cmd/bd/label.go. The add and remove commands have nearly identical structure.","design":"Extract common batch operation logic into a shared helper function that takes the operation type as a parameter.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-24T01:01:36.971666-07:00","updated_at":"2025-10-24T13:51:54.416434-07:00","closed_at":"2025-10-24T12:40:43.046348-07:00","dependencies":[{"issue_id":"bd-53","depends_on_id":"bd-52","type":"parent-child","created_at":"2025-10-24T13:17:40.325899-07:00","created_by":"renumber"}]}
{"id":"bd-54","title":"Convert repeated strings to constants (goconst)","description":"12 instances of repeated strings that should be constants: \"alice\", \"windows\", \"bd-114\", \"daemon\", \"import\", \"healthy\", \"unhealthy\", \"1.0.0\", \"custom-1\", \"custom-2\"","design":"Create package-level or test-level constants for frequently used test strings and command names.","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-24T01:01:36.9778-07:00","updated_at":"2025-10-24T13:51:54.439751-07:00","dependencies":[{"issue_id":"bd-54","depends_on_id":"bd-52","type":"parent-child","created_at":"2025-10-24T13:17:40.326123-07:00","created_by":"renumber"}]}
{"id":"bd-55","title":"Refactor high complexity functions (gocyclo)","description":"11 functions exceed cyclomatic complexity threshold (\u003e30): runDaemonLoop (42), importIssuesCore (71), TestLabelCommands (67), issueDataChanged (39), etc.","design":"Break down complex functions into smaller, testable units. Extract validation, error handling, and business logic into separate functions.","status":"open","priority":1,"issue_type":"task","created_at":"2025-10-24T01:01:36.989066-07:00","updated_at":"2025-10-24T13:51:54.417035-07:00","dependencies":[{"issue_id":"bd-55","depends_on_id":"bd-52","type":"parent-child","created_at":"2025-10-24T13:17:40.323992-07:00","created_by":"renumber"}]}
{"id":"bd-56","title":"Fix revive style issues (78 issues)","description":"Style violations: unused parameters (many cmd/args in cobra commands), missing exported comments, stuttering names (SQLiteStorage), indent-error-flow issues.","design":"Rename unused params to _, add godoc comments to exported types, fix stuttering names, simplify control flow.","status":"open","priority":3,"issue_type":"task","created_at":"2025-10-24T01:01:36.99984-07:00","updated_at":"2025-10-24T13:51:54.417341-07:00","dependencies":[{"issue_id":"bd-56","depends_on_id":"bd-52","type":"parent-child","created_at":"2025-10-24T13:17:40.322412-07:00","created_by":"renumber"}]}
{"id":"bd-57","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).","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-24T01:01:37.0139-07:00","updated_at":"2025-10-24T13:51:54.417632-07:00","dependencies":[{"issue_id":"bd-57","depends_on_id":"bd-52","type":"parent-child","created_at":"2025-10-24T13:17:40.324202-07:00","created_by":"renumber"}]}
{"id":"bd-58","title":"Handle unchecked errors (errcheck - 683 issues)","description":"683 unchecked error returns, mostly in tests (Close, Rollback, RemoveAll). Many already excluded in config but still showing up.","design":"Review .golangci.yml exclude-rules. Most defer Close/Rollback errors in tests can be ignored. Add systematic exclusions or explicit _ = assignments where appropriate.","status":"open","priority":3,"issue_type":"task","created_at":"2025-10-24T01:01:37.018404-07:00","updated_at":"2025-10-24T13:51:54.41793-07:00","dependencies":[{"issue_id":"bd-58","depends_on_id":"bd-52","type":"parent-child","created_at":"2025-10-24T13:17:40.324423-07:00","created_by":"renumber"}]}
{"id":"bd-59","title":"Update LINTING.md with current baseline","description":"After cleanup, document the remaining acceptable baseline in LINTING.md so we can track regression.","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-24T01:01:37.02745-07:00","updated_at":"2025-10-24T13:51:54.419194-07:00","dependencies":[{"issue_id":"bd-59","depends_on_id":"bd-52","type":"parent-child","created_at":"2025-10-24T13:17:40.327184-07:00","created_by":"renumber"},{"issue_id":"bd-59","depends_on_id":"bd-53","type":"blocks","created_at":"2025-10-24T13:17:40.327422-07:00","created_by":"renumber"},{"issue_id":"bd-59","depends_on_id":"bd-54","type":"blocks","created_at":"2025-10-24T13:17:40.327627-07:00","created_by":"renumber"},{"issue_id":"bd-59","depends_on_id":"bd-55","type":"blocks","created_at":"2025-10-24T13:17:40.327827-07:00","created_by":"renumber"},{"issue_id":"bd-59","depends_on_id":"bd-56","type":"blocks","created_at":"2025-10-24T13:17:40.32803-07:00","created_by":"renumber"},{"issue_id":"bd-59","depends_on_id":"bd-57","type":"blocks","created_at":"2025-10-24T13:17:40.328233-07:00","created_by":"renumber"},{"issue_id":"bd-59","depends_on_id":"bd-58","type":"blocks","created_at":"2025-10-24T13:51:54.447799-07:00","created_by":"renumber"}]}
{"id":"bd-53","title":"Fix code duplication in label.go (dupl)","description":"Lines 72-120 duplicate lines 122-170 in cmd/bd/label.go. The add and remove commands have nearly identical structure.","design":"Extract common batch operation logic into a shared helper function that takes the operation type as a parameter.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-24T01:01:36.971666-07:00","updated_at":"2025-10-24T13:51:54.416434-07:00","closed_at":"2025-10-24T12:40:43.046348-07:00","dependencies":[{"issue_id":"bd-53","depends_on_id":"bd-52","type":"parent-child","created_at":"2025-10-24T14:02:30.991442-07:00","created_by":"import"}]}
{"id":"bd-54","title":"Convert repeated strings to constants (goconst)","description":"12 instances of repeated strings that should be constants: \"alice\", \"windows\", \"bd-114\", \"daemon\", \"import\", \"healthy\", \"unhealthy\", \"1.0.0\", \"custom-1\", \"custom-2\"","design":"Create package-level or test-level constants for frequently used test strings and command names.","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-24T01:01:36.9778-07:00","updated_at":"2025-10-24T13:51:54.439751-07:00","dependencies":[{"issue_id":"bd-54","depends_on_id":"bd-52","type":"parent-child","created_at":"2025-10-24T14:02:30.991816-07:00","created_by":"import"}]}
{"id":"bd-55","title":"Refactor high complexity functions (gocyclo)","description":"11 functions exceed cyclomatic complexity threshold (\u003e30): runDaemonLoop (42), importIssuesCore (71), TestLabelCommands (67), issueDataChanged (39), etc.","design":"Break down complex functions into smaller, testable units. Extract validation, error handling, and business logic into separate functions.","status":"open","priority":1,"issue_type":"task","created_at":"2025-10-24T01:01:36.989066-07:00","updated_at":"2025-10-24T13:51:54.417035-07:00","dependencies":[{"issue_id":"bd-55","depends_on_id":"bd-52","type":"parent-child","created_at":"2025-10-24T14:02:30.992131-07:00","created_by":"import"}]}
{"id":"bd-56","title":"Fix revive style issues (78 issues)","description":"Style violations: unused parameters (many cmd/args in cobra commands), missing exported comments, stuttering names (SQLiteStorage), indent-error-flow issues.","design":"Rename unused params to _, add godoc comments to exported types, fix stuttering names, simplify control flow.","status":"open","priority":3,"issue_type":"task","created_at":"2025-10-24T01:01:36.99984-07:00","updated_at":"2025-10-24T13:51:54.417341-07:00","dependencies":[{"issue_id":"bd-56","depends_on_id":"bd-52","type":"parent-child","created_at":"2025-10-24T14:02:30.992429-07:00","created_by":"import"}]}
{"id":"bd-57","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).","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-24T01:01:37.0139-07:00","updated_at":"2025-10-24T13:51:54.417632-07:00","dependencies":[{"issue_id":"bd-57","depends_on_id":"bd-52","type":"parent-child","created_at":"2025-10-24T14:02:30.992717-07:00","created_by":"import"}]}
{"id":"bd-58","title":"Handle unchecked errors (errcheck - 683 issues)","description":"683 unchecked error returns, mostly in tests (Close, Rollback, RemoveAll). Many already excluded in config but still showing up.","design":"Review .golangci.yml exclude-rules. Most defer Close/Rollback errors in tests can be ignored. Add systematic exclusions or explicit _ = assignments where appropriate.","status":"open","priority":3,"issue_type":"task","created_at":"2025-10-24T01:01:37.018404-07:00","updated_at":"2025-10-24T13:51:54.41793-07:00","dependencies":[{"issue_id":"bd-58","depends_on_id":"bd-52","type":"parent-child","created_at":"2025-10-24T14:02:30.993013-07:00","created_by":"import"}]}
{"id":"bd-59","title":"Update LINTING.md with current baseline","description":"After cleanup, document the remaining acceptable baseline in LINTING.md so we can track regression.","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-24T01:01:37.02745-07:00","updated_at":"2025-10-24T13:51:54.419194-07:00","dependencies":[{"issue_id":"bd-59","depends_on_id":"bd-52","type":"parent-child","created_at":"2025-10-24T14:02:30.99329-07:00","created_by":"import"},{"issue_id":"bd-59","depends_on_id":"bd-53","type":"blocks","created_at":"2025-10-24T14:02:30.993569-07:00","created_by":"import"},{"issue_id":"bd-59","depends_on_id":"bd-54","type":"blocks","created_at":"2025-10-24T14:02:30.99386-07:00","created_by":"import"},{"issue_id":"bd-59","depends_on_id":"bd-55","type":"blocks","created_at":"2025-10-24T14:02:30.994149-07:00","created_by":"import"},{"issue_id":"bd-59","depends_on_id":"bd-56","type":"blocks","created_at":"2025-10-24T14:02:30.994432-07:00","created_by":"import"},{"issue_id":"bd-59","depends_on_id":"bd-57","type":"blocks","created_at":"2025-10-24T14:02:30.994756-07:00","created_by":"import"},{"issue_id":"bd-59","depends_on_id":"bd-58","type":"blocks","created_at":"2025-10-24T14:02:30.995152-07:00","created_by":"import"}]}
{"id":"bd-6","title":"Add transaction support to storage layer for atomic multi-operation workflows","description":"Currently each storage method (CreateIssue, UpdateIssue, etc.) starts its own transaction. This makes it impossible to perform atomic multi-step operations like collision resolution. Add support for passing *sql.Tx through the storage interface, or create transaction-aware versions of methods. This would make remapCollisions and other batch operations truly atomic.","status":"closed","priority":4,"issue_type":"feature","created_at":"2025-10-21T23:53:44.31362-07:00","updated_at":"2025-10-24T13:51:54.3808-07:00","closed_at":"2025-10-14T02:51:52.199176-07:00"}
{"id":"bd-60","title":"Fix Windows CI test failures (5 failing tests)","description":"Windows CI has 5 flaky/failing tests: TestTryDaemonLockDetectsRunning, TestIsDaemonRunning_CurrentProcess (PID detection issues), TestScripts/import, TestMetricsSnapshot/uptime, TestSocketCleanup (socket in use).","design":"Investigate Windows-specific PID/process detection and socket cleanup. These may be race conditions or platform differences in how Windows handles process IDs and file locks.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-10-24T09:28:17.976175-07:00","updated_at":"2025-10-24T13:51:54.419583-07:00","closed_at":"2025-10-24T09:36:59.351114-07:00"}
{"id":"bd-61","title":"Fix flaky TestMetricsSnapshot/memory_stats on Linux","description":"Linux CI test TestMetricsSnapshot/memory_stats fails with \"Expected non-zero memory allocation\". Appears to be a flaky test - metrics_test.go:168.","design":"Add retry logic or wait for GC stats to populate, or adjust test expectations for timing.","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-10-24T09:28:17.98305-07:00","updated_at":"2025-10-24T13:51:54.419826-07:00","closed_at":"2025-10-24T13:20:34.09678-07:00"}
{"id":"bd-62","title":"Investigate GH#144: FOREIGN KEY regression in v0.16.0","description":"v0.16.0 introduced FOREIGN KEY constraint error when closing issues via MCP (daemon mode). CLI works fine.\n\n**Key Finding**: Only change between v0.15.0 and v0.16.0 that could affect FK behavior is:\n- modernc.org/sqlite bumped from 1.38.2 to 1.39.1 (commit fbe63bf)\n\n**Evidence**:\n- User reports v0.15.0 worked perfectly (Oct 24, 04:35 UTC)\n- v0.16.0 fails with 'constraint failed: FOREIGN KEY constraint failed (787)'\n- Error occurs in both close() tool and update(status=closed) smart routing\n- CLI 'bd close' works fine, only MCP/daemon fails\n- No changes to CloseIssue() implementation between versions\n- No changes to schema or RPC server handlers\n\n**Next Steps**:\n1. Test downgrading sqlite to 1.38.2\n2. Check modernc.org/sqlite changelog for FK-related changes\n3. Reproduce locally with MCP\n4. If sqlite is the culprit, either fix or pin version\n\n**Related**: GH #144","status":"closed","priority":0,"issue_type":"bug","created_at":"2025-10-24T11:24:39.423407-07:00","updated_at":"2025-10-24T13:51:54.420068-07:00","closed_at":"2025-10-24T11:49:16.683734-07:00"}
{"id":"bd-63","title":"Test FK regression fix","description":"","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-24T11:25:36.132893-07:00","updated_at":"2025-10-24T13:51:54.420353-07:00","closed_at":"2025-10-24T11:25:38.270206-07:00"}
{"id":"bd-64","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).\n\n**Issue:** bd-62, GH #144\n\n**Symptom:** CloseIssue fails with \"FOREIGN KEY constraint failed (787)\" when called via MCP/daemon, but works fine via CLI.\n\n**Root Cause:** Unknown - likely stricter FK enforcement in SQLite 3.50.4 or modernc.org wrapper changes.\n\n**Workaround:** Pinned to v1.38.2 (SQLite 3.49.x)\n\n**TODO:**\n1. Monitor modernc.org/sqlite releases for fixes\n2. Check SQLite 3.50.5+ changelogs for FK-related fixes\n3. Investigate why daemon mode fails but CLI succeeds (connection reuse? transaction isolation?)\n4. Consider filing upstream issue with reproducible test case\n5. Upgrade when safe","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-24T11:49:12.836292-07:00","updated_at":"2025-10-24T13:51:54.439968-07:00","dependencies":[{"issue_id":"bd-64","depends_on_id":"bd-62","type":"discovered-from","created_at":"2025-10-24T13:17:40.321578-07:00","created_by":"renumber"}]}
{"id":"bd-64","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).\n\n**Issue:** bd-62, GH #144\n\n**Symptom:** CloseIssue fails with \"FOREIGN KEY constraint failed (787)\" when called via MCP/daemon, but works fine via CLI.\n\n**Root Cause:** Unknown - likely stricter FK enforcement in SQLite 3.50.4 or modernc.org wrapper changes.\n\n**Workaround:** Pinned to v1.38.2 (SQLite 3.49.x)\n\n**TODO:**\n1. Monitor modernc.org/sqlite releases for fixes\n2. Check SQLite 3.50.5+ changelogs for FK-related fixes\n3. Investigate why daemon mode fails but CLI succeeds (connection reuse? transaction isolation?)\n4. Consider filing upstream issue with reproducible test case\n5. Upgrade when safe","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-24T11:49:12.836292-07:00","updated_at":"2025-10-24T13:51:54.439968-07:00","dependencies":[{"issue_id":"bd-64","depends_on_id":"bd-62","type":"discovered-from","created_at":"2025-10-24T14:02:30.995484-07:00","created_by":"import"}]}
{"id":"bd-65","title":"CI still failing despite local lint passing - config v2 issue","description":"Despite fixing all lint issues locally (golangci-lint reports 0 issues), CI continues to fail for dependabot PRs and test/lint checks.\n\n**Local environment:**\n- golangci-lint v2.5.0 \n- .golangci.yml version: \"2\" (v2 config format)\n- Result: 0 issues ✅\n\n**CI environment:**\n- golangci/golangci-lint-action@v8\n- golangci-lint version: v2.5.0\n- Result: FAILING ❌\n\n**Root cause suspects:**\n\n1. **Config v2 compatibility**: The golangci-lint-action@v8 may not properly support config version \"2\" format, causing exclusion rules to be ignored\n\n2. **Environment differences**: \n - CI runs on Ubuntu (linux/amd64)\n - Local runs on macOS (darwin/arm64)\n - Pattern matching in exclusions might behave differently\n\n3. **Default linters**: CI might enable different default linters that aren't covered by our exclusions\n\n4. **Config parsing**: The action might not properly read .golangci.yml or apply settings correctly\n\n**Evidence:**\n- Commits 963181d and c2c7eda fixed issues locally\n- git push succeeded\n- But CI still showing failures on dependabot PRs","design":"**Investigation steps:**\n\n1. Access actual CI logs from failing runs to see exact error messages\n2. Test exact CI command locally: `golangci-lint run --timeout=5m ./...`\n3. Check if .golangci.yml is being loaded by adding debug output\n4. Compare default linters between local and CI: `golangci-lint linters`\n5. Test config v1 format vs v2 format\n\n**Potential fixes:**\n\nA. **Switch to config v1**: Revert .golangci.yml to version 1 format (older but more stable)\n\nB. **Explicit linter control**: Use `disable-all: true` and only enable specific linters\n\nC. **Update action version**: Try newer golangci-lint-action or pin different golangci-lint version\n\nD. **Inline exclusions**: Move exclusions from YAML to nolint directives in code\n\nE. **CI override**: Add `args: --disable=gocyclo,gosec` to CI workflow","acceptance_criteria":"- CI passes lint checks with 0 issues\n- Dependabot PRs can merge successfully \n- Local and CI environments report same lint results\n- Config is maintainable and exclusions work reliably","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-10-24T13:01:08.700153-07:00","updated_at":"2025-10-24T13:51:54.420836-07:00","closed_at":"2025-10-24T13:07:11.028211-07:00"}
{"id":"bd-66","title":"Feature: Use external_ref as primary matching key for import updates","description":"Implement external_ref-based matching for imports to enable hybrid workflows with external systems (Jira, GitHub, Linear).\n\n## Problem\nCurrent import collision detection treats any content change as a collision, preventing users from syncing updates from external systems without creating duplicates.\n\n## Solution\nUse external_ref field as primary matching key during imports. When an incoming issue has external_ref set:\n- Search for existing issue with same external_ref\n- If found, UPDATE (not collision)\n- If not found, create new issue\n- Never match local issues (without external_ref)\n\n## Use Cases\n- Jira integration: Import backlog, add local tasks, re-sync updates\n- GitHub integration: Import issues, track with local subtasks, sync status\n- Linear integration: Team coordination with local breakdown\n\n## Reference\nGitHub issue #142: https://github.com/steveyegge/beads/issues/142","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-10-24T13:08:19.797803-07:00","updated_at":"2025-10-24T13:51:54.421064-07:00","closed_at":"2025-10-24T13:35:23.287493-07:00"}
{"id":"bd-67","title":"Feature: Use external_ref as primary matching key for import updates","description":"Implement external_ref-based matching for imports to enable hybrid workflows with external systems (Jira, GitHub, Linear).\n\n## Problem\nCurrent import collision detection treats any content change as a collision, preventing users from syncing updates from external systems without creating duplicates.\n\n## Solution\nUse external_ref field as primary matching key during imports. When an incoming issue has external_ref set:\n- Search for existing issue with same external_ref\n- If found, UPDATE (not collision)\n- If not found, create new issue\n- Never match local issues (without external_ref)\n\n## Use Cases\n- Jira integration: Import backlog, add local tasks, re-sync updates\n- GitHub integration: Import issues, track with local subtasks, sync status\n- Linear integration: Team coordination with local breakdown\n\n## Reference\nGitHub issue #142: https://github.com/steveyegge/beads/issues/142","status":"open","priority":1,"issue_type":"epic","created_at":"2025-10-24T13:08:19.797803-07:00","updated_at":"2025-10-24T13:51:54.421296-07:00"}
@@ -100,7 +106,7 @@
{"id":"bd-72","title":"Update documentation for external_ref import behavior","description":"Document the new external_ref-based import matching behavior.\n\n## Files to Update\n- README.md: Add external_ref import section\n- QUICKSTART.md: Add example of Jira/GitHub integration workflow\n- AGENTS.md: Document external_ref import behavior for AI agents\n- FAQ.md: Add Q\u0026A about external system integration\n- Add examples/ directory entry showing Jira/GitHub/Linear integration\n\n## Key Points to Document\n- How external_ref matching works\n- That local issues are protected from external imports\n- Example workflow: import → add local tasks → re-import updates\n- ID conflict resolution behavior","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-24T13:08:46.390992-07:00","updated_at":"2025-10-24T13:51:54.42238-07:00","closed_at":"2025-10-24T13:35:23.288575-07:00"}
{"id":"bd-73","title":"Code review: external_ref import feature","description":"Comprehensive code review of external_ref import implementation.\n\n## Review Checklist\n- [ ] Storage layer changes are correct and efficient\n- [ ] Index created properly on external_ref column\n- [ ] Collision detection logic handles all edge cases\n- [ ] Import flow correctly distinguishes external vs local issues\n- [ ] No breaking changes to existing workflows\n- [ ] Tests cover all scenarios (see bd-71)\n- [ ] Documentation is clear and complete (see bd-72)\n- [ ] Performance impact is acceptable\n- [ ] Error messages are helpful\n- [ ] Logging is appropriate for debugging\n\n## Oracle Review\nUse oracle tool to analyze implementation for correctness, edge cases, and potential issues.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-24T13:08:46.395433-07:00","updated_at":"2025-10-24T13:51:54.440268-07:00","closed_at":"2025-10-24T13:35:23.288755-07:00"}
{"id":"bd-74","title":"Code review: external_ref import feature","description":"Comprehensive code review of external_ref import implementation.\n\n## Review Checklist\n- [ ] Storage layer changes are correct and efficient\n- [ ] Index created properly on external_ref column\n- [ ] Collision detection logic handles all edge cases\n- [ ] Import flow correctly distinguishes external vs local issues\n- [ ] No breaking changes to existing workflows\n- [ ] Tests cover all scenarios (see-109)\n- [ ] Documentation is clear and complete (see-110)\n- [ ] Performance impact is acceptable\n- [ ] Error messages are helpful\n- [ ] Logging is appropriate for debugging\n\n## Oracle Review\nUse oracle tool to analyze implementation for correctness, edge cases, and potential issues.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-24T13:08:46.395433-07:00","updated_at":"2025-10-24T13:51:54.422845-07:00","closed_at":"2025-10-24T13:35:26.749271-07:00"}
{"id":"bd-75","title":"Warn when multiple beads databases detected in workspace hierarchy","description":"Add detection and warning when multiple .beads directories exist in the workspace hierarchy to prevent confusion and database pollution.\n\n## Problem\nUsers can accidentally work in the wrong beads database when multiple exist (e.g., ~/src/beads and ~/src/fred/beads), leading to:\n- Database pollution from cross-contamination\n- Lost work when changes go to wrong database\n- Confusion about which database is active\n\n## Solution\nOn startup, scan parent directories for additional .beads directories and:\n- Warn if multiple found\n- Show which database is currently active\n- Suggest consolidation or cleanup\n\n## Detection Strategy\n1. Walk up directory tree from CWD\n2. Check for .beads/bd.db in each level\n3. If multiple found, show warning with paths\n4. Optionally: check common sibling directories\n\n## Example Warning\n```\n⚠ Multiple beads databases detected:\n - /Users/stevey/src/beads/.beads (ACTIVE - 111 issues)\n - /Users/stevey/src/fred/beads/.beads (202 issues)\n \nConsider consolidating or removing unused databases.\n```","status":"open","priority":2,"issue_type":"feature","created_at":"2025-10-24T13:17:08.772366-07:00","updated_at":"2025-10-24T13:51:54.423054-07:00","dependencies":[{"issue_id":"bd-75","depends_on_id":"bd-67","type":"discovered-from","created_at":"2025-10-24T13:35:23.245342-07:00","created_by":"stevey"}]}
{"id":"bd-75","title":"Warn when multiple beads databases detected in workspace hierarchy","description":"Add detection and warning when multiple .beads directories exist in the workspace hierarchy to prevent confusion and database pollution.\n\n## Problem\nUsers can accidentally work in the wrong beads database when multiple exist (e.g., ~/src/beads and ~/src/fred/beads), leading to:\n- Database pollution from cross-contamination\n- Lost work when changes go to wrong database\n- Confusion about which database is active\n\n## Solution\nOn startup, scan parent directories for additional .beads directories and:\n- Warn if multiple found\n- Show which database is currently active\n- Suggest consolidation or cleanup\n\n## Detection Strategy\n1. Walk up directory tree from CWD\n2. Check for .beads/bd.db in each level\n3. If multiple found, show warning with paths\n4. Optionally: check common sibling directories\n\n## Example Warning\n```\n⚠ Multiple beads databases detected:\n - /Users/stevey/src/beads/.beads (ACTIVE - 111 issues)\n - /Users/stevey/src/fred/beads/.beads (202 issues)\n \nConsider consolidating or removing unused databases.\n```","status":"open","priority":2,"issue_type":"feature","created_at":"2025-10-24T13:17:08.772366-07:00","updated_at":"2025-10-24T13:51:54.423054-07:00","dependencies":[{"issue_id":"bd-75","depends_on_id":"bd-67","type":"discovered-from","created_at":"2025-10-24T14:02:30.995787-07:00","created_by":"import"}]}
{"id":"bd-76","title":"Optimize auto-flush to use incremental updates","description":"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.","status":"closed","priority":3,"issue_type":"feature","created_at":"2025-10-24T13:31:30.813833-07:00","updated_at":"2025-10-24T13:51:54.423317-07:00","closed_at":"2025-10-14T02:51:52.200141-07:00"}
{"id":"bd-77","title":"Auto-flush writes test pollution and session work to git-tracked issues.jsonl","description":"Auto-flush exports ALL issues from DB to issues.jsonl every 5 seconds, including:\n- Test issues (bd-4053 through bd-4059 were version test junk)\n- Issues created during debugging sessions\n- Test pollution from stress tests\n- Temporary diagnostic issues\n\nThis pollutes the git-tracked issues.jsonl with garbage that shouldn't be committed.\n\nExample from today:\n- Git had 49 clean issues\n- Our DB grew to 100+ with test junk and session work\n- Auto-flush wrote all 100+ to issues.jsonl\n- Git status showed modified issues.jsonl with 50+ unwanted issues\n\nImpact:\n- Pollutes git history with test/debug garbage\n- Makes code review difficult (noise in diffs)\n- Can't distinguish real work from session artifacts\n- Other team members pull polluted issues\n\nSolutions to consider:\n1. Disable auto-flush by default (require explicit --enable-auto-flush)\n2. Add .beadsignore to exclude issue ID patterns\n3. Make auto-flush only export 'real' issues (exclude test-*)\n4. Require manual 'bd sync' for git commit\n5. Auto-flush to separate file (.beads/session.jsonl vs issues.jsonl)\n\nRelated: bd-128 (test pollution), isolation_test.go (test DB separation)","design":"## Analysis\n\nConfirmed the issue exists - bd-127 through bd-19 are test pollution in the git-tracked issues.jsonl.\n\n### Solution Evaluation:\n\n**Option 1: Disable auto-flush by default** ❌\n- Breaks the auto-sync workflow that users rely on\n- Requires manual intervention which defeats the purpose\n- Not recommended\n\n**Option 2: Add .beadsignore** ⚠️\n- Complex to implement (pattern matching, configuration)\n- Doesn't solve root cause: test issues in production DB\n- Better to prevent pollution at source\n\n**Option 3: Filter on export** ❌\n- Doesn't solve root cause\n- Test issues still pollute production DB\n- Complicates export logic\n\n**Option 4: Manual 'bd sync'** ❌\n- Same issues as Option 1\n- Breaks automated workflow\n\n**Option 5: Separate session file** ❌\n- Splits issue tracking across files\n- Confusing for users\n- Import/export complexity\n\n### RECOMMENDED SOLUTION:\n\n**Fix the root cause: Tests should NEVER touch the production database**\n\nThe real problem is that Go tests ARE properly isolated (they use temp DBs), but someone must be manually creating test issues in the production DB during development/debugging.\n\n**Best fix:**\n1. Document that production DB is for real work only\n2. Add a convenience command: `bd test-create` that uses a separate test database\n3. Clean up the existing test pollution: bd-127 through bd-19\n4. Consider adding a git pre-commit hook to warn about suspicious issues\n\nThis preserves auto-flush (which is valuable) while preventing pollution at the source.","notes":"## Resolution\n\n**Root Cause Identified:**\nThe issue was NOT a bug in auto-flush, but rather test pollution in the production database from manual testing/debugging. Go tests are properly isolated using temp directories.\n\n**Actions Taken:**\n1. Cleaned up test pollution: deleted bd-127 through bd-19 (all \"Version test issue\" entries)\n2. Verified auto-flush is working correctly - it exports the database as designed\n3. Confirmed Go test isolation works properly (uses temp dirs, not production DB)\n\n**Prevention Strategy:**\n- Production database (.beads/) should only contain real work issues\n- Manual testing should use throwaway databases or test scripts\n- Go tests already use isolated temp databases\n- Auto-flush is working as intended and should remain enabled\n\n**Conclusion:**\nThis was user error, not a system bug. The auto-flush mechanism is correct - it should export ALL database contents. The problem was polluting the production database with test issues in the first place.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-10-24T13:31:30.814245-07:00","updated_at":"2025-10-24T13:51:54.440545-07:00","closed_at":"2025-10-22T00:05:29.864829-07:00"}
{"id":"bd-78","title":"Fix flaky TestMetricsSnapshot/memory_stats on Linux","description":"Linux CI test TestMetricsSnapshot/memory_stats fails with \"Expected non-zero memory allocation\". Appears to be a flaky test - metrics_test.go:168.","design":"Add retry logic or wait for GC stats to populate, or adjust test expectations for timing.","status":"open","priority":2,"issue_type":"bug","created_at":"2025-10-24T13:31:30.814562-07:00","updated_at":"2025-10-24T13:51:54.423894-07:00"}