From 25e6fba8661d65f1e120abcf82efd06d3474d1ab Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Fri, 31 Oct 2025 12:50:41 -0700 Subject: [PATCH] bd sync: 2025-10-31 12:50:41 --- .beads/beads.jsonl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.beads/beads.jsonl b/.beads/beads.jsonl index 57d2faec..8b2907f7 100644 --- a/.beads/beads.jsonl +++ b/.beads/beads.jsonl @@ -120,14 +120,14 @@ {"id":"bd-a58f","content_hash":"1ad5a25551a831608ba67a8ab2a1c2cf9e5326f3a5731e83c2b51123e67befa1","title":"Fix or remove TestRemapCollisions test","description":"TestRemapCollisions in internal/storage/sqlite/collision_test.go is failing because we removed the collision remapping implementation (bd-8e05).\n\nOptions:\n1. Remove the test entirely (collision remapping no longer supported)\n2. Update test to verify the new behavior (error when collisions detected)\n\nThe test expects RemapCollisions to work, but now it returns an error because hash IDs don't need collision remapping.\n\nFile: internal/storage/sqlite/collision_test.go:730","status":"closed","priority":1,"issue_type":"chore","created_at":"2025-10-30T22:30:33.13258-07:00","updated_at":"2025-10-31T00:19:23.870428-07:00","closed_at":"2025-10-31T00:19:23.870428-07:00","labels":["cleanup","hash-ids","tests"],"dependencies":[{"issue_id":"bd-a58f","depends_on_id":"bd-8e05","type":"blocks","created_at":"2025-10-30T22:30:33.135067-07:00","created_by":"stevey"}]} {"id":"bd-a599b676","content_hash":"919b081fad12885acfc2ff1defd8beb20b737db1372eff62306740371ab3b05e","title":"Test hash ID v2","description":"","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-30T14:24:38.507863-07:00","updated_at":"2025-10-30T17:05:26.038418-07:00","closed_at":"2025-10-30T14:24:47.272891-07:00"} {"id":"bd-a5a8bec0","content_hash":"fb509cc456ea9de6000129ad7780e417d2f06361e577738709a9ccf1c3b1f3b1","title":"bd find-duplicates - AI-powered duplicate detection","description":"Find semantically duplicate issues.\n\nApproaches:\n1. Mechanical: Exact title/description matching\n2. Embeddings: Cosine similarity (cheap, scalable)\n3. AI: LLM-based semantic comparison (expensive, accurate)\n\nUses embeddings by default for \u003e100 issues.\n\nFiles: cmd/bd/find_duplicates.go (new)","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-29T19:42:29.853269-07:00","updated_at":"2025-10-30T17:05:26.001063-07:00","closed_at":"2025-10-29T16:43:30.367455-07:00"} -{"id":"bd-a5e2bd80","content_hash":"d429410e478f428289b91d4fd258797d1140adf105b54a05fb6b7fa62c91f67f","title":"Hash-based IDs with hierarchical children","description":"Replace sequential auto-increment IDs (bd-1c63eb84, bd-9063acda) with content-hash based IDs (bd-af78e9a2) and hierarchical sequential children (bd-af78e9a2.1, .2, .3).\n\n## Motivation\nCurrent sequential IDs cause collision problems when multiple clones work offline:\n- Non-deterministic convergence in N-way scenarios (bd-cbed9619.1, bd-e6d71828)\n- Complex collision resolution logic (~2,100 LOC)\n- UNIQUE constraint violations during import\n- Requires coordination between workers\n\nHash-based IDs eliminate collisions entirely at the top level, while hierarchical sequential children provide human-friendly IDs within naturally-coordinated contexts (epic ownership).\n\n## Benefits\n- ✅ Collision-free distributed ID generation (top-level)\n- ✅ Human-friendly IDs for related work (epic children)\n- ✅ Eliminates ~2,100 LOC of collision handling code\n- ✅ Better git merge behavior (different IDs = different JSONL lines)\n- ✅ True offline-first workflows\n- ✅ Simpler than dual-system (no alias counter to coordinate)\n- ✅ Natural work breakdown structure encoding in IDs\n- ✅ Enables parallel CI/CD workers without coordination\n\n## Design\n\n### ID Structure\n- **Storage:** bd-af78e9a2 (prefix + 8-char SHA256)\n- **CLI input:** Both bd-af78e9a2 AND a3f8e9 accepted (prefix optional)\n- **CLI output:** bd-af78e9a2 (always show prefix for copy-paste clarity)\n- **External refs:** bd-af78e9a2 (in commits, docs, unambiguous)\n\n**Why keep prefix in storage:**\n- Clear in external contexts (git commits, docs, Slack)\n- Grep-able across files\n- Distinguishable from git commit SHAs\n- Supports multiple databases (bd-, ticket-, bug- prefixes)\n\n**Why make optional in CLI:**\n- Less typing: bd show a3f8e9 works\n- Git-style convenience\n- Prefix inferred from context (bd command)\n\n### Hierarchical Children\n- **Epic children:** bd-af78e9a2.1, bd-af78e9a2.2, bd-af78e9a2.3 (sequential per parent)\n- **Nested epics:** bd-af78e9a2.1.1, bd-af78e9a2.1.2 (up to 3 levels deep)\n- **Leaf tasks:** Any issue without children\n\n### Example Hierarchy\n```\nbd-a3f8e9 [epic] \"Auth System\"\n ├─ bd-a3f8e9.1 [epic] \"Login Flow\"\n │ ├─ bd-a3f8e9.1.1 [task] \"Design login UI\"\n │ ├─ bd-a3f8e9.1.2 [task] \"Backend validation\"\n │ └─ bd-a3f8e9.1.3 [task] \"Integration tests\"\n ├─ bd-a3f8e9.2 [epic] \"Password Reset\"\n │ └─ bd-a3f8e9.2.1 [task] \"Email templates\"\n └─ bd-a3f8e9.3 [task] \"Update documentation\"\n```\n\n### CLI Usage\n```bash\n# All of these work (prefix optional in input):\nbd show a3f8e9\nbd show bd-a3f8e9\nbd show a3f8e9.1\nbd show bd-a3f8e9.1.2\n\n# Output always shows prefix:\nbd-a3f8e9 [epic] Auth System\n Status: open\n ...\n\n# External references use full ID:\ngit commit -m \"Implement login (bd-a3f8e9.1)\"\n```\n\n### Collision Characteristics\n- **Top-level:** NONE (content-based hash)\n- **Epic children:** RARE (epics have natural ownership, sequential creation)\n- **When they occur:** Easy to resolve (small scope, clear context)\n\n### Storage\n- JSONL stores full hierarchical IDs with prefix: bd-a3f8e9.1.2\n- Child counters table: child_counters(parent_id, last_child)\n- Counter per parent at any depth\n\n### Limits\n- Max depth: 3 levels (prevents over-decomposition)\n- Max breadth: Unlimited (tested up to 347 children)\n- Max ID length: ~20 chars at depth 3 (bd-a3f8e9.12.34.56)\n\n## Breaking Change\nThis is a v2.0 feature requiring migration. Provide bd migrate --hash-ids tool.\n\n## Timeline\n~8 weeks (Phase 1: Hash IDs 3w, Phase 2: Hierarchical children 3w, Phase 3: Testing 2w)\nSimplified from original 9-week estimate due to removal of alias system.\n\n## Dependencies\nShould complete after bd-7c5915ae (cleanup validation).","status":"open","priority":1,"issue_type":"epic","created_at":"2025-10-29T21:23:49.592315-07:00","updated_at":"2025-10-30T17:05:26.008001-07:00"} +{"id":"bd-a5e2bd80","content_hash":"d429410e478f428289b91d4fd258797d1140adf105b54a05fb6b7fa62c91f67f","title":"Hash-based IDs with hierarchical children","description":"Replace sequential auto-increment IDs (bd-1c63eb84, bd-9063acda) with content-hash based IDs (bd-af78e9a2) and hierarchical sequential children (bd-af78e9a2.1, .2, .3).\n\n## Motivation\nCurrent sequential IDs cause collision problems when multiple clones work offline:\n- Non-deterministic convergence in N-way scenarios (bd-cbed9619.1, bd-e6d71828)\n- Complex collision resolution logic (~2,100 LOC)\n- UNIQUE constraint violations during import\n- Requires coordination between workers\n\nHash-based IDs eliminate collisions entirely at the top level, while hierarchical sequential children provide human-friendly IDs within naturally-coordinated contexts (epic ownership).\n\n## Benefits\n- ✅ Collision-free distributed ID generation (top-level)\n- ✅ Human-friendly IDs for related work (epic children)\n- ✅ Eliminates ~2,100 LOC of collision handling code\n- ✅ Better git merge behavior (different IDs = different JSONL lines)\n- ✅ True offline-first workflows\n- ✅ Simpler than dual-system (no alias counter to coordinate)\n- ✅ Natural work breakdown structure encoding in IDs\n- ✅ Enables parallel CI/CD workers without coordination\n\n## Design\n\n### ID Structure\n- **Storage:** bd-af78e9a2 (prefix + 8-char SHA256)\n- **CLI input:** Both bd-af78e9a2 AND a3f8e9 accepted (prefix optional)\n- **CLI output:** bd-af78e9a2 (always show prefix for copy-paste clarity)\n- **External refs:** bd-af78e9a2 (in commits, docs, unambiguous)\n\n**Why keep prefix in storage:**\n- Clear in external contexts (git commits, docs, Slack)\n- Grep-able across files\n- Distinguishable from git commit SHAs\n- Supports multiple databases (bd-, ticket-, bug- prefixes)\n\n**Why make optional in CLI:**\n- Less typing: bd show a3f8e9 works\n- Git-style convenience\n- Prefix inferred from context (bd command)\n\n### Hierarchical Children\n- **Epic children:** bd-af78e9a2.1, bd-af78e9a2.2, bd-af78e9a2.3 (sequential per parent)\n- **Nested epics:** bd-af78e9a2.1.1, bd-af78e9a2.1.2 (up to 3 levels deep)\n- **Leaf tasks:** Any issue without children\n\n### Example Hierarchy\n```\nbd-a3f8e9 [epic] \"Auth System\"\n ├─ bd-a3f8e9.1 [epic] \"Login Flow\"\n │ ├─ bd-a3f8e9.1.1 [task] \"Design login UI\"\n │ ├─ bd-a3f8e9.1.2 [task] \"Backend validation\"\n │ └─ bd-a3f8e9.1.3 [task] \"Integration tests\"\n ├─ bd-a3f8e9.2 [epic] \"Password Reset\"\n │ └─ bd-a3f8e9.2.1 [task] \"Email templates\"\n └─ bd-a3f8e9.3 [task] \"Update documentation\"\n```\n\n### CLI Usage\n```bash\n# All of these work (prefix optional in input):\nbd show a3f8e9\nbd show bd-a3f8e9\nbd show a3f8e9.1\nbd show bd-a3f8e9.1.2\n\n# Output always shows prefix:\nbd-a3f8e9 [epic] Auth System\n Status: open\n ...\n\n# External references use full ID:\ngit commit -m \"Implement login (bd-a3f8e9.1)\"\n```\n\n### Collision Characteristics\n- **Top-level:** NONE (content-based hash)\n- **Epic children:** RARE (epics have natural ownership, sequential creation)\n- **When they occur:** Easy to resolve (small scope, clear context)\n\n### Storage\n- JSONL stores full hierarchical IDs with prefix: bd-a3f8e9.1.2\n- Child counters table: child_counters(parent_id, last_child)\n- Counter per parent at any depth\n\n### Limits\n- Max depth: 3 levels (prevents over-decomposition)\n- Max breadth: Unlimited (tested up to 347 children)\n- Max ID length: ~20 chars at depth 3 (bd-a3f8e9.12.34.56)\n\n## Breaking Change\nThis is a v2.0 feature requiring migration. Provide bd migrate --hash-ids tool.\n\n## Timeline\n~8 weeks (Phase 1: Hash IDs 3w, Phase 2: Hierarchical children 3w, Phase 3: Testing 2w)\nSimplified from original 9-week estimate due to removal of alias system.\n\n## Dependencies\nShould complete after bd-7c5915ae (cleanup validation).","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-10-29T21:23:49.592315-07:00","updated_at":"2025-10-31T12:50:41.121302-07:00","closed_at":"2025-10-31T12:50:41.121302-07:00"} {"id":"bd-a5e2bd80.1","content_hash":"0eda8184a08dfc7fbdc5486ea619a9313f751d2fe1b126905d34a43b1b871421","title":"Migration tool: sequential → hash IDs","description":"Create migration tool to convert sequential IDs to hierarchical hash-based IDs.\n\n## Command\n```bash\nbd migrate --to-hash-ids [--dry-run]\n```\n\n## Process\n1. For each top-level issue (no parent):\n - Generate hash ID from content\n - Create mapping: bd-1c63eb84 → bd-a3f8e9a2\n \n2. For each child issue (has parent):\n - Find parent's new hash ID\n - Assign sequential child number based on creation order\n - bd-b47c034e (parent: bd-1c63eb84) → bd-a3f8e9a2.1\n \n3. Update all references:\n - Dependencies (blocks, parent-child)\n - Comments (issue_id foreign keys)\n - External refs (if containing old IDs)\n\n4. Preserve:\n - Creation timestamps\n - All content\n - All relationships\n - History in comments\n\n## Output\n- Mapping file: old_id → new_id (for reference)\n- Updated JSONL with new IDs\n- Migration log\n\n## Validation\n- Verify all relationships intact\n- Check no orphaned issues\n- Confirm total count unchanged\n- Test rollback procedure\n\n## Safety\n- Backup database before migration\n- Dry-run mode shows what would change\n- Rollback script provided","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-30T17:00:11.081377-07:00","updated_at":"2025-10-30T17:05:26.034628-07:00","closed_at":"2025-10-30T16:23:30.380142-07:00","dependencies":[{"issue_id":"bd-a5e2bd80.1","depends_on_id":"bd-a5e2bd80","type":"parent-child","created_at":"2025-10-30T17:00:11.086162-07:00","created_by":"import-remap"},{"issue_id":"bd-a5e2bd80.1","depends_on_id":"bd-a5e2bd80.5","type":"blocks","created_at":"2025-10-30T17:00:11.086508-07:00","created_by":"import-remap"},{"issue_id":"bd-a5e2bd80.1","depends_on_id":"bd-a5e2bd80.7","type":"blocks","created_at":"2025-10-30T17:00:11.086873-07:00","created_by":"import-remap"}]} -{"id":"bd-a5e2bd80.10","content_hash":"56929e57c09610ede74cd5d6f9e8dfa71c74412183cc53e646f72a2324025ad0","title":"Test: N-clone scenario with hash IDs (no collisions)","description":"Comprehensive test to verify hash IDs eliminate collision problems.\n\n## Test: TestHashIDsNClones\n\n### Purpose\nVerify that N clones can work offline and sync without ID collisions using hash IDs.\n\n### Test Scenario\n```\nSetup:\n- 1 bare remote repo\n- 5 clones (A, B, C, D, E)\n\nOffline Work:\n- Each clone creates 10 issues with different titles\n- No coordination, no network access\n- Total: 50 unique issues\n\nSync:\n- Clones sync in random order\n- Each pull/import other clones' issues\n\nExpected Result:\n- All 5 clones converge to 50 issues\n- Zero ID collisions\n- Zero remapping needed\n- Alias conflicts resolved deterministically\n```\n\n### Implementation\nFile: cmd/bd/beads_hashid_test.go (new)\n\n```go\nfunc TestHashIDsFiveClones(t *testing.T) {\n tmpDir := t.TempDir()\n remoteDir := setupBareRepo(t, tmpDir)\n \n // Setup 5 clones\n clones := make(map[string]string)\n for _, name := range []string{\"A\", \"B\", \"C\", \"D\", \"E\"} {\n clones[name] = setupClone(t, tmpDir, remoteDir, name)\n }\n \n // Each clone creates 10 issues offline\n for name, dir := range clones {\n for i := 0; i \u003c 10; i++ {\n createIssue(t, dir, fmt.Sprintf(\"%s-issue-%d\", name, i))\n }\n // No sync yet!\n }\n \n // Sync in random order\n syncOrder := []string{\"C\", \"A\", \"E\", \"B\", \"D\"}\n for _, name := range syncOrder {\n syncClone(t, clones[name], name)\n }\n \n // Final convergence round\n for _, name := range []string{\"A\", \"B\", \"C\", \"D\", \"E\"} {\n finalPull(t, clones[name], name)\n }\n \n // Verify all clones have all 50 issues\n for name, dir := range clones {\n issues := getIssues(t, dir)\n if len(issues) != 50 {\n t.Errorf(\"Clone %s: expected 50 issues, got %d\", name, len(issues))\n }\n \n // Verify all issue IDs are hash-based\n for _, issue := range issues {\n if !strings.HasPrefix(issue.ID, \"bd-\") || len(issue.ID) != 11 {\n t.Errorf(\"Invalid hash ID: %s\", issue.ID)\n }\n }\n }\n \n // Verify no collision resolution occurred\n // (This would be in logs if it happened)\n \n t.Log(\"✓ All 5 clones converged to 50 issues with zero collisions\")\n}\n```\n\n### Edge Case Tests\n\n#### Test: Hash Collision Detection (Artificial)\n```go\nfunc TestHashCollisionDetection(t *testing.T) {\n // Artificially inject collision by mocking hash function\n // Verify system detects and handles it\n}\n```\n\n#### Test: Alias Conflicts Resolved Deterministically\n```go\nfunc TestAliasConflictsNClones(t *testing.T) {\n // Two clones assign same alias to different issues\n // Verify deterministic resolution (content-hash ordering)\n // Verify all clones converge to same alias assignments\n}\n```\n\n#### Test: Mixed Sequential and Hash IDs (Should Fail)\n```go\nfunc TestMixedIDsRejected(t *testing.T) {\n // Try to import JSONL with sequential IDs into hash-ID database\n // Verify error or warning\n}\n```\n\n### Performance Test\n\n#### Benchmark: Hash ID Generation\n```go\nfunc BenchmarkHashIDGeneration(b *testing.B) {\n for i := 0; i \u003c b.N; i++ {\n GenerateHashID(\"title\", \"description\", time.Now(), \"workspace-id\")\n }\n}\n\n// Expected: \u003c 1μs per generation\n```\n\n#### Benchmark: N-Clone Convergence Time\n```go\nfunc BenchmarkNCloneConvergence(b *testing.B) {\n for _, n := range []int{3, 5, 10, 20} {\n b.Run(fmt.Sprintf(\"N=%d\", n), func(b *testing.B) {\n // Measure total convergence time\n })\n }\n}\n\n// Expected: Linear scaling O(N)\n```\n\n### Acceptance Criteria\n- TestHashIDsFiveClones passes reliably (10/10 runs)\n- Zero ID collisions in any scenario\n- All clones converge in single round (not multi-round like old system)\n- Alias conflicts resolved deterministically\n- Performance benchmarks meet targets (\u003c1μs hash gen)\n\n## Files to Create\n- cmd/bd/beads_hashid_test.go\n\n## Comparison to Old System\nThis test replaces:\n- TestTwoCloneCollision (bd-71107098) - no longer needed\n- TestThreeCloneCollision (bd-cbed9619) - no longer needed\n- TestFiveCloneCollision (bd-a40f374f) - no longer needed\n\nOld system required complex collision resolution and multi-round convergence.\nNew system: single-round convergence with zero collisions.","status":"open","priority":1,"issue_type":"task","created_at":"2025-10-29T21:27:26.954107-07:00","updated_at":"2025-10-30T17:05:26.013668-07:00","dependencies":[{"issue_id":"bd-a5e2bd80.10","depends_on_id":"bd-a5e2bd80","type":"parent-child","created_at":"2025-10-29T21:27:26.955522-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.10","depends_on_id":"bd-a5e2bd80.11","type":"blocks","created_at":"2025-10-29T21:27:26.956175-07:00","created_by":"stevey"}]} +{"id":"bd-a5e2bd80.10","content_hash":"56929e57c09610ede74cd5d6f9e8dfa71c74412183cc53e646f72a2324025ad0","title":"Test: N-clone scenario with hash IDs (no collisions)","description":"Comprehensive test to verify hash IDs eliminate collision problems.\n\n## Test: TestHashIDsNClones\n\n### Purpose\nVerify that N clones can work offline and sync without ID collisions using hash IDs.\n\n### Test Scenario\n```\nSetup:\n- 1 bare remote repo\n- 5 clones (A, B, C, D, E)\n\nOffline Work:\n- Each clone creates 10 issues with different titles\n- No coordination, no network access\n- Total: 50 unique issues\n\nSync:\n- Clones sync in random order\n- Each pull/import other clones' issues\n\nExpected Result:\n- All 5 clones converge to 50 issues\n- Zero ID collisions\n- Zero remapping needed\n- Alias conflicts resolved deterministically\n```\n\n### Implementation\nFile: cmd/bd/beads_hashid_test.go (new)\n\n```go\nfunc TestHashIDsFiveClones(t *testing.T) {\n tmpDir := t.TempDir()\n remoteDir := setupBareRepo(t, tmpDir)\n \n // Setup 5 clones\n clones := make(map[string]string)\n for _, name := range []string{\"A\", \"B\", \"C\", \"D\", \"E\"} {\n clones[name] = setupClone(t, tmpDir, remoteDir, name)\n }\n \n // Each clone creates 10 issues offline\n for name, dir := range clones {\n for i := 0; i \u003c 10; i++ {\n createIssue(t, dir, fmt.Sprintf(\"%s-issue-%d\", name, i))\n }\n // No sync yet!\n }\n \n // Sync in random order\n syncOrder := []string{\"C\", \"A\", \"E\", \"B\", \"D\"}\n for _, name := range syncOrder {\n syncClone(t, clones[name], name)\n }\n \n // Final convergence round\n for _, name := range []string{\"A\", \"B\", \"C\", \"D\", \"E\"} {\n finalPull(t, clones[name], name)\n }\n \n // Verify all clones have all 50 issues\n for name, dir := range clones {\n issues := getIssues(t, dir)\n if len(issues) != 50 {\n t.Errorf(\"Clone %s: expected 50 issues, got %d\", name, len(issues))\n }\n \n // Verify all issue IDs are hash-based\n for _, issue := range issues {\n if !strings.HasPrefix(issue.ID, \"bd-\") || len(issue.ID) != 11 {\n t.Errorf(\"Invalid hash ID: %s\", issue.ID)\n }\n }\n }\n \n // Verify no collision resolution occurred\n // (This would be in logs if it happened)\n \n t.Log(\"✓ All 5 clones converged to 50 issues with zero collisions\")\n}\n```\n\n### Edge Case Tests\n\n#### Test: Hash Collision Detection (Artificial)\n```go\nfunc TestHashCollisionDetection(t *testing.T) {\n // Artificially inject collision by mocking hash function\n // Verify system detects and handles it\n}\n```\n\n#### Test: Alias Conflicts Resolved Deterministically\n```go\nfunc TestAliasConflictsNClones(t *testing.T) {\n // Two clones assign same alias to different issues\n // Verify deterministic resolution (content-hash ordering)\n // Verify all clones converge to same alias assignments\n}\n```\n\n#### Test: Mixed Sequential and Hash IDs (Should Fail)\n```go\nfunc TestMixedIDsRejected(t *testing.T) {\n // Try to import JSONL with sequential IDs into hash-ID database\n // Verify error or warning\n}\n```\n\n### Performance Test\n\n#### Benchmark: Hash ID Generation\n```go\nfunc BenchmarkHashIDGeneration(b *testing.B) {\n for i := 0; i \u003c b.N; i++ {\n GenerateHashID(\"title\", \"description\", time.Now(), \"workspace-id\")\n }\n}\n\n// Expected: \u003c 1μs per generation\n```\n\n#### Benchmark: N-Clone Convergence Time\n```go\nfunc BenchmarkNCloneConvergence(b *testing.B) {\n for _, n := range []int{3, 5, 10, 20} {\n b.Run(fmt.Sprintf(\"N=%d\", n), func(b *testing.B) {\n // Measure total convergence time\n })\n }\n}\n\n// Expected: Linear scaling O(N)\n```\n\n### Acceptance Criteria\n- TestHashIDsFiveClones passes reliably (10/10 runs)\n- Zero ID collisions in any scenario\n- All clones converge in single round (not multi-round like old system)\n- Alias conflicts resolved deterministically\n- Performance benchmarks meet targets (\u003c1μs hash gen)\n\n## Files to Create\n- cmd/bd/beads_hashid_test.go\n\n## Comparison to Old System\nThis test replaces:\n- TestTwoCloneCollision (bd-71107098) - no longer needed\n- TestThreeCloneCollision (bd-cbed9619) - no longer needed\n- TestFiveCloneCollision (bd-a40f374f) - no longer needed\n\nOld system required complex collision resolution and multi-round convergence.\nNew system: single-round convergence with zero collisions.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-29T21:27:26.954107-07:00","updated_at":"2025-10-31T12:48:26.90768-07:00","closed_at":"2025-10-31T12:48:26.90768-07:00","dependencies":[{"issue_id":"bd-a5e2bd80.10","depends_on_id":"bd-a5e2bd80","type":"parent-child","created_at":"2025-10-29T21:27:26.955522-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.10","depends_on_id":"bd-a5e2bd80.11","type":"blocks","created_at":"2025-10-29T21:27:26.956175-07:00","created_by":"stevey"}]} {"id":"bd-a5e2bd80.11","content_hash":"cb24777a804129f91ae8d96937c762ba4877e2a0273d389d099f678ed2080a54","title":"Delete collision resolution code","description":"Remove ~2,100 LOC of ID collision detection and resolution code (no longer needed with hash IDs).\n\n## Files to Delete Entirely\n```\ninternal/storage/sqlite/collision.go (~800 LOC)\ninternal/storage/sqlite/collision_test.go (~300 LOC)\ncmd/bd/autoimport_collision_test.go (~400 LOC)\n```\n\n## Code to Remove from Existing Files\n\n### internal/importer/importer.go\nRemove:\n- `DetectCollisions()` calls\n- `ScoreCollisions()` logic\n- `RemapCollisions()` calls\n- `handleRename()` function\n- All collision-related error handling\n\nKeep:\n- Basic import logic\n- Exact match detection (idempotent import)\n\n### beads_twoclone_test.go\nRemove:\n- `TestTwoCloneCollision` (bd-71107098)\n- `TestThreeCloneCollision` (bd-cbed9619)\n- `TestFiveCloneCollision` (bd-a40f374f)\n- All N-way collision tests\n\n### cmd/bd/import.go\nRemove:\n- `--resolve-collisions` flag\n- `--dry-run` collision preview\n- Collision reporting\n\n## Issues Closed by This Change\n- bd-71107098: Add test for symmetric collision\n--89: Content-hash collision resolution\n- bd-cbed9619: N-way collision resolution epic\n- bd-cbed9619.5: Add ScoreCollisions (already done but now unnecessary)\n- bd-cbed9619.4: Make DetectCollisions read-only\n- bd-cbed9619.3: ResolveNWayCollisions function\n- bd-cbed9619.2: Multi-round import convergence\n- bd-cbed9619.1: Multi-round convergence for N-way collisions\n- bd-e6d71828: Transaction + retry logic for collisions\n- bd-70419816: Test case for symmetric collision\n\n## Verification Steps\n1. `grep -r \"collision\" --include=\"*.go\"` → should only find alias conflicts\n2. `go test ./...` → all tests pass\n3. `go build ./cmd/bd` → clean build\n4. Check LOC reduction: `git diff --stat`\n\n## Expected Metrics\n- **Files deleted**: 3\n- **LOC removed**: ~2,100\n- **Test coverage**: Should increase (less untested code)\n- **Binary size**: Slightly smaller\n\n## Caution\nDo NOT delete:\n- Alias conflict resolution (new code in bd-a6abe1c7)\n- Duplicate detection (bd-581b80b3, bd-149) - different from ID collisions\n- Merge conflict resolution (bd-7e7ddffa.1, bd-5f483051) - git conflicts, not ID collisions\n\n## Files to Modify\n- internal/importer/importer.go (remove collision handling)\n- cmd/bd/import.go (remove --resolve-collisions flag)\n- beads_twoclone_test.go (remove collision tests)\n- Delete: internal/storage/sqlite/collision.go\n- Delete: internal/storage/sqlite/collision_test.go \n- Delete: cmd/bd/autoimport_collision_test.go\n\n## Testing\n- Ensure all remaining tests pass\n- Manual test: create issue on two clones, sync → no collisions\n- Verify error if somehow hash collision occurs (extremely unlikely)","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-29T21:25:50.976383-07:00","updated_at":"2025-10-31T01:00:58.757335-07:00","closed_at":"2025-10-31T01:00:58.757335-07:00","dependencies":[{"issue_id":"bd-a5e2bd80.11","depends_on_id":"bd-a5e2bd80","type":"parent-child","created_at":"2025-10-29T21:25:50.977857-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.11","depends_on_id":"bd-0953ea41","type":"blocks","created_at":"2025-10-29T21:25:50.978395-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.11","depends_on_id":"bd-a5e2bd80.12","type":"blocks","created_at":"2025-10-29T21:25:50.978842-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.11","depends_on_id":"bd-a5e2bd80.7","type":"blocks","created_at":"2025-10-30T14:22:59.348038-07:00","created_by":"import-remap"},{"issue_id":"bd-a5e2bd80.11","depends_on_id":"bd-a5e2bd80.5","type":"blocks","created_at":"2025-10-30T17:00:11.090105-07:00","created_by":"import-remap"},{"issue_id":"bd-a5e2bd80.11","depends_on_id":"bd-a5e2bd80.4","type":"blocks","created_at":"2025-10-30T17:00:11.090454-07:00","created_by":"import-remap"}]} {"id":"bd-a5e2bd80.12","content_hash":"d565476761fdf87fd8ec031cdae0e6698734ace357c4f00e1b9947e4d7101eb7","title":"Update JSONL format to use hash IDs","description":"Update JSONL format to store hierarchical hash-based IDs.\n\n## Changes\n- ID field: bd-af78e9a2 (top-level) or bd-af78e9a2.1.2 (hierarchical)\n- No alias field needed (removed from original plan)\n- All other fields remain the same\n\n## Example\n```json\n{\"id\":\"bd-af78e9a2\",\"title\":\"Auth System\",\"type\":\"epic\",...}\n{\"id\":\"bd-af78e9a2.1\",\"title\":\"Login Flow\",\"type\":\"epic\",...}\n{\"id\":\"bd-af78e9a2.1.1\",\"title\":\"Design UI\",\"type\":\"task\",...}\n```\n\n## Benefits\n- Hierarchical structure visible in JSONL\n- Git merge conflicts reduced (different IDs = different lines)\n- Easy to grep for epic children: grep \"bd-af78e9a2\\.\" issues.jsonl\n\n## Backward Compatibility\nMigration tool will convert bd-1c63eb84 → bd-{hash} with mapping preserved.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-29T21:24:47.408106-07:00","updated_at":"2025-10-30T17:05:26.009699-07:00","closed_at":"2025-10-30T14:27:39.953114-07:00","dependencies":[{"issue_id":"bd-a5e2bd80.12","depends_on_id":"bd-a5e2bd80","type":"parent-child","created_at":"2025-10-29T21:24:47.409489-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.12","depends_on_id":"bd-0953ea41","type":"blocks","created_at":"2025-10-29T21:24:47.409977-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.12","depends_on_id":"bd-a5e2bd80.13","type":"blocks","created_at":"2025-10-29T21:29:45.975499-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.12","depends_on_id":"bd-a5e2bd80.7","type":"blocks","created_at":"2025-10-30T14:22:59.347557-07:00","created_by":"import-remap"}]} {"id":"bd-a5e2bd80.13","content_hash":"64ad81d1a67f119ed3b9c66e215252aab9e569926e0a60586a61bc38bd8659b8","title":"Add child_counters table to database schema","description":"Add child_counters table to support sequential child ID generation within parent contexts.\n\n## Schema\n```sql\nCREATE TABLE child_counters (\n parent_id TEXT PRIMARY KEY,\n last_child INTEGER NOT NULL DEFAULT 0,\n FOREIGN KEY (parent_id) REFERENCES issues(id) ON DELETE CASCADE\n);\n```\n\n## Usage\n- Counter per parent (at any depth)\n- Atomic increment: INSERT...ON CONFLICT DO UPDATE\n- bd-a3f8e9 → .1, .2, .3\n- bd-a3f8e9.1 → .1.1, .1.2, .1.3\n- Works up to 3 levels deep","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-29T21:24:13.968241-07:00","updated_at":"2025-10-30T17:05:26.008932-07:00","closed_at":"2025-10-30T13:32:05.83292-07:00","dependencies":[{"issue_id":"bd-a5e2bd80.13","depends_on_id":"bd-a5e2bd80","type":"parent-child","created_at":"2025-10-29T21:24:13.96959-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.13","depends_on_id":"bd-a5e2bd80.14","type":"blocks","created_at":"2025-10-29T21:29:45.952824-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.13","depends_on_id":"bd-a5e2bd80.6","type":"blocks","created_at":"2025-10-30T17:00:11.084198-07:00","created_by":"import-remap"}]} {"id":"bd-a5e2bd80.14","content_hash":"58dfc45f7ac82d158cabc4d431a05ef585f7501562f6b130922155fec7f87135","title":"Design hash ID generation algorithm","description":"Design and specify the hash-based ID generation algorithm.\n\n## Requirements\n- Deterministic: same inputs → same ID\n- Collision-resistant: ~2^32 space for 8-char hex\n- Fast: \u003c1μs per generation\n- Includes timestamp for uniqueness\n- Includes creator/workspace for distributed uniqueness\n\n## Proposed Algorithm\n```go\nfunc GenerateIssueID(title, desc string, created time.Time, workspaceID string) string {\n h := sha256.New()\n h.Write([]byte(title))\n h.Write([]byte(desc))\n h.Write([]byte(created.Format(time.RFC3339Nano)))\n h.Write([]byte(workspaceID))\n hash := hex.EncodeToString(h.Sum(nil))\n return \"bd-\" + hash[:8] // 8-char prefix = 2^32 space\n}\n```\n\n## Open Questions\n1. 8 chars (2^32) or 16 chars (2^64) for collision resistance?\n2. Include priority/type in hash? (Pro: more entropy. Con: immutable)\n3. How to handle workspace ID generation? (hostname? UUID?)\n4. What if title+desc change? (Answer: ID stays same - hash only used at creation)\n\n## Deliverables\n- Design doc: docs/HASH_ID_DESIGN.md\n- Collision probability analysis\n- Performance benchmarks\n- Prototype implementation in internal/types/id_generator.go","notes":"## Next Session: Continue bd-a5e2bd80.5\n\nWe've completed:\n- ✅ bd-a5e2bd80.6: Hash ID algorithm (returns full 64-char hash)\n- ✅ bd-a5e2bd80.13: child_counters table + getNextChildNumber()\n- ✅ Docs updated for 6-char progressive design\n\n**TODO for bd-a5e2bd80.5:**\nImplement progressive collision retry in CreateIssue():\n1. Try hash[:6] first (bd-a3f2dd)\n2. On UNIQUE constraint → try hash[:7] (bd-a3f2dda) \n3. On collision again → try hash[:8] (bd-a3f2dda8)\n4. Max 3 attempts, then error\n\nLocation: internal/storage/sqlite/sqlite.go CreateIssue() around line 748\nPattern: Detect sqlite UNIQUE constraint error, retry with longer hash\n\nSee: internal/types/id_generator.go GenerateHashID() - now returns full hash","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-29T21:24:01.843634-07:00","updated_at":"2025-10-30T17:05:26.008476-07:00","closed_at":"2025-10-30T15:42:04.615691-07:00","dependencies":[{"issue_id":"bd-a5e2bd80.14","depends_on_id":"bd-a5e2bd80","type":"parent-child","created_at":"2025-10-29T21:24:01.844994-07:00","created_by":"stevey"}]} -{"id":"bd-a5e2bd80.15","content_hash":"1c44b9918f43a4c29fa73326e9dedb27015bc1ebae27ff72e7ba3967a0a8ddf4","title":"Update documentation for hash IDs and aliases","description":"Update documentation for hash-based hierarchical ID system.\n\n## Files to Update\n- README.md: Quick example of hash IDs and hierarchical children\n- QUICKSTART.md: Show bd create with --parent flag\n- commands/create.md: Document --parent flag and depth limits\n- AGENTS.md: Update examples to use hash ID format\n- FAQ.md: Add \"Why hash IDs?\" section\n\n## Topics to Cover\n### Hash IDs\n- Why content-based hashing?\n- Collision-free guarantees\n- Git-style prefix matching\n- Example: bd show a3f8e9\n\n### Hierarchical Children\n- Epic → child tasks with sequential IDs\n- Up to 3 levels deep\n- Natural work breakdown structure\n- Example: bd-a3f8e9.1.2\n\n### Migration\n- How to migrate from sequential IDs\n- Backward compatibility (old IDs in comments/docs)\n- Timeline and breaking change notice\n\n### Best Practices\n- When to use nested epics vs flat tasks\n- Epic ownership for collision avoidance\n- Using bd tree for visualization\n- Querying hierarchies\n\n## Examples\nInclude real-world examples:\n- Small project: 1-level hierarchy (epic → tasks)\n- Large project: 2-level (epic → sub-epics → tasks)\n- Complex: 3-level (epic → features → stories → tasks)","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-29T21:28:10.979971-07:00","updated_at":"2025-10-30T17:05:26.014966-07:00","dependencies":[{"issue_id":"bd-a5e2bd80.15","depends_on_id":"bd-a5e2bd80","type":"parent-child","created_at":"2025-10-29T21:28:10.981344-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.15","depends_on_id":"bd-6eecc9be","type":"blocks","created_at":"2025-10-29T21:28:10.981767-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.15","depends_on_id":"bd-a5e2bd80.16","type":"blocks","created_at":"2025-10-29T21:28:10.982167-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.15","depends_on_id":"bd-a5e2bd80.1","type":"blocks","created_at":"2025-10-30T17:00:11.093979-07:00","created_by":"import-remap"}]} +{"id":"bd-a5e2bd80.15","content_hash":"7e9f7d63752d5c1c22c685b0f5d58efcf14014948a5fb637a572a8d34e78611b","title":"Update documentation for hash IDs and aliases","description":"Update documentation for hash-based hierarchical ID system.\n\n## Files to Update\n- README.md: Quick example of hash IDs and hierarchical children\n- QUICKSTART.md: Show bd create with --parent flag\n- commands/create.md: Document --parent flag and depth limits\n- AGENTS.md: Update examples to use hash ID format\n- FAQ.md: Add \"Why hash IDs?\" section\n\n## Topics to Cover\n### Hash IDs\n- Why content-based hashing?\n- Collision-free guarantees\n- Git-style prefix matching\n- Example: bd show a3f8e9\n\n### Hierarchical Children\n- Epic → child tasks with sequential IDs\n- Up to 3 levels deep\n- Natural work breakdown structure\n- Example: bd-a3f8e9.1.2\n\n### Migration\n- How to migrate from sequential IDs\n- Backward compatibility (old IDs in comments/docs)\n- Timeline and breaking change notice\n\n### Best Practices\n- When to use nested epics vs flat tasks\n- Epic ownership for collision avoidance\n- Using bd tree for visualization\n- Querying hierarchies\n\n## Examples\nInclude real-world examples:\n- Small project: 1-level hierarchy (epic → tasks)\n- Large project: 2-level (epic → sub-epics → tasks)\n- Complex: 3-level (epic → features → stories → tasks)","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-29T21:28:10.979971-07:00","updated_at":"2025-10-31T12:50:32.459491-07:00","closed_at":"2025-10-31T12:50:32.459491-07:00","dependencies":[{"issue_id":"bd-a5e2bd80.15","depends_on_id":"bd-a5e2bd80","type":"parent-child","created_at":"2025-10-29T21:28:10.981344-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.15","depends_on_id":"bd-6eecc9be","type":"blocks","created_at":"2025-10-29T21:28:10.981767-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.15","depends_on_id":"bd-a5e2bd80.16","type":"blocks","created_at":"2025-10-29T21:28:10.982167-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.15","depends_on_id":"bd-a5e2bd80.1","type":"blocks","created_at":"2025-10-30T17:00:11.093979-07:00","created_by":"import-remap"}]} {"id":"bd-a5e2bd80.16","content_hash":"07d57a6c273c712250bbb96ca4db01c0845b4aa054c879f023c25e4e1fd48789","title":"Add hierarchy visualization commands","description":"Add commands to visualize and navigate hierarchical issue structures.\n\n## Commands\n\n### bd tree \u003cid\u003e\nShow hierarchical tree view:\n```\nbd tree a3f8e9\n\nbd-a3f8e9 [epic] Auth System\n├─ bd-a3f8e9.1 [epic] Login Flow\n│ ├─ bd-a3f8e9.1.1 [task] Design login UI ✓\n│ ├─ bd-a3f8e9.1.2 [task] Backend validation (in progress)\n│ └─ bd-a3f8e9.1.3 [task] Integration tests\n├─ bd-a3f8e9.2 [epic] Password Reset ✓\n└─ bd-a3f8e9.3 [task] Update documentation\n```\n\n### bd stats \u003cid\u003e --recursive\nShow progress statistics:\n```\nAuth System (bd-a3f8e9): 7/27 complete (25%)\n Login Flow: 2/3 complete (67%)\n Password Reset: 3/3 complete (100%) ✓\n Documentation: 2/21 complete (10%)\n```\n\n### bd list \u003cid\u003e --leaves\nShow only leaf nodes (actual work):\n```\nbd list a3f8e9 --leaves\n\nbd-a3f8e9.1.1 [task] Design login UI\nbd-a3f8e9.1.2 [task] Backend validation\n...\n```\n\n## Sort Order\n- Implement numeric comparison of ID components\n- Ensure bd-a3f8e9.10 comes after bd-a3f8e9.9 (not lexicographic)","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-29T21:26:53.751795-07:00","updated_at":"2025-10-30T17:05:26.012949-07:00","dependencies":[{"issue_id":"bd-a5e2bd80.16","depends_on_id":"bd-a5e2bd80","type":"parent-child","created_at":"2025-10-29T21:26:53.753259-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.16","depends_on_id":"bd-67335f92","type":"blocks","created_at":"2025-10-29T21:26:53.753733-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.16","depends_on_id":"bd-a6abe1c7","type":"blocks","created_at":"2025-10-29T21:26:53.754112-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.16","depends_on_id":"bd-a5e2bd80.3","type":"blocks","created_at":"2025-10-30T17:00:11.088976-07:00","created_by":"import-remap"},{"issue_id":"bd-a5e2bd80.16","depends_on_id":"bd-a5e2bd80.2","type":"blocks","created_at":"2025-10-30T17:00:11.089373-07:00","created_by":"import-remap"}]} {"id":"bd-a5e2bd80.2","content_hash":"e81f6af609fb707773b386ead4defedd146fc0f937eb60e6bfcf3bb63224237d","title":"Implement hierarchical child ID generation","description":"Implement sequential child ID generation within parent contexts.\n\n## Function Signature\n```go\nfunc (s *SQLiteStorage) getNextChildID(ctx context.Context, parentID string) (string, error)\n```\n\n## Logic\n1. Insert or update child_counters for parent_id\n2. Return incremented counter\n3. Format as parentID.{counter}\n4. Works at any depth (bd-a3f8e9.1 → bd-a3f8e9.1.5)\n\n## Collision Handling\n- In single-player mode: No collisions (sequential)\n- In multi-player mode (future): Rare collisions, manual resolution needed\n- Epic ownership makes collisions naturally rare\n\n## Integration\n- Called from CreateIssue when --parent flag is used\n- Validates parent exists and depth \u003c= 3","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-30T17:00:11.080196-07:00","updated_at":"2025-10-30T17:05:26.03427-07:00","closed_at":"2025-10-30T14:39:56.341051-07:00","dependencies":[{"issue_id":"bd-a5e2bd80.2","depends_on_id":"bd-a5e2bd80","type":"parent-child","created_at":"2025-10-30T17:00:11.084596-07:00","created_by":"import-remap"},{"issue_id":"bd-a5e2bd80.2","depends_on_id":"bd-a5e2bd80.13","type":"blocks","created_at":"2025-10-30T17:00:11.084959-07:00","created_by":"import-remap"},{"issue_id":"bd-a5e2bd80.2","depends_on_id":"bd-a5e2bd80.4","type":"blocks","created_at":"2025-10-30T17:00:11.085361-07:00","created_by":"import-remap"}]} {"id":"bd-a5e2bd80.3","content_hash":"efff5848d0fca24560542dd9aff3e903514cb2c804cd7b9d7b6a117dadc2ae60","title":"CLI accepts hash ID prefixes (Git-style)","description":"Implement prefix-optional parsing for hash IDs throughout CLI.\n\n## Parsing Logic\n```go\n// internal/utils/id_parser.go\nfunc ParseIssueID(input string) (issueID string, err error) {\n // Already has prefix: bd-a3f8e9 or bd-a3f8e9.1.2\n if strings.HasPrefix(input, \"bd-\") {\n return input, nil\n }\n \n // Add prefix: a3f8e9 → bd-a3f8e9\n // Works with hierarchical too: a3f8e9.1.2 → bd-a3f8e9.1.2\n return \"bd-\" + input, nil\n}\n\n// Prefix matching for partial IDs\nfunc ResolvePartialID(input string) (fullID string, err error) {\n parsedID := ParseIssueID(input) // Ensure prefix\n \n // Query with LIKE for prefix match\n matches := storage.FindByPrefix(parsedID)\n \n if len(matches) == 0 {\n return \"\", fmt.Errorf(\"no issue found matching %q\", input)\n }\n if len(matches) \u003e 1 {\n return \"\", fmt.Errorf(\"ambiguous ID %q matches: %v\", input, matches)\n }\n return matches[0].ID, nil\n}\n```\n\n## Behavior Examples\n```bash\n# All these inputs work:\nbd show a3f8e9 # Add prefix → bd-a3f8e9\nbd show bd-a3f8e9 # Already has prefix\nbd show a3f8 # Shorter prefix match\nbd show a3f8e9.1 # Hierarchical without prefix\nbd show bd-a3f8e9.1.2 # Full hierarchical with prefix\n\n# Output always shows full ID with prefix:\nbd-a3f8e9 [epic] Auth System\nbd-a3f8e9.1 [epic] Login Flow\nbd-a3f8e9.1.2 [task] Backend validation\n```\n\n## Error Handling\n```bash\n# Not found\n$ bd show xyz789\nError: no issue found matching \"bd-xyz789\"\n\n# Ambiguous (multiple matches)\n$ bd show a3\nError: ambiguous ID \"bd-a3\" matches: bd-a3f8e9, bd-a34721, bd-a38f92\nUse more characters to disambiguate.\n```\n\n## Commands to Update\nAll commands that accept issue IDs:\n\n### Read operations\n- bd show \u003cid\u003e\n- bd list --id \u003cids\u003e\n- bd dep tree \u003cid\u003e\n\n### Write operations\n- bd update \u003cid\u003e\n- bd close \u003cid\u003e\n- bd reopen \u003cid\u003e\n- bd label add \u003cid\u003e\n- bd dep add \u003cid1\u003e \u003cid2\u003e\n- bd comment add \u003cid\u003e\n\n### Multiple IDs\n```bash\n# All work with optional prefix:\nbd show a3f8e9 b7c2d1 # → bd-a3f8e9 bd-b7c2d1\nbd dep add a3f8e9.1 b7c2d1 # → bd-a3f8e9.1 blocks bd-b7c2d1\n```\n\n## Display Format\n**Always show full ID with prefix** in output for:\n- Copy-paste clarity\n- External reference (git commits, docs)\n- Unambiguous identification\n\n```bash\n$ bd list\nbd-a3f8e9 [P1] Auth System [epic] open\nbd-b7c2d1 [P2] Fix daemon crash [bug] open\nbd-1a2b3c4d [P3] Add logging [task] open\n```\n\n## Files to Create/Modify\n- internal/utils/id_parser.go (new - parsing logic)\n- cmd/bd/show.go\n- cmd/bd/update.go\n- cmd/bd/close.go\n- cmd/bd/reopen.go\n- cmd/bd/dep.go\n- cmd/bd/label.go\n- cmd/bd/comment.go\n- cmd/bd/list.go\n\n## Testing\n- Test prefix omitted: a3f8e9 → bd-a3f8e9\n- Test prefix included: bd-a3f8e9 → bd-a3f8e9\n- Test hierarchical: a3f8e9.1.2 → bd-a3f8e9.1.2\n- Test partial match: a3f8 → bd-a3f8e9\n- Test ambiguous ID error\n- Test not found error\n- Test mixed input: bd show a3f8e9 bd-b7c2d1","notes":"Implementation complete for CLI direct mode. All read and write commands now accept issue IDs with or without prefix (e.g., '170' or 'bd-a5e2bd80.3').\n\nCompleted:\n- Created internal/utils/id_parser.go with ParseIssueID and ResolvePartialID\n- Updated all CLI commands (show, update, close, reopen, dep, label, comments)\n- Added comprehensive tests for ID parsing\n- Works with both sequential IDs (current) and hierarchical IDs (for future hash implementation)\n\nNote: RPC/daemon mode still needs prefix in current implementation. RPC handlers need update (bd-a5e2bd80.9 will handle this as part of hash ID rollout).","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-30T17:00:11.078468-07:00","updated_at":"2025-10-30T17:05:26.033272-07:00","closed_at":"2025-10-30T15:22:04.984996-07:00","dependencies":[{"issue_id":"bd-a5e2bd80.3","depends_on_id":"bd-a5e2bd80","type":"parent-child","created_at":"2025-10-30T17:00:11.09279-07:00","created_by":"import-remap"},{"issue_id":"bd-a5e2bd80.3","depends_on_id":"bd-a5e2bd80.13","type":"blocks","created_at":"2025-10-30T17:00:11.093202-07:00","created_by":"import-remap"},{"issue_id":"bd-a5e2bd80.3","depends_on_id":"bd-a5e2bd80.4","type":"blocks","created_at":"2025-10-30T17:00:11.093596-07:00","created_by":"import-remap"}]} @@ -136,7 +136,7 @@ {"id":"bd-a5e2bd80.6","content_hash":"e5e68e05a19b8e08b51a6d91cda937b5a5006651d6db7aa47d9ad43473b98a2f","title":"Design hash ID generation algorithm","description":"Design and specify the hash-based ID generation algorithm.\n\n## Requirements\n- Deterministic: same inputs → same ID\n- Collision-resistant: ~2^32 space for 8-char hex\n- Fast: \u003c1μs per generation\n- Includes timestamp for uniqueness\n- Includes creator/workspace for distributed uniqueness\n\n## Proposed Algorithm\n```go\nfunc GenerateIssueID(title, desc string, created time.Time, workspaceID string) string {\n h := sha256.New()\n h.Write([]byte(title))\n h.Write([]byte(desc))\n h.Write([]byte(created.Format(time.RFC3339Nano)))\n h.Write([]byte(workspaceID))\n hash := hex.EncodeToString(h.Sum(nil))\n return \"bd-\" + hash[:8] // 8-char prefix = 2^32 space\n}\n```\n\n## Open Questions\n1. 8 chars (2^32) or 16 chars (2^64) for collision resistance?\n2. Include priority/type in hash? (Pro: more entropy. Con: immutable)\n3. How to handle workspace ID generation? (hostname? UUID?)\n4. What if title+desc change? (Answer: ID stays same - hash only used at creation)\n\n## Deliverables\n- Design doc: docs/HASH_ID_DESIGN.md\n- Collision probability analysis\n- Performance benchmarks\n- Prototype implementation in internal/types/id_generator.go","notes":"## Next Session: Continue bd-0953ea41\n\nWe've completed:\n- ✅ bd-a5e2bd80.14: Hash ID algorithm (returns full 64-char hash)\n- ✅ bd-a5e2bd80.13: child_counters table + getNextChildNumber()\n- ✅ Docs updated for 6-char progressive design\n\n**TODO for bd-0953ea41:**\nImplement progressive collision retry in CreateIssue():\n1. Try hash[:6] first (bd-a3f2dd)\n2. On UNIQUE constraint → try hash[:7] (bd-a3f2dda) \n3. On collision again → try hash[:8] (bd-a3f2dda8)\n4. Max 3 attempts, then error\n\nLocation: internal/storage/sqlite/sqlite.go CreateIssue() around line 748\nPattern: Detect sqlite UNIQUE constraint error, retry with longer hash\n\nSee: internal/types/id_generator.go GenerateHashID() - now returns full hash","status":"in_progress","priority":1,"issue_type":"task","created_at":"2025-10-30T17:00:11.075356-07:00","updated_at":"2025-10-30T17:05:26.032243-07:00","dependencies":[{"issue_id":"bd-a5e2bd80.6","depends_on_id":"bd-a5e2bd80","type":"parent-child","created_at":"2025-10-30T17:00:11.082653-07:00","created_by":"import-remap"}]} {"id":"bd-a5e2bd80.7","content_hash":"c2041472fbce7fbe3fc32be28f61d276ea725f649344c87a1fca0f9c054999b3","title":"Implement hash ID generation in CreateIssue","description":"Implement hash ID generation in CreateIssue function.\n\n## For Top-Level Issues\n```go\nfunc generateHashID(prefix, title, description, creator string, timestamp time.Time) string {\n content := fmt.Sprintf(\"%s|%s|%s|%d\", title, description, creator, timestamp.UnixNano())\n hash := sha256.Sum256([]byte(content))\n shortHash := hex.EncodeToString(hash[:4]) // 8 hex chars\n return fmt.Sprintf(\"%s-%s\", prefix, shortHash)\n}\n```\n\n## For Child Issues\n```go\nfunc (s *SQLiteStorage) createChildIssue(parentID string, issue *types.Issue) error {\n // Validate parent exists and depth \u003c= 3\n childNum := s.getNextChildID(parentID)\n issue.ID = fmt.Sprintf(\"%s.%d\", parentID, childNum)\n // ... create issue\n}\n```\n\n## CLI Integration\n```bash\nbd create \"Auth System\" # → bd-a3f8e9a2\nbd create \"Login Flow\" --parent a3f8e9 # → bd-a3f8e9a2.1\nbd create \"Design UI\" --parent a3f8e9.1 # → bd-a3f8e9a2.1.1\n```\n\n## Validation\n- Reject depth \u003e 3\n- Ensure parent exists\n- Check parent is epic type (optional, for UX)","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-30T14:22:59.345449-07:00","updated_at":"2025-10-30T17:05:26.019075-07:00","closed_at":"2025-10-30T14:12:17.327987-07:00","dependencies":[{"issue_id":"bd-a5e2bd80.7","depends_on_id":"bd-a5e2bd80","type":"parent-child","created_at":"2025-10-30T14:22:59.346356-07:00","created_by":"import-remap"},{"issue_id":"bd-a5e2bd80.7","depends_on_id":"bd-a5e2bd80.14","type":"blocks","created_at":"2025-10-30T14:22:59.346967-07:00","created_by":"import-remap"},{"issue_id":"bd-a5e2bd80.7","depends_on_id":"bd-a5e2bd80.6","type":"blocks","created_at":"2025-10-30T17:00:11.08975-07:00","created_by":"import-remap"}]} {"id":"bd-a5e2bd80.8","content_hash":"4b348a47b8819c70bed5407a8a785605238738f5e56e344a8140a13de2c5dfd8","title":"Dogfood: Migrate beads repo to hash IDs","description":"Final validation: migrate the beads project itself to hash-based IDs.\n\n## Purpose\nDogfooding the migration on beads' own issue database to:\n1. Validate migration tool works on real data\n2. Discover edge cases\n3. Verify all workflows still work\n4. Build confidence for users\n\n## Pre-Migration Checklist\n- [ ] All bd-a5e2bd80 child tasks completed\n- [ ] All tests pass: `go test ./...`\n- [ ] Migration tool tested on test databases\n- [ ] Documentation updated\n- [ ] MCP server updated and published\n- [ ] Clean git status\n\n## Migration Steps\n\n### 1. Create Backup\n```bash\n# Backup database\ncp -r .beads .beads.backup-1761798568\n\n# Backup JSONL\ncp .beads/beads.jsonl .beads/beads.jsonl.backup\n\n# Create git branch for migration\ngit checkout -b hash-id-migration\ngit add .beads.backup-*\ngit commit -m \"Pre-migration backup\"\n```\n\n### 2. Run Migration (Dry Run)\n```bash\nbd migrate --hash-ids --dry-run \u003e migration-plan.txt\ncat migration-plan.txt\n\n# Review:\n# - Number of issues to migrate\n# - Hash collision check (should be zero)\n# - Text reference updates\n# - Dependency updates\n```\n\n### 3. Run Migration (Real)\n```bash\nbd migrate --hash-ids 2\u003e\u00261 | tee migration-log.txt\n\n# Expected output:\n# ✓ Backup created: .beads/beads.db.backup-1234567890\n# ✓ Generated 150 hash IDs\n# ✓ No hash collisions detected\n# ✓ Updated issues table schema\n# ✓ Updated 150 issue IDs\n# ✓ Updated 87 dependencies\n# ✓ Updated 234 text references\n# ✓ Exported to .beads/beads.jsonl\n# ✓ Migration complete!\n```\n\n### 4. Validation\n\n#### Database Integrity\n```bash\n# Check all issues have hash IDs\nbd list | grep -v \"bd-[a-f0-9]\\{8\\}\" \u0026\u0026 echo \"FAIL: Non-hash IDs found\"\n\n# Check all issues have aliases\nsqlite3 .beads/beads.db \"SELECT COUNT(*) FROM issues WHERE alias IS NULL\"\n# Should be 0\n\n# Check no alias duplicates\nsqlite3 .beads/beads.db \"SELECT alias, COUNT(*) FROM issues GROUP BY alias HAVING COUNT(*) \u003e 1\"\n# Should be empty\n```\n\n#### Functionality Tests\n```bash\n# Test show by hash ID\nbd show bd-\n\n# Test show by alias\nbd show #1\n\n# Test create new issue\nbd create \"Test issue after migration\" -p 2\n# Should get hash ID + alias\n\n# Test update\nbd update #1 --priority 1\n\n# Test dependencies\nbd dep tree #1\n\n# Test export\nbd export\ngit diff .beads/beads.jsonl\n# Should show hash IDs\n```\n\n#### Text Reference Validation\n```bash\n# Check that old IDs were updated in descriptions\ngrep -r \"bd-[0-9]\\{1,3\\}[^a-f0-9]\" .beads/beads.jsonl \u0026\u0026 echo \"FAIL: Old ID format found\"\n\n# Verify hash ID references exist\ngrep -o \"bd-[a-f0-9]\\{8\\}\" .beads/beads.jsonl | sort -u | wc -l\n# Should match number of hash IDs\n```\n\n### 5. Commit Migration\n```bash\ngit add .beads/beads.jsonl .beads/beads.db\ngit commit -m \"Migrate to hash-based IDs (v2.0)\n\n- Migrated 150 issues to hash IDs\n- Preserved aliases (#1-#150)\n- Updated 87 dependencies\n- Updated 234 text references\n- Zero hash collisions\n\nMigration log: migration-log.txt\"\n\ngit push origin hash-id-migration\n```\n\n### 6. Create PR\n```bash\ngh pr create --title \"Migrate to hash-based IDs (v2.0)\" --body \"## Summary\nMigrates beads project to hash-based IDs as part of v2.0 release.\n\n## Migration Stats\n- Issues migrated: 150\n- Dependencies updated: 87\n- Text references updated: 234\n- Hash collisions: 0\n- Aliases assigned: 150\n\n## Validation\n- ✅ All tests pass\n- ✅ Database integrity verified\n- ✅ All workflows tested (show, update, create, deps)\n- ✅ Text references updated correctly\n- ✅ Export produces valid JSONL\n\n## Files Changed\n- `.beads/beads.jsonl` - Hash IDs in all entries\n- `.beads/beads.db` - Schema updated with aliases\n\n## Rollback\nIf issues arise:\n\\`\\`\\`bash\nmv .beads.backup-1234567890 .beads\nbd export\n\\`\\`\\`\n\nSee migration-log.txt for full details.\"\n```\n\n### 7. Merge and Cleanup\n```bash\n# After PR approval\ngit checkout main\ngit merge hash-id-migration\ngit push origin main\n\n# Tag release\ngit tag v2.0.0\ngit push origin v2.0.0\n\n# Cleanup\nrm migration-log.txt migration-plan.txt\ngit checkout .beads.backup-* # Keep in git history\n```\n\n## Rollback Procedure\nIf migration fails or has issues:\n\n```bash\n# Restore backup\nmv .beads .beads.failed-migration\nmv .beads.backup-1234567890 .beads\n\n# Regenerate JSONL\nbd export\n\n# Verify restoration\nbd list\ngit diff .beads/beads.jsonl\n\n# Cleanup\ngit checkout hash-id-migration\ngit reset --hard main\n```\n\n## Post-Migration Communication\n\n### GitHub Issue/Discussion\n```markdown\n## Beads v2.0 Released: Hash-Based IDs\n\nWe've migrated beads to hash-based IDs! 🎉\n\n**What changed:**\n- Issues now use hash IDs (bd-af78e9a2) instead of sequential (bd-cb64c226.3)\n- Human-friendly aliases (#42) for easy reference\n- Zero collision risk in distributed workflows\n\n**Action required:**\nIf you have a local clone, you need to migrate:\n\n\\`\\`\\`bash\ngit pull origin main\nbd migrate --hash-ids\ngit push origin main\n\\`\\`\\`\n\nSee MIGRATION.md for details.\n\n**Benefits:**\n- ✅ No more ID collisions\n- ✅ Work offline without coordination\n- ✅ Simpler codebase (-2,100 LOC)\n\nQuestions? Reply here or see docs/HASH_IDS.md\n```\n\n## Success Criteria\n- [ ] Migration completes without errors\n- [ ] All validation checks pass\n- [ ] PR merged to main\n- [ ] v2.0.0 tagged and released\n- [ ] Documentation updated\n- [ ] Community notified\n- [ ] No rollback needed within 1 week\n\n## Files to Create\n- migration-log.txt (transient)\n- migration-plan.txt (transient)\n\n## Timeline\nExecute after all other bd-a5e2bd80 tasks complete (estimated: ~8 weeks from start)","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-29T21:29:28.591526-07:00","updated_at":"2025-10-31T01:58:36.015649-07:00","closed_at":"2025-10-31T01:58:36.015649-07:00","dependencies":[{"issue_id":"bd-a5e2bd80.8","depends_on_id":"bd-a5e2bd80","type":"parent-child","created_at":"2025-10-29T21:29:28.59248-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.8","depends_on_id":"bd-6eecc9be","type":"blocks","created_at":"2025-10-29T21:29:28.593033-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.8","depends_on_id":"bd-a5e2bd80.10","type":"blocks","created_at":"2025-10-29T21:29:28.593437-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.8","depends_on_id":"bd-a5e2bd80.15","type":"blocks","created_at":"2025-10-29T21:29:28.593876-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.8","depends_on_id":"bd-a5e2bd80.9","type":"blocks","created_at":"2025-10-29T21:29:28.594521-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.8","depends_on_id":"bd-a5e2bd80.1","type":"blocks","created_at":"2025-10-30T17:00:11.085763-07:00","created_by":"import-remap"}]} -{"id":"bd-a5e2bd80.9","content_hash":"cf15dbd5b7dbc10b54ea8d0d173899983bcdc0775b799e77c9334ec3f9fcba14","title":"Update MCP server for hash IDs","description":"Update beads-mcp server to support hash IDs and aliases.\n\n## Changes Needed\n\n### 1. MCP Function Signatures (No Change)\nFunctions already use issue IDs as strings, so they work with hash IDs:\n\n```python\n# These already work!\nbeads_show(issue_id: str) # Accepts bd-af78e9a2 or #42\nbeads_update(issue_id: str, ...) # Accepts both formats\nbeads_close(issue_ids: List[str]) # Accepts both formats\n```\n\n### 2. Add Alias Resolution Helper\nFile: integrations/beads-mcp/src/beads_mcp/server.py\n\n```python\ndef resolve_issue_id(issue_id: str) -\u003e str:\n \"\"\"Resolve alias to hash ID if needed.\"\"\"\n # Hash ID: pass through\n if issue_id.startswith('bd-') and len(issue_id) == 11:\n return issue_id\n \n # Alias: #42 or 42\n alias_str = issue_id.lstrip('#')\n try:\n alias = int(alias_str)\n # Call bd to resolve\n result = subprocess.run(\n ['bd', 'alias', 'find', f'bd-{alias}'],\n capture_output=True, text=True\n )\n if result.returncode == 0:\n return result.stdout.strip()\n except ValueError:\n pass\n \n # Invalid format\n raise ValueError(f\"Invalid issue ID: {issue_id}\")\n```\n\n### 3. Update Response Formatting\nShow aliases in responses:\n\n```python\n@server.call_tool()\nasync def beads_show(issue_id: str) -\u003e List[TextContent]:\n resolved_id = resolve_issue_id(issue_id)\n \n result = subprocess.run(['bd', 'show', resolved_id], ...)\n \n # Parse response and add alias info\n # Format: \"bd-af78e9a2 (alias: #42)\"\n ...\n```\n\n### 4. Add beads_alias_* Functions\n\n```python\n@server.call_tool()\nasync def beads_alias_list() -\u003e List[TextContent]:\n \"\"\"List all alias mappings.\"\"\"\n result = subprocess.run(['bd', 'alias', 'list'], ...)\n return [TextContent(type=\"text\", text=result.stdout)]\n\n@server.call_tool()\nasync def beads_alias_set(alias: int, issue_id: str) -\u003e List[TextContent]:\n \"\"\"Manually assign alias to issue.\"\"\"\n result = subprocess.run(['bd', 'alias', 'set', str(alias), issue_id], ...)\n return [TextContent(type=\"text\", text=result.stdout)]\n\n@server.call_tool()\nasync def beads_alias_compact() -\u003e List[TextContent]:\n \"\"\"Compact aliases to fill gaps.\"\"\"\n result = subprocess.run(['bd', 'alias', 'compact'], ...)\n return [TextContent(type=\"text\", text=result.stdout)]\n```\n\n### 5. Update Documentation\nFile: integrations/beads-mcp/README.md\n\n```markdown\n## Issue IDs (v2.0+)\n\nThe MCP server accepts both hash IDs and aliases:\n\n```python\n# Using hash IDs\nawait beads_show(issue_id=\"bd-af78e9a2\")\n\n# Using aliases\nawait beads_show(issue_id=\"#42\")\nawait beads_show(issue_id=\"42\") # Shorthand\n```\n\n## Alias Management\n\nNew functions for alias control:\n\n- `beads_alias_list()` - List all alias mappings\n- `beads_alias_set(alias, issue_id)` - Manually assign alias\n- `beads_alias_compact()` - Compact aliases to fill gaps\n\n## Migration\n\nAfter migrating to hash IDs:\n1. Update beads-mcp: `pip install --upgrade beads-mcp`\n2. Restart MCP server\n3. All existing workflows continue to work\n```\n\n### 6. Version Compatibility\nDetect and handle both v1.x and v2.0 formats:\n\n```python\ndef detect_beads_version() -\u003e str:\n \"\"\"Detect if beads is using sequential or hash IDs.\"\"\"\n result = subprocess.run(['bd', 'list', '-n', '1'], ...)\n first_id = parse_first_issue_id(result.stdout)\n \n if first_id.startswith('bd-') and len(first_id) \u003e 11:\n return '2.0' # Hash ID\n else:\n return '1.x' # Sequential ID\n\n# On startup\nbeads_version = detect_beads_version()\nlogger.info(f\"Detected beads version: {beads_version}\")\n```\n\n## Testing\n\n### Unit Tests\nFile: integrations/beads-mcp/tests/test_hash_ids.py\n\n```python\ndef test_resolve_hash_id():\n \"\"\"Hash IDs pass through unchanged.\"\"\"\n assert resolve_issue_id(\"bd-af78e9a2\") == \"bd-af78e9a2\"\n\ndef test_resolve_alias():\n \"\"\"Aliases resolve to hash IDs.\"\"\"\n # Mock bd alias find command\n assert resolve_issue_id(\"#42\") == \"bd-af78e9a2\"\n assert resolve_issue_id(\"42\") == \"bd-af78e9a2\"\n\ndef test_invalid_id():\n \"\"\"Invalid IDs raise ValueError.\"\"\"\n with pytest.raises(ValueError):\n resolve_issue_id(\"invalid\")\n```\n\n### Integration Tests\n```python\nasync def test_show_with_hash_id(mcp_server):\n result = await mcp_server.beads_show(issue_id=\"bd-af78e9a2\")\n assert \"bd-af78e9a2\" in result[0].text\n\nasync def test_show_with_alias(mcp_server):\n result = await mcp_server.beads_show(issue_id=\"#42\")\n assert \"bd-af78e9a2\" in result[0].text # Resolved\n```\n\n## Backward Compatibility\nThe MCP server should work with both:\n- Beads v1.x (sequential IDs)\n- Beads v2.0+ (hash IDs)\n\nDetection happens at runtime based on issue ID format.\n\n## Files to Modify\n- integrations/beads-mcp/src/beads_mcp/server.py\n- integrations/beads-mcp/README.md\n- integrations/beads-mcp/tests/test_hash_ids.py (new)\n- integrations/beads-mcp/pyproject.toml (bump version)\n\n## Deployment\n```bash\ncd integrations/beads-mcp\n# Bump version to 2.0.0\npoetry version 2.0.0\n# Publish to PyPI\npoetry publish --build\n```","status":"open","priority":1,"issue_type":"task","created_at":"2025-10-29T21:28:45.256074-07:00","updated_at":"2025-10-30T17:05:26.015489-07:00","dependencies":[{"issue_id":"bd-a5e2bd80.9","depends_on_id":"bd-a5e2bd80","type":"parent-child","created_at":"2025-10-29T21:28:45.257315-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.9","depends_on_id":"bd-67335f92","type":"blocks","created_at":"2025-10-29T21:28:45.258057-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.9","depends_on_id":"bd-a5e2bd80.3","type":"blocks","created_at":"2025-10-30T17:00:11.092331-07:00","created_by":"import-remap"}]} +{"id":"bd-a5e2bd80.9","content_hash":"cf15dbd5b7dbc10b54ea8d0d173899983bcdc0775b799e77c9334ec3f9fcba14","title":"Update MCP server for hash IDs","description":"Update beads-mcp server to support hash IDs and aliases.\n\n## Changes Needed\n\n### 1. MCP Function Signatures (No Change)\nFunctions already use issue IDs as strings, so they work with hash IDs:\n\n```python\n# These already work!\nbeads_show(issue_id: str) # Accepts bd-af78e9a2 or #42\nbeads_update(issue_id: str, ...) # Accepts both formats\nbeads_close(issue_ids: List[str]) # Accepts both formats\n```\n\n### 2. Add Alias Resolution Helper\nFile: integrations/beads-mcp/src/beads_mcp/server.py\n\n```python\ndef resolve_issue_id(issue_id: str) -\u003e str:\n \"\"\"Resolve alias to hash ID if needed.\"\"\"\n # Hash ID: pass through\n if issue_id.startswith('bd-') and len(issue_id) == 11:\n return issue_id\n \n # Alias: #42 or 42\n alias_str = issue_id.lstrip('#')\n try:\n alias = int(alias_str)\n # Call bd to resolve\n result = subprocess.run(\n ['bd', 'alias', 'find', f'bd-{alias}'],\n capture_output=True, text=True\n )\n if result.returncode == 0:\n return result.stdout.strip()\n except ValueError:\n pass\n \n # Invalid format\n raise ValueError(f\"Invalid issue ID: {issue_id}\")\n```\n\n### 3. Update Response Formatting\nShow aliases in responses:\n\n```python\n@server.call_tool()\nasync def beads_show(issue_id: str) -\u003e List[TextContent]:\n resolved_id = resolve_issue_id(issue_id)\n \n result = subprocess.run(['bd', 'show', resolved_id], ...)\n \n # Parse response and add alias info\n # Format: \"bd-af78e9a2 (alias: #42)\"\n ...\n```\n\n### 4. Add beads_alias_* Functions\n\n```python\n@server.call_tool()\nasync def beads_alias_list() -\u003e List[TextContent]:\n \"\"\"List all alias mappings.\"\"\"\n result = subprocess.run(['bd', 'alias', 'list'], ...)\n return [TextContent(type=\"text\", text=result.stdout)]\n\n@server.call_tool()\nasync def beads_alias_set(alias: int, issue_id: str) -\u003e List[TextContent]:\n \"\"\"Manually assign alias to issue.\"\"\"\n result = subprocess.run(['bd', 'alias', 'set', str(alias), issue_id], ...)\n return [TextContent(type=\"text\", text=result.stdout)]\n\n@server.call_tool()\nasync def beads_alias_compact() -\u003e List[TextContent]:\n \"\"\"Compact aliases to fill gaps.\"\"\"\n result = subprocess.run(['bd', 'alias', 'compact'], ...)\n return [TextContent(type=\"text\", text=result.stdout)]\n```\n\n### 5. Update Documentation\nFile: integrations/beads-mcp/README.md\n\n```markdown\n## Issue IDs (v2.0+)\n\nThe MCP server accepts both hash IDs and aliases:\n\n```python\n# Using hash IDs\nawait beads_show(issue_id=\"bd-af78e9a2\")\n\n# Using aliases\nawait beads_show(issue_id=\"#42\")\nawait beads_show(issue_id=\"42\") # Shorthand\n```\n\n## Alias Management\n\nNew functions for alias control:\n\n- `beads_alias_list()` - List all alias mappings\n- `beads_alias_set(alias, issue_id)` - Manually assign alias\n- `beads_alias_compact()` - Compact aliases to fill gaps\n\n## Migration\n\nAfter migrating to hash IDs:\n1. Update beads-mcp: `pip install --upgrade beads-mcp`\n2. Restart MCP server\n3. All existing workflows continue to work\n```\n\n### 6. Version Compatibility\nDetect and handle both v1.x and v2.0 formats:\n\n```python\ndef detect_beads_version() -\u003e str:\n \"\"\"Detect if beads is using sequential or hash IDs.\"\"\"\n result = subprocess.run(['bd', 'list', '-n', '1'], ...)\n first_id = parse_first_issue_id(result.stdout)\n \n if first_id.startswith('bd-') and len(first_id) \u003e 11:\n return '2.0' # Hash ID\n else:\n return '1.x' # Sequential ID\n\n# On startup\nbeads_version = detect_beads_version()\nlogger.info(f\"Detected beads version: {beads_version}\")\n```\n\n## Testing\n\n### Unit Tests\nFile: integrations/beads-mcp/tests/test_hash_ids.py\n\n```python\ndef test_resolve_hash_id():\n \"\"\"Hash IDs pass through unchanged.\"\"\"\n assert resolve_issue_id(\"bd-af78e9a2\") == \"bd-af78e9a2\"\n\ndef test_resolve_alias():\n \"\"\"Aliases resolve to hash IDs.\"\"\"\n # Mock bd alias find command\n assert resolve_issue_id(\"#42\") == \"bd-af78e9a2\"\n assert resolve_issue_id(\"42\") == \"bd-af78e9a2\"\n\ndef test_invalid_id():\n \"\"\"Invalid IDs raise ValueError.\"\"\"\n with pytest.raises(ValueError):\n resolve_issue_id(\"invalid\")\n```\n\n### Integration Tests\n```python\nasync def test_show_with_hash_id(mcp_server):\n result = await mcp_server.beads_show(issue_id=\"bd-af78e9a2\")\n assert \"bd-af78e9a2\" in result[0].text\n\nasync def test_show_with_alias(mcp_server):\n result = await mcp_server.beads_show(issue_id=\"#42\")\n assert \"bd-af78e9a2\" in result[0].text # Resolved\n```\n\n## Backward Compatibility\nThe MCP server should work with both:\n- Beads v1.x (sequential IDs)\n- Beads v2.0+ (hash IDs)\n\nDetection happens at runtime based on issue ID format.\n\n## Files to Modify\n- integrations/beads-mcp/src/beads_mcp/server.py\n- integrations/beads-mcp/README.md\n- integrations/beads-mcp/tests/test_hash_ids.py (new)\n- integrations/beads-mcp/pyproject.toml (bump version)\n\n## Deployment\n```bash\ncd integrations/beads-mcp\n# Bump version to 2.0.0\npoetry version 2.0.0\n# Publish to PyPI\npoetry publish --build\n```","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-29T21:28:45.256074-07:00","updated_at":"2025-10-31T12:48:26.908204-07:00","closed_at":"2025-10-31T12:48:26.908204-07:00","dependencies":[{"issue_id":"bd-a5e2bd80.9","depends_on_id":"bd-a5e2bd80","type":"parent-child","created_at":"2025-10-29T21:28:45.257315-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.9","depends_on_id":"bd-67335f92","type":"blocks","created_at":"2025-10-29T21:28:45.258057-07:00","created_by":"stevey"},{"issue_id":"bd-a5e2bd80.9","depends_on_id":"bd-a5e2bd80.3","type":"blocks","created_at":"2025-10-30T17:00:11.092331-07:00","created_by":"import-remap"}]} {"id":"bd-a6abe1c7","content_hash":"2a102864134b5192b5ee4e2a773cb4860b4330c9f3242b094ce8e92b01d20d80","title":"Implement hierarchical child ID generation","description":"Implement sequential child ID generation within parent contexts.\n\n## Function Signature\n```go\nfunc (s *SQLiteStorage) getNextChildID(ctx context.Context, parentID string) (string, error)\n```\n\n## Logic\n1. Insert or update child_counters for parent_id\n2. Return incremented counter\n3. Format as parentID.{counter}\n4. Works at any depth (bd-a3f8e9.1 → bd-a3f8e9.1.5)\n\n## Collision Handling\n- In single-player mode: No collisions (sequential)\n- In multi-player mode (future): Rare collisions, manual resolution needed\n- Epic ownership makes collisions naturally rare\n\n## Integration\n- Called from CreateIssue when --parent flag is used\n- Validates parent exists and depth \u003c= 3","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-30T17:00:11.081086-07:00","updated_at":"2025-10-31T01:00:21.975536-07:00","closed_at":"2025-10-31T01:00:21.975536-07:00"} {"id":"bd-a6bf","content_hash":"b058f09aff2b2d4815d3049594f99d6413f15df7cf68312d28842ca0afb8bdb3","title":"Test Epic","description":"","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-10-31T12:00:30.155159-07:00","updated_at":"2025-10-31T12:01:34.540363-07:00","closed_at":"2025-10-31T12:01:34.540363-07:00"} {"id":"bd-a8b6","content_hash":"6c1f879d04bd7cde0cce6054137fc4bcdb25c96e9d3aad84f61dfbffe408df4d","title":"Remove collision resolution system (~724 LOC in collision.go)","description":"The entire collision resolution system can be removed now that hash IDs eliminate real collisions:\n- internal/storage/sqlite/collision.go (724 lines)\n- Functions: DetectCollisions, ScoreCollisions, RemapCollisions, remapCollisionsOnce\n- Types: CollisionResult, CollisionDetail, RenameDetail\n- Hash-based IDs only have accidental collisions that are resolved by adding another character from hash\n- This removes ~2,100 LOC of complex collision resolution logic mentioned in HASH_ID_DESIGN.md","notes":"Completed: Removed collision.go code from 704 → 138 lines. Deleted RemapCollisions, ScoreCollisions, and all remapping logic. Only DetectCollisions remains for idempotent import checking.","status":"closed","priority":1,"issue_type":"chore","created_at":"2025-10-30T22:16:48.499527-07:00","updated_at":"2025-10-31T00:48:17.498074-07:00","closed_at":"2025-10-31T00:48:17.498074-07:00","labels":["cleanup","hash-ids","major-refactor"]}