diff --git a/.beads/beads.jsonl b/.beads/beads.jsonl index 44fc3321..9345f332 100644 --- a/.beads/beads.jsonl +++ b/.beads/beads.jsonl @@ -353,6 +353,7 @@ {"id":"bd-e652","content_hash":"6b95b33d0961d545d24063112c77f58dd09e7a6352c94525e2d3b3ed88b53b3e","title":"bd doctor doesn't detect version mismatches or stale daemons","description":"","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-10-31T21:07:56.957214-07:00","updated_at":"2025-11-01T17:05:36.615761-07:00","closed_at":"2025-11-01T17:05:36.615761-07:00","source_repo":".","dependencies":[{"issue_id":"bd-e652","depends_on_id":"bd-2752a7a2","type":"discovered-from","created_at":"2025-10-31T21:07:56.958708-07:00","created_by":"stevey"}]} {"id":"bd-e6d71828","content_hash":"954fa43e14d3302e5ba105d062b8ad48777f49cd759f9a1d827f03c29ddee9bd","title":"Add transaction + retry logic for N-way collision resolution","description":"## Problem\nCurrent N-way collision resolution fails on UNIQUE constraint violations during convergence rounds when 5+ clones sync. The RemapCollisions function is non-atomic and performs operations sequentially:\n1. Delete old issues (CASCADE deletes dependencies)\n2. Create remapped issues (can fail with UNIQUE constraint)\n3. Recreate dependencies\n4. Update text references\n\nFailure at step 2 leaves database in inconsistent state.\n\n## Solution\nWrap collision resolution in database transaction with retry logic:\n- Make entire RemapCollisions operation atomic\n- Retry up to 3 times on UNIQUE constraint failures\n- Re-sync counters between retries\n- Add better error messages for debugging\n\n## Implementation\nLocation: internal/storage/sqlite/collision.go:342 (RemapCollisions function)\n\n```go\n// Retry up to 3 times on UNIQUE constraint failures\nfor attempt := 0; attempt \u003c 3; attempt++ {\n err := s.db.ExecInTransaction(func(tx *sql.Tx) error {\n // All collision resolution operations\n })\n if !isUniqueConstraintError(err) {\n return err\n }\n s.SyncAllCounters(ctx)\n}\n```\n\n## Success Criteria\n- 5-clone collision test passes reliably\n- No partial state on UNIQUE constraint errors\n- Automatic recovery from transient ID conflicts\n\n## References\n- See beads_nway_test.go:124 for the KNOWN LIMITATION comment\n- Related to-7c5915ae (transaction support)","notes":"## Progress Made\n\n1. Added `ExecInTransaction` helper to SQLiteStorage for atomic database operations\n2. Added `IsUniqueConstraintError` function to detect UNIQUE constraint violations\n3. Wrapped `RemapCollisions` with retry logic (up to 3 attempts) with counter sync between retries\n4. Enhanced `handleRename` to detect and handle race conditions where target ID already exists\n5. Added defensive checks for when old ID has been deleted by another clone\n\n## Test Results\n\nThe changes improve N-way collision handling but don't fully solve the problem:\n- Original error: `UNIQUE constraint failed: issues.id` during first convergence round\n- With changes: Test proceeds further but encounters different collision scenarios\n- New error: `target ID already exists with different content` in later convergence rounds\n\n## Root Cause Analysis\n\nThe issue is more complex than initially thought. In N-way scenarios:\n1. Clone A remaps bd-1c63eb84 → test-2 → test-4\n2. Clone B remaps bd-1c63eb84 → test-3 → test-4 \n3. Both try to create test-4, but with different intermediate states\n4. This creates legitimate content collisions that require additional resolution\n\n## Next Steps \n\nThe full solution requires:\n1. Making remapping fully deterministic across clones (same input → same remapped ID)\n2. OR making `handleRename` more tolerant of mid-flight collisions\n3. OR implementing full transaction support for multi-step collision resolution -7c5915ae)\n\nThe retry logic added here provides a foundation but isn't sufficient for complex N-way scenarios.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-29T10:22:32.716678-07:00","updated_at":"2025-11-02T16:46:45.864479-08:00","closed_at":"2025-11-02T16:46:45.864479-08:00","source_repo":".","dependencies":[{"issue_id":"bd-e6d71828","depends_on_id":"bd-cbed9619.1","type":"related","created_at":"2025-10-29T10:44:44.14653-07:00","created_by":"daemon"}]} {"id":"bd-e8be4224","content_hash":"0acf678278ed57153a042264fa4dc04245faf1706224fa94d8b0d767a5c2fa2e","title":"Batch test 3","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-29T15:29:01.964091-07:00","updated_at":"2025-10-31T12:00:43.183212-07:00","closed_at":"2025-10-31T12:00:43.183212-07:00","source_repo":"."} +{"id":"bd-e962","content_hash":"69ccdf623e0e87862baf1279ab7c886d3390561a52948d23271e1bfe945ba36c","title":"Test Issue for Crash","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-11-19T14:39:28.3839855-07:00","updated_at":"2025-11-19T14:39:28.3839855-07:00","source_repo":"."} {"id":"bd-e98221b3","content_hash":"4a4f6912d8de8bf0f9ae867be1a25d83c5a6991383e3aa192537747500bebc6a","title":"Update AGENTS.md and README.md with \"bd daemons\" documentation","description":"Document the new \"bd daemons\" command and all subcommands in AGENTS.md and README.md. Include examples and troubleshooting guidance.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-26T19:41:11.099254-07:00","updated_at":"2025-11-06T20:06:49.219318-08:00","closed_at":"2025-11-06T19:51:57.75321-08:00","source_repo":"."} {"id":"bd-eb3c","content_hash":"6c7a46d58e565a27e3a7a5375bb1ad8345094bdef422dce52239ee4b7e559143","title":"UX nightmare: multiple ways daemon can fail with misleading messages","description":"","status":"closed","priority":0,"issue_type":"epic","created_at":"2025-10-31T21:08:09.090553-07:00","updated_at":"2025-11-01T20:27:42.79962-07:00","closed_at":"2025-11-01T20:27:42.79962-07:00","source_repo":"."} {"id":"bd-eef03e0a","content_hash":"a7dd31781359f078a172d6d34ceed26be83a3bc8159c05c2026c96717e98a314","title":"Stress test: event storm handling","description":"Simulate 100+ rapid JSONL writes. Verify debouncer batches to single import. Verify no data loss. Test daemon stability.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-29T20:49:49.138725-07:00","updated_at":"2025-10-31T19:18:50.682925-07:00","closed_at":"2025-10-31T19:18:50.682925-07:00","source_repo":"."} diff --git a/examples/monitor-webui/main.go b/examples/monitor-webui/main.go index afc20e21..a42586ff 100644 --- a/examples/monitor-webui/main.go +++ b/examples/monitor-webui/main.go @@ -48,6 +48,13 @@ var ( ) func main() { + defer func() { + if r := recover(); r != nil { + fmt.Fprintf(os.Stderr, "PANIC in main: %v\n", r) + } + fmt.Println("Main function exiting") + }() + flag.Parse() // Find database path if not specified @@ -319,6 +326,11 @@ func handleWebSocket(w http.ResponseWriter, r *http.Request) { // handleWebSocketBroadcast sends messages to all connected WebSocket clients func handleWebSocketBroadcast() { + defer func() { + if r := recover(); r != nil { + fmt.Fprintf(os.Stderr, "PANIC in handleWebSocketBroadcast: %v\n", r) + } + }() for { // Wait for message to broadcast message := <-wsBroadcast @@ -340,6 +352,11 @@ func handleWebSocketBroadcast() { // pollMutations polls the daemon for mutations and broadcasts them to WebSocket clients func pollMutations() { + defer func() { + if r := recover(); r != nil { + fmt.Fprintf(os.Stderr, "PANIC in pollMutations: %v\n", r) + } + }() lastPollTime := int64(0) // Start from beginning ticker := time.NewTicker(2 * time.Second) // Poll every 2 seconds