From f3f5c142ce5e6e428223c30dedd79f1bbcc18d62 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Tue, 4 Nov 2025 12:36:59 -0800 Subject: [PATCH 1/2] bd sync: 2025-11-04 12:36:59 --- .beads/beads.jsonl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.beads/beads.jsonl b/.beads/beads.jsonl index 04cbec25..d588d470 100644 --- a/.beads/beads.jsonl +++ b/.beads/beads.jsonl @@ -176,7 +176,7 @@ {"id":"bd-9f20","content_hash":"523ec11b4c3e82c8d5b36deeb284ebae19eee2090a7051b599da79366c642238","title":"DetectCycles SQL query has bug preventing cycle detection","description":"The DetectCycles function's SQL query has a bug in the LIKE filter that prevents it from detecting cycles.\n\nCurrent code (line 571):\n```sql\nAND p.path NOT LIKE '%' || d.depends_on_id || '→%'\n```\n\nThis prevents ANY revisit to nodes, including returning to the start node to complete a cycle.\n\nFix:\n```sql\nAND (d.depends_on_id = p.start_id OR p.path NOT LIKE '%' || d.depends_on_id || '→%')\n```\n\nThis allows revisiting the start node (to detect the cycle) while still preventing intermediate node revisits.\n\nImpact: Currently DetectCycles cannot detect any cycles, but this hasn't been noticed because AddDependency prevents cycles from being created. The function would only matter if cycles were manually inserted into the database.","status":"closed","priority":3,"issue_type":"bug","created_at":"2025-11-01T22:50:32.552763-07:00","updated_at":"2025-11-01T22:52:02.247443-07:00","closed_at":"2025-11-01T22:52:02.247443-07:00"} {"id":"bd-9f4a","content_hash":"ff058f9bad890bee6a00df24c846f523980473d47c702097164deea7504886a4","title":"Document external_ref in content hash behavior","description":"The content hash includes external_ref, which has implications that should be documented.\n\nCurrent behavior:\n- external_ref is included in content hash calculation (collision.go:158-160)\n- Changing external_ref changes content hash\n- This means: local issue → add external_ref → different hash\n\nImplications:\n- Local issue + external_ref addition = looks like 'new content'\n- May not match by content hash in some scenarios\n- Generally correct behavior, but subtle\n\nAction items:\n- Document in code comments\n- Add to ARCHITECTURE.md or similar\n- Add test demonstrating this behavior\n- Consider if this is desired long-term\n\nRelated: bd-1022\nFiles: internal/storage/sqlite/collision.go:158-160","status":"open","priority":4,"issue_type":"task","created_at":"2025-11-02T15:32:47.715458-08:00","updated_at":"2025-11-02T15:32:47.715458-08:00"} {"id":"bd-a03d5e36","content_hash":"b4ee73e439a133a77e5df27e3e457674dbc9968fdbee0dc630175726960bb8cf","title":"Improve integration test coverage for stateful features","description":"","design":"## Context\n\nbd-70419816 revealed a critical gap: the export deduplication feature had unit tests but no integration tests simulating real-world git operations. This led to silent data loss in production.\n\n## Root Cause\n- Unit tests only tested functions in isolation\n- No integration tests for git operations (pull, reset, checkout) modifying JSONL\n- No tests validating export_hashes and JSONL stay in sync\n- Missing tests for stateful distributed system interactions (DB + JSONL + git)\n\n## Completed (bd-70419816)\n✓ TestJSONLIntegrityValidation - unit tests for validation logic\n✓ TestImportClearsExportHashes - tests import clears hashes\n✓ TestExportIntegrityAfterJSONLTruncation - simulates git reset (would have caught bd-70419816)\n✓ TestExportIntegrityAfterJSONLDeletion - tests recovery from file deletion\n✓ TestMultipleExportsStayConsistent - tests repeated exports\n\n## Still Needed (High Priority)\n1. Multi-repo sync test - two clones staying in sync after push/pull\n2. Auto-flush integration test - JSONL integrity preserved during auto-flush\n3. Daemon auto-sync integration test - complex state management\n4. Import after corruption test - recovery from partial data loss\n\n## Medium Priority\n- Partial export failure handling (disk full, network interruption)\n- Concurrent export/import race conditions\n- Large dataset performance tests (1000+ issues)\n- Export hash migration tests (version upgrades)\n\n## Testing Principles\n1. Test real-world scenarios: git ops, user errors, system failures, concurrent ops\n2. Integration tests for stateful systems (DB + files + git)\n3. Regression test for every bug fix\n4. Test invariants: JSONL count == DB count, hash consistency, etc.\n\n## Key Lesson\nStateful distributed systems need integration tests, not just unit tests.","acceptance_criteria":"- [ ] Multi-repo sync test implemented\n- [ ] Auto-flush integration test implemented \n- [ ] Daemon auto-sync integration test implemented\n- [ ] Testing guidelines added to CONTRIBUTING.md\n- [ ] CI runs integration tests\n- [ ] All critical workflows have integration test coverage","status":"open","priority":2,"issue_type":"epic","created_at":"2025-10-29T21:53:15.397137-07:00","updated_at":"2025-10-30T17:12:58.206063-07:00"} -{"id":"bd-a101","content_hash":"5b6bf9f0dd9fdae06d6596dfb1c2a96262510428f891344162e58f59db46880b","title":"Support separate branch for beads commits","description":"Allow beads to commit to a separate branch (e.g., beads-metadata) using git worktrees to support protected main branch workflows.\n\nSolves GitHub Issue #205 - Users need to protect main branch while maintaining beads workflow.\n\nKey advantages:\n- Works on any git platform\n- Main branch stays protected \n- No disruption to user's working directory\n- Backward compatible (opt-in via config)\n- Minimal disk overhead (sparse checkout)\n\nTotal estimate: 17-24 days (4-6 weeks with parallel work)","status":"open","priority":1,"issue_type":"epic","created_at":"2025-11-02T15:21:20.098247-08:00","updated_at":"2025-11-03T22:13:20.959505-08:00","external_ref":"GH-205"} +{"id":"bd-a101","content_hash":"805d60a6f4d9205a7e0498f63e9c0bd98a36eb86800304a123cd9122f694b5ab","title":"Support separate branch for beads commits","description":"Allow beads to commit to a separate branch (e.g., beads-metadata) using git worktrees to support protected main branch workflows.\n\nSolves GitHub Issue #205 - Users need to protect main branch while maintaining beads workflow.\n\nKey advantages:\n- Works on any git platform\n- Main branch stays protected \n- No disruption to user's working directory\n- Backward compatible (opt-in via config)\n- Minimal disk overhead (sparse checkout)\n\nTotal estimate: 17-24 days (4-6 weeks with parallel work)","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-11-02T15:21:20.098247-08:00","updated_at":"2025-11-04T12:36:53.772727-08:00","closed_at":"2025-11-04T12:36:53.772727-08:00","external_ref":"GH-205"} {"id":"bd-a1691807","content_hash":"23f0119ee9df98f1bf6d648dba06065c156963064ef1c7308dfb02c8bdd5bc58","title":"Integration test: mutation to export latency","description":"Measure time from bd create to JSONL update. Verify \u003c500ms latency. Test with multiple rapid mutations to verify batching.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-29T20:49:49.105247-07:00","updated_at":"2025-10-31T12:00:43.198883-07:00","closed_at":"2025-10-31T12:00:43.198883-07:00"} {"id":"bd-a40f374f","content_hash":"599448515198700decd2494cf0cea3335b013c573bdcbda90a151564585cf845","title":"bd validate - Comprehensive health check","description":"Run all validation checks in one command.\n\nChecks:\n- Duplicates\n- Orphaned dependencies\n- Test pollution\n- Git conflicts\n\nSupports --fix-all for auto-repair.\n\nDepends on bd-cbed9619.1, bd-0dcea000, bd-31aab707, bd-9826b69a.\n\nFiles: cmd/bd/validate.go (new)","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-29T20:02:47.956664-07:00","updated_at":"2025-10-30T17:12:58.195108-07:00","closed_at":"2025-10-29T20:02:15.318966-07:00"} {"id":"bd-a4b5","content_hash":"3966f6f9ab3202fe740f2936c7743f679ea42b75803c99465176ccf69ffd9dd7","title":"Implement git worktree management","description":"Create git worktree lifecycle management for separate beads branch.\n\nTasks:\n- Create internal/git/worktree.go\n- Implement CreateBeadsWorktree(branch, path)\n- Implement RemoveBeadsWorktree(path)\n- Implement CheckWorktreeHealth(path)\n- Configure sparse checkout (only .beads/)\n- Implement SyncJSONLToWorktree()\n- Handle worktree errors gracefully\n- Auto-cleanup on config change\n\nEstimated effort: 3-4 days","acceptance_criteria":"- Worktree created successfully on first use\n- Sparse checkout limits to .beads/ only\n- Health check detects and fixes broken worktrees\n- JSONL synced correctly before commits\n- Cleanup removes worktree completely","status":"closed","priority":1,"issue_type":"task","created_at":"2025-11-02T15:22:35.56423-08:00","updated_at":"2025-11-02T17:19:13.187639-08:00","closed_at":"2025-11-02T17:19:13.187642-08:00","dependencies":[{"issue_id":"bd-a4b5","depends_on_id":"bd-a101","type":"parent-child","created_at":"2025-11-02T15:22:48.359843-08:00","created_by":"stevey"}]} @@ -205,7 +205,7 @@ {"id":"bd-c825f867","content_hash":"27cecaa2dc6cdabb2ae77fd65fbf8dca8f4c536bdf140a13b25cdd16376c9845","title":"Add docs/architecture/event_driven.md","description":"Copy event_driven_daemon.md into docs/ folder. Add to documentation index.","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-28T16:20:02.431399-07:00","updated_at":"2025-10-30T17:12:58.221939-07:00"} {"id":"bd-c947dd1b","content_hash":"79bd51b46b28bc16cfc19cd19a4dd4f57f45cd1e902b682788d355b03ec00b2a","title":"Remove Daemon Storage Cache","description":"The daemon's multi-repo storage cache is the root cause of stale data bugs. Since global daemon is deprecated, we only ever serve one repository, making the cache unnecessary complexity. This epic removes the cache entirely for simpler, more reliable direct storage access.","design":"For local daemon (single repository), eliminate the cache entirely:\n- Use s.storage field directly (opened at daemon startup)\n- Remove getStorageForRequest() routing logic\n- Remove server_cache_storage.go entirely (~300 lines)\n- Remove cache-related tests\n- Simplify Server struct\n\nBenefits:\n✅ No staleness bugs: Always using live SQLite connection\n✅ Simpler code: Remove ~300 lines of cache management\n✅ Easier debugging: Direct storage access, no cache indirection\n✅ Same performance: Cache was always 1 entry for local daemon anyway","acceptance_criteria":"- Daemon has no storage cache code\n- All tests pass\n- MCP integration works\n- No stale data bugs\n- Documentation updated\n- Performance validated","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-10-28T10:50:15.126939-07:00","updated_at":"2025-10-30T17:12:58.21743-07:00","closed_at":"2025-10-28T10:49:53.612049-07:00"} {"id":"bd-c9a482db","content_hash":"35c1ad124187c21b4e8dae7db46ea5d00173d33234a9b815ded7dcf0ab51078e","title":"Add internal/ai package for AI-assisted repairs","description":"Add AI integration package to support AI-powered repair commands.\n\nProviders:\n- Anthropic (Claude)\n- OpenAI\n- Ollama (local)\n\nFeatures:\n- Conflict resolution analysis\n- Duplicate detection via embeddings\n- Configuration via env vars (BEADS_AI_PROVIDER, BEADS_AI_API_KEY, etc.)\n\nSee repair_commands.md lines 357-425 for design.","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-28T19:37:55.722841-07:00","updated_at":"2025-10-30T17:12:58.180177-07:00"} -{"id":"bd-caa9","content_hash":"aa97994c8474a1380ff7f9c9db681c6d6dda62839b1ddc13312a6813029b6404","title":"Migration tool for existing users","description":"Ensure smooth migration for existing users to separate branch workflow.\n\nTasks:\n- Add bd migrate --separate-branch command\n- Detect existing repos, migrate cleanly\n- Preserve git history\n- Add rollback mechanism\n- Test migration on beads' own repo (dogfooding)\n- Communication plan (GitHub discussion, docs)\n- Version compatibility checks\n\nEstimated effort: 2-3 days","acceptance_criteria":"- Existing users can migrate without data loss\n- Rollback works if migration fails\n- Clear communication about breaking changes (if any)\n- beads project itself migrated successfully (dogfooding)\n- Migration tested on 5+ real-world repos","status":"open","priority":1,"issue_type":"task","created_at":"2025-11-02T15:22:35.627388-08:00","updated_at":"2025-11-02T17:19:13.188386-08:00","dependencies":[{"issue_id":"bd-caa9","depends_on_id":"bd-a101","type":"parent-child","created_at":"2025-11-02T15:22:48.382619-08:00","created_by":"stevey"}]} +{"id":"bd-caa9","content_hash":"aa97994c8474a1380ff7f9c9db681c6d6dda62839b1ddc13312a6813029b6404","title":"Migration tool for existing users","description":"Ensure smooth migration for existing users to separate branch workflow.\n\nTasks:\n- Add bd migrate --separate-branch command\n- Detect existing repos, migrate cleanly\n- Preserve git history\n- Add rollback mechanism\n- Test migration on beads' own repo (dogfooding)\n- Communication plan (GitHub discussion, docs)\n- Version compatibility checks\n\nEstimated effort: 2-3 days","acceptance_criteria":"- Existing users can migrate without data loss\n- Rollback works if migration fails\n- Clear communication about breaking changes (if any)\n- beads project itself migrated successfully (dogfooding)\n- Migration tested on 5+ real-world repos","status":"closed","priority":1,"issue_type":"task","created_at":"2025-11-02T15:22:35.627388-08:00","updated_at":"2025-11-04T12:36:53.789201-08:00","closed_at":"2025-11-04T12:36:53.789201-08:00","dependencies":[{"issue_id":"bd-caa9","depends_on_id":"bd-a101","type":"parent-child","created_at":"2025-11-02T15:22:48.382619-08:00","created_by":"stevey"}]} {"id":"bd-cb2f","content_hash":"99b9c1c19d5e9f38308d78f09763426777797f133d4c86edd579419e7ba4043f","title":"Week 1 task","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-11-03T19:11:59.358093-08:00","updated_at":"2025-11-03T19:11:59.358093-08:00","labels":["frontend","week2"]} {"id":"bd-cb64c226.1","content_hash":"0bfd0735c8985d3b3e4906e44f22b06fb24758c6d795188226e920bd8b3e7cf8","title":"Performance Validation","description":"Confirm no performance regression from cache removal","acceptance_criteria":"- Benchmarks show no significant regression\n- Document performance characteristics\n- Confirm single SQLite connection is reused\n\nBenchmarks: go test -bench=. -benchmem ./internal/rpc/...\n\nMetrics to track:\n- Request latency (p50, p99)\n- Throughput (requests/sec)\n- Memory usage\n- SQLite connection overhead\n\nExpected results:\n- Latency: Same or better (no cache overhead)\n- Throughput: Same (cache was always 1 entry)\n- Memory: Lower (no cache structs)\n- Connection overhead: Zero (single connection reused)","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-28T10:50:15.126019-07:00","updated_at":"2025-10-30T17:12:58.216721-07:00","closed_at":"2025-10-28T10:49:45.021037-07:00"} {"id":"bd-cb64c226.10","content_hash":"2dbe416cf266952236a03ed414e5f7f9eb5526d69b70d0821ca0d59b2bc22305","title":"Delete server_cache_storage.go","description":"Remove the entire cache implementation file (~286 lines)","acceptance_criteria":"- File deleted from repository\n- No compilation errors\n- No references to deleted functions\n\nFunctions being removed:\n- StorageCacheEntry struct\n- evictStaleStorage() - LRU eviction\n- evictCacheBasedOnMemory() - memory pressure eviction\n- getStorageForRequest() - cache lookup and routing\n- findDatabaseForCwd() - database discovery\n- evictStorageForRequest() - manual eviction","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-27T22:55:38.729299-07:00","updated_at":"2025-10-30T17:12:58.212391-07:00","closed_at":"2025-10-28T14:08:38.064592-07:00"} From 14bdc6fe684f7a712778d044c0acb545c2765c43 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Tue, 4 Nov 2025 12:39:38 -0800 Subject: [PATCH 2/2] Add bd migrate --to-separate-branch command Implements bd-caa9: migration tool for existing users to enable separate branch workflow. Features: - bd migrate --to-separate-branch sets sync.branch config - Validates branch names (no whitespace/empty) - Dry-run support with --dry-run flag - Idempotent (safe to run multiple times) - JSON output support - Clear next steps for users Closes bd-caa9, bd-a101 Amp-Thread-ID: https://ampcode.com/threads/T-93c3427d-12a1-4d9a-8690-1d0cfe22188f Co-authored-by: Amp --- cmd/bd/migrate.go | 158 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/cmd/bd/migrate.go b/cmd/bd/migrate.go index 514bad20..7cf00afc 100644 --- a/cmd/bd/migrate.go +++ b/cmd/bd/migrate.go @@ -31,6 +31,7 @@ This command: - Migrates old databases to beads.db - Updates schema version metadata - Migrates sequential IDs to hash-based IDs (with --to-hash-ids) +- Enables separate branch workflow (with --to-separate-branch) - Removes stale databases (with confirmation)`, Run: func(cmd *cobra.Command, _ []string) { autoYes, _ := cmd.Flags().GetBool("yes") @@ -39,6 +40,7 @@ This command: updateRepoID, _ := cmd.Flags().GetBool("update-repo-id") toHashIDs, _ := cmd.Flags().GetBool("to-hash-ids") inspect, _ := cmd.Flags().GetBool("inspect") + toSeparateBranch, _ := cmd.Flags().GetString("to-separate-branch") // Handle --update-repo-id first if updateRepoID { @@ -52,6 +54,12 @@ This command: return } + // Handle --to-separate-branch + if toSeparateBranch != "" { + handleToSeparateBranch(toSeparateBranch, dryRun) + return + } + // Find .beads directory beadsDir := findBeadsDir() if beadsDir == "" { @@ -898,6 +906,155 @@ func handleInspect() { } } +// handleToSeparateBranch configures separate branch workflow for existing repos +func handleToSeparateBranch(branch string, dryRun bool) { + // Validate branch name + b := strings.TrimSpace(branch) + if b == "" || strings.ContainsAny(b, " \t\n") { + if jsonOutput { + outputJSON(map[string]interface{}{ + "error": "invalid_branch", + "message": "Branch name cannot be empty or contain whitespace", + }) + } else { + fmt.Fprintf(os.Stderr, "Error: invalid branch name '%s'\n", branch) + fmt.Fprintf(os.Stderr, "Branch name cannot be empty or contain whitespace\n") + } + os.Exit(1) + } + + // Find .beads directory + beadsDir := findBeadsDir() + if beadsDir == "" { + if jsonOutput { + outputJSON(map[string]interface{}{ + "error": "no_beads_directory", + "message": "No .beads directory found. Run 'bd init' first.", + }) + } else { + fmt.Fprintf(os.Stderr, "Error: no .beads directory found\n") + fmt.Fprintf(os.Stderr, "Hint: run 'bd init' to initialize bd\n") + } + os.Exit(1) + } + + // Load config + cfg, err := loadOrCreateConfig(beadsDir) + if err != nil { + if jsonOutput { + outputJSON(map[string]interface{}{ + "error": "config_load_failed", + "message": err.Error(), + }) + } else { + fmt.Fprintf(os.Stderr, "Error: failed to load config: %v\n", err) + } + os.Exit(1) + } + + // Check database exists + targetPath := cfg.DatabasePath(beadsDir) + if _, err := os.Stat(targetPath); os.IsNotExist(err) { + if jsonOutput { + outputJSON(map[string]interface{}{ + "error": "database_missing", + "message": "Database not found. Run 'bd init' first.", + }) + } else { + fmt.Fprintf(os.Stderr, "Error: database not found: %s\n", targetPath) + fmt.Fprintf(os.Stderr, "Hint: run 'bd init' to initialize bd\n") + } + os.Exit(1) + } + + // Open database + store, err := sqlite.New(targetPath) + if err != nil { + if jsonOutput { + outputJSON(map[string]interface{}{ + "error": "database_open_failed", + "message": err.Error(), + }) + } else { + fmt.Fprintf(os.Stderr, "Error: failed to open database: %v\n", err) + } + os.Exit(1) + } + defer func() { _ = store.Close() }() + + // Get current sync.branch config + ctx := context.Background() + current, _ := store.GetConfig(ctx, "sync.branch") + + // Dry-run mode + if dryRun { + if jsonOutput { + outputJSON(map[string]interface{}{ + "dry_run": true, + "previous": current, + "branch": b, + "changed": current != b, + }) + } else { + fmt.Println("Dry run mode - no changes will be made") + if current == b { + fmt.Printf("sync.branch already set to '%s'\n", b) + } else { + fmt.Printf("Would set sync.branch: '%s' → '%s'\n", current, b) + } + } + return + } + + // Check if already set + if current == b { + if jsonOutput { + outputJSON(map[string]interface{}{ + "status": "noop", + "branch": b, + "message": "sync.branch already set to this value", + }) + } else { + color.Green("✓ sync.branch already set to '%s'\n", b) + fmt.Println("No changes needed") + } + return + } + + // Update sync.branch config + if err := store.SetConfig(ctx, "sync.branch", b); err != nil { + if jsonOutput { + outputJSON(map[string]interface{}{ + "error": "config_update_failed", + "message": err.Error(), + }) + } else { + fmt.Fprintf(os.Stderr, "Error: failed to set sync.branch: %v\n", err) + } + os.Exit(1) + } + + // Success output + if jsonOutput { + outputJSON(map[string]interface{}{ + "status": "success", + "previous": current, + "branch": b, + "message": "Enabled separate branch workflow", + }) + } else { + color.Green("✓ Enabled separate branch workflow\n\n") + fmt.Printf("Set sync.branch to '%s'\n\n", b) + fmt.Println("Next steps:") + fmt.Println(" 1. Restart the daemon to create worktree and start committing to the branch:") + fmt.Printf(" bd daemon restart\n") + fmt.Printf(" bd daemon start --auto-commit\n\n") + fmt.Println(" 2. Your existing data is preserved - no changes to git history") + fmt.Println(" 3. Future issue updates will be committed to the separate branch") + fmt.Println("\nSee docs/PROTECTED_BRANCHES.md for complete workflow guide") + } +} + func init() { migrateCmd.Flags().Bool("yes", false, "Auto-confirm cleanup prompts") migrateCmd.Flags().Bool("cleanup", false, "Remove old database files after migration") @@ -905,6 +1062,7 @@ func init() { migrateCmd.Flags().Bool("update-repo-id", false, "Update repository ID (use after changing git remote)") migrateCmd.Flags().Bool("to-hash-ids", false, "Migrate sequential IDs to hash-based IDs") migrateCmd.Flags().Bool("inspect", false, "Show migration plan and database state for AI agent analysis") + migrateCmd.Flags().String("to-separate-branch", "", "Enable separate branch workflow (e.g., 'beads-metadata')") migrateCmd.Flags().BoolVar(&jsonOutput, "json", false, "Output migration statistics in JSON format") rootCmd.AddCommand(migrateCmd) }