From a5f5fcbee507868cd7f1119b4fd21eb468452fbc Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Thu, 30 Oct 2025 17:00:15 -0700 Subject: [PATCH] bd sync: 2025-10-30 17:00:15 --- .beads/beads.jsonl | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/.beads/beads.jsonl b/.beads/beads.jsonl index 25bd586d..4e06d7ce 100644 --- a/.beads/beads.jsonl +++ b/.beads/beads.jsonl @@ -59,20 +59,20 @@ {"id":"bd-163","content_hash":"6440d1ece0a91c8f49adc09aafa7a998b049bcd51f257125ad8bc0b7b03e317b","title":"Update AGENTS.md with event-driven mode","description":"Document BEADS_DAEMON_MODE env var. Explain opt-in during Phase 1. Add troubleshooting for watcher failures.","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-29T23:05:13.986452-07:00","updated_at":"2025-10-29T23:05:13.986452-07:00","dependencies":[{"issue_id":"bd-163","depends_on_id":"bd-164","type":"parent-child","created_at":"2025-10-29T21:19:36.206187-07:00","created_by":"import-remap"}]} {"id":"bd-164","content_hash":"e246bdc448f3780a929c66c8f0c495a2044ab6c810a1af9810310df306269f6b","title":"Event-driven daemon architecture","description":"Replace 5-second polling sync loop with event-driven architecture that reacts instantly to changes. Eliminates stale data issues while reducing CPU ~60%. Key components: FileWatcher (fsnotify), Debouncer (500ms), RPC mutation events, optional git hooks. Target latency: \u003c500ms (vs 5000ms). See event_driven_daemon.md for full design.","notes":"## Implementation Progress\n\n**Completed:**\n1. ✅ Mutation events infrastructure (bd-143 equivalent)\n - MutationEvent channel in RPC server\n - Events emitted for all write operations: create, update, close, label add/remove, dep add/remove, comment add\n - Non-blocking emission with dropped event counter\n\n2. ✅ FileWatcher with fsnotify (bd-141 related)\n - Watches .beads/issues.jsonl and .git/refs/heads\n - 500ms debounce\n - Polling fallback if fsnotify unavailable\n\n3. ✅ Debouncer (bd-144 equivalent)\n - 500ms debounce for both export and import triggers\n - Thread-safe trigger/cancel\n\n4. ✅ Separate export-only and import-only functions\n - createExportFunc(): exports + optional commit/push (no pull/import)\n - createAutoImportFunc(): pull + import (no export)\n - Target latency \u003c500ms achieved by avoiding full sync\n\n5. ✅ Dropped events safety net (bd-83 related)\n - Atomic counter tracks dropped mutation events\n - 60-second health check triggers export if events were dropped\n - Prevents silent data loss from event storms\n\n**Still Needed:**\n- Platform-specific tests (bd-139)\n- Integration test for mutation→export latency (bd-140)\n- Unit tests for FileWatcher (bd-141)\n- Unit tests for Debouncer (bd-144)\n- Event storm stress test (bd-83)\n- Documentation update (bd-142)\n\n**Next Steps:**\nAdd comprehensive test coverage before enabling events mode by default.","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-10-29T21:19:36.203436-07:00","updated_at":"2025-10-29T21:19:36.203436-07:00","closed_at":"2025-10-29T15:53:34.022335-07:00"} {"id":"bd-165","content_hash":"d429410e478f428289b91d4fd258797d1140adf105b54a05fb6b7fa62c91f67f","title":"Hash-based IDs with hierarchical children","description":"Replace sequential auto-increment IDs (bd-1, bd-2) 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-108, bd-109)\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-74 (cleanup validation).","status":"open","priority":1,"issue_type":"epic","created_at":"2025-10-29T21:23:49.592315-07:00","updated_at":"2025-10-30T00:32:21.431272-07:00"} -{"id":"bd-166","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-168\n\nWe've completed:\n- ✅ bd-166: Hash ID algorithm (returns full 64-char hash)\n- ✅ bd-167: child_counters table + getNextChildNumber()\n- ✅ Docs updated for 6-char progressive design\n\n**TODO for bd-168:**\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-29T21:24:01.843634-07:00","updated_at":"2025-10-30T14:04:13.585075-07:00","dependencies":[{"issue_id":"bd-166","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-29T21:24:01.844994-07:00","created_by":"stevey"}]} -{"id":"bd-167","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-30T13:32:05.83292-07:00","closed_at":"2025-10-30T13:32:05.83292-07:00","dependencies":[{"issue_id":"bd-167","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-29T21:24:13.96959-07:00","created_by":"stevey"},{"issue_id":"bd-167","depends_on_id":"bd-166","type":"blocks","created_at":"2025-10-29T21:29:45.952824-07:00","created_by":"stevey"}]} -{"id":"bd-168","content_hash":"1a53798d7a2eaf014f90a399745beb62b4bb265c9d03713f0b00dbc54c3073e2","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)","notes":"Work completed on feature/hash-ids branch. Reverted from main to avoid breaking changes. Will merge after migration strategy (bd-173) is ready.","status":"open","priority":1,"issue_type":"task","created_at":"2025-10-29T21:24:29.412237-07:00","updated_at":"2025-10-30T14:17:14.485149-07:00","dependencies":[{"issue_id":"bd-168","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-29T21:24:29.413417-07:00","created_by":"stevey"},{"issue_id":"bd-168","depends_on_id":"bd-166","type":"blocks","created_at":"2025-10-29T21:24:29.413823-07:00","created_by":"stevey"}]} -{"id":"bd-169","content_hash":"c27f166d842efb8caa1c104b1eaf430e430b3da84cfb13a085cc732a713faaf9","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-1 → bd-{hash} with mapping preserved.","status":"open","priority":1,"issue_type":"task","created_at":"2025-10-29T21:24:47.408106-07:00","updated_at":"2025-10-30T00:25:26.029456-07:00","dependencies":[{"issue_id":"bd-169","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-29T21:24:47.409489-07:00","created_by":"stevey"},{"issue_id":"bd-169","depends_on_id":"bd-168","type":"blocks","created_at":"2025-10-29T21:24:47.409977-07:00","created_by":"stevey"},{"issue_id":"bd-169","depends_on_id":"bd-167","type":"blocks","created_at":"2025-10-29T21:29:45.975499-07:00","created_by":"stevey"}]} +{"id":"bd-166","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-5407\n\nWe've completed:\n- ✅ bd-5406: Hash ID algorithm (returns full 64-char hash)\n- ✅ bd-167: child_counters table + getNextChildNumber()\n- ✅ Docs updated for 6-char progressive design\n\n**TODO for bd-5407:**\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:00:11.103772-07:00","closed_at":"2025-10-30T15:42:04.615691-07:00","dependencies":[{"issue_id":"bd-166","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-29T21:24:01.844994-07:00","created_by":"stevey"}]} +{"id":"bd-167","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-30T13:32:05.83292-07:00","closed_at":"2025-10-30T13:32:05.83292-07:00","dependencies":[{"issue_id":"bd-167","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-29T21:24:13.96959-07:00","created_by":"stevey"},{"issue_id":"bd-167","depends_on_id":"bd-166","type":"blocks","created_at":"2025-10-29T21:29:45.952824-07:00","created_by":"stevey"},{"issue_id":"bd-167","depends_on_id":"bd-5406","type":"blocks","created_at":"2025-10-30T17:00:11.084198-07:00","created_by":"import-remap"}]} +{"id":"bd-168","content_hash":"ab47c2fc2ee3b5beef46c3159a07c374d2201472d6a57366f7bdf56dd0b3187b","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)","notes":"Work completed on feature/hash-ids branch. Reverted from main to avoid breaking changes. Will merge after migration strategy (bd-5411) is ready.","status":"open","priority":1,"issue_type":"task","created_at":"2025-10-30T17:00:11.077819-07:00","updated_at":"2025-10-30T17:00:11.100152-07:00"} +{"id":"bd-169","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-1 → 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-30T14:27:39.953114-07:00","closed_at":"2025-10-30T14:27:39.953114-07:00","dependencies":[{"issue_id":"bd-169","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-29T21:24:47.409489-07:00","created_by":"stevey"},{"issue_id":"bd-169","depends_on_id":"bd-168","type":"blocks","created_at":"2025-10-29T21:24:47.409977-07:00","created_by":"stevey"},{"issue_id":"bd-169","depends_on_id":"bd-167","type":"blocks","created_at":"2025-10-29T21:29:45.975499-07:00","created_by":"stevey"},{"issue_id":"bd-169","depends_on_id":"bd-192","type":"blocks","created_at":"2025-10-30T14:22:59.347557-07:00","created_by":"import-remap"}]} {"id":"bd-17","content_hash":"404b82a19dde2fdece7eb6bb3b816db7906e81a03a5a05341ed631af7a2a8e87","title":"Remove unreachable RPC methods","description":"Several RPC server and client methods are unreachable and should be removed:\n\nServer methods (internal/rpc/server.go):\n- `Server.GetLastImportTime` (line 2116)\n- `Server.SetLastImportTime` (line 2123)\n- `Server.findJSONLPath` (line 2255)\n\nClient methods (internal/rpc/client.go):\n- `Client.Import` (line 311) - RPC import not used (daemon uses autoimport)\n\nEvidence:\n```bash\ngo run golang.org/x/tools/cmd/deadcode@latest -test ./...\n```\n\nImpact: Removes ~80 LOC of unused RPC code","acceptance_criteria":"- Remove the 4 unreachable methods (~80 LOC total)\n- Verify no callers: `grep -r \"GetLastImportTime\\|SetLastImportTime\\|findJSONLPath\" .`\n- All tests pass: `go test ./internal/rpc/...`\n- Daemon functionality works: test daemon start/stop/operations","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-27T20:30:19.962209-07:00","updated_at":"2025-10-28T16:07:26.103703-07:00","closed_at":"2025-10-28T16:07:26.103703-07:00","labels":["cleanup","dead-code","phase-1","rpc"],"dependencies":[{"issue_id":"bd-17","depends_on_id":"bd-26","type":"parent-child","created_at":"2025-10-27T20:30:19.965239-07:00","created_by":"daemon"}]} -{"id":"bd-170","content_hash":"9a2120c5d56a818ae4d0b2acc3518b6705d62b6cb866703dd524e3bc1a462397","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","status":"open","priority":1,"issue_type":"task","created_at":"2025-10-29T21:25:06.256317-07:00","updated_at":"2025-10-30T00:32:47.510446-07:00","dependencies":[{"issue_id":"bd-170","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-29T21:25:06.257796-07:00","created_by":"stevey"},{"issue_id":"bd-170","depends_on_id":"bd-167","type":"blocks","created_at":"2025-10-29T21:25:06.258307-07:00","created_by":"stevey"},{"issue_id":"bd-170","depends_on_id":"bd-169","type":"blocks","created_at":"2025-10-29T21:29:45.993274-07:00","created_by":"stevey"}]} -{"id":"bd-171","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":"open","priority":1,"issue_type":"task","created_at":"2025-10-29T21:25:27.389191-07:00","updated_at":"2025-10-30T00:24:05.531466-07:00","dependencies":[{"issue_id":"bd-171","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-29T21:25:27.390611-07:00","created_by":"stevey"},{"issue_id":"bd-171","depends_on_id":"bd-167","type":"blocks","created_at":"2025-10-29T21:25:27.391127-07:00","created_by":"stevey"},{"issue_id":"bd-171","depends_on_id":"bd-169","type":"blocks","created_at":"2025-10-29T21:25:27.39154-07:00","created_by":"stevey"}]} -{"id":"bd-172","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-86)\n- `TestThreeCloneCollision` (bd-185)\n- `TestFiveCloneCollision` (bd-151)\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-86: Add test for symmetric collision\n--89: Content-hash collision resolution\n- bd-185: N-way collision resolution epic\n- bd-95: Add ScoreCollisions (already done but now unnecessary)\n- bd-96: Make DetectCollisions read-only\n- bd-97: ResolveNWayCollisions function\n- bd-98: Multi-round import convergence\n- bd-108: Multi-round convergence for N-way collisions\n- bd-109: Transaction + retry logic for collisions\n- bd-160: 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-171)\n- Duplicate detection (bd-59, bd-149) - different from ID collisions\n- Merge conflict resolution (bd-65, bd-103) - 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":"open","priority":1,"issue_type":"task","created_at":"2025-10-29T21:25:50.976383-07:00","updated_at":"2025-10-29T23:14:44.171339-07:00","dependencies":[{"issue_id":"bd-172","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-29T21:25:50.977857-07:00","created_by":"stevey"},{"issue_id":"bd-172","depends_on_id":"bd-168","type":"blocks","created_at":"2025-10-29T21:25:50.978395-07:00","created_by":"stevey"},{"issue_id":"bd-172","depends_on_id":"bd-169","type":"blocks","created_at":"2025-10-29T21:25:50.978842-07:00","created_by":"stevey"}]} -{"id":"bd-173","content_hash":"9d78f9471bf147696d5295cc89324d3486feb4bbe16c6e89524320fab229bcd1","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-1 → 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-5 (parent: bd-1) → 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":"open","priority":1,"issue_type":"task","created_at":"2025-10-29T21:26:24.563993-07:00","updated_at":"2025-10-30T00:26:03.862157-07:00","dependencies":[{"issue_id":"bd-173","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-29T21:26:24.565325-07:00","created_by":"stevey"},{"issue_id":"bd-173","depends_on_id":"bd-168","type":"blocks","created_at":"2025-10-29T21:26:24.565945-07:00","created_by":"stevey"}]} -{"id":"bd-174","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-30T00:25:24.186868-07:00","dependencies":[{"issue_id":"bd-174","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-29T21:26:53.753259-07:00","created_by":"stevey"},{"issue_id":"bd-174","depends_on_id":"bd-170","type":"blocks","created_at":"2025-10-29T21:26:53.753733-07:00","created_by":"stevey"},{"issue_id":"bd-174","depends_on_id":"bd-171","type":"blocks","created_at":"2025-10-29T21:26:53.754112-07:00","created_by":"stevey"}]} +{"id":"bd-170","content_hash":"9a2120c5d56a818ae4d0b2acc3518b6705d62b6cb866703dd524e3bc1a462397","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","status":"open","priority":1,"issue_type":"task","created_at":"2025-10-30T17:00:11.079825-07:00","updated_at":"2025-10-30T17:00:11.079825-07:00"} +{"id":"bd-171","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":"open","priority":1,"issue_type":"task","created_at":"2025-10-30T17:00:11.081086-07:00","updated_at":"2025-10-30T17:00:11.081086-07:00"} +{"id":"bd-172","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-86)\n- `TestThreeCloneCollision` (bd-185)\n- `TestFiveCloneCollision` (bd-151)\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-86: Add test for symmetric collision\n--89: Content-hash collision resolution\n- bd-185: N-way collision resolution epic\n- bd-95: Add ScoreCollisions (already done but now unnecessary)\n- bd-96: Make DetectCollisions read-only\n- bd-97: ResolveNWayCollisions function\n- bd-98: Multi-round import convergence\n- bd-108: Multi-round convergence for N-way collisions\n- bd-109: Transaction + retry logic for collisions\n- bd-160: 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-171)\n- Duplicate detection (bd-59, bd-149) - different from ID collisions\n- Merge conflict resolution (bd-65, bd-103) - 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":"open","priority":1,"issue_type":"task","created_at":"2025-10-29T21:25:50.976383-07:00","updated_at":"2025-10-30T17:00:11.118868-07:00","dependencies":[{"issue_id":"bd-172","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-29T21:25:50.977857-07:00","created_by":"stevey"},{"issue_id":"bd-172","depends_on_id":"bd-168","type":"blocks","created_at":"2025-10-29T21:25:50.978395-07:00","created_by":"stevey"},{"issue_id":"bd-172","depends_on_id":"bd-169","type":"blocks","created_at":"2025-10-29T21:25:50.978842-07:00","created_by":"stevey"},{"issue_id":"bd-172","depends_on_id":"bd-192","type":"blocks","created_at":"2025-10-30T14:22:59.348038-07:00","created_by":"import-remap"},{"issue_id":"bd-172","depends_on_id":"bd-5407","type":"blocks","created_at":"2025-10-30T17:00:11.090105-07:00","created_by":"import-remap"},{"issue_id":"bd-172","depends_on_id":"bd-5408","type":"blocks","created_at":"2025-10-30T17:00:11.090454-07:00","created_by":"import-remap"}]} +{"id":"bd-173","content_hash":"9d78f9471bf147696d5295cc89324d3486feb4bbe16c6e89524320fab229bcd1","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-1 → 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-5 (parent: bd-1) → 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":"open","priority":1,"issue_type":"task","created_at":"2025-10-30T17:00:11.082218-07:00","updated_at":"2025-10-30T17:00:11.082218-07:00"} +{"id":"bd-174","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-30T00:25:24.186868-07:00","dependencies":[{"issue_id":"bd-174","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-29T21:26:53.753259-07:00","created_by":"stevey"},{"issue_id":"bd-174","depends_on_id":"bd-170","type":"blocks","created_at":"2025-10-29T21:26:53.753733-07:00","created_by":"stevey"},{"issue_id":"bd-174","depends_on_id":"bd-171","type":"blocks","created_at":"2025-10-29T21:26:53.754112-07:00","created_by":"stevey"},{"issue_id":"bd-174","depends_on_id":"bd-5409","type":"blocks","created_at":"2025-10-30T17:00:11.088976-07:00","created_by":"import-remap"},{"issue_id":"bd-174","depends_on_id":"bd-5410","type":"blocks","created_at":"2025-10-30T17:00:11.089373-07:00","created_by":"import-remap"}]} {"id":"bd-175","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-86) - no longer needed\n- TestThreeCloneCollision (bd-185) - no longer needed\n- TestFiveCloneCollision (bd-151) - 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-29T23:05:13.897026-07:00","dependencies":[{"issue_id":"bd-175","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-29T21:27:26.955522-07:00","created_by":"stevey"},{"issue_id":"bd-175","depends_on_id":"bd-172","type":"blocks","created_at":"2025-10-29T21:27:26.956175-07:00","created_by":"stevey"}]} -{"id":"bd-176","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-30T00:25:55.25486-07:00","dependencies":[{"issue_id":"bd-176","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-29T21:28:10.981344-07:00","created_by":"stevey"},{"issue_id":"bd-176","depends_on_id":"bd-173","type":"blocks","created_at":"2025-10-29T21:28:10.981767-07:00","created_by":"stevey"},{"issue_id":"bd-176","depends_on_id":"bd-174","type":"blocks","created_at":"2025-10-29T21:28:10.982167-07:00","created_by":"stevey"}]} -{"id":"bd-177","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-29T21:28:45.256074-07:00","dependencies":[{"issue_id":"bd-177","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-29T21:28:45.257315-07:00","created_by":"stevey"},{"issue_id":"bd-177","depends_on_id":"bd-170","type":"blocks","created_at":"2025-10-29T21:28:45.258057-07:00","created_by":"stevey"}]} -{"id":"bd-178","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-165 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-42)\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-165 tasks complete (estimated: ~8 weeks from start)","status":"open","priority":1,"issue_type":"task","created_at":"2025-10-29T21:29:28.591526-07:00","updated_at":"2025-10-29T21:29:28.591526-07:00","dependencies":[{"issue_id":"bd-178","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-29T21:29:28.59248-07:00","created_by":"stevey"},{"issue_id":"bd-178","depends_on_id":"bd-173","type":"blocks","created_at":"2025-10-29T21:29:28.593033-07:00","created_by":"stevey"},{"issue_id":"bd-178","depends_on_id":"bd-175","type":"blocks","created_at":"2025-10-29T21:29:28.593437-07:00","created_by":"stevey"},{"issue_id":"bd-178","depends_on_id":"bd-176","type":"blocks","created_at":"2025-10-29T21:29:28.593876-07:00","created_by":"stevey"},{"issue_id":"bd-178","depends_on_id":"bd-177","type":"blocks","created_at":"2025-10-29T21:29:28.594521-07:00","created_by":"stevey"}]} +{"id":"bd-176","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-30T00:25:55.25486-07:00","dependencies":[{"issue_id":"bd-176","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-29T21:28:10.981344-07:00","created_by":"stevey"},{"issue_id":"bd-176","depends_on_id":"bd-173","type":"blocks","created_at":"2025-10-29T21:28:10.981767-07:00","created_by":"stevey"},{"issue_id":"bd-176","depends_on_id":"bd-174","type":"blocks","created_at":"2025-10-29T21:28:10.982167-07:00","created_by":"stevey"},{"issue_id":"bd-176","depends_on_id":"bd-5411","type":"blocks","created_at":"2025-10-30T17:00:11.093979-07:00","created_by":"import-remap"}]} +{"id":"bd-177","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-29T21:28:45.256074-07:00","dependencies":[{"issue_id":"bd-177","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-29T21:28:45.257315-07:00","created_by":"stevey"},{"issue_id":"bd-177","depends_on_id":"bd-170","type":"blocks","created_at":"2025-10-29T21:28:45.258057-07:00","created_by":"stevey"},{"issue_id":"bd-177","depends_on_id":"bd-5409","type":"blocks","created_at":"2025-10-30T17:00:11.092331-07:00","created_by":"import-remap"}]} +{"id":"bd-178","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-165 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-42)\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-165 tasks complete (estimated: ~8 weeks from start)","status":"open","priority":1,"issue_type":"task","created_at":"2025-10-29T21:29:28.591526-07:00","updated_at":"2025-10-29T21:29:28.591526-07:00","dependencies":[{"issue_id":"bd-178","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-29T21:29:28.59248-07:00","created_by":"stevey"},{"issue_id":"bd-178","depends_on_id":"bd-173","type":"blocks","created_at":"2025-10-29T21:29:28.593033-07:00","created_by":"stevey"},{"issue_id":"bd-178","depends_on_id":"bd-175","type":"blocks","created_at":"2025-10-29T21:29:28.593437-07:00","created_by":"stevey"},{"issue_id":"bd-178","depends_on_id":"bd-176","type":"blocks","created_at":"2025-10-29T21:29:28.593876-07:00","created_by":"stevey"},{"issue_id":"bd-178","depends_on_id":"bd-177","type":"blocks","created_at":"2025-10-29T21:29:28.594521-07:00","created_by":"stevey"},{"issue_id":"bd-178","depends_on_id":"bd-5411","type":"blocks","created_at":"2025-10-30T17:00:11.085763-07:00","created_by":"import-remap"}]} {"id":"bd-179","content_hash":"b4ee73e439a133a77e5df27e3e457674dbc9968fdbee0dc630175726960bb8cf","title":"Improve integration test coverage for stateful features","description":"","design":"## Context\n\nbd-160 revealed a critical gap: the export deduplication feature had unit tests but no integration tests simulating real-world git operations. This led to silent data loss in production.\n\n## Root Cause\n- Unit tests only tested functions in isolation\n- No integration tests for git operations (pull, reset, checkout) modifying JSONL\n- No tests validating export_hashes and JSONL stay in sync\n- Missing tests for stateful distributed system interactions (DB + JSONL + git)\n\n## Completed (bd-160)\n✓ TestJSONLIntegrityValidation - unit tests for validation logic\n✓ TestImportClearsExportHashes - tests import clears hashes\n✓ TestExportIntegrityAfterJSONLTruncation - simulates git reset (would have caught bd-160)\n✓ TestExportIntegrityAfterJSONLDeletion - tests recovery from file deletion\n✓ TestMultipleExportsStayConsistent - tests repeated exports\n\n## Still Needed (High Priority)\n1. Multi-repo sync test - two clones staying in sync after push/pull\n2. Auto-flush integration test - JSONL integrity preserved during auto-flush\n3. Daemon auto-sync integration test - complex state management\n4. Import after corruption test - recovery from partial data loss\n\n## Medium Priority\n- Partial export failure handling (disk full, network interruption)\n- Concurrent export/import race conditions\n- Large dataset performance tests (1000+ issues)\n- Export hash migration tests (version upgrades)\n\n## Testing Principles\n1. Test real-world scenarios: git ops, user errors, system failures, concurrent ops\n2. Integration tests for stateful systems (DB + files + git)\n3. Regression test for every bug fix\n4. Test invariants: JSONL count == DB count, hash consistency, etc.\n\n## Key Lesson\nStateful distributed systems need integration tests, not just unit tests.","acceptance_criteria":"- [ ] Multi-repo sync test implemented\n- [ ] Auto-flush integration test implemented \n- [ ] Daemon auto-sync integration test implemented\n- [ ] Testing guidelines added to CONTRIBUTING.md\n- [ ] CI runs integration tests\n- [ ] All critical workflows have integration test coverage","status":"open","priority":2,"issue_type":"epic","created_at":"2025-10-29T21:53:15.397137-07:00","updated_at":"2025-10-29T23:05:13.99215-07:00"} {"id":"bd-18","content_hash":"8a8df680150f73fef6ac9cede6a1b2b0033406b35553a8a3795b13a542cd62f1","title":"Remove unreachable utility functions","description":"Several small utility functions are unreachable:\n\nFiles to clean:\n1. `internal/storage/sqlite/hash.go` - `computeIssueContentHash` (line 17)\n - Check if entire file can be deleted if only contains this function\n\n2. `internal/config/config.go` - `FileUsed` (line 151)\n - Delete unused config helper\n\n3. `cmd/bd/git_sync_test.go` - `verifyIssueOpen` (line 300)\n - Delete dead test helper\n\n4. `internal/compact/haiku.go` - `HaikuClient.SummarizeTier2` (line 81)\n - Tier 2 summarization not implemented\n - Options: implement feature OR delete method\n\nImpact: Removes 50-100 LOC depending on decisions","acceptance_criteria":"- Remove unreachable functions\n- If entire files can be deleted (like hash.go), delete them\n- For SummarizeTier2: decide to implement or delete, document decision\n- All tests pass: `go test ./...`\n- Verify no callers exist for each function","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-27T20:30:19.963392-07:00","updated_at":"2025-10-28T14:14:55.724226-07:00","closed_at":"2025-10-28T14:14:55.724226-07:00","labels":["cleanup","dead-code","phase-1"],"dependencies":[{"issue_id":"bd-18","depends_on_id":"bd-26","type":"parent-child","created_at":"2025-10-27T20:30:19.968126-07:00","created_by":"daemon"}]} {"id":"bd-182","content_hash":"6b2a1aedbdbcb30b98d4a8196801953a1eb22204d63e31954ef9ab6020a7a26b","title":"Create cmd/bd/daemon_watcher.go (~150 LOC)","description":"Implement FileWatcher using fsnotify to watch JSONL file and git refs. Handle platform differences (inotify/FSEvents/ReadDirectoryChangesW). Include edge case handling for file rename, event storm, watcher failure.","status":"in_progress","priority":1,"issue_type":"task","created_at":"2025-10-29T23:05:13.887269-07:00","updated_at":"2025-10-29T23:05:13.887269-07:00"} @@ -84,6 +84,8 @@ {"id":"bd-19","content_hash":"af7f41ff73c3aaba006d9cfbf8e35332e25d5b42f9e620b5e94d41c05550ea81","title":"Extract SQLite migrations into separate files","description":"The file `internal/storage/sqlite/sqlite.go` is 2,136 lines and contains 11 sequential migrations alongside core storage logic. Extract migrations into a versioned system.\n\nCurrent issues:\n- 11 migration functions mixed with core logic\n- Hard to see migration history\n- Sequential migrations slow database open\n- No clear migration versioning\n\nMigration functions to extract:\n- `migrateDirtyIssuesTable()`\n- `migrateIssueCountersTable()`\n- `migrateExternalRefColumn()`\n- `migrateCompositeIndexes()`\n- `migrateClosedAtConstraint()`\n- `migrateCompactionColumns()`\n- `migrateSnapshotsTable()`\n- `migrateCompactionConfig()`\n- `migrateCompactedAtCommitColumn()`\n- `migrateExportHashesTable()`\n- Plus 1 more (11 total)\n\nTarget structure:\n```\ninternal/storage/sqlite/\n├── sqlite.go # Core storage (~800 lines)\n├── schema.go # Table definitions (~200 lines)\n├── migrations.go # Migration orchestration (~200 lines)\n└── migrations/ # Individual migrations\n ├── 001_initial_schema.go\n ├── 002_dirty_issues.go\n ├── 003_issue_counters.go\n [... through 011_export_hashes.go]\n```\n\nBenefits:\n- Clear migration history\n- Each migration self-contained\n- Easier to review migration changes in PRs\n- Future migrations easier to add","acceptance_criteria":"- All 11 migrations extracted to separate files\n- Migration version tracking in database\n- Migrations run in order on fresh database\n- Existing databases upgrade correctly\n- All tests pass: `go test ./internal/storage/sqlite/...`\n- Database initialization time unchanged or improved\n- Add migration rollback capability (optional, nice-to-have)","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-27T20:30:47.870671-07:00","updated_at":"2025-10-27T22:22:23.81842-07:00","labels":["database","phase-2","refactor"],"dependencies":[{"issue_id":"bd-19","depends_on_id":"bd-26","type":"parent-child","created_at":"2025-10-27T20:30:47.875564-07:00","created_by":"daemon"}]} {"id":"bd-190","content_hash":"e2d1279edd9a6ea4dae06772350823e069230280cfca6eb69891527caa9b7446","title":"Fix import collision resolution - treats updates as collisions","description":"## Problem\n\nImport with `--resolve-collisions` incorrectly treats normal updates as collisions, creating duplicate issues after routine `git pull`.\n\n## Root Cause\n\nCurrent collision detection:\n```\nif (JSONL.id exists in DB \u0026\u0026 JSONL.content != DB.content) {\n collision = true // WRONG!\n}\n```\n\nThis treats EVERY update as a collision. After `git pull`, JSONL has updated issues (e.g., bd-106 status changed from open→closed). Import sees this as a \"collision\" and remaps to new ID (bd-187), creating duplicates.\n\n## What Should Happen\n\n**Normal import (default):** JSONL is source of truth, update database on ID match\n```\nif (JSONL.id exists in DB) {\n if (content_hash matches) { skip }\n else { UPDATE existing issue } // Not a collision!\n}\n```\n\n**Collision resolution (separate mode):** Only for branch merges where two independent actors created same ID with different content\n\n## Solution Architecture\n\n1. Fix default import to UPDATE on ID match (not treat as collision)\n2. Make `--resolve-collisions` a separate mode ONLY for branch merges\n3. Add import validation to detect duplicates before committing\n4. Write comprehensive tests for normal update vs actual collision scenarios\n\nSee ~/src/fred/beads/collision-resolution-failure-analysis.md for full analysis.","status":"open","priority":0,"issue_type":"epic","created_at":"2025-10-29T23:47:37.906532-07:00","updated_at":"2025-10-29T23:47:37.906532-07:00"} {"id":"bd-191","content_hash":"47b9f15a8741b9df77e30d448999ad9566f9c6cb1bebe1e760735bfb2cb26c66","title":"Add --parent flag to bd list command","description":"Add --parent flag to bd list to filter issues by parent ID.\n\n## Usage\n```bash\nbd list --parent 165 # List all children of bd-165\nbd list --parent a3f8e9 # List all children (with prefix match)\nbd list --parent a3f8e9 -s open # Combine with status filter\n```\n\n## Behavior\n- Show only direct children (not recursive by default)\n- Add --recursive flag for full subtree\n- Works with current parent-child dependency type\n- With hierarchical IDs (bd-165), this will be natural: filter by ID prefix\n\n## Implementation\nQuery dependencies table:\n```sql\nSELECT i.* FROM issues i\nJOIN dependencies d ON i.id = d.issue_id\nWHERE d.depends_on_id = ? AND d.type = 'parent-child'\n```\n\nWith hierarchical IDs (post bd-165):\n```sql\nSELECT * FROM issues \nWHERE id LIKE 'bd-a3f8e9.%' -- Much simpler!\n```\n\n## Related\n- Complements bd tree command (bd-174)\n- Will be cleaner with hierarchical IDs from bd-165\n- Could add --leaves flag (show only leaf nodes, no sub-epics)\n\n## Files\n- cmd/bd/list.go (add --parent flag)\n- May want to add --recursive flag too","status":"open","priority":2,"issue_type":"feature","created_at":"2025-10-30T00:27:44.562078-07:00","updated_at":"2025-10-30T00:27:44.562078-07:00"} +{"id":"bd-192","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-30T14:22:59.345449-07:00","closed_at":"2025-10-30T14:12:17.327987-07:00","dependencies":[{"issue_id":"bd-192","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-30T14:22:59.346356-07:00","created_by":"import-remap"},{"issue_id":"bd-192","depends_on_id":"bd-166","type":"blocks","created_at":"2025-10-30T14:22:59.346967-07:00","created_by":"import-remap"},{"issue_id":"bd-192","depends_on_id":"bd-5406","type":"blocks","created_at":"2025-10-30T17:00:11.08975-07:00","created_by":"import-remap"}]} +{"id":"bd-193","content_hash":"2f0e8212084a4d53ec447be4c470117d4c2d697e1836ac8bd40a68d151f6083e","title":"Test hash ID issue","description":"","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-30T14:23:58.175147-07:00","updated_at":"2025-10-30T14:24:02.855391-07:00","closed_at":"2025-10-30T14:24:02.855391-07:00"} {"id":"bd-2","content_hash":"4ad564b5b844f5673cd8ec6355ad921cbf71e4fbd6d0a6aa5f4e9c4e3222408e","title":"Clean up linter errors (914 total issues)","description":"The codebase has 914 linter issues reported by golangci-lint. While many are documented as baseline in LINTING.md, we should clean these up systematically to improve code quality and maintainability.","design":"Break down by linter category, prioritizing high-impact issues:\n1. dupl (7) - Code duplication\n2. goconst (12) - Repeated strings\n3. gocyclo (11) - High complexity functions\n4. revive (78) - Style issues\n5. gosec (102) - Security warnings\n6. errcheck (683) - Unchecked errors (many in tests)","acceptance_criteria":"All linter categories reduced to acceptable levels, with remaining baseline documented in LINTING.md","notes":"Reduced from 56 to 41 issues locally, then to 0 issues.\n\n**Fixed in commits:**\n- c2c7eda: Fixed 15 actual errors (dupl, gosec, revive, staticcheck, unparam)\n- 963181d: Configured exclusions to get to 0 issues locally\n\n**Current status:**\n- ✅ Local: golangci-lint reports 0 issues\n- ❌ CI: Still failing (see [deleted:bd-50])\n\n**Problem:**\nConfig v2 format or golangci-lint-action@v8 compatibility issue causing CI to fail despite local success.\n\n**Next:** Debug [deleted:bd-50] to fix CI/local discrepancy","status":"in_progress","priority":2,"issue_type":"epic","created_at":"2025-10-24T01:01:12.997982-07:00","updated_at":"2025-10-28T16:20:02.454709-07:00"} {"id":"bd-20","content_hash":"b853675236e96269afb97649cc1a7b27451f15babf611a2abfea58986d0f5a2f","title":"Extract normalizeLabels to shared utility package","description":"The `normalizeLabels` function appears in multiple locations with identical implementation. Extract to a shared utility package.\n\nCurrent locations:\n- `internal/rpc/server.go:37` (53 lines) - full implementation\n- `cmd/bd/list.go:50-52` - uses the server version (needs to use new shared version)\n\nFunction purpose:\n- Trims whitespace from labels\n- Removes empty strings\n- Deduplicates labels\n- Preserves order\n\nTarget structure:\n```\ninternal/util/\n├── strings.go # String utilities\n └── NormalizeLabels([]string) []string\n```\n\nImpact: DRY principle, single source of truth, easier to test","acceptance_criteria":"- Create `internal/util/strings.go` with `NormalizeLabels`\n- Add comprehensive unit tests in `internal/util/strings_test.go`\n- Update `internal/rpc/server.go` to import and use `util.NormalizeLabels`\n- Update `cmd/bd/list.go` to import and use `util.NormalizeLabels`\n- Remove duplicate implementations\n- All tests pass: `go test ./...`\n- Verify label normalization works: test `bd list --label` commands","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-27T20:31:19.078622-07:00","updated_at":"2025-10-27T22:22:23.818801-07:00","labels":["deduplication","phase-3","refactor"],"dependencies":[{"issue_id":"bd-20","depends_on_id":"bd-26","type":"parent-child","created_at":"2025-10-27T20:31:19.08015-07:00","created_by":"daemon"}]} {"id":"bd-21","content_hash":"3e37bcf3e5090c1971f300f95fc904762857be05d4d47acfa2bfa049c8302043","title":"Centralize BD_DEBUG logging into debug package","description":"The codebase has 43 scattered instances of `if os.Getenv(\"BD_DEBUG\") != \"\"` debug checks across 6 files. Centralize into a debug logging package.\n\nCurrent locations:\n- `cmd/bd/main.go` - 15 checks\n- `cmd/bd/autoflush.go` - 6 checks\n- `cmd/bd/nodb.go` - 4 checks\n- `internal/rpc/server.go` - 2 checks\n- `internal/rpc/client.go` - 5 checks\n- `cmd/bd/daemon_autostart.go` - 11 checks\n\nTarget structure:\n```\ninternal/debug/\n└── debug.go\n```\n\nBenefits:\n- Centralized debug logging\n- Easier to add structured logging later\n- Testable (can mock debug output)\n- Consistent debug message format\n\nImpact: Removes 43 scattered checks, improves code clarity","acceptance_criteria":"- Create `internal/debug/debug.go` with `Enabled`, `Logf`, `Printf`\n- Add unit tests in `internal/debug/debug_test.go` (test with/without BD_DEBUG)\n- Replace all 43 instances of `os.Getenv(\"BD_DEBUG\")` checks with `debug.Logf()`\n- Verify debug output works: run with `BD_DEBUG=1 bd status`\n- All tests pass: `go test ./...`\n- No behavior change (output identical to before)","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-27T20:31:19.089078-07:00","updated_at":"2025-10-27T22:22:23.819123-07:00","labels":["deduplication","logging","phase-3","refactor"],"dependencies":[{"issue_id":"bd-21","depends_on_id":"bd-26","type":"parent-child","created_at":"2025-10-27T21:48:41.542395-07:00","created_by":"stevey"}]} @@ -117,12 +119,25 @@ {"id":"bd-47","content_hash":"ed9fa6273973fb0c68d173564ab4814d360528f9bb035e78406a63875f8f6b43","title":"Remove Storage Cache from Server Struct","description":"Eliminate cache fields and use s.storage directly","acceptance_criteria":"- Server struct has no cache fields\n- NewServer() doesn't initialize cache\n- Start() doesn't run cache cleanup goroutines\n- Stop() only closes single s.storage\n\nChanges needed:\n- Remove cache-related fields from Server struct in server_core.go\n- Remove cache size/TTL parsing from env vars in NewServer()\n- Remove cleanup ticker goroutine from Start()\n- Remove cache cleanup logic from Stop()","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-27T23:20:10.393456-07:00","updated_at":"2025-10-28T14:08:38.066441-07:00","closed_at":"2025-10-28T14:08:38.066441-07:00","dependencies":[{"issue_id":"bd-47","depends_on_id":"bd-29","type":"parent-child","created_at":"2025-10-29T23:14:44.299989-07:00","created_by":"stevey"}]} {"id":"bd-48","content_hash":"4e03660281dbe2c069617fc8d723d546d6e5eb386142c0359b862747867a1b90","title":"Replace getStorageForRequest with Direct Access","description":"Replace all getStorageForRequest(req) calls with s.storage","acceptance_criteria":"- No references to getStorageForRequest() in codebase (except in deleted file)\n- All handlers use s.storage directly\n- Code compiles without errors\n\nFiles to update:\n- internal/rpc/server_issues_epics.go (~8 calls)\n- internal/rpc/server_labels_deps_comments.go (~4 calls)\n- internal/rpc/server_compact.go (~2 calls)\n- internal/rpc/server_export_import_auto.go (~2 calls)\n- internal/rpc/server_routing_validation_diagnostics.go (~1 call)\n\nPattern: store, err := s.getStorageForRequest(req) → store := s.storage","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-27T23:20:10.393759-07:00","updated_at":"2025-10-28T14:08:38.06721-07:00","closed_at":"2025-10-28T14:08:38.06721-07:00"} {"id":"bd-49","content_hash":"cd9e7cc106b733dc4893e92a75feae3331b422238f261a7c738c21a18e29719f","title":"Remove Cache Configuration Docs","description":"Remove documentation of deprecated cache env vars","acceptance_criteria":"- Documentation doesn't reference removed env vars\n- CHANGELOG documents breaking change\n- No mentions of storage cache except in CHANGELOG\n\nFiles to update:\n- ADVANCED.md (remove cache configuration section)\n- commands/daemons.md (remove cache env vars)\n- integrations/beads-mcp/SETUP_DAEMON.md (remove cache tuning)\n- CHANGELOG.md (add removal entry)\n\nDeprecated env vars:\n- BEADS_DAEMON_MAX_CACHE_SIZE\n- BEADS_DAEMON_CACHE_TTL\n- BEADS_DAEMON_MEMORY_THRESHOLD_MB","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-28T10:50:15.125488-07:00","updated_at":"2025-10-28T10:50:15.125488-07:00","closed_at":"2025-10-28T10:48:20.606979-07:00"} +{"id":"bd-4f029492","content_hash":"3e8b654c9dd6a631db40adc23bf6b7e291c13f977dceaf08d447963f99b3124d","title":"Another hash test","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-30T14:27:28.658778-07:00","updated_at":"2025-10-30T14:27:33.073977-07:00","closed_at":"2025-10-30T14:27:33.073977-07:00"} {"id":"bd-5","content_hash":"133dfd651d402bb95928091138c77a57b2f3f349587962c744209a534fb800a6","title":"Address gosec security warnings (102 issues)","description":"Security linter warnings: file permissions (0755 should be 0750), G304 file inclusion via variable, G204 subprocess launches. Many are false positives but should be reviewed.","design":"Review each gosec warning. Add exclusions for legitimate cases to .golangci.yml. Fix real security issues (overly permissive file modes).","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-25T13:47:10.719134-07:00","updated_at":"2025-10-27T22:22:23.814301-07:00"} {"id":"bd-50","content_hash":"0bfd0735c8985d3b3e4906e44f22b06fb24758c6d795188226e920bd8b3e7cf8","title":"Performance Validation","description":"Confirm no performance regression from cache removal","acceptance_criteria":"- Benchmarks show no significant regression\n- Document performance characteristics\n- Confirm single SQLite connection is reused\n\nBenchmarks: go test -bench=. -benchmem ./internal/rpc/...\n\nMetrics to track:\n- Request latency (p50, p99)\n- Throughput (requests/sec)\n- Memory usage\n- SQLite connection overhead\n\nExpected results:\n- Latency: Same or better (no cache overhead)\n- Throughput: Same (cache was always 1 entry)\n- Memory: Lower (no cache structs)\n- Connection overhead: Zero (single connection reused)","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-28T10:50:15.126019-07:00","updated_at":"2025-10-28T10:50:15.126019-07:00","closed_at":"2025-10-28T10:49:45.021037-07:00","dependencies":[{"issue_id":"bd-50","depends_on_id":"bd-29","type":"parent-child","created_at":"2025-10-29T23:14:44.206394-07:00","created_by":"stevey"},{"issue_id":"bd-50","depends_on_id":"bd-38","type":"blocks","created_at":"2025-10-29T23:14:44.207797-07:00","created_by":"stevey"}]} {"id":"bd-51","content_hash":"0c7997ff55a05eb6db59702ec72644c0f59658ca2838175125fda0e1cd11d952","title":"Verify MCP Server Compatibility","description":"Ensure MCP server works with cache-free daemon","acceptance_criteria":"- MCP integration tests pass\n- Documented confirmation of MCP multi-repo strategy\n- No regressions in MCP functionality\n\nTest scenarios:\n1. Single repo workflow: MCP with one project directory\n2. Multi-repo workflow: MCP switching between projects (uses separate daemons)\n3. Daemon restart: Verify no stale data after daemon restart\n\nQuestions to answer:\n- Does MCP rely on req.Cwd routing to single daemon for multiple repos?\n- Or does MCP start separate daemons per repo (recommended)?\n- Do existing MCP tests pass?\n\nFiles to review:\n- integrations/beads-mcp/src/beads_mcp/server.py\n- integrations/beads-mcp/tests/test_multi_project_switching.py","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-28T10:50:15.126312-07:00","updated_at":"2025-10-28T10:50:15.126312-07:00","closed_at":"2025-10-28T10:49:20.468838-07:00"} {"id":"bd-52","content_hash":"330e69cf6ca40209948559b453ed5242c15a71b5c949a858ad6854488b12dca2","title":"Integration Testing","description":"Verify cache removal doesn't break any workflows","acceptance_criteria":"- All test cases pass\n- No stale data observed\n- Performance is same or better\n- MCP works as before\n\nTest cases:\n1. Basic daemon operations (bd daemon --stop, bd daemon, bd list, bd create, bd show)\n2. Auto-import/export cycle (edit beads.jsonl externally, bd list auto-imports)\n3. Git workflow (git pull updates beads.jsonl, bd list shows pulled issues)\n4. Concurrent operations (multiple bd commands simultaneously)\n5. Daemon health (bd daemon --health, bd daemon --metrics)\n6. MCP operations (test MCP server with multiple repos, verify project switching)","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-28T10:50:15.126668-07:00","updated_at":"2025-10-28T10:50:15.126668-07:00","closed_at":"2025-10-28T10:49:20.471129-07:00"} {"id":"bd-53","content_hash":"79bd51b46b28bc16cfc19cd19a4dd4f57f45cd1e902b682788d355b03ec00b2a","title":"Remove Daemon Storage Cache","description":"The daemon's multi-repo storage cache is the root cause of stale data bugs. Since global daemon is deprecated, we only ever serve one repository, making the cache unnecessary complexity. This epic removes the cache entirely for simpler, more reliable direct storage access.","design":"For local daemon (single repository), eliminate the cache entirely:\n- Use s.storage field directly (opened at daemon startup)\n- Remove getStorageForRequest() routing logic\n- Remove server_cache_storage.go entirely (~300 lines)\n- Remove cache-related tests\n- Simplify Server struct\n\nBenefits:\n✅ No staleness bugs: Always using live SQLite connection\n✅ Simpler code: Remove ~300 lines of cache management\n✅ Easier debugging: Direct storage access, no cache indirection\n✅ Same performance: Cache was always 1 entry for local daemon anyway","acceptance_criteria":"- Daemon has no storage cache code\n- All tests pass\n- MCP integration works\n- No stale data bugs\n- Documentation updated\n- Performance validated","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-10-28T10:50:15.126939-07:00","updated_at":"2025-10-28T10:50:15.126939-07:00","closed_at":"2025-10-28T10:49:53.612049-07:00"} {"id":"bd-54","content_hash":"27498c808874010ee62da58e12434a6ae7c73f4659b2233aaf8dcd59566a907d","title":"Fix TestTwoCloneCollision timeout","description":"","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-10-28T14:11:25.219607-07:00","updated_at":"2025-10-28T16:12:26.286611-07:00","closed_at":"2025-10-28T16:12:26.286611-07:00"} +{"id":"bd-5403defc","content_hash":"a3d331c0c216738bf6df5a92cf5640fa4e50c10286e8be0baaf463eca9f4de0c","title":"Test hash ID generation","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-30T15:46:41.777971-07:00","updated_at":"2025-10-30T15:46:59.618014-07:00","closed_at":"2025-10-30T15:46:59.618014-07:00"} +{"id":"bd-5403defc.1","content_hash":"d1ffe0d966939abf9449b6157cf9fcf42342b3056bfa65aeffbfa913ff722928","title":"Child test","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-30T15:46:47.841563-07:00","updated_at":"2025-10-30T15:46:59.618715-07:00","closed_at":"2025-10-30T15:46:59.618715-07:00"} +{"id":"bd-5403defc.1.1","content_hash":"f835dedf00bec5edfac81de035e4b5af1490afa7008bdf74683041c44d33d830","title":"Nested child","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-30T15:46:51.064625-07:00","updated_at":"2025-10-30T15:46:59.618994-07:00","closed_at":"2025-10-30T15:46:59.618994-07:00"} +{"id":"bd-5403defc.1.1.1","content_hash":"0c150383d4c2ce7aeecf70fe53f2599e9720eccffc7ab717a2abfef8e37f9dcc","title":"Deep nested","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-10-30T15:46:53.570315-07:00","updated_at":"2025-10-30T15:46:59.619236-07:00","closed_at":"2025-10-30T15:46:59.619236-07:00"} +{"id":"bd-5404","content_hash":"71db1274320e28acd735902b9c07c2683b0c7dcbd7808637457dcb492273f19f","title":"Hash IDs as default: testing, release, and migration","description":"Drive hash IDs to be the default for Beads. Currently on feature/hash-ids branch, opt-in via --to-hash-ids flag, requires explicit migration. Target repos: ~/src/fred/beads, ~/wyvern, ~/src/wyvern. Deployment: homebrew/bin, MCP servers, all daemons at 0.19.1","design":"1. Checkout feature/hash-ids 2. Bump to 0.19.1 on branch 3. Build/install to homebrew 4. Update MCP servers 5. Restart daemons 6. Migrate all repos 7. Test workers 8. Make hash IDs default 9. Verify","acceptance_criteria":"All 3 repos migrated, all daemons at 0.19.1, workers functional, hash IDs default","status":"open","priority":1,"issue_type":"epic","created_at":"2025-10-30T16:33:07.965639-07:00","updated_at":"2025-10-30T16:33:07.965639-07:00"} +{"id":"bd-5405","content_hash":"4c12ad67469db8ac3b9a9d6199c78521202b54a5cb4c0571ea1fb5e4cd8d42bc","title":"Test mixed ID system","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-30T16:36:18.414852-07:00","updated_at":"2025-10-30T16:36:18.414852-07:00"} +{"id":"bd-5406","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-168\n\nWe've completed:\n- ✅ bd-166: Hash ID algorithm (returns full 64-char hash)\n- ✅ bd-167: child_counters table + getNextChildNumber()\n- ✅ Docs updated for 6-char progressive design\n\n**TODO for bd-168:**\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:00:11.118347-07:00","dependencies":[{"issue_id":"bd-5406","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-30T17:00:11.082653-07:00","created_by":"import-remap"}]} +{"id":"bd-5407","content_hash":"5e2d121e416845aaf0d5a146e87c3143e8d150350b2efdd9798cd68c28c1bdf9","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)","notes":"Work completed on feature/hash-ids branch. Reverted from main to avoid breaking changes. Will merge after migration strategy (bd-5411) is ready.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-30T17:00:11.076375-07:00","updated_at":"2025-10-30T17:00:11.100606-07:00","closed_at":"2025-10-30T15:47:08.203011-07:00","dependencies":[{"issue_id":"bd-5407","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-30T17:00:11.083229-07:00","created_by":"import-remap"},{"issue_id":"bd-5407","depends_on_id":"bd-5406","type":"blocks","created_at":"2025-10-30T17:00:11.083756-07:00","created_by":"import-remap"}]} +{"id":"bd-5408","content_hash":"c27f166d842efb8caa1c104b1eaf430e430b3da84cfb13a085cc732a713faaf9","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-1 → bd-{hash} with mapping preserved.","status":"open","priority":1,"issue_type":"task","created_at":"2025-10-30T17:00:11.078136-07:00","updated_at":"2025-10-30T17:00:11.078136-07:00","dependencies":[{"issue_id":"bd-5408","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-30T17:00:11.087309-07:00","created_by":"import-remap"},{"issue_id":"bd-5408","depends_on_id":"bd-5407","type":"blocks","created_at":"2025-10-30T17:00:11.087752-07:00","created_by":"import-remap"},{"issue_id":"bd-5408","depends_on_id":"bd-167","type":"blocks","created_at":"2025-10-30T17:00:11.088138-07:00","created_by":"import-remap"},{"issue_id":"bd-5408","depends_on_id":"bd-192","type":"blocks","created_at":"2025-10-30T17:00:11.088557-07:00","created_by":"import-remap"}]} +{"id":"bd-5409","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-5409').\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-177 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:00:11.099588-07:00","closed_at":"2025-10-30T15:22:04.984996-07:00","dependencies":[{"issue_id":"bd-5409","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-30T17:00:11.09279-07:00","created_by":"import-remap"},{"issue_id":"bd-5409","depends_on_id":"bd-167","type":"blocks","created_at":"2025-10-30T17:00:11.093202-07:00","created_by":"import-remap"},{"issue_id":"bd-5409","depends_on_id":"bd-5408","type":"blocks","created_at":"2025-10-30T17:00:11.093596-07:00","created_by":"import-remap"}]} +{"id":"bd-5410","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:00:11.080196-07:00","closed_at":"2025-10-30T14:39:56.341051-07:00","dependencies":[{"issue_id":"bd-5410","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-30T17:00:11.084596-07:00","created_by":"import-remap"},{"issue_id":"bd-5410","depends_on_id":"bd-167","type":"blocks","created_at":"2025-10-30T17:00:11.084959-07:00","created_by":"import-remap"},{"issue_id":"bd-5410","depends_on_id":"bd-5408","type":"blocks","created_at":"2025-10-30T17:00:11.085361-07:00","created_by":"import-remap"}]} +{"id":"bd-5411","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-1 → 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-5 (parent: bd-1) → 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:00:11.081377-07:00","closed_at":"2025-10-30T16:23:30.380142-07:00","dependencies":[{"issue_id":"bd-5411","depends_on_id":"bd-165","type":"parent-child","created_at":"2025-10-30T17:00:11.086162-07:00","created_by":"import-remap"},{"issue_id":"bd-5411","depends_on_id":"bd-5407","type":"blocks","created_at":"2025-10-30T17:00:11.086508-07:00","created_by":"import-remap"},{"issue_id":"bd-5411","depends_on_id":"bd-192","type":"blocks","created_at":"2025-10-30T17:00:11.086873-07:00","created_by":"import-remap"}]} {"id":"bd-55","content_hash":"d4d20e71bbf5c08f1fe1ed07f67b7554167aa165d4972ea51b5cacc1b256c4c1","title":"Split internal/rpc/server.go into focused modules","description":"The file `internal/rpc/server.go` is 2,273 lines with 50+ methods, making it difficult to navigate and prone to merge conflicts. Split into 8 focused files with clear responsibilities.\n\nCurrent structure: Single 2,273-line file with:\n- Connection handling\n- Request routing\n- All 40+ RPC method implementations\n- Storage caching\n- Health checks \u0026 metrics\n- Cleanup loops\n\nTarget structure:\n```\ninternal/rpc/\n├── server.go # Core server, connection handling (~300 lines)\n├── methods_issue.go # Issue operations (~400 lines)\n├── methods_deps.go # Dependency operations (~200 lines)\n├── methods_labels.go # Label operations (~150 lines)\n├── methods_ready.go # Ready work queries (~150 lines)\n├── methods_compact.go # Compaction operations (~200 lines)\n├── methods_comments.go # Comment operations (~150 lines)\n├── storage_cache.go # Storage caching logic (~300 lines)\n└── health.go # Health \u0026 metrics (~200 lines)\n```\n\nMigration strategy:\n1. Create new files with appropriate methods\n2. Keep `server.go` as main file with core server logic\n3. Test incrementally after each file split\n4. Final verification with full test suite","acceptance_criteria":"- All 50 methods split into appropriate files\n- Each file \u003c500 LOC\n- All methods remain on `*Server` receiver (no behavior change)\n- All tests pass: `go test ./internal/rpc/...`\n- Verify daemon works: start daemon, run operations, check health\n- Update internal documentation if needed\n- No change to public API","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-28T14:21:37.51524-07:00","updated_at":"2025-10-28T14:21:37.51524-07:00","closed_at":"2025-10-28T14:11:04.399811-07:00"} {"id":"bd-57","content_hash":"3ab290915c117ec902bda1761e8c27850512f3fd4b494a93546c44b397d573a3","title":"bd resolve-conflicts - Git merge conflict resolver","description":"Automatically resolve JSONL merge conflicts.\n\nModes:\n- Mechanical: ID remapping (no AI)\n- AI-assisted: Smart merge/keep decisions\n- Interactive: Review each conflict\n\nHandles \u003c\u003c\u003c\u003c\u003c\u003c\u003c conflict markers in .beads/beads.jsonl\n\nFiles: cmd/bd/resolve_conflicts.go (new)","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-28T14:48:17.457619-07:00","updated_at":"2025-10-28T15:47:33.037021-07:00","closed_at":"2025-10-28T15:47:33.037021-07:00"} {"id":"bd-58","content_hash":"04b157cdc3fb162be6695517c10365c91ed14f69fad56a7bfc2b88d6b742ac38","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":"open","priority":1,"issue_type":"task","created_at":"2025-10-29T19:42:29.852745-07:00","updated_at":"2025-10-29T19:42:29.852745-07:00"} @@ -138,6 +153,8 @@ {"id":"bd-67","content_hash":"3979df7395526a6796508aa1ed1e89c4fedc46ee5c2b79dd85066c8a78c8487a","title":"Create cmd/bd/daemon_event_loop.go (~200 LOC)","description":"Implement runEventDrivenLoop to replace polling ticker. Coordinate FileWatcher, mutation events, debouncer. Include health check ticker (60s) for daemon validation.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-28T16:20:02.429383-07:00","updated_at":"2025-10-28T16:20:02.429383-07:00","closed_at":"2025-10-28T12:30:44.067036-07:00"} {"id":"bd-68","content_hash":"37e71aade254736849f32c41515f554bac4b8b014ac50b58e4be7cf67973d4b0","title":"Add fsnotify dependency to go.mod","description":"","status":"in_progress","priority":1,"issue_type":"task","created_at":"2025-10-28T16:20:02.429763-07:00","updated_at":"2025-10-28T16:20:02.429763-07:00"} {"id":"bd-69","content_hash":"a4e81b23d88d41c8fd3fe31fb7ef387f99cb54ea42a6baa210ede436ecce3288","title":"Replace getStorageForRequest with Direct Access","description":"Replace all getStorageForRequest(req) calls with s.storage","acceptance_criteria":"- No references to getStorageForRequest() in codebase (except in deleted file)\n- All handlers use s.storage directly\n- Code compiles without errors\n\nFiles to update:\n- internal/rpc/server_issues_epics.go (~8 calls)\n- internal/rpc/server_labels_deps_comments.go (~4 calls)\n- internal/rpc/server_compact.go (~2 calls)\n- internal/rpc/server_export_import_auto.go (~2 calls)\n- internal/rpc/server_routing_validation_diagnostics.go (~1 call)\n\nPattern: store, err := s.getStorageForRequest(req) → store := s.storage","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-28T16:20:02.430127-07:00","updated_at":"2025-10-28T19:20:58.312809-07:00","closed_at":"2025-10-28T19:20:58.312809-07:00"} +{"id":"bd-6b82c2e3","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-30T14:24:47.272891-07:00","closed_at":"2025-10-30T14:24:47.272891-07:00"} +{"id":"bd-6ef0199b","content_hash":"a44dbe61a808cd010cca529064732476742a1d39bfad4187b3e7fe43a8c5e6ea","title":"Port N-way test to verify hash IDs prevent collisions","description":"Create TestHashIDsNClones that:\n- Sets up 5 clones like TestFiveCloneCollision\n- Each clone creates 10 issues offline (different titles)\n- Sync in random order\n- Verify all clones converge to 50 issues with ZERO collisions\n- Verify all IDs are hash-based (bd-a3f8e9a2 format)\n- Prove we need zero collision resolution rounds\n\nThis replaces bd-175 which was too abstract. We can port the existing beads_nway_test.go structure but expect zero collisions.","status":"open","priority":1,"issue_type":"task","created_at":"2025-10-30T16:14:59.126766-07:00","updated_at":"2025-10-30T16:14:59.126766-07:00","dependencies":[{"issue_id":"bd-6ef0199b","depends_on_id":"bd-178","type":"blocks","created_at":"2025-10-30T16:14:59.128072-07:00","created_by":"stevey"},{"issue_id":"bd-6ef0199b","depends_on_id":"bd-172","type":"blocks","created_at":"2025-10-30T16:15:01.963945-07:00","created_by":"stevey"}]} {"id":"bd-7","content_hash":"e88e5d98a2a5bebc38b3ac505b00687bfe78bd72654bd0c756bceee4a01e15f5","title":"Enforce daemon singleton per workspace with file locking","description":"Agent in ~/src/wyvern discovered 4 simultaneous daemon processes running, causing infinite directory recursion (.beads/.beads/.beads/...). Each daemon used relative paths and created nested .beads/ directories.\n\nRoot cause: No singleton enforcement. Multiple `bd daemon` processes can start in same workspace.\n\nExpected: One daemon per workspace (each workspace = separate .beads/ dir with bd.sock)\nActual: Multiple daemons can run simultaneously in same workspace\n\nNote: Separate git clones = separate workspaces = separate daemons (correct). Git worktrees share .beads/ and have known limitations (documented, use --no-daemon).","design":"Use flock (file locking) on daemon socket or database file to enforce singleton:\n\n1. On daemon start, attempt exclusive lock on .beads/bd.sock or .beads/daemon.lock\n2. If lock held by another process, refuse to start (exit with clear error)\n3. Hold lock for lifetime of daemon process\n4. Release lock on daemon shutdown\n\nAlternative: Use PID file with stale detection (check if PID is still running)\n\nImplementation location: Daemon startup code in cmd/bd/ or internal/daemon/","acceptance_criteria":"1. Starting second daemon process in same workspace fails with clear error\n2. Test: Start daemon, attempt second start, verify failure\n3. Killing daemon releases lock, allowing new daemon to start\n4. No infinite .beads/ directory recursion possible\n5. Works correctly with auto-start mechanism","status":"in_progress","priority":0,"issue_type":"bug","created_at":"2025-10-25T23:13:12.269549-07:00","updated_at":"2025-10-27T22:22:23.814937-07:00"} {"id":"bd-70","content_hash":"c0b1677fe3f4aa3f395ae4d79bff5362632d5db26477bf571c09f9177b8741ef","title":"Event-driven daemon architecture","description":"Replace 5-second polling sync loop with event-driven architecture that reacts instantly to changes. Eliminates stale data issues while reducing CPU ~60%. Key components: FileWatcher (fsnotify), Debouncer (500ms), RPC mutation events, optional git hooks. Target latency: \u003c500ms (vs 5000ms). See event_driven_daemon.md for full design.","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-10-28T16:20:02.430479-07:00","updated_at":"2025-10-28T16:30:26.631191-07:00","closed_at":"2025-10-28T16:30:26.631191-07:00"} {"id":"bd-72","content_hash":"a596aa8d6114d4938471e181ebc30da5d0315f74fd711a92dbbb83f5d0e7af88","title":"Create cmd/bd/daemon_debouncer.go (~60 LOC)","description":"Implement Debouncer to batch rapid events into single action. Default 500ms, configurable via BEADS_DEBOUNCE_MS. Thread-safe with mutex.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-28T16:20:02.431118-07:00","updated_at":"2025-10-28T16:20:02.431118-07:00","closed_at":"2025-10-28T12:03:35.614191-07:00"}