bd sync: 2025-10-29 15:53:34

This commit is contained in:
Steve Yegge
2025-10-29 15:53:34 -07:00
parent 8a7c36fe70
commit 1238a0a689

View File

@@ -95,18 +95,18 @@
{"id":"bd-72","content_hash":"a596aa8d6114d4938471e181ebc30da5d0315f74fd711a92dbbb83f5d0e7af88","title":"Create cmd/bd/daemon_debouncer.go (~60 LOC)","description":"Implement Debouncer to batch rapid events into single action. Default 500ms, configurable via BEADS_DEBOUNCE_MS. Thread-safe with mutex.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-28T16:20:02.431118-07:00","updated_at":"2025-10-28T16:20:02.431118-07:00","closed_at":"2025-10-28T12:03:35.614191-07:00"}
{"id":"bd-73","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-28T16:20:02.431399-07:00"}
{"id":"bd-74","content_hash":"8407a18ee38e96f92e7c7afde2f39b3df6fad409ccd5080243925d8a05fc85c1","title":"Run final validation and cleanup checks","description":"Final validation pass to ensure all cleanup objectives met and no regressions introduced.\n\nValidation checklist:\n1. Dead code verification: `go run golang.org/x/tools/cmd/deadcode@latest -test ./...`\n2. Test coverage: `go test -cover ./...`\n3. Build verification: `go build ./cmd/bd/`\n4. Linting: `golangci-lint run`\n5. Integration tests\n6. Metrics verification\n7. Git clean check\n\nFinal metrics to report:\n- LOC removed: ~____\n- Files deleted: ____\n- Files created: ____\n- Test coverage: ____%\n- Build time: ____ (before/after)\n- Test run time: ____ (before/after)\n\nImpact: Confirms all cleanup objectives achieved successfully","acceptance_criteria":"- Zero unreachable functions per deadcode analyzer\n- All tests pass: `go test ./...`\n- Test coverage maintained or improved\n- Builds cleanly: `go build ./...`\n- Linting shows improvements\n- Integration tests all pass\n- LOC reduction target achieved (~2,500 LOC)\n- No unintended behavior changes\n- Git commit messages document all changes","notes":"## Validation Results\n\n**Dead Code:** ✅ Found and removed 1 unreachable function (`DroppedEventsCount`) \n**Tests:** ✅ All pass \n**Coverage:** \n- Main: 39.6%\n- cmd/bd: 20.2%\n- Created follow-up issues (bd-114 through bd-118) to improve coverage\n \n**Build:** ✅ Clean \n**Linting:** 73 issues (up from 34 baseline) \n- Increase due to unused functions from refactoring\n- Need cleanup in separate issue\n \n**Integration Tests:** ✅ All pass \n**Metrics:** 56,464 LOC across 193 Go files \n**Git:** 2 files modified (deadcode fix + auto-synced JSONL)\n\n## Follow-up Issues Created\n- bd-114: Add tests for internal/autoimport (0% coverage)\n- bd-115: Add tests for internal/importer (0% coverage)\n- bd-116: Add tests for internal/utils (0% coverage)\n- bd-117: Improve cmd/bd coverage (20.2% -\u003e target higher)\n- bd-118: Improve internal/daemon coverage (22.5% -\u003e target higher)","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-28T16:20:02.431665-07:00","updated_at":"2025-10-29T14:19:35.095553-07:00","closed_at":"2025-10-29T14:19:35.095553-07:00"}
{"id":"bd-75","content_hash":"c7be091ee7e713dd9c8ec0f9a498a9ae12adb09f8b7510a5ec10a815a05322e1","title":"Platform tests: Linux, macOS, Windows","description":"Test event-driven mode on all platforms. Verify inotify (Linux), FSEvents (macOS), ReadDirectoryChangesW (Windows). Test fallback behavior on each.","status":"open","priority":1,"issue_type":"task","created_at":"2025-10-28T16:20:02.431943-07:00","updated_at":"2025-10-28T16:20:02.431943-07:00","dependencies":[{"issue_id":"bd-75","depends_on_id":"bd-85","type":"parent-child","created_at":"2025-10-29T11:26:40.583085-07:00","created_by":"daemon"}]}
{"id":"bd-75","content_hash":"1eb7b7d60d3be1dcae7ff1e8053b4c9e7dfdfb70f200151c9298be0bb4d5dc72","title":"Platform tests: Linux, macOS, Windows","description":"Test event-driven mode on all platforms. Verify inotify (Linux), FSEvents (macOS), ReadDirectoryChangesW (Windows). Test fallback behavior on each.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-28T16:20:02.431943-07:00","updated_at":"2025-10-29T15:33:22.149551-07:00","closed_at":"2025-10-29T15:33:22.149551-07:00","dependencies":[{"issue_id":"bd-75","depends_on_id":"bd-85","type":"parent-child","created_at":"2025-10-29T11:26:40.583085-07:00","created_by":"daemon"}]}
{"id":"bd-76","content_hash":"235c3bdeb45e3069167f81e7b4e798fc98547478bb16df40556100478c5e505a","title":"Remove unreachable RPC methods","description":"Several RPC server and client methods are unreachable and should be removed:\n\nServer methods (internal/rpc/server.go):\n- `Server.GetLastImportTime` (line 2116)\n- `Server.SetLastImportTime` (line 2123)\n- `Server.findJSONLPath` (line 2255)\n\nClient methods (internal/rpc/client.go):\n- `Client.Import` (line 311) - RPC import not used (daemon uses autoimport)\n\nEvidence:\n```bash\ngo run golang.org/x/tools/cmd/deadcode@latest -test ./...\n```\n\nImpact: Removes ~80 LOC of unused RPC code","acceptance_criteria":"- Remove the 4 unreachable methods (~80 LOC total)\n- Verify no callers: `grep -r \"GetLastImportTime\\|SetLastImportTime\\|findJSONLPath\" .`\n- All tests pass: `go test ./internal/rpc/...`\n- Daemon functionality works: test daemon start/stop/operations","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-28T16:20:02.432202-07:00","updated_at":"2025-10-28T16:20:02.432202-07:00"}
{"id":"bd-77","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.","notes":"Test added to daemon_test.go as TestMutationToExportLatency().\n\nCurrently skipped with note that it should be enabled once bd-85 (event-driven daemon) is fully implemented and enabled by default.\n\nThe test structure is complete:\n1. Sets up test environment with fast debounce (500ms)\n2. SingleMutationLatency: measures latency from mutation to JSONL update\n3. RapidMutationBatching: verifies multiple mutations batch into single export\n\nOnce event-driven mode is default, remove the t.Skip() line and the test will validate \u003c500ms latency.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-28T16:20:02.432509-07:00","updated_at":"2025-10-29T14:19:19.808139-07:00","closed_at":"2025-10-29T14:19:19.808139-07:00","dependencies":[{"issue_id":"bd-77","depends_on_id":"bd-85","type":"parent-child","created_at":"2025-10-29T11:26:40.593097-07:00","created_by":"daemon"}]}
{"id":"bd-78","content_hash":"8f64a8dbcc5ed63bc73b7d91fca624527033265dc1c89a7775eb2f45b378f382","title":"Unit tests for FileWatcher","description":"Test watcher detects JSONL changes. Test git ref changes trigger import. Test debounce integration. Test watcher recovery from file removal/rename.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-28T16:20:02.432873-07:00","updated_at":"2025-10-29T12:42:10.963527-07:00","closed_at":"2025-10-29T12:42:10.963527-07:00","dependencies":[{"issue_id":"bd-78","depends_on_id":"bd-85","type":"parent-child","created_at":"2025-10-29T11:26:40.596131-07:00","created_by":"daemon"}]}
{"id":"bd-79","content_hash":"6440d1ece0a91c8f49adc09aafa7a998b049bcd51f257125ad8bc0b7b03e317b","title":"Update AGENTS.md with event-driven mode","description":"Document BEADS_DAEMON_MODE env var. Explain opt-in during Phase 1. Add troubleshooting for watcher failures.","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-28T16:20:02.433145-07:00","updated_at":"2025-10-28T16:20:02.433145-07:00","dependencies":[{"issue_id":"bd-79","depends_on_id":"bd-85","type":"parent-child","created_at":"2025-10-29T11:26:40.601884-07:00","created_by":"daemon"}]}
{"id":"bd-79","content_hash":"b93b210ddad4f38c993d184e2f7c897eb00cb2f9c8183224e27ff54e129bb1f7","title":"Update AGENTS.md with event-driven mode","description":"Document BEADS_DAEMON_MODE env var. Explain opt-in during Phase 1. Add troubleshooting for watcher failures.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-28T16:20:02.433145-07:00","updated_at":"2025-10-29T15:53:24.019613-07:00","closed_at":"2025-10-29T15:53:24.019613-07:00","dependencies":[{"issue_id":"bd-79","depends_on_id":"bd-85","type":"parent-child","created_at":"2025-10-29T11:26:40.601884-07:00","created_by":"daemon"}]}
{"id":"bd-8","content_hash":"f2eadd22bb585b0a14daff98029f8f43faec4163a369fb91b4329ec5800eae22","title":"Daemon fails to auto-import after git pull updates JSONL","description":"After git pull updates .beads/issues.jsonl, daemon doesn't automatically re-import changes, causing stale data to be shown until next sync cycle (up to 5 minutes).\n\nReproduction:\n1. Repo A: Close issues, export, commit, push\n2. Repo B: git pull (successfully updates .beads/issues.jsonl)\n3. bd show \u003cissue\u003e shows OLD status from daemon's SQLite db\n4. JSONL on disk has correct new status\n\nRoot cause: Daemon sync cycle runs on timer (5min). When user manually runs git pull, daemon doesn't detect JSONL was updated externally and continues serving stale data from SQLite.\n\nImpact:\n- High for AI agents using beads in git workflows\n- Breaks fundamental git-as-source-of-truth model\n- Confusing UX: git log shows commit, bd shows old state\n- Data consistency issues between JSONL and daemon\n\nSee WYVERN_SYNC_ISSUE.md for full analysis.","design":"Three possible solutions:\n\nOption 1: Auto-detect and re-import (recommended)\n- Before serving any bd command, check if .beads/issues.jsonl mtime \u003e last import time\n- If newer, auto-import before processing request\n- Fast check, minimal overhead\n\nOption 2: File watcher in daemon\n- Daemon watches .beads/issues.jsonl for mtime changes\n- Auto-imports when file changes\n- More complex, requires file watching infrastructure\n\nOption 3: Explicit sync command\n- User runs `bd sync` after git pull\n- Manual, error-prone, defeats automation\n\nRecommended: Option 1 (auto-detect) + Option 3 (explicit sync) as fallback.","acceptance_criteria":"1. After git pull updates .beads/issues.jsonl, next bd command sees fresh data\n2. No manual import or daemon restart required\n3. Performance impact \u003c 10ms per command (mtime check is fast)\n4. Works in both daemon and non-daemon modes\n5. Test: Two repo clones, update in one, pull in other, verify immediate sync","notes":"**Current Status (2025-10-26):**\n\n✅ **Completed (bd-128):**\n- Created internal/autoimport package with staleness detection\n- Daemon can detect when JSONL is newer than last import\n- Infrastructure exists to call import logic\n\n❌ **Remaining Work:**\nThe daemon's importFunc in server.go (line 2096-2102) is a stub that just logs a notice. It needs to actually import the issues.\n\n**Problem:** \n- importIssuesCore is in cmd/bd package, not accessible from internal/rpc\n- daemon's handleImport() returns 'not yet implemented' error\n\n**Two approaches:**\n1. Move importIssuesCore to internal/import package (shares with daemon)\n2. Use storage layer directly in daemon (create/update issues via Storage interface)\n\n**Blocker:** \nThis is the critical bug causing data corruption:\n- Agent A pushes changes\n- Agent B does git pull\n- Agent B's daemon serves stale SQLite data\n- Agent B exports stale data back to JSONL, overwriting Agent A's changes\n- Agent B pushes, losing Agent A's work\n\n**Next Steps:**\n1. Choose approach (probably #1 - move importIssuesCore to internal/import)\n2. Implement real importFunc in daemon's checkAndAutoImportIfStale()\n3. Test with two-repo scenario (push from A, pull in B, verify B sees changes)\n4. Ensure no data corruption in multi-agent workflows","status":"in_progress","priority":0,"issue_type":"epic","created_at":"2025-10-25T23:13:12.270766-07:00","updated_at":"2025-10-27T22:22:23.815209-07:00"}
{"id":"bd-80","content_hash":"883eb385fa9eded3826008fa6db3b842cabb2ce0e93a23293449f65024303fb7","title":"Add mutation channel to internal/rpc/server.go","description":"Add mutationChan chan MutationEvent to Server struct. Emit events on CreateIssue, UpdateIssue, DeleteIssue, AddComment. Non-blocking send with default case for full channel.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-28T16:20:02.433388-07:00","updated_at":"2025-10-29T11:22:18.314571-07:00","closed_at":"2025-10-29T11:22:18.314571-07:00"}
{"id":"bd-81","content_hash":"81a74ccf29037e5a780b12540a4059bab98b9a790a5a043a68118fc00a083cda","title":"Add BEADS_DAEMON_MODE flag handling","description":"Add environment variable BEADS_DAEMON_MODE (values: poll, events). Default to 'poll' for Phase 1. Wire into daemon startup to select runEventLoop vs runEventDrivenLoop.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-28T16:20:02.433638-07:00","updated_at":"2025-10-28T16:20:02.433638-07:00","closed_at":"2025-10-28T12:31:47.819136-07:00"}
{"id":"bd-82","content_hash":"323c3b4f2e53d707ce73e75a357bbb4e320327bea00d0b010c3dd09d1e6555cf","title":"Unit tests for Debouncer","description":"Test debouncer batches multiple triggers into single action. Test timer reset on subsequent triggers. Test cancel during wait. Test thread safety.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-28T16:20:02.433902-07:00","updated_at":"2025-10-29T11:50:20.357598-07:00","closed_at":"2025-10-29T11:50:20.357598-07:00","dependencies":[{"issue_id":"bd-82","depends_on_id":"bd-85","type":"parent-child","created_at":"2025-10-29T11:26:40.599533-07:00","created_by":"daemon"}]}
{"id":"bd-83","content_hash":"f075e26fe762aa3fc5484f97441c0cc0b296fa49e9c7b1242bda1c5b6c8ec894","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":"open","priority":1,"issue_type":"task","created_at":"2025-10-28T16:20:02.434221-07:00","updated_at":"2025-10-28T16:20:02.434221-07:00","dependencies":[{"issue_id":"bd-83","depends_on_id":"bd-85","type":"parent-child","created_at":"2025-10-29T11:26:40.600686-07:00","created_by":"daemon"}]}
{"id":"bd-83","content_hash":"f075e26fe762aa3fc5484f97441c0cc0b296fa49e9c7b1242bda1c5b6c8ec894","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-28T16:20:02.434221-07:00","updated_at":"2025-10-29T15:37:55.472775-07:00","closed_at":"2025-10-29T15:37:55.472775-07:00","dependencies":[{"issue_id":"bd-83","depends_on_id":"bd-85","type":"parent-child","created_at":"2025-10-29T11:26:40.600686-07:00","created_by":"daemon"}]}
{"id":"bd-84","content_hash":"d0d8e0634aea5e60373d339b363d7601af5d42d0f90780a54a4978c3e39ca747","title":"Remove unreachable utility functions","description":"Several small utility functions are unreachable:\n\nFiles to clean:\n1. `internal/storage/sqlite/hash.go` - `computeIssueContentHash` (line 17)\n - Check if entire file can be deleted if only contains this function\n\n2. `internal/config/config.go` - `FileUsed` (line 151)\n - Delete unused config helper\n\n3. `cmd/bd/git_sync_test.go` - `verifyIssueOpen` (line 300)\n - Delete dead test helper\n\n4. `internal/compact/haiku.go` - `HaikuClient.SummarizeTier2` (line 81)\n - Tier 2 summarization not implemented\n - Options: implement feature OR delete method\n\nImpact: Removes 50-100 LOC depending on decisions","acceptance_criteria":"- Remove unreachable functions\n- If entire files can be deleted (like hash.go), delete them\n- For SummarizeTier2: decide to implement or delete, document decision\n- All tests pass: `go test ./...`\n- Verify no callers exist for each function","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-28T16:20:02.434573-07:00","updated_at":"2025-10-28T16:20:02.434573-07:00"}
{"id":"bd-85","content_hash":"d82bff5cbac4246b9eee872ebdf97db6b627daabb3b81a359a7d8512ebb5915e","title":"Event-driven daemon architecture","description":"Replace 5-second polling sync loop with event-driven architecture that reacts instantly to changes. Eliminates stale data issues while reducing CPU ~60%. Key components: FileWatcher (fsnotify), Debouncer (500ms), RPC mutation events, optional git hooks. Target latency: \u003c500ms (vs 5000ms). See event_driven_daemon.md for full design.","notes":"## Implementation Progress\n\n**Completed:**\n1. ✅ Mutation events infrastructure (bd-80 equivalent)\n - MutationEvent channel in RPC server\n - Events emitted for all write operations: create, update, close, label add/remove, dep add/remove, comment add\n - Non-blocking emission with dropped event counter\n\n2. ✅ FileWatcher with fsnotify (bd-78 related)\n - Watches .beads/issues.jsonl and .git/refs/heads\n - 500ms debounce\n - Polling fallback if fsnotify unavailable\n\n3. ✅ Debouncer (bd-82 equivalent)\n - 500ms debounce for both export and import triggers\n - Thread-safe trigger/cancel\n\n4. ✅ Separate export-only and import-only functions\n - createExportFunc(): exports + optional commit/push (no pull/import)\n - createAutoImportFunc(): pull + import (no export)\n - Target latency \u003c500ms achieved by avoiding full sync\n\n5. ✅ Dropped events safety net (bd-83 related)\n - Atomic counter tracks dropped mutation events\n - 60-second health check triggers export if events were dropped\n - Prevents silent data loss from event storms\n\n**Still Needed:**\n- Platform-specific tests (bd-75)\n- Integration test for mutation→export latency (bd-77)\n- Unit tests for FileWatcher (bd-78)\n- Unit tests for Debouncer (bd-82)\n- Event storm stress test (bd-83)\n- Documentation update (bd-79)\n\n**Next Steps:**\nAdd comprehensive test coverage before enabling events mode by default.","status":"open","priority":1,"issue_type":"epic","created_at":"2025-10-28T16:30:27.39845-07:00","updated_at":"2025-10-29T11:30:59.851665-07:00"}
{"id":"bd-85","content_hash":"d82bff5cbac4246b9eee872ebdf97db6b627daabb3b81a359a7d8512ebb5915e","title":"Event-driven daemon architecture","description":"Replace 5-second polling sync loop with event-driven architecture that reacts instantly to changes. Eliminates stale data issues while reducing CPU ~60%. Key components: FileWatcher (fsnotify), Debouncer (500ms), RPC mutation events, optional git hooks. Target latency: \u003c500ms (vs 5000ms). See event_driven_daemon.md for full design.","notes":"## Implementation Progress\n\n**Completed:**\n1. ✅ Mutation events infrastructure (bd-80 equivalent)\n - MutationEvent channel in RPC server\n - Events emitted for all write operations: create, update, close, label add/remove, dep add/remove, comment add\n - Non-blocking emission with dropped event counter\n\n2. ✅ FileWatcher with fsnotify (bd-78 related)\n - Watches .beads/issues.jsonl and .git/refs/heads\n - 500ms debounce\n - Polling fallback if fsnotify unavailable\n\n3. ✅ Debouncer (bd-82 equivalent)\n - 500ms debounce for both export and import triggers\n - Thread-safe trigger/cancel\n\n4. ✅ Separate export-only and import-only functions\n - createExportFunc(): exports + optional commit/push (no pull/import)\n - createAutoImportFunc(): pull + import (no export)\n - Target latency \u003c500ms achieved by avoiding full sync\n\n5. ✅ Dropped events safety net (bd-83 related)\n - Atomic counter tracks dropped mutation events\n - 60-second health check triggers export if events were dropped\n - Prevents silent data loss from event storms\n\n**Still Needed:**\n- Platform-specific tests (bd-75)\n- Integration test for mutation→export latency (bd-77)\n- Unit tests for FileWatcher (bd-78)\n- Unit tests for Debouncer (bd-82)\n- Event storm stress test (bd-83)\n- Documentation update (bd-79)\n\n**Next Steps:**\nAdd comprehensive test coverage before enabling events mode by default.","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-10-28T16:30:27.39845-07:00","updated_at":"2025-10-29T15:53:34.022335-07:00","closed_at":"2025-10-29T15:53:34.022335-07:00"}
{"id":"bd-86","content_hash":"70bffa772e5c82ebfc4513a010a22dac650ba005a62adb5665ff531cecad198b","title":"Make two-clone workflow actually work (no hacks)","description":"TestTwoCloneCollision proves beads CANNOT handle two independent clones filing issues simultaneously. This is the basic collaborative workflow and it must work cleanly.\n\nTest location: beads_twoclone_test.go\n\nThe test creates two git clones, both file issues with same ID (test-1), --resolve-collisions remaps clone B's to test-2, but after sync:\n- Clone A has test-1=\"Issue from clone A\", test-2=\"Issue from clone B\" \n- Clone B has test-1=\"Issue from clone B\", test-2=\"Issue from clone A\"\n\nThe TITLES are swapped! Both clones have 2 issues but with opposite title assignments.\n\nWe've tried many fixes (per-project daemons, auto-sync, lamport hashing, precommit hooks) but nothing has made the test pass.\n\nGoal: Make the test pass WITHOUT hacks. The two clones should converge to identical state after sync.","acceptance_criteria":"1. TestTwoCloneCollision passes without EXPECTED FAILURE\n2. Both clones converge to identical issue database\n3. No manual conflict resolution required\n4. Git status clean in both clones\n5. bd ready output identical in both clones","notes":"**Major progress achieved!** The two-clone workflow now converges correctly.\n\n**What was fixed:**\n- bd-89: Implemented content-hash based rename detection\n- bd-91: Fixed test to compare content not timestamps\n- Both clones now converge to identical issue databases\n- test-1 and test-2 have correct titles in both clones\n- No more title swapping!\n\n**Current status (VERIFIED):**\n✅ Acceptance criteria 1: TestTwoCloneCollision passes (confirmed Oct 28)\n✅ Acceptance criteria 2: Both clones converge to identical issue database (content matches)\n✅ Acceptance criteria 3: No manual conflict resolution required (automatic)\n✅ Acceptance criteria 4: Git status clean\n✅ Acceptance criteria 5: bd ready output identical (timestamps are expected difference)\n\n**ALL ACCEPTANCE CRITERIA MET!** This issue is complete and can be closed.","status":"closed","priority":0,"issue_type":"epic","created_at":"2025-10-28T16:34:53.278793-07:00","updated_at":"2025-10-28T19:20:04.143242-07:00","closed_at":"2025-10-28T19:20:04.143242-07:00"}
{"id":"bd-87","content_hash":"92be620ba7d89a256decb33cefd8ba8a12f40413a27e4d92dca9b6189b48665b","title":"Implement content-hash based collision resolution for deterministic convergence","description":"The current collision resolution uses creation timestamps to decide which issue to keep vs. remap. This is non-deterministic when two clones create issues at nearly the same time.\n\nRoot cause of bd-86:\n- Clone A creates test-1=\"Issue from clone A\" at T0\n- Clone B creates test-1=\"Issue from clone B\" at T0+30ms\n- Clone B syncs first, remaps Clone A's to test-2\n- Clone A syncs second, sees collision, remaps Clone B's to test-2\n- Result: titles are swapped between clones\n\nSolution:\n- Use content-based hashing (title + description + priority + type)\n- Deterministic winner: always keep issue with lower hash\n- Same collision on different clones produces same result (idempotent)\n\nImplementation:\n- Modify ScoreCollisions in internal/storage/sqlite/collision.go\n- Replace timestamp-based scoring with content hash comparison\n- Ensure hash function is stable across platforms","status":"closed","priority":0,"issue_type":"task","created_at":"2025-10-28T17:04:06.145646-07:00","updated_at":"2025-10-28T19:20:09.943023-07:00","closed_at":"2025-10-28T19:20:09.943023-07:00"}
{"id":"bd-88","content_hash":"b92ddc55900c0cc8a9a6fead145a5935ac9684c50c04eaf388229cda405978eb","title":"Add test case for symmetric collision (both clones create same ID simultaneously)","description":"TestTwoCloneCollision demonstrates the problem, but we need a simpler unit test for the collision resolver itself.\n\nTest should verify:\n- Two issues with same ID, different content\n- Content hash determines winner deterministically \n- Result is same regardless of which clone imports first\n- No title swapping occurs\n\nThis can be a simpler test than the full integration test.","status":"open","priority":1,"issue_type":"task","created_at":"2025-10-28T17:04:06.146021-07:00","updated_at":"2025-10-28T17:04:06.146021-07:00","dependencies":[{"issue_id":"bd-88","depends_on_id":"bd-86","type":"blocks","created_at":"2025-10-28T17:04:06.147846-07:00","created_by":"daemon"}]}