diff --git a/commands/renumber.md b/commands/renumber.md deleted file mode 100644 index 1bd7e6bb..00000000 --- a/commands/renumber.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -description: Renumber all issues sequentially -argument-hint: [--dry-run] [--force] ---- - -Renumber all issues sequentially to eliminate gaps in the ID space. - -## What It Does - -- Renumber all issues starting from 1 (keeps chronological order) -- Update all dependency links (all types) -- Update all text references in descriptions, notes, acceptance criteria -- Show mapping report: old ID -> new ID -- Export updated database to JSONL - -## Usage - -- **Preview**: `bd renumber --dry-run` -- **Apply**: `bd renumber --force` - -## Risks - -⚠️ **Warning**: This operation cannot be undone! - -- May break external references in GitHub issues, docs, commits -- Git history may become confusing -- Backup recommended before running - -Use sparingly - only when ID gaps are problematic. diff --git a/docs/collision-resolution-failure-analysis.md b/docs/collision-resolution-failure-analysis.md deleted file mode 100644 index 341e8242..00000000 --- a/docs/collision-resolution-failure-analysis.md +++ /dev/null @@ -1,289 +0,0 @@ -# Collision Resolution Failure Analysis - -**Date:** 2025-10-29 -**Incident:** Import with `--resolve-collisions` created duplicate issues after routine `git pull` - -## The Incident - -1. User performed routine `git pull` in `~/src/fred/beads` -2. JSONL file updated from remote (canonical database: `~/src/beads`) -3. Import failed with collision detection for: bd-106, bd-108, bd-172, bd-175 -4. Used `--resolve-collisions` which remapped them to bd-187, bd-188, bd-189, bd-190 -5. **Result:** Database now has BOTH versions - 6 duplicate issues created - -## The Evidence - -### Canonical Database (~/src/beads) -- Total issues: 509 -- JSONL lines: 165 -- bd-106: status=closed -- bd-108: status=closed -- bd-172: status=open -- bd-175: status=open - -### Polluted Database (~/src/fred/beads) -- Total issues: 515 (6 extra) -- JSONL lines: 171 (6 extra - includes the remapped duplicates) -- **Original issues:** - - bd-106: status=**open** (should be closed) - - bd-108: status=closed ✓ - - bd-172: status=open ✓ - - bd-175: status=open ✓ -- **Duplicate issues created:** - - bd-187: "Import validation falsely reports data loss" (originally bd-106) - - bd-188: "Fix multi-round convergence" (duplicate of bd-108) - - bd-189: "Delete collision resolution code" (duplicate of bd-172) - - bd-190: "Test: N-clone scenario" (duplicate of bd-175) - -### The JSONL State -The JSONL file now contains BOTH versions: -- bd-106 (closed) - from remote -- bd-187 (open) - remapped local version -- bd-108, bd-188 - duplicates -- bd-172, bd-189 - duplicates -- bd-175, bd-190 - duplicates - -## Root Cause Analysis - -### What Should Have Happened -1. Import detects bd-106 in JSONL has same ID as database -2. Compares content: JSONL version is newer/different -3. **Updates the existing bd-106 in database** (status: open → closed) -4. No duplicates created - -### What Actually Happened -1. Import detected bd-106 as a "collision" -2. Collision resolution assumed: "Two different issues both want ID bd-106" -3. Remapped local bd-106 → bd-187 -4. Imported JSONL bd-106 as new issue -5. **Result:** Both versions now exist - -### The Fundamental Design Flaw - -**Collision resolution conflates two completely different scenarios:** - -#### Scenario A: Normal Update (NOT a collision) -- JSONL has bd-106 (status=closed) -- Database has bd-106 (status=open) -- **This is a normal update** - JSONL is source of truth after git pull -- **Should:** Update database bd-106 to match JSONL -- **Should NOT:** Treat as collision - -#### Scenario B: Actual Collision (IS a collision) -- Branch A creates bd-106: "Fix authentication bug" -- Branch B creates bd-106: "Add new feature" -- Both branches merge into main -- **This is a true collision** - two different issues want same ID -- **Should:** Remap one to new ID, keep both - -### The Broken Logic - -The current collision detection logic appears to be: -``` -if (JSONL.id exists in database && JSONL.content != database.content) { - collision = true -} -``` - -This is **catastrophically wrong** because it treats every update as a collision. - -### What It Should Be - -**Idempotent import logic:** -``` -if (JSONL.id exists in database) { - if (JSONL.content_hash == database.content_hash) { - // Exact match - skip - } else { - // UPDATE the existing issue - database.update(JSONL.id, JSONL.content) - } -} -``` - -**Collision detection (only for branch merges):** -``` -Collisions only occur when: -1. Git merge conflict in JSONL file (<<<<<<< markers present) -2. Two JSONL entries have same ID but different content -3. User explicitly wants to keep both versions -``` - -## Why This Keeps Happening - -1. **Conceptual confusion:** "Collision" is being used for two different things: - - Content difference (normal update) - - ID conflict (actual collision) - -2. **Wrong default:** Import should default to "update on ID match" - - Current: Default to "collision on content difference" - -3. **Tool misuse:** `--resolve-collisions` is being used for normal imports - - Should only be needed for branch merge scenarios - -4. **No distinction:** Code doesn't distinguish between: - - Import after `git pull` (JSONL is authoritative) - - Import after branch merge (need conflict resolution) - -## The Correct Mental Model - -### Import Modes - -1. **Normal Import (default):** - - Purpose: Sync database with JSONL (source of truth) - - Behavior: Update issues on ID match, create new ones - - Use case: After `git pull`, switching branches, fresh clone - - Should NEVER create duplicates - -2. **Collision Resolution Import:** - - Purpose: Merge two independent databases that both created same IDs - - Behavior: Remap conflicting IDs to preserve both versions - - Use case: Branch merge where two devs independently created bd-42 - - Creates duplicates BY DESIGN (but with different content) - -### The Missing Piece: Import Intent - -The import command needs to know: -- "Trust the JSONL, update my database" (normal mode) -- "JSONL and database are both valid, resolve conflicts" (collision mode) - -Current implementation assumes EVERY import is a collision scenario. - -## Immediate Impact - -User now has: -- 6 duplicate issues polluting the database -- Incorrect JSONL synced to remote (if pushed) -- Canonical database potentially corrupted -- Zero trust in the import system - -## The Solution Architecture - -### Phase 1: Fix Import Default Behavior -```go -// Default import: JSONL is source of truth -func Import(jsonlPath string) error { - for each issue in JSONL { - if issue.ID exists in DB { - if issue.content_hash == DB.content_hash { - skip // identical - } else { - UPDATE DB issue // JSONL wins - } - } else { - CREATE new issue - } - } -} -``` - -### Phase 2: Collision Resolution (Separate Mode) -```bash -# Only when you KNOW you have a collision (branch merge) -bd import --resolve-collisions - -# Should ONLY be used when: -# 1. Git shows merge conflict in JSONL -# 2. You want to preserve both versions -# 3. You understand duplicates will be created -``` - -### Phase 3: Collision Detection (During Merge) -```bash -# Helper for detecting actual collisions in JSONL -bd detect-collisions - -# Shows: -# - Issues with same ID, different content in JSONL conflict markers -# - Suggests resolution strategies -# - DOES NOT modify database -``` - -## Testing Strategy - -### Test 1: Normal Update (Currently Broken) -```bash -# Setup: Database has bd-42 (status=open) -# JSONL has bd-42 (status=closed) -bd import -i issues.jsonl - -# Expected: bd-42 updated to status=closed -# Actual: Collision detected, bd-42 remapped to bd-XXX -# FAILURE ❌ -``` - -### Test 2: Actual Collision -```bash -# Setup: Branch merge creates duplicate bd-42 in JSONL -# bd-42 (title="Fix bug") in HEAD -# bd-42 (title="Add feature") in BASE -bd import -i issues.jsonl --resolve-collisions - -# Expected: Remap one to bd-XXX, keep both -# Actual: TBD -``` - -### Test 3: Idempotent Import -```bash -# Import same JSONL twice -bd import -i issues.jsonl -bd import -i issues.jsonl - -# Expected: Second import is no-op -# Actual: TBD -``` - -## Recommended Actions - -### Immediate (User Recovery) -1. Identify all duplicate pairs (bd-106/bd-187, etc.) -2. Manually merge duplicates using `bd merge` -3. Export clean database to JSONL -4. Force push to reset remote - -### Short-term (Fix Import) -1. **Create bd-XXX:** Rewrite import logic to default to UPDATE, not collision -2. **Create bd-XXX:** Add `--merge-mode` flag for actual collisions -3. **Create bd-XXX:** Write comprehensive import tests -4. **Create bd-XXX:** Document when to use collision resolution - -### Long-term (Prevent Recurrence) -1. **Create bd-XXX:** Add import validation (detect duplicates before committing) -2. **Create bd-XXX:** Add `bd validate` command to check database health -3. **Create bd-XXX:** Remove collision resolution entirely (use merge tools instead) -4. **Create bd-XXX:** Implement content-addressable IDs to prevent collisions - -## Historical Context - -This is NOT the first time this has happened: -- Multiple prior incidents of duplicate issues -- Repeated attempts to fix collision resolution -- bd-94: Epic to fix N-way collision convergence -- bd-109: Transaction + retry logic for collisions -- **Pattern:** We keep treating symptoms, not root cause - -## The Deeper Problem - -**beads is trying to solve distributed consensus without a consensus algorithm.** - -We're essentially trying to do what Git does (merge distributed changes) but: -- Git uses content-addressable storage (SHA hashes) -- Git has explicit merge semantics -- Git forces users to resolve conflicts manually -- **beads assumes automatic resolution is possible** - -The N-way collision problem, the import pollution, the duplicate issues - they're all symptoms of trying to sync mutable IDs across independent actors. - -## Conclusion - -The collision resolution feature is fundamentally broken because: -1. It treats normal updates as collisions -2. It has no concept of "source of truth" -3. It creates duplicates when it should update -4. It's being used for the wrong use case - -**The fix is not to improve collision resolution.** -**The fix is to make normal import work correctly first.** -**Then, collision resolution becomes a rare edge case for branch merges.** - -Until then, every `git pull` is a potential database corruption event.