docs: update deletion docs for tombstones, add changelog entries

- Rewrote DELETIONS.md to document inline tombstones (replacing legacy
  deletions.jsonl approach)
- Added tombstone feature entries to CHANGELOG.md under Unreleased
- Fixed duplicate 0.29.0 header in CHANGELOG.md
- Ran bd migrate-tombstones on beads repo (dogfooding)
- Closed bd-vw8 epic (all 12 dependencies complete)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-13 07:27:55 -08:00
parent 5ec2038e5c
commit d80d82d693
3 changed files with 140 additions and 131 deletions

View File

@@ -309,7 +309,7 @@
{"id":"bd-d7e88238","title":"Rapid 3","description":"","status":"open","priority":3,"issue_type":"task","created_at":"2025-10-29T19:11:57.459655-07:00","updated_at":"2025-10-30T17:12:58.189494-07:00"}
{"id":"bd-d9e0","title":"Extract validation functions to validators.go","description":"Move validatePriority, validateStatus, validateIssueType, validateTitle, validateEstimatedMinutes, validateFieldUpdate to validators.go","status":"closed","priority":1,"issue_type":"task","created_at":"2025-11-01T19:28:54.915909-07:00","updated_at":"2025-11-01T23:37:07.480417-07:00","closed_at":"2025-11-01T23:37:07.480417-07:00"}
{"id":"bd-dcd6f14b","title":"Batch test 4","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-29T15:29:02.053523-07:00","updated_at":"2025-10-31T12:00:43.182861-07:00","closed_at":"2025-10-31T12:00:43.182861-07:00"}
{"id":"bd-dd6f6d26","title":"Fix autoimport tests for content-hash collision scoring","description":"## Overview\nThree autoimport tests are failing after bd-cbed9619.4 because they expect behavior based on the old reference-counting collision resolution, but the system now uses deterministic content-hash scoring.\n\n## Failing Tests\n1. `TestAutoImportMultipleCollisionsRemapped` - expects local versions preserved\n2. `TestAutoImportAllCollisionsRemapped` - expects local versions preserved \n3. `TestAutoImportCollisionRemapMultipleFields` - expects specific collision resolution behavior\n\n## Root Cause\nThese tests were written when ScoreCollisions used reference counting to determine which version to keep. Now it uses content-hash comparison (introduced in commit 2e87329), which produces different but deterministic results.\n\n## Example\nOld behavior: Issue with more references would be kept\nNew behavior: Issue with lexicographically lower content hash is kept\n\n## Solution\nUpdate each test to:\n1. Verify the new content-hash based behavior is correct\n2. Check that the remapped issue (not necessarily local/remote) has the expected content\n3. Ensure dependencies are preserved on the correct remapped issue\n\n## Acceptance Criteria\n- All three autoimport tests pass\n- Tests verify content-hash determinism (same collision always resolves the same way)\n- Tests check dependency preservation on remapped issues\n- Test documentation explains content-hash scoring expectations\n\n## Files to Modify\n- `cmd/bd/autoimport_collision_test.go`\n\n## Testing\nRun: `go test ./cmd/bd -run \"TestAutoImport.*Collision\" -v`","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-28T19:12:56.344193-07:00","updated_at":"2025-10-30T17:12:58.178703-07:00","closed_at":"2025-10-28T19:18:35.106895-07:00","dependencies":[{"issue_id":"bd-dd6f6d26","depends_on_id":"bd-cbed9619.4","type":"discovered-from","created_at":"2025-10-28T19:12:56.345276-07:00","created_by":"daemon"}]}
{"id":"bd-dd6f6d26","title":"Fix autoimport tests for content-hash collision scoring","description":"## Overview\nThree autoimport tests are failing after [deleted:bd-cbed9619.4] because they expect behavior based on the old reference-counting collision resolution, but the system now uses deterministic content-hash scoring.\n\n## Failing Tests\n1. `TestAutoImportMultipleCollisionsRemapped` - expects local versions preserved\n2. `TestAutoImportAllCollisionsRemapped` - expects local versions preserved \n3. `TestAutoImportCollisionRemapMultipleFields` - expects specific collision resolution behavior\n\n## Root Cause\nThese tests were written when ScoreCollisions used reference counting to determine which version to keep. Now it uses content-hash comparison (introduced in commit 2e87329), which produces different but deterministic results.\n\n## Example\nOld behavior: Issue with more references would be kept\nNew behavior: Issue with lexicographically lower content hash is kept\n\n## Solution\nUpdate each test to:\n1. Verify the new content-hash based behavior is correct\n2. Check that the remapped issue (not necessarily local/remote) has the expected content\n3. Ensure dependencies are preserved on the correct remapped issue\n\n## Acceptance Criteria\n- All three autoimport tests pass\n- Tests verify content-hash determinism (same collision always resolves the same way)\n- Tests check dependency preservation on remapped issues\n- Test documentation explains content-hash scoring expectations\n\n## Files to Modify\n- `cmd/bd/autoimport_collision_test.go`\n\n## Testing\nRun: `go test ./cmd/bd -run \"TestAutoImport.*Collision\" -v`","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-28T19:12:56.344193-07:00","updated_at":"2025-12-13T07:27:37.180226-08:00","closed_at":"2025-10-28T19:18:35.106895-07:00","dependencies":[{"issue_id":"bd-dd6f6d26","depends_on_id":"bd-cbed9619.4","type":"discovered-from","created_at":"2025-10-28T19:12:56.345276-07:00","created_by":"daemon"}]}
{"id":"bd-de6","title":"Fix FindBeadsDir to prioritize main repo .beads for worktrees","description":"The FindBeadsDir function should prioritize finding .beads in the main repository root when accessed from a worktree, rather than finding worktree-local .beads directories. This ensures proper sharing of the database across all worktrees.","status":"open","priority":2,"issue_type":"bug","created_at":"2025-12-07T16:48:36.883117467-07:00","updated_at":"2025-12-07T16:48:36.883117467-07:00"}
{"id":"bd-df11","title":"Add import metrics for external_ref matching statistics","description":"Add observability for external_ref matching behavior during imports to help debug and optimize import operations.\n\nMetrics to track:\n- Number of issues matched by external_ref\n- Number of issues matched by ID\n- Number of issues matched by content hash\n- Number of external_ref updates vs creates\n- Average import time with vs without external_ref\n\nOutput format:\n- Add to ImportResult struct\n- Include in import command output\n- Consider structured logging\n\nUse cases:\n- Debugging slow imports\n- Understanding match distribution\n- Optimizing import performance\n\nRelated: bd-1022","status":"open","priority":4,"issue_type":"chore","created_at":"2025-11-02T15:32:46.157899-08:00","updated_at":"2025-11-02T15:32:46.157899-08:00"}
{"id":"bd-df190564","title":"bd repair-deps - Orphaned dependency cleaner","description":"Find and fix orphaned dependency references.\n\nImplementation:\n- Scan all issues for dependencies pointing to non-existent issues\n- Report orphaned refs\n- Auto-fix with --fix flag\n- Interactive mode with --interactive\n\nFiles: cmd/bd/repair_deps.go (new)","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-29T19:42:29.852745-07:00","updated_at":"2025-10-31T18:24:19.418221-07:00","closed_at":"2025-10-31T18:24:19.418221-07:00"}
@@ -373,6 +373,7 @@
{"id":"bd-fc2d","title":"Refactor sqlite.go (2298 lines)","description":"Break down internal/storage/sqlite/sqlite.go into smaller, more focused modules. The file is currently 2298 lines and should be split into logical components.","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-11-01T19:28:40.899111-07:00","updated_at":"2025-11-01T21:43:55.870724-07:00","closed_at":"2025-11-01T21:43:55.870724-07:00"}
{"id":"bd-fd56","title":"Wrap git operations in GitClient interface","description":"Create internal/daemonrunner/git.go with GitClient interface (HasUpstream, HasChanges, Commit, Push, Pull). Default implementation using os/exec. Use in Syncer and Run loop for testability.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-11-01T11:41:14.88734-07:00","updated_at":"2025-11-01T22:56:08.354697-07:00","closed_at":"2025-11-01T22:56:08.354697-07:00"}
{"id":"bd-fd8753d9","title":"Document bd edit command and verify MCP exclusion","description":"Follow-up from PR #152:\n1. Add \"bd edit\" to AGENTS.md with \"Humans only\" note\n2. Verify MCP server doesn't expose bd edit command\n3. Consider adding test for command registration","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-26T13:23:47.982295-07:00","updated_at":"2025-10-30T17:12:58.226229-07:00"}
{"id":"bd-fi05","title":"bd sync fails with orphaned issues and duplicate ID conflict","description":"After fixing the deleted_at TEXT column scanning bug (commit 18b1eb2), bd sync still fails with two issues:\n\n1. Orphan Detection Warning: 12 orphaned child issues whose parents no longer exist (bd-cb64c226.* and bd-cbed9619.*)\n\n2. Import Failure: UNIQUE constraint failed for bd-360 - this tombstone exists in both DB and JSONL\n\nError: \"Import failed: error creating depth-0 issues: bulk insert issues: failed to insert issue bd-360: sqlite3: constraint failed: UNIQUE constraint failed: issues.id\"\n\nFix options:\n- Delete orphaned child issues with bd delete\n- Resolve bd-360 duplicate (in deletions.jsonl vs tombstone in DB)\n- Reset sync branch: git branch -f beads-sync main \u0026\u0026 git push --force-with-lease origin beads-sync","status":"open","priority":2,"issue_type":"bug","created_at":"2025-12-13T07:14:33.831346-08:00","updated_at":"2025-12-13T07:14:41.268141-08:00"}
{"id":"bd-fl4","title":"Test sync branch setup","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-30T00:26:47.520507-08:00","updated_at":"2025-11-30T00:33:23.105998-08:00","closed_at":"2025-11-30T00:33:23.105998-08:00"}
{"id":"bd-fmc","title":"string(rune()) pattern in other test files still only works for single digits","description":"The bd-c5m fix only addressed worktree_divergence_test.go, but several other test files have the same issue:\n\n- cmd/bd/stale_test.go:218, 234\n- cmd/bd/export_integrity_integration_test.go:45-47, 279-280\n- internal/storage/sqlite/comments_test.go:146, 165\n- internal/storage/sqlite/blocked_cache_test.go:292\n- internal/storage/sqlite/compact_bench_test.go:117\n- internal/storage/sqlite/cycle_detection_test.go:418\n\nThese use string(rune('0'+i)) which produces wrong characters when i \u003e= 10.\nShould use strconv.Itoa(i) instead.","status":"closed","priority":4,"issue_type":"bug","created_at":"2025-12-02T22:29:12.355705-08:00","updated_at":"2025-12-02T22:39:51.844638-08:00","closed_at":"2025-12-02T22:39:51.844638-08:00"}
{"id":"bd-fmo","title":"Missing test for IsExpiredTombstone edge cases","description":"## Problem\n\nThe `IsExpiredTombstone()` function has several edge cases that aren't directly tested:\n\n1. **Invalid DeletedAt timestamp** - Returns false (safety), but not tested\n2. **ttl=0 fallback to DefaultTombstoneTTL** - Not tested directly\n3. **Tombstone with empty DeletedAt** - Returns false, tested implicitly but not explicitly\n4. **Clock skew grace period effect** - Not tested (would need time mocking)\n\n## Current Test Coverage\n\nThe existing tests use `time.Now()` which makes them non-deterministic:\n```go\nDeletedAt: time.Now().Add(-24 * time.Hour).Format(time.RFC3339)\n```\n\nThis works but doesn't test the expiration boundary precisely.\n\n## Recommendation\n\nAdd explicit unit tests for `IsExpiredTombstone()`:\n\n```go\nfunc TestIsExpiredTombstone(t *testing.T) {\n tests := []struct {\n name string\n status string\n deletedAt string\n ttl time.Duration\n expected bool\n }{\n {\"non-tombstone returns false\", \"open\", \"2024-01-01T00:00:00Z\", 0, false},\n {\"empty deleted_at returns false\", \"tombstone\", \"\", 0, false},\n {\"invalid timestamp returns false\", \"tombstone\", \"not-a-date\", 0, false},\n {\"within TTL returns false\", \"tombstone\", time.Now().Add(-1*time.Hour).Format(time.RFC3339), 24*time.Hour, false},\n {\"beyond TTL returns true\", \"tombstone\", time.Now().Add(-48*time.Hour).Format(time.RFC3339), 24*time.Hour, true},\n }\n // ...\n}\n```\n\n## Files\n- internal/merge/merge.go:255-290\n- internal/merge/merge_test.go (add TestIsExpiredTombstone)","status":"closed","priority":3,"issue_type":"task","created_at":"2025-12-05T16:36:09.106135-08:00","updated_at":"2025-12-07T02:16:16.881654-08:00","closed_at":"2025-12-07T02:16:16.881654-08:00"}
@@ -441,7 +442,7 @@
{"id":"bd-v0x","title":"Auto-detect issue prefix from existing JSONL in 'bd init'","description":"When running `bd init` in a fresh clone with existing JSONL, it should auto-detect the issue prefix from the JSONL file instead of requiring `--prefix`.\n\nCurrently you must specify `--prefix ef` manually. But the JSONL file already contains issues like `ef-1it`, `ef-1jp` etc., so the prefix is known.\n\n**Ideal UX**:\n```\n$ bd init\nDetected issue prefix 'ef' from existing JSONL (38 issues).\n✓ Database initialized...\n```\n\nThis would make fresh clone hydration a single command: `bd init` with no flags.","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-11-27T20:21:21.049215-08:00","updated_at":"2025-11-30T10:50:04.275631-08:00","closed_at":"2025-11-28T21:57:11.164293-08:00"}
{"id":"bd-v29","title":"Deletions pruning doesn't include results in JSON output","description":"## Problem\n\nWhen `bd compact --json` runs with deletions pruning, the prune results are silently discarded:\n\n```go\n// Only report if there were deletions to prune\nif result.PrunedCount \u003e 0 {\n if jsonOutput {\n // JSON output will be included in the main response\n return // \u003c-- BUG: results are NOT included anywhere\n }\n ...\n}\n```\n\n## Location\n`cmd/bd/compact.go:925-929`\n\n## Impact\n- JSON consumers don't know deletions were pruned\n- No way to audit pruning via automation\n\n## Fix\nReturn prune results and include in JSON output structure:\n\n```json\n{\n \"success\": true,\n \"compacted\": {...},\n \"deletions_pruned\": {\n \"count\": 5,\n \"retention_days\": 7\n }\n}\n```","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-11-25T12:48:59.730979-08:00","updated_at":"2025-11-25T15:11:54.363653-08:00","closed_at":"2025-11-25T15:11:54.363653-08:00"}
{"id":"bd-vs9","title":"Fix unparam unused parameter in cmd/bd/doctor.go:541","description":"Linting issue: checkHooksQuick - path is unused (unparam) at cmd/bd/doctor.go:541:22. Error: func checkHooksQuick(path string) string {","status":"open","priority":0,"issue_type":"bug","created_at":"2025-12-07T15:35:17.02177046-07:00","updated_at":"2025-12-07T15:35:17.02177046-07:00"}
{"id":"bd-vw8","title":"Switch from deletions manifest to inline tombstones","description":"Replace the current deletions.jsonl manifest with inline tombstone records in issues.jsonl.\n\n## Problem Statement\n\nThe current deletions manifest approach has several issues:\n\n1. **Wild poisoning** - A stale clone's deletions manifest can poison fresh databases when synced\n2. **Two-level merge inconsistency** - Git content merge and beads snapshot merge use different bases\n3. **SyncJSONLToWorktree overwrites** - Blindly copies local JSONL to worktree, losing remote issues\n4. **3-day TTL too aggressive** - Deletions expire before dormant branches get merged\n\n## Proposed Solution: Inline Tombstones\n\nInstead of a separate deletions.jsonl file, embed deletion records directly in issues.jsonl:\n\n```json\n{\"id\":\"beads-abc\",\"status\":\"tombstone\",\"title\":\"Original title\",\"deleted_at\":\"2025-12-01T...\",\"deleted_by\":\"user\",\"expires_at\":\"2025-12-31T...\"}\n```\n\n### Benefits\n\n1. **Single source of truth** - No separate manifest to sync/merge\n2. **Participates in normal 3-way merge** - Deletion conflicts resolved same as other fields\n3. **Atomic with issue data** - Can't have orphaned deletions or missing tombstones\n4. **Preserves metadata** - Can optionally keep title/type for audit trail\n\n### Design Decisions Needed\n\n1. **TTL duration** - 30 days default? Configurable via config.yaml?\n2. **Tombstone content** - Minimal (just ID + timestamps) vs. full issue preservation?\n3. **Migration path** - How to handle existing deletions.jsonl files?\n4. **Status value** - Use \"tombstone\" or \"deleted\"? (tombstone clearer, deleted more intuitive)\n5. **Merge semantics** - Does tombstone always win, or use updated_at like other fields?\n\n### Migration Strategy\n\n1. On import, convert deletions.jsonl entries to tombstones in JSONL\n2. Deprecate but still read deletions.jsonl for backward compatibility\n3. Stop writing to deletions.jsonl after N versions\n\n## Related Issues\n\n- GitHub #464: Beads deletes issues (fresh clone sync problem)\n- bd-2e0: Add TTL to deletions manifest entries (current 3-day TTL)\n- bd-53c: bd sync corrupts issues.jsonl in multi-clone environments","status":"open","priority":1,"issue_type":"epic","created_at":"2025-12-05T13:42:24.384792-08:00","updated_at":"2025-12-05T13:42:24.384792-08:00","dependencies":[{"issue_id":"bd-vw8","depends_on_id":"bd-1r5","type":"blocks","created_at":"2025-12-05T14:57:31.111259-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-2m7","type":"blocks","created_at":"2025-12-05T14:57:33.57722-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-dli","type":"blocks","created_at":"2025-12-05T14:57:34.902784-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-zvg","type":"blocks","created_at":"2025-12-05T14:57:36.665817-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-fbj","type":"blocks","created_at":"2025-12-05T15:14:59.081452-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-olt","type":"blocks","created_at":"2025-12-05T15:14:59.118268-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-0ih","type":"blocks","created_at":"2025-12-05T15:14:59.155689-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-3b4","type":"blocks","created_at":"2025-12-05T15:14:59.192575-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-dve","type":"blocks","created_at":"2025-12-05T15:14:59.227233-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-8f9","type":"blocks","created_at":"2025-12-05T15:14:59.262816-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-s3v","type":"blocks","created_at":"2025-12-05T15:14:59.299481-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-okh","type":"blocks","created_at":"2025-12-05T15:14:59.337656-08:00","created_by":"daemon"}]}
{"id":"bd-vw8","title":"Switch from deletions manifest to inline tombstones","description":"Replace the current deletions.jsonl manifest with inline tombstone records in issues.jsonl.\n\n## Problem Statement\n\nThe current deletions manifest approach has several issues:\n\n1. **Wild poisoning** - A stale clone's deletions manifest can poison fresh databases when synced\n2. **Two-level merge inconsistency** - Git content merge and beads snapshot merge use different bases\n3. **SyncJSONLToWorktree overwrites** - Blindly copies local JSONL to worktree, losing remote issues\n4. **3-day TTL too aggressive** - Deletions expire before dormant branches get merged\n\n## Proposed Solution: Inline Tombstones\n\nInstead of a separate deletions.jsonl file, embed deletion records directly in issues.jsonl:\n\n```json\n{\"id\":\"beads-abc\",\"status\":\"tombstone\",\"title\":\"Original title\",\"deleted_at\":\"2025-12-01T...\",\"deleted_by\":\"user\",\"expires_at\":\"2025-12-31T...\"}\n```\n\n### Benefits\n\n1. **Single source of truth** - No separate manifest to sync/merge\n2. **Participates in normal 3-way merge** - Deletion conflicts resolved same as other fields\n3. **Atomic with issue data** - Can't have orphaned deletions or missing tombstones\n4. **Preserves metadata** - Can optionally keep title/type for audit trail\n\n### Design Decisions Needed\n\n1. **TTL duration** - 30 days default? Configurable via config.yaml?\n2. **Tombstone content** - Minimal (just ID + timestamps) vs. full issue preservation?\n3. **Migration path** - How to handle existing deletions.jsonl files?\n4. **Status value** - Use \"tombstone\" or \"deleted\"? (tombstone clearer, deleted more intuitive)\n5. **Merge semantics** - Does tombstone always win, or use updated_at like other fields?\n\n### Migration Strategy\n\n1. On import, convert deletions.jsonl entries to tombstones in JSONL\n2. Deprecate but still read deletions.jsonl for backward compatibility\n3. Stop writing to deletions.jsonl after N versions\n\n## Related Issues\n\n- GitHub #464: Beads deletes issues (fresh clone sync problem)\n- bd-2e0: Add TTL to deletions manifest entries (current 3-day TTL)\n- bd-53c: bd sync corrupts issues.jsonl in multi-clone environments","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-12-05T13:42:24.384792-08:00","updated_at":"2025-12-13T07:25:56.968329-08:00","closed_at":"2025-12-13T07:25:56.968329-08:00","dependencies":[{"issue_id":"bd-vw8","depends_on_id":"bd-1r5","type":"blocks","created_at":"2025-12-05T14:57:31.111259-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-2m7","type":"blocks","created_at":"2025-12-05T14:57:33.57722-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-dli","type":"blocks","created_at":"2025-12-05T14:57:34.902784-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-zvg","type":"blocks","created_at":"2025-12-05T14:57:36.665817-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-fbj","type":"blocks","created_at":"2025-12-05T15:14:59.081452-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-olt","type":"blocks","created_at":"2025-12-05T15:14:59.118268-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-0ih","type":"blocks","created_at":"2025-12-05T15:14:59.155689-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-3b4","type":"blocks","created_at":"2025-12-05T15:14:59.192575-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-dve","type":"blocks","created_at":"2025-12-05T15:14:59.227233-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-8f9","type":"blocks","created_at":"2025-12-05T15:14:59.262816-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-s3v","type":"blocks","created_at":"2025-12-05T15:14:59.299481-08:00","created_by":"daemon"},{"issue_id":"bd-vw8","depends_on_id":"bd-okh","type":"blocks","created_at":"2025-12-05T15:14:59.337656-08:00","created_by":"daemon"}]}
{"id":"bd-wcl","title":"Document CLI + hooks as recommended approach over MCP","description":"Update documentation to position CLI + bd prime hooks as the primary recommended approach over MCP server, explaining why minimizing context matters even with large context windows (compute cost, energy, environment, latency).","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-12T00:15:25.923025-08:00","updated_at":"2025-11-26T20:13:52.891053-08:00","closed_at":"2025-11-26T18:06:51.020351-08:00"}
{"id":"bd-wmo","title":"PruneDeletions iterates map non-deterministically","description":"## Problem\n\n`PruneDeletions` iterates over `loadResult.Records` which is a map. Go maps iterate in random order, so:\n\n1. `result.PrunedIDs` order is non-deterministic\n2. `kept` slice order is non-deterministic → `WriteDeletions` output order varies\n\n## Location\n`internal/deletions/deletions.go:213`\n\n## Impact\n- Git diffs are noisy (file changes order on each prune)\n- Tests could be flaky if they depend on order\n- Harder to debug/audit\n\n## Fix\nSort by ID or timestamp before iterating:\n\n```go\n// Convert map to slice and sort\nvar records []DeletionRecord\nfor _, r := range loadResult.Records {\n records = append(records, r)\n}\nsort.Slice(records, func(i, j int) bool {\n return records[i].ID \u003c records[j].ID\n})\n```","status":"closed","priority":3,"issue_type":"bug","created_at":"2025-11-25T12:49:11.290916-08:00","updated_at":"2025-11-25T15:15:21.903649-08:00","closed_at":"2025-11-25T15:15:21.903649-08:00"}
{"id":"bd-wucl","title":"Add Result.ConvertedToTombstone counter for import statistics","description":"The import Result struct has SkippedDeleted but doesn't distinguish between:\n1. Issues skipped because they're in deletions manifest (old behavior)\n2. Issues converted from deletions.jsonl to tombstones (new bd-dve behavior)\n\nAdd a new ConvertedToTombstone counter to track legacy deletions converted to tombstones during import. This provides better visibility into the migration process.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-07T01:40:34.097611-08:00","updated_at":"2025-12-07T02:18:09.466608-08:00","closed_at":"2025-12-07T02:18:09.466608-08:00","dependencies":[{"issue_id":"bd-wucl","depends_on_id":"bd-dve","type":"blocks","created_at":"2025-12-07T01:41:28.242971-08:00","created_by":"daemon"}]}