diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 71754e67..4c9f9c37 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -56,7 +56,7 @@ {"id":"bd-4or","title":"Add tests for daemon functionality","description":"Critical daemon functions have 0% test coverage including daemon lifecycle, health checks, and RPC server functionality. These are essential for system reliability and need comprehensive test coverage.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-18T07:00:26.916050465-07:00","updated_at":"2025-12-19T09:54:57.017114822-07:00","closed_at":"2025-12-18T12:29:06.134014366-07:00","dependencies":[{"issue_id":"bd-4or","depends_on_id":"bd-6ss","type":"discovered-from","created_at":"2025-12-18T07:00:26.919347253-07:00","created_by":"matt"}]} {"id":"bd-4p3k","title":"Release v0.34.0","description":"Minor version release for beads v0.34.0. This bead serves as my persistent work assignment; the actual release steps are tracked in an attached wisp.","status":"tombstone","priority":1,"issue_type":"task","created_at":"2025-12-22T03:03:20.73092-08:00","updated_at":"2025-12-22T03:05:03.168622-08:00","deleted_at":"2025-12-22T03:05:03.168622-08:00","deleted_by":"daemon","delete_reason":"delete","original_type":"task"} {"id":"bd-4q8","title":"bd cleanup --hard should skip tombstone creation for true permanent deletion","description":"## Problem\n\nWhen using bd cleanup --hard --older-than N --force, the command:\n1. Deletes closed issues older than N days (converting them to tombstones with NOW timestamp)\n2. Then tries to prune tombstones older than N days (finds none because they were just created)\n\nThis leaves the database bloated with fresh tombstones that will not be pruned.\n\n## Expected Behavior\n\nIn --hard mode, the deletion should be permanent without creating tombstones, since the user explicitly requested bypassing sync safety.\n\n## Workaround\n\nManually delete from database: sqlite3 .beads/beads.db 'DELETE FROM issues WHERE status=tombstone'\n\n## Fix Options\n\n1. In --hard mode, use a different delete path that does not create tombstones\n2. After deleting, immediately prune the just-created tombstones regardless of age\n3. Pass a skip_tombstone flag to the delete operation\n\nOption 1 is cleanest - --hard should mean permanent delete without tombstone.","status":"tombstone","priority":1,"issue_type":"bug","created_at":"2025-12-16T01:33:36.580657-08:00","updated_at":"2025-12-17T16:11:17.070763-08:00","deleted_at":"2025-12-17T16:11:17.070763-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"bug"} -{"id":"bd-4qfb","title":"Improve bd doctor output formatting for better readability","description":"Improve bd doctor output formatting for better readability.\n\n## Current State\nDoctor output is a wall of text with:\n- All checks shown (even passing ones)\n- No visual hierarchy\n- Hard to spot failures in long output\n\n## Target Output\n\n```\n$ bd doctor\n\nbd doctor v0.35.0\n\nSummary: 24 checks passed, 1 warning, 0 errors\n\n─────────────────────────────────────────────────\n⚠ Warnings (1)\n─────────────────────────────────────────────────\n\n[hooks] Git hooks outdated\n Current version: 0.34.0\n Latest version: 0.35.0\n Fix: bd hooks install\n\n─────────────────────────────────────────────────\n✓ Passed (24) [use --verbose to show details]\n─────────────────────────────────────────────────\n```\n\nWith --verbose:\n```\n$ bd doctor --verbose\n\nbd doctor v0.35.0\n\nSummary: 24 checks passed, 1 warning, 0 errors\n\n─────────────────────────────────────────────────\n⚠ Warnings (1)\n─────────────────────────────────────────────────\n\n[hooks] Git hooks outdated\n ...\n\n─────────────────────────────────────────────────\n✓ Passed (24)\n─────────────────────────────────────────────────\n\n Database\n ✓ Database exists\n ✓ Database readable\n ✓ Schema up to date\n \n Git Hooks\n ✓ Pre-commit hook installed\n ✓ Post-merge hook installed\n ⚠ Hooks version mismatch (see above)\n \n Sync\n ✓ Sync branch configured\n ✓ Remote accessible\n ...\n```\n\n## Implementation\n\n### 1. Add check categories (cmd/bd/doctor/categories.go)\n\n```go\ntype Category string\n\nconst (\n CatDatabase Category = \"Database\"\n CatHooks Category = \"Git Hooks\"\n CatSync Category = \"Sync\"\n CatDaemon Category = \"Daemon\"\n CatConfig Category = \"Configuration\"\n CatIntegrity Category = \"Data Integrity\"\n)\n\n// Assign categories to checks\nvar checkCategories = map[string]Category{\n \"database-exists\": CatDatabase,\n \"database-readable\": CatDatabase,\n \"schema-version\": CatDatabase,\n \"pre-commit-hook\": CatHooks,\n \"post-merge-hook\": CatHooks,\n \"hooks-version\": CatHooks,\n \"sync-branch\": CatSync,\n \"remote-access\": CatSync,\n // ... etc\n}\n```\n\n### 2. Add --verbose flag\n\n```go\n// In cmd/bd/doctor.go init()\ndoctorCmd.Flags().BoolP(\"verbose\", \"v\", false, \"Show all checks including passed\")\n```\n\n### 3. Create formatter (cmd/bd/doctor/format.go)\n\n```go\ntype Formatter struct {\n verbose bool\n noColor bool\n}\n\nfunc (f *Formatter) Format(results []CheckResult) string {\n var buf strings.Builder\n \n // Count by status\n passed, warnings, errors := countByStatus(results)\n \n // Header\n buf.WriteString(fmt.Sprintf(\"bd doctor v%s\\n\\n\", version.Version))\n buf.WriteString(fmt.Sprintf(\"Summary: %d passed, %d warnings, %d errors\\n\\n\", \n passed, warnings, errors))\n \n // Errors section (always show)\n if errors \u003e 0 {\n f.writeSection(\u0026buf, \"✗ Errors\", filterByStatus(results, StatusError))\n }\n \n // Warnings section (always show)\n if warnings \u003e 0 {\n f.writeSection(\u0026buf, \"⚠ Warnings\", filterByStatus(results, StatusWarning))\n }\n \n // Passed section (only with --verbose)\n if f.verbose \u0026\u0026 passed \u003e 0 {\n f.writePassedSection(\u0026buf, filterByStatus(results, StatusPassed))\n } else if passed \u003e 0 {\n buf.WriteString(fmt.Sprintf(\"✓ Passed (%d) [use --verbose to show details]\\n\", passed))\n }\n \n return buf.String()\n}\n\nfunc (f *Formatter) writeSection(buf *strings.Builder, title string, results []CheckResult) {\n buf.WriteString(\"─────────────────────────────────────────────────\\n\")\n buf.WriteString(title + \"\\n\")\n buf.WriteString(\"─────────────────────────────────────────────────\\n\\n\")\n \n for _, r := range results {\n buf.WriteString(fmt.Sprintf(\"[%s] %s\\n\", r.CheckName, r.Message))\n if r.Details != \"\" {\n buf.WriteString(fmt.Sprintf(\" %s\\n\", r.Details))\n }\n if r.Fix != \"\" {\n buf.WriteString(fmt.Sprintf(\" Fix: %s\\n\", r.Fix))\n }\n buf.WriteString(\"\\n\")\n }\n}\n\nfunc (f *Formatter) writePassedSection(buf *strings.Builder, results []CheckResult) {\n // Group by category\n byCategory := groupByCategory(results)\n \n buf.WriteString(\"─────────────────────────────────────────────────\\n\")\n buf.WriteString(fmt.Sprintf(\"✓ Passed (%d)\\n\", len(results)))\n buf.WriteString(\"─────────────────────────────────────────────────\\n\\n\")\n \n for _, cat := range categoryOrder {\n if checks, ok := byCategory[cat]; ok {\n buf.WriteString(fmt.Sprintf(\" %s\\n\", cat))\n for _, r := range checks {\n buf.WriteString(fmt.Sprintf(\" ✓ %s\\n\", r.Message))\n }\n buf.WriteString(\"\\n\")\n }\n }\n}\n```\n\n### 4. Update run function\n\n```go\nfunc runDoctor(cmd *cobra.Command, args []string) {\n verbose, _ := cmd.Flags().GetBool(\"verbose\")\n noColor, _ := cmd.Flags().GetBool(\"no-color\")\n \n results := runAllChecks()\n \n formatter := \u0026Formatter{verbose: verbose, noColor: noColor}\n fmt.Print(formatter.Format(results))\n \n // Exit code based on results\n if hasErrors(results) {\n os.Exit(1)\n }\n}\n```\n\n## Files to Modify\n\n1. **cmd/bd/doctor.go** - Add --verbose flag, update run function\n2. **cmd/bd/doctor/format.go** - New file for formatting logic\n3. **cmd/bd/doctor/categories.go** - New file for check categorization\n4. **cmd/bd/doctor/common.go** - Add Status field to CheckResult if missing\n\n## Testing\n\n```bash\n# Default output (concise)\nbd doctor\n\n# Verbose output\nbd doctor --verbose\n\n# JSON output (should still work)\nbd doctor --json\n```\n\n## Success Criteria\n- Summary line at top with counts\n- Only failures/warnings shown by default\n- --verbose shows grouped passed checks\n- Visual separators between sections\n- Exit code 1 if errors, 0 otherwise","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-13T09:29:27.557578+11:00","updated_at":"2025-12-23T12:35:55.008696-08:00"} +{"id":"bd-4qfb","title":"Improve bd doctor output formatting for better readability","description":"Improve bd doctor output formatting for better readability.\n\n## Current State\nDoctor output is a wall of text with:\n- All checks shown (even passing ones)\n- No visual hierarchy\n- Hard to spot failures in long output\n\n## Target Output\n\n```\n$ bd doctor\n\nbd doctor v0.35.0\n\nSummary: 24 checks passed, 1 warning, 0 errors\n\n─────────────────────────────────────────────────\n⚠ Warnings (1)\n─────────────────────────────────────────────────\n\n[hooks] Git hooks outdated\n Current version: 0.34.0\n Latest version: 0.35.0\n Fix: bd hooks install\n\n─────────────────────────────────────────────────\n✓ Passed (24) [use --verbose to show details]\n─────────────────────────────────────────────────\n```\n\nWith --verbose:\n```\n$ bd doctor --verbose\n\nbd doctor v0.35.0\n\nSummary: 24 checks passed, 1 warning, 0 errors\n\n─────────────────────────────────────────────────\n⚠ Warnings (1)\n─────────────────────────────────────────────────\n\n[hooks] Git hooks outdated\n ...\n\n─────────────────────────────────────────────────\n✓ Passed (24)\n─────────────────────────────────────────────────\n\n Database\n ✓ Database exists\n ✓ Database readable\n ✓ Schema up to date\n \n Git Hooks\n ✓ Pre-commit hook installed\n ✓ Post-merge hook installed\n ⚠ Hooks version mismatch (see above)\n \n Sync\n ✓ Sync branch configured\n ✓ Remote accessible\n ...\n```\n\n## Implementation\n\n### 1. Add check categories (cmd/bd/doctor/categories.go)\n\n```go\ntype Category string\n\nconst (\n CatDatabase Category = \"Database\"\n CatHooks Category = \"Git Hooks\"\n CatSync Category = \"Sync\"\n CatDaemon Category = \"Daemon\"\n CatConfig Category = \"Configuration\"\n CatIntegrity Category = \"Data Integrity\"\n)\n\n// Assign categories to checks\nvar checkCategories = map[string]Category{\n \"database-exists\": CatDatabase,\n \"database-readable\": CatDatabase,\n \"schema-version\": CatDatabase,\n \"pre-commit-hook\": CatHooks,\n \"post-merge-hook\": CatHooks,\n \"hooks-version\": CatHooks,\n \"sync-branch\": CatSync,\n \"remote-access\": CatSync,\n // ... etc\n}\n```\n\n### 2. Add --verbose flag\n\n```go\n// In cmd/bd/doctor.go init()\ndoctorCmd.Flags().BoolP(\"verbose\", \"v\", false, \"Show all checks including passed\")\n```\n\n### 3. Create formatter (cmd/bd/doctor/format.go)\n\n```go\ntype Formatter struct {\n verbose bool\n noColor bool\n}\n\nfunc (f *Formatter) Format(results []CheckResult) string {\n var buf strings.Builder\n \n // Count by status\n passed, warnings, errors := countByStatus(results)\n \n // Header\n buf.WriteString(fmt.Sprintf(\"bd doctor v%s\\n\\n\", version.Version))\n buf.WriteString(fmt.Sprintf(\"Summary: %d passed, %d warnings, %d errors\\n\\n\", \n passed, warnings, errors))\n \n // Errors section (always show)\n if errors \u003e 0 {\n f.writeSection(\u0026buf, \"✗ Errors\", filterByStatus(results, StatusError))\n }\n \n // Warnings section (always show)\n if warnings \u003e 0 {\n f.writeSection(\u0026buf, \"⚠ Warnings\", filterByStatus(results, StatusWarning))\n }\n \n // Passed section (only with --verbose)\n if f.verbose \u0026\u0026 passed \u003e 0 {\n f.writePassedSection(\u0026buf, filterByStatus(results, StatusPassed))\n } else if passed \u003e 0 {\n buf.WriteString(fmt.Sprintf(\"✓ Passed (%d) [use --verbose to show details]\\n\", passed))\n }\n \n return buf.String()\n}\n\nfunc (f *Formatter) writeSection(buf *strings.Builder, title string, results []CheckResult) {\n buf.WriteString(\"─────────────────────────────────────────────────\\n\")\n buf.WriteString(title + \"\\n\")\n buf.WriteString(\"─────────────────────────────────────────────────\\n\\n\")\n \n for _, r := range results {\n buf.WriteString(fmt.Sprintf(\"[%s] %s\\n\", r.CheckName, r.Message))\n if r.Details != \"\" {\n buf.WriteString(fmt.Sprintf(\" %s\\n\", r.Details))\n }\n if r.Fix != \"\" {\n buf.WriteString(fmt.Sprintf(\" Fix: %s\\n\", r.Fix))\n }\n buf.WriteString(\"\\n\")\n }\n}\n\nfunc (f *Formatter) writePassedSection(buf *strings.Builder, results []CheckResult) {\n // Group by category\n byCategory := groupByCategory(results)\n \n buf.WriteString(\"─────────────────────────────────────────────────\\n\")\n buf.WriteString(fmt.Sprintf(\"✓ Passed (%d)\\n\", len(results)))\n buf.WriteString(\"─────────────────────────────────────────────────\\n\\n\")\n \n for _, cat := range categoryOrder {\n if checks, ok := byCategory[cat]; ok {\n buf.WriteString(fmt.Sprintf(\" %s\\n\", cat))\n for _, r := range checks {\n buf.WriteString(fmt.Sprintf(\" ✓ %s\\n\", r.Message))\n }\n buf.WriteString(\"\\n\")\n }\n }\n}\n```\n\n### 4. Update run function\n\n```go\nfunc runDoctor(cmd *cobra.Command, args []string) {\n verbose, _ := cmd.Flags().GetBool(\"verbose\")\n noColor, _ := cmd.Flags().GetBool(\"no-color\")\n \n results := runAllChecks()\n \n formatter := \u0026Formatter{verbose: verbose, noColor: noColor}\n fmt.Print(formatter.Format(results))\n \n // Exit code based on results\n if hasErrors(results) {\n os.Exit(1)\n }\n}\n```\n\n## Files to Modify\n\n1. **cmd/bd/doctor.go** - Add --verbose flag, update run function\n2. **cmd/bd/doctor/format.go** - New file for formatting logic\n3. **cmd/bd/doctor/categories.go** - New file for check categorization\n4. **cmd/bd/doctor/common.go** - Add Status field to CheckResult if missing\n\n## Testing\n\n```bash\n# Default output (concise)\nbd doctor\n\n# Verbose output\nbd doctor --verbose\n\n# JSON output (should still work)\nbd doctor --json\n```\n\n## Success Criteria\n- Summary line at top with counts\n- Only failures/warnings shown by default\n- --verbose shows grouped passed checks\n- Visual separators between sections\n- Exit code 1 if errors, 0 otherwise","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-13T09:29:27.557578+11:00","updated_at":"2025-12-23T12:35:55.008696-08:00","dependencies":[{"issue_id":"bd-4qfb","depends_on_id":"bd-iz5t","type":"parent-child","created_at":"2025-12-23T12:44:07.972517-08:00","created_by":"daemon"}]} {"id":"bd-4ri","title":"Fix TestFallbackToDirectModeEnablesFlush deadlock causing 10min test timeout","description":"## Problem\n\nTestFallbackToDirectModeEnablesFlush in direct_mode_test.go deadlocks for 9m59s before timing out, causing the entire test suite to take 10+ minutes instead of \u003c10 seconds.\n\n## Root Cause\n\nDatabase lock contention between test cleanup and flushToJSONL():\n- Test cleanup (line 36) tries to close DB via defer\n- flushToJSONL() (line 132) is still accessing DB\n- Results in deadlock: database/sql.(*DB).Close() waits for mutex while GetJSONLFileHash() holds it\n\n## Stack Trace Evidence\n\n```\ngoroutine 512 [sync.Mutex.Lock, 9 minutes]:\ndatabase/sql.(*DB).Close(0x14000643790)\n .../database/sql/sql.go:927 +0x84\ngithub.com/steveyegge/beads/cmd/bd.TestFallbackToDirectModeEnablesFlush.func1()\n .../direct_mode_test.go:36 +0xf4\n\nWhile goroutine running flushToJSONL() holds DB connection via GetJSONLFileHash()\n```\n\n## Impact\n\n- Test suite: 10+ minutes → should be \u003c10 seconds\n- ALL other tests pass in ~4 seconds\n- This ONE test accounts for 99.9% of test runtime\n\n## Related\n\nThis is the EXACT same issue documented in MAIN_TEST_REFACTOR_NOTES.md for why main_test.go refactoring was deferred - global state manipulation + DB cleanup = deadlock.\n\n## Fix Approaches\n\n1. **Add proper cleanup sequencing** - stop flush goroutines BEFORE closing DB\n2. **Use test-specific DB lifecycle** - ensure flush completes before cleanup\n3. **Mock the flush mechanism** - avoid real DB for testing this code path \n4. **Add explicit timeout handling** - fail fast with clear error instead of hanging\n\n## Files\n\n- cmd/bd/direct_mode_test.go:36-132\n- cmd/bd/autoflush.go:353 (validateJSONLIntegrity)\n- cmd/bd/autoflush.go:508 (flushToJSONLWithState)\n\n## Acceptance\n\n- Test passes without timeout\n- Test suite completes in \u003c10 seconds\n- No deadlock between cleanup and flush operations","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-11-21T20:09:00.794372-05:00","updated_at":"2025-12-17T23:13:40.533279-08:00","closed_at":"2025-12-17T17:25:07.626617-08:00"} {"id":"bd-4sfl","title":"Merge: bd-14ie","description":"branch: polecat/toast\ntarget: main\nsource_issue: bd-14ie\nrig: beads","status":"closed","priority":2,"issue_type":"merge-request","created_at":"2025-12-19T23:23:37.360782-08:00","updated_at":"2025-12-20T23:17:26.997276-08:00","closed_at":"2025-12-20T23:17:26.997276-08:00","close_reason":"Branches nuked, MRs obsolete"} {"id":"bd-4uoc","title":"Code Review Followup Summary: PR #481 + PR #551","description":"## Merged PRs Summary\n\n### PR #551: Persist close_reason to issues table\n- ✅ Merged successfully\n- ✅ Bug fix: close_reason now persisted in database column (not just events table)\n- ✅ Comprehensive test coverage added\n- ✅ Handles reopen case (clearing close_reason)\n\n**Followup Issues Filed:**\n- bd-lxzx: Document close_reason in JSONL export format\n- bd-077e: Update CLI documentation for close_reason field\n\n---\n\n### PR #481: Context Engineering Optimizations (80-90% context reduction)\n- ✅ Merged successfully \n- ✅ Lazy tool discovery: discover_tools() + get_tool_info()\n- ✅ Minimal issue models: IssueMinimal (~80% smaller than full Issue)\n- ✅ Result compaction: Auto-compacts results \u003e20 items\n- ✅ All 28 tests passing\n- ⚠️ Breaking change: ready() and list() return type changed\n\n**Followup Issues Filed:**\n- bd-b318: Add integration tests for CompactedResult\n- bd-4u2b: Make compaction settings configurable (THRESHOLD, PREVIEW_COUNT)\n- bd-2kf8: Document CompactedResult response format in CONTEXT_ENGINEERING.md\n- bd-pdr2: Document backwards compatibility considerations\n\n---\n\n## Overall Assessment\n\nBoth PRs are production-ready with solid implementations. All critical functionality works and tests pass. Followup issues focus on:\n1. Documentation improvements (5 issues)\n2. Integration test coverage (1 issue)\n3. Configuration flexibility (1 issue)\n4. Backwards compatibility guidance (1 issue)\n\nNo critical bugs or design issues found.\n\n## Review Completed By\nCode review process completed. Issues auto-created for tracking improvements.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-14T14:25:59.214886-08:00","updated_at":"2025-12-14T14:25:59.214886-08:00","dependencies":[{"issue_id":"bd-4uoc","depends_on_id":"bd-otf4","type":"discovered-from","created_at":"2025-12-14T14:25:59.216884-08:00","created_by":"stevey","metadata":"{}"},{"issue_id":"bd-4uoc","depends_on_id":"bd-z86n","type":"discovered-from","created_at":"2025-12-14T14:25:59.217296-08:00","created_by":"stevey","metadata":"{}"}]} @@ -131,7 +131,7 @@ {"id":"bd-ao0s","title":"bd graph crashes with --no-daemon on closed issues","description":"The `bd graph` command panics with nil pointer dereference when using `--no-daemon` flag on an issue with closed children.\n\n**Reproduction:**\n```bash\nbd graph bd-qqc --no-daemon\n# panic: runtime error: invalid memory address or nil pointer dereference\n# in main.computeDependencyCounts\n```\n\n**Stack trace:**\n```\npanic: runtime error: invalid memory address or nil pointer dereference\n[signal SIGSEGV: segmentation violation code=0x2 addr=0x20 pc=0x1010bdfb0]\n\ngoroutine 1 [running]:\nmain.computeDependencyCounts(...)\n /Users/stevey/gt/beads/crew/emma/cmd/bd/graph.go:428\nmain.renderGraph(0x1400033bb80, 0x0)\n /Users/stevey/gt/beads/crew/emma/cmd/bd/graph.go:307 +0x300\n```\n\n**Location:** cmd/bd/graph.go:428 - computeDependencyCounts() not handling nil case","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-18T22:57:36.972585-08:00","updated_at":"2025-12-20T01:13:29.206821-08:00","closed_at":"2025-12-20T01:13:29.206821-08:00","close_reason":"Fixed: pass subgraph instead of nil to renderGraph"} {"id":"bd-au0","title":"Command Set Standardization \u0026 Flag Consistency","description":"Comprehensive improvements to bd command set based on 2025 audit findings.\n\n## Background\nSee docs/command-audit-2025.md for detailed analysis.\n\n## Goals\n1. Standardize flag naming and behavior across all commands\n2. Add missing flags for feature parity\n3. Fix naming confusion\n4. Improve consistency in JSON output\n\n## Success Criteria\n- All mutating commands support --dry-run (no --preview variants)\n- bd update supports label operations\n- bd search has filter parity with bd list\n- Priority flags accept both int and P0-P4 format everywhere\n- JSON output is consistent across all commands","status":"open","priority":2,"issue_type":"epic","created_at":"2025-11-21T21:05:55.672749-05:00","updated_at":"2025-11-21T21:05:55.672749-05:00"} {"id":"bd-au0.10","title":"Add global verbosity flags (--verbose, --quiet)","description":"Add consistent verbosity controls across all commands.\n\n**Current state:**\n- bd init has --quiet flag\n- No other commands have verbosity controls\n- Debug output controlled by BD_VERBOSE env var\n\n**Proposal:**\nAdd persistent flags:\n- --verbose / -v: Enable debug output\n- --quiet / -q: Suppress non-essential output\n\n**Implementation:**\n- Add to rootCmd.PersistentFlags()\n- Replace BD_VERBOSE checks with flag checks\n- Standardize output levels:\n * Quiet: Errors only\n * Normal: Errors + success messages\n * Verbose: Errors + success + debug info\n\n**Files to modify:**\n- cmd/bd/main.go (add flags)\n- internal/debug/debug.go (respect flags)\n- Update all commands to respect quiet mode\n\n**Testing:**\n- Verify --verbose shows debug output\n- Verify --quiet suppresses normal output\n- Ensure errors always show regardless of mode","status":"open","priority":3,"issue_type":"task","created_at":"2025-11-21T21:08:21.600209-05:00","updated_at":"2025-11-21T21:08:21.600209-05:00","dependencies":[{"issue_id":"bd-au0.10","depends_on_id":"bd-au0","type":"parent-child","created_at":"2025-11-21T21:08:21.602557-05:00","created_by":"daemon","metadata":"{}"}]} -{"id":"bd-au0.5","title":"Add date and priority filters to bd search","description":"Add date and priority filters to bd search for parity with bd list.\n\n## Current State\nbd search supports: --status, --type, --assignee, --label, --limit\nbd list supports: all of the above PLUS date ranges and priority filters\n\n## Filters to Add\n\n### Priority Filters\n```bash\nbd search \"query\" --priority 1 # Exact priority\nbd search \"query\" --priority-min 0 # P0 and above (higher priority)\nbd search \"query\" --priority-max 2 # P2 and below (lower priority)\n```\n\n### Date Filters\n```bash\nbd search \"query\" --created-after 2025-01-01\nbd search \"query\" --created-before 2025-12-31\nbd search \"query\" --updated-after 2025-01-01\nbd search \"query\" --closed-after 2025-01-01\n```\n\n### Content Filters\n```bash\nbd search \"query\" --desc-contains \"bug\"\nbd search \"query\" --notes-contains \"todo\"\nbd search \"query\" --empty-description # Issues with no description\nbd search \"query\" --no-assignee # Unassigned issues\nbd search \"query\" --no-labels # Issues without labels\n```\n\n## Files to Modify\n\n### 1. cmd/bd/search.go\nAdd flag definitions in init():\n```go\nsearchCmd.Flags().IntP(\"priority\", \"p\", -1, \"Filter by exact priority (0-4)\")\nsearchCmd.Flags().Int(\"priority-min\", -1, \"Filter by minimum priority\")\nsearchCmd.Flags().Int(\"priority-max\", -1, \"Filter by maximum priority\")\nsearchCmd.Flags().String(\"created-after\", \"\", \"Filter by creation date (YYYY-MM-DD)\")\nsearchCmd.Flags().String(\"created-before\", \"\", \"Filter by creation date\")\nsearchCmd.Flags().String(\"updated-after\", \"\", \"Filter by update date\")\nsearchCmd.Flags().String(\"updated-before\", \"\", \"Filter by update date\")\nsearchCmd.Flags().String(\"closed-after\", \"\", \"Filter by close date\")\nsearchCmd.Flags().String(\"closed-before\", \"\", \"Filter by close date\")\nsearchCmd.Flags().String(\"desc-contains\", \"\", \"Filter by description content\")\nsearchCmd.Flags().String(\"notes-contains\", \"\", \"Filter by notes content\")\nsearchCmd.Flags().Bool(\"empty-description\", false, \"Filter issues with empty description\")\nsearchCmd.Flags().Bool(\"no-assignee\", false, \"Filter unassigned issues\")\nsearchCmd.Flags().Bool(\"no-labels\", false, \"Filter issues without labels\")\n```\n\n### 2. internal/rpc/protocol.go\nUpdate SearchArgs struct:\n```go\ntype SearchArgs struct {\n Query string\n Filter types.IssueFilter\n // Already has most fields via IssueFilter\n}\n```\n\nNote: types.IssueFilter already has these fields - just need to wire them up!\n\n### 3. cmd/bd/search.go Run function\nParse flags and populate filter:\n```go\nif priority, _ := cmd.Flags().GetInt(\"priority\"); priority \u003e= 0 {\n filter.Priority = \u0026priority\n}\nif createdAfter, _ := cmd.Flags().GetString(\"created-after\"); createdAfter != \"\" {\n t, err := time.Parse(\"2006-01-02\", createdAfter)\n if err != nil {\n FatalError(\"invalid date format for --created-after: %v\", err)\n }\n filter.CreatedAfter = \u0026t\n}\n// ... similar for other flags\n```\n\n## Implementation Steps\n\n1. **Check types.IssueFilter** - verify all needed fields exist\n2. **Add flags to search.go** init()\n3. **Parse flags** in Run function\n4. **Pass to SearchIssues** via filter\n5. **Test all combinations**\n\n## Testing\n```bash\n# Create test issues\nbd create \"Test P1\" -p 1\nbd create \"Test P2\" -p 2 --description \"Has description\"\n\n# Test filters\nbd search \"\" --priority 1\nbd search \"\" --priority-min 0 --priority-max 1\nbd search \"\" --empty-description\nbd search \"\" --desc-contains \"description\"\n```\n\n## Success Criteria\n- All filters work in both direct and daemon mode\n- Date parsing handles YYYY-MM-DD format\n- --json output includes filtered results\n- Help text documents all new flags","status":"open","priority":1,"issue_type":"task","created_at":"2025-11-21T21:07:05.496726-05:00","updated_at":"2025-12-23T12:33:54.30175-08:00","dependencies":[{"issue_id":"bd-au0.5","depends_on_id":"bd-au0","type":"parent-child","created_at":"2025-11-21T21:07:05.497762-05:00","created_by":"daemon","metadata":"{}"}]} +{"id":"bd-au0.5","title":"Add date and priority filters to bd search","description":"Add date and priority filters to bd search for parity with bd list.\n\n## Current State\nbd search supports: --status, --type, --assignee, --label, --limit\nbd list supports: all of the above PLUS date ranges and priority filters\n\n## Filters to Add\n\n### Priority Filters\n```bash\nbd search \"query\" --priority 1 # Exact priority\nbd search \"query\" --priority-min 0 # P0 and above (higher priority)\nbd search \"query\" --priority-max 2 # P2 and below (lower priority)\n```\n\n### Date Filters\n```bash\nbd search \"query\" --created-after 2025-01-01\nbd search \"query\" --created-before 2025-12-31\nbd search \"query\" --updated-after 2025-01-01\nbd search \"query\" --closed-after 2025-01-01\n```\n\n### Content Filters\n```bash\nbd search \"query\" --desc-contains \"bug\"\nbd search \"query\" --notes-contains \"todo\"\nbd search \"query\" --empty-description # Issues with no description\nbd search \"query\" --no-assignee # Unassigned issues\nbd search \"query\" --no-labels # Issues without labels\n```\n\n## Files to Modify\n\n### 1. cmd/bd/search.go\nAdd flag definitions in init():\n```go\nsearchCmd.Flags().IntP(\"priority\", \"p\", -1, \"Filter by exact priority (0-4)\")\nsearchCmd.Flags().Int(\"priority-min\", -1, \"Filter by minimum priority\")\nsearchCmd.Flags().Int(\"priority-max\", -1, \"Filter by maximum priority\")\nsearchCmd.Flags().String(\"created-after\", \"\", \"Filter by creation date (YYYY-MM-DD)\")\nsearchCmd.Flags().String(\"created-before\", \"\", \"Filter by creation date\")\nsearchCmd.Flags().String(\"updated-after\", \"\", \"Filter by update date\")\nsearchCmd.Flags().String(\"updated-before\", \"\", \"Filter by update date\")\nsearchCmd.Flags().String(\"closed-after\", \"\", \"Filter by close date\")\nsearchCmd.Flags().String(\"closed-before\", \"\", \"Filter by close date\")\nsearchCmd.Flags().String(\"desc-contains\", \"\", \"Filter by description content\")\nsearchCmd.Flags().String(\"notes-contains\", \"\", \"Filter by notes content\")\nsearchCmd.Flags().Bool(\"empty-description\", false, \"Filter issues with empty description\")\nsearchCmd.Flags().Bool(\"no-assignee\", false, \"Filter unassigned issues\")\nsearchCmd.Flags().Bool(\"no-labels\", false, \"Filter issues without labels\")\n```\n\n### 2. internal/rpc/protocol.go\nUpdate SearchArgs struct:\n```go\ntype SearchArgs struct {\n Query string\n Filter types.IssueFilter\n // Already has most fields via IssueFilter\n}\n```\n\nNote: types.IssueFilter already has these fields - just need to wire them up!\n\n### 3. cmd/bd/search.go Run function\nParse flags and populate filter:\n```go\nif priority, _ := cmd.Flags().GetInt(\"priority\"); priority \u003e= 0 {\n filter.Priority = \u0026priority\n}\nif createdAfter, _ := cmd.Flags().GetString(\"created-after\"); createdAfter != \"\" {\n t, err := time.Parse(\"2006-01-02\", createdAfter)\n if err != nil {\n FatalError(\"invalid date format for --created-after: %v\", err)\n }\n filter.CreatedAfter = \u0026t\n}\n// ... similar for other flags\n```\n\n## Implementation Steps\n\n1. **Check types.IssueFilter** - verify all needed fields exist\n2. **Add flags to search.go** init()\n3. **Parse flags** in Run function\n4. **Pass to SearchIssues** via filter\n5. **Test all combinations**\n\n## Testing\n```bash\n# Create test issues\nbd create \"Test P1\" -p 1\nbd create \"Test P2\" -p 2 --description \"Has description\"\n\n# Test filters\nbd search \"\" --priority 1\nbd search \"\" --priority-min 0 --priority-max 1\nbd search \"\" --empty-description\nbd search \"\" --desc-contains \"description\"\n```\n\n## Success Criteria\n- All filters work in both direct and daemon mode\n- Date parsing handles YYYY-MM-DD format\n- --json output includes filtered results\n- Help text documents all new flags","status":"open","priority":1,"issue_type":"task","created_at":"2025-11-21T21:07:05.496726-05:00","updated_at":"2025-12-23T12:33:54.30175-08:00","dependencies":[{"issue_id":"bd-au0.5","depends_on_id":"bd-au0","type":"parent-child","created_at":"2025-11-21T21:07:05.497762-05:00","created_by":"daemon","metadata":"{}"},{"issue_id":"bd-au0.5","depends_on_id":"bd-iz5t","type":"parent-child","created_at":"2025-12-23T12:44:07.657303-08:00","created_by":"daemon"}]} {"id":"bd-au0.6","title":"Add comprehensive filters to bd export","description":"Enhance bd export with filtering options for selective exports.\n\n**Currently only has:**\n- --status\n\n**Add filters:**\n- --label, --label-any\n- --assignee\n- --type\n- --priority, --priority-min, --priority-max\n- --created-after, --created-before\n- --updated-after, --updated-before\n\n**Use case:**\n- Export only open issues: bd export --status open\n- Export high-priority bugs: bd export --type bug --priority-max 1\n- Export recent issues: bd export --created-after 2025-01-01\n\n**Files to modify:**\n- cmd/bd/export.go\n- Reuse filter logic from list.go","status":"open","priority":1,"issue_type":"task","created_at":"2025-11-21T21:07:19.431307-05:00","updated_at":"2025-11-21T21:07:19.431307-05:00","dependencies":[{"issue_id":"bd-au0.6","depends_on_id":"bd-au0","type":"parent-child","created_at":"2025-11-21T21:07:19.432983-05:00","created_by":"daemon","metadata":"{}"}]} {"id":"bd-au0.7","title":"Audit and standardize JSON output across all commands","description":"Ensure consistent JSON format and error handling when --json flag is used.\n\n**Scope:**\n1. Verify all commands respect --json flag\n2. Standardize success response format\n3. Standardize error response format\n4. Document JSON schemas\n\n**Commands to audit:**\n- Core CRUD: create, update, delete, show, list, search ✓\n- Queries: ready, blocked, stale, count, stats, status\n- Deps: dep add/remove/tree/cycles\n- Labels: label commands\n- Comments: comments add/list/delete\n- Epics: epic status/close-eligible\n- Export/import: already support --json ✓\n\n**Testing:**\n- Success cases return valid JSON\n- Error cases return valid JSON (not plain text)\n- Consistent field naming (snake_case vs camelCase)\n- Array vs object wrapping consistency","status":"open","priority":1,"issue_type":"task","created_at":"2025-11-21T21:07:35.304424-05:00","updated_at":"2025-11-21T21:07:35.304424-05:00","dependencies":[{"issue_id":"bd-au0.7","depends_on_id":"bd-au0","type":"parent-child","created_at":"2025-11-21T21:07:35.305663-05:00","created_by":"daemon","metadata":"{}"}]} {"id":"bd-au0.8","title":"Improve clean vs cleanup command naming/documentation","description":"Clarify the difference between bd clean and bd cleanup to reduce user confusion.\n\n**Current state:**\n- bd clean: Remove temporary artifacts (.beads/bd.sock, logs, etc.)\n- bd cleanup: Delete old closed issues from database\n\n**Options:**\n1. Rename for clarity:\n - bd clean → bd clean-temp\n - bd cleanup → bd cleanup-issues\n \n2. Keep names but improve help text and documentation\n\n3. Add prominent warnings in help output\n\n**Preferred approach:** Option 2 (improve documentation)\n- Update short/long descriptions in commands\n- Add examples to help text\n- Update README.md\n- Add cross-references in help output\n\n**Files to modify:**\n- cmd/bd/clean.go\n- cmd/bd/cleanup.go\n- README.md or ADVANCED.md","status":"open","priority":2,"issue_type":"task","created_at":"2025-11-21T21:07:49.960534-05:00","updated_at":"2025-11-21T21:07:49.960534-05:00","dependencies":[{"issue_id":"bd-au0.8","depends_on_id":"bd-au0","type":"parent-child","created_at":"2025-11-21T21:07:49.962743-05:00","created_by":"daemon","metadata":"{}"}]} @@ -220,7 +220,7 @@ {"id":"bd-fu83","title":"Fix daemon/direct mode inconsistency in relate and duplicate commands","description":"The relate.go and duplicate.go commands have inconsistent daemon/direct mode handling:\n\nWhen daemonClient is connected, they resolve IDs via RPC but then perform updates directly via store.UpdateIssue(), bypassing the daemon.\n\nAffected locations:\n- relate.go:125-139 (runRelate update)\n- relate.go:235-246 (runUnrelate update) \n- duplicate.go:120 (runDuplicate update)\n- duplicate.go:207 (runSupersede update)\n\nShould either use RPC for updates when daemon is running, or document why direct access is intentional.","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-16T20:52:54.164189-08:00","updated_at":"2025-12-21T21:47:14.10222-08:00","closed_at":"2025-12-21T21:47:14.10222-08:00","close_reason":"Already implemented in commit 3ec517cc: relate, unrelate, duplicate, supersede now use RPC Update when daemon available"} {"id":"bd-fx7v","title":"Improve test coverage for cmd/bd/doctor/fix (23.9% → 50%)","description":"The doctor/fix package has only 23.9% test coverage. The doctor fix functionality is important for troubleshooting.\n\nCurrent coverage: 23.9%\nTarget coverage: 50%","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-13T20:43:05.67127-08:00","updated_at":"2025-12-13T21:01:20.839298-08:00"} {"id":"bd-fy4q","title":"Phase 1.2 follow-up: Clarify format storage","description":"Phase 1.2 created the bdt executable structure but issues.toon is currently stored in JSONL format, not TOON format.\n\nThis is intentional for now:\n- Phase 1.2 (bd-jv4w): Just infrastructure - separate binary, separate directory\n- Phase 1.3 (bd-j0tr): Implement actual TOON encoding/writing\n\nFor now, keep as-is: filename '.toon' signals intent, content is JSONL (interim format). Phase 1.3 will switch to actual TOON.","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-19T14:03:19.491040345-07:00","updated_at":"2025-12-19T14:03:19.491040345-07:00","dependencies":[{"issue_id":"bd-fy4q","depends_on_id":"bd-jv4w","type":"discovered-from","created_at":"2025-12-19T14:03:19.498933555-07:00","created_by":"daemon"}]} -{"id":"bd-g4b4","title":"bd close hooks: context check and notifications","description":"Add hook system to bd close for notifications and custom actions.\n\n## Scope (MVP)\n\nImplement **command hooks only** for bd close. Deferred: notify, webhook types.\n\n## Implementation\n\n### 1. Config Schema\n\nAdd to internal/configfile/config.go:\n\n```go\ntype HooksConfig struct {\n OnClose []HookEntry `yaml:\"on_close,omitempty\"`\n}\n\ntype HookEntry struct {\n Command string `yaml:\"command\"` // Shell command to run\n Name string `yaml:\"name,omitempty\"` // Optional display name\n}\n```\n\nAdd `Hooks HooksConfig` field to Config struct.\n\n### 2. Hook Execution\n\nCreate internal/hooks/close_hooks.go:\n\n```go\nfunc RunCloseHooks(ctx context.Context, cfg *configfile.Config, issue *types.Issue) error {\n for _, hook := range cfg.Hooks.OnClose {\n cmd := exec.CommandContext(ctx, \"sh\", \"-c\", hook.Command)\n cmd.Env = append(os.Environ(),\n \"BEAD_ID=\"+issue.ID,\n \"BEAD_TITLE=\"+issue.Title,\n \"BEAD_TYPE=\"+string(issue.IssueType),\n \"BEAD_PRIORITY=\"+strconv.Itoa(issue.Priority),\n \"BEAD_CLOSE_REASON=\"+issue.CloseReason,\n )\n cmd.Stdout = os.Stdout\n cmd.Stderr = os.Stderr\n if err := cmd.Run(); err \\!= nil {\n // Log warning but dont fail the close\n fmt.Fprintf(os.Stderr, \"Warning: close hook %q failed: %v\\n\", hook.Name, err)\n }\n }\n return nil\n}\n```\n\n### 3. Integration Point\n\nIn cmd/bd/close.go, after successful close:\n\n```go\n// Run close hooks\nif cfg := configfile.Load(); cfg \\!= nil {\n hooks.RunCloseHooks(ctx, cfg, closedIssue)\n}\n```\n\n### 4. Example Config\n\n```yaml\n# .beads/config.yaml\nhooks:\n on_close:\n - name: show-next\n command: bd ready --limit 1\n - name: context-check \n command: echo \"Issue $BEAD_ID closed. Check context if nearing limit.\"\n```\n\n## Environment Variables\n\n| Variable | Description |\n|----------|-------------|\n| BEAD_ID | Issue ID (e.g., bd-abc1) |\n| BEAD_TITLE | Issue title |\n| BEAD_TYPE | Issue type (task, bug, feature, etc.) |\n| BEAD_PRIORITY | Priority (0-4) |\n| BEAD_CLOSE_REASON | Close reason if provided |\n\n## Testing\n\nAdd test in internal/hooks/close_hooks_test.go:\n- Test hook execution with mock config\n- Test env vars are set correctly\n- Test hook failure doesnt block close\n\n## Files to Create/Modify\n\n1. **Create:** internal/hooks/close_hooks.go\n2. **Create:** internal/hooks/close_hooks_test.go \n3. **Modify:** internal/configfile/config.go (add HooksConfig)\n4. **Modify:** cmd/bd/close.go (call RunCloseHooks)\n5. **Modify:** docs/CONFIG.md (document hooks config)\n\n## Out of Scope (Future)\n\n- notify hook type (gt mail integration)\n- webhook type (HTTP POST)\n- on_create, on_update hooks\n- Hook timeout configuration\n- Parallel hook execution","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-22T17:03:56.183461-08:00","updated_at":"2025-12-23T12:27:35.531819-08:00"} +{"id":"bd-g4b4","title":"bd close hooks: context check and notifications","description":"Add hook system to bd close for notifications and custom actions.\n\n## Scope (MVP)\n\nImplement **command hooks only** for bd close. Deferred: notify, webhook types.\n\n## Implementation\n\n### 1. Config Schema\n\nAdd to internal/configfile/config.go:\n\n```go\ntype HooksConfig struct {\n OnClose []HookEntry `yaml:\"on_close,omitempty\"`\n}\n\ntype HookEntry struct {\n Command string `yaml:\"command\"` // Shell command to run\n Name string `yaml:\"name,omitempty\"` // Optional display name\n}\n```\n\nAdd `Hooks HooksConfig` field to Config struct.\n\n### 2. Hook Execution\n\nCreate internal/hooks/close_hooks.go:\n\n```go\nfunc RunCloseHooks(ctx context.Context, cfg *configfile.Config, issue *types.Issue) error {\n for _, hook := range cfg.Hooks.OnClose {\n cmd := exec.CommandContext(ctx, \"sh\", \"-c\", hook.Command)\n cmd.Env = append(os.Environ(),\n \"BEAD_ID=\"+issue.ID,\n \"BEAD_TITLE=\"+issue.Title,\n \"BEAD_TYPE=\"+string(issue.IssueType),\n \"BEAD_PRIORITY=\"+strconv.Itoa(issue.Priority),\n \"BEAD_CLOSE_REASON=\"+issue.CloseReason,\n )\n cmd.Stdout = os.Stdout\n cmd.Stderr = os.Stderr\n if err := cmd.Run(); err \\!= nil {\n // Log warning but dont fail the close\n fmt.Fprintf(os.Stderr, \"Warning: close hook %q failed: %v\\n\", hook.Name, err)\n }\n }\n return nil\n}\n```\n\n### 3. Integration Point\n\nIn cmd/bd/close.go, after successful close:\n\n```go\n// Run close hooks\nif cfg := configfile.Load(); cfg \\!= nil {\n hooks.RunCloseHooks(ctx, cfg, closedIssue)\n}\n```\n\n### 4. Example Config\n\n```yaml\n# .beads/config.yaml\nhooks:\n on_close:\n - name: show-next\n command: bd ready --limit 1\n - name: context-check \n command: echo \"Issue $BEAD_ID closed. Check context if nearing limit.\"\n```\n\n## Environment Variables\n\n| Variable | Description |\n|----------|-------------|\n| BEAD_ID | Issue ID (e.g., bd-abc1) |\n| BEAD_TITLE | Issue title |\n| BEAD_TYPE | Issue type (task, bug, feature, etc.) |\n| BEAD_PRIORITY | Priority (0-4) |\n| BEAD_CLOSE_REASON | Close reason if provided |\n\n## Testing\n\nAdd test in internal/hooks/close_hooks_test.go:\n- Test hook execution with mock config\n- Test env vars are set correctly\n- Test hook failure doesnt block close\n\n## Files to Create/Modify\n\n1. **Create:** internal/hooks/close_hooks.go\n2. **Create:** internal/hooks/close_hooks_test.go \n3. **Modify:** internal/configfile/config.go (add HooksConfig)\n4. **Modify:** cmd/bd/close.go (call RunCloseHooks)\n5. **Modify:** docs/CONFIG.md (document hooks config)\n\n## Out of Scope (Future)\n\n- notify hook type (gt mail integration)\n- webhook type (HTTP POST)\n- on_create, on_update hooks\n- Hook timeout configuration\n- Parallel hook execution","status":"open","priority":3,"issue_type":"feature","created_at":"2025-12-22T17:03:56.183461-08:00","updated_at":"2025-12-23T12:27:35.531819-08:00","dependencies":[{"issue_id":"bd-g4b4","depends_on_id":"bd-iz5t","type":"parent-child","created_at":"2025-12-23T12:44:07.811793-08:00","created_by":"daemon"}]} {"id":"bd-g9eu","title":"Investigate TestRoutingIntegration failure","description":"TestRoutingIntegration/maintainer_with_SSH_remote failed during pre-commit check with \"expected role maintainer, got contributor\".\nThis occurred while running `go test -short ./...` on darwin/arm64.\nThe failure appears unrelated to storage/sqlite changes.\nNeed to investigate if this is a flaky test or environmental issue.","status":"open","priority":2,"issue_type":"task","created_at":"2025-11-20T15:55:19.337094-08:00","updated_at":"2025-11-20T15:55:19.337094-08:00"} {"id":"bd-gjla","title":"Test Thread","description":"Initial message for threading test","status":"tombstone","priority":2,"issue_type":"message","created_at":"2025-12-16T18:19:51.704324-08:00","updated_at":"2025-12-17T16:11:17.070763-08:00","dependencies":[{"issue_id":"bd-gjla","depends_on_id":"bd-f5cc","type":"duplicates","created_at":"2025-12-18T13:45:31.137191-08:00","created_by":"migration"}],"deleted_at":"2025-12-17T16:11:17.070763-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"message"} {"id":"bd-gocx","title":"Run bump-version.sh 0.32.1","description":"Execute ./scripts/bump-version.sh 0.32.1 to update all version references","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-20T21:53:18.470174-08:00","updated_at":"2025-12-20T21:54:54.500836-08:00","closed_at":"2025-12-20T21:54:54.500836-08:00","close_reason":"Version bumped to 0.32.1","dependencies":[{"issue_id":"bd-gocx","depends_on_id":"bd-an4s","type":"parent-child","created_at":"2025-12-20T21:53:18.471793-08:00","created_by":"daemon"},{"issue_id":"bd-gocx","depends_on_id":"bd-x3j8","type":"blocks","created_at":"2025-12-20T21:53:29.688436-08:00","created_by":"daemon"}]} @@ -250,13 +250,14 @@ {"id":"bd-iic1","title":"Phase 2.2: Switch bdt storage to TOON format","description":"Currently bdt stores issues in JSONL format in issues.toon file. Phase 2.2 must implement actual TOON format storage - this is the fundamental goal of the bdtoon project.\n\n## Current State (Phase 2.1)\n- issues.toon stores JSONL (intermediate format)\n- --toon flag allows output in TOON format for LLM consumption\n- Problem: We're not actually using TOON as the fundamental storage format\n\n## Required Work (Phase 2.2)\n1. Switch issue file I/O to write TOON format instead of JSONL\n - Update cmd/bdt/storage.go to use EncodeTOON for writing\n - Update cmd/bdt/storage.go to decode TOON (currently decodes JSON)\n - Ensure round-trip: write TOON → read TOON → write TOON is byte-identical\n\n2. Update command implementations\n - cmd/bdt/create.go: Write newly created issues to TOON format\n - cmd/bdt/list.go: Read issues from TOON format\n - cmd/bdt/show.go: Read from TOON format\n - cmd/bdt/import.go: Convert imported JSONL to TOON\n - cmd/bdt/export.go: Export TOON to JSONL (for bd compatibility)\n\n3. Implement TOON parser that handles gotoon's encoder-only limitation\n - Since gotoon doesn't decode TOON, need custom TOON→JSON decoder\n - OR continue storing TOON but decoding via intermediate JSON conversion\n\n4. Git merge driver optimization\n - TOON is line-oriented, better for 3-way merges than binary formats\n - Configure git merge driver for .toon files\n\n5. Comprehensive testing\n - Round-trip tests: Issue → TOON → storage → read → Issue\n - Merge conflict resolution tests with TOON format\n - Large issue set performance tests\n\n## Success Criteria\n- issues.toon stores actual TOON format (not JSONL)\n- bdt list reads from TOON file\n- bdt create writes to TOON file\n- Round-trip: create issue → list → show returns identical data\n- All 65+ tests still passing\n- Performance comparable to JSONL storage","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-19T14:05:41.394964404-07:00","updated_at":"2025-12-19T14:37:17.879612634-07:00","closed_at":"2025-12-19T14:37:17.879612634-07:00"} {"id":"bd-in7","title":"Test message","description":"Hello world","status":"closed","priority":2,"issue_type":"message","created_at":"2025-12-17T23:16:13.184946-08:00","updated_at":"2025-12-18T17:42:26.000073-08:00","closed_at":"2025-12-17T23:37:38.563369-08:00"} {"id":"bd-indn","title":"bd template commands fail with daemon mode","description":"The `bd template show` and `bd template instantiate` commands fail with 'Error loading template: no database connection' when daemon is running.\n\n**Reproduction:**\n```bash\nbd daemon --start\nbd template show bd-qqc # Error: no database connection\nbd template show bd-qqc --no-daemon # Works\n```\n\n**Expected:** Template commands should work with daemon like other commands.\n\n**Workaround:** Use `--no-daemon` flag.\n\n**Location:** Likely in cmd/bd/template.go - daemon RPC path not implemented for template operations.","status":"open","priority":2,"issue_type":"bug","created_at":"2025-12-18T22:57:35.16596-08:00","updated_at":"2025-12-18T22:57:35.16596-08:00"} -{"id":"bd-io8c","title":"Improve test coverage for internal/syncbranch (33.0% → 70%)","description":"Improve test coverage for internal/syncbranch package from 27% to 70%.\n\n## Current State\n- Coverage: 27.0%\n- Files: syncbranch.go, worktree.go\n- Tests: syncbranch_test.go (basic tests exist)\n\n## Functions Needing Tests\n\n### syncbranch.go (config management)\n- [x] ValidateBranchName - has tests\n- [ ] Get - needs store mock tests\n- [ ] GetFromYAML - needs YAML parsing tests\n- [ ] IsConfigured - needs file system tests\n- [ ] IsConfiguredWithDB - needs DB path tests\n- [ ] Set - needs store mock tests\n- [ ] Unset - needs store mock tests\n\n### worktree.go (git operations) - PRIORITY\n- [ ] CommitToSyncBranch - needs git repo fixture tests\n- [ ] PullFromSyncBranch - needs merge scenario tests\n- [ ] CheckDivergence - needs ahead/behind tests\n- [ ] ResetToRemote - needs reset scenario tests\n- [ ] performContentMerge - needs 3-way merge tests\n- [ ] extractJSONLFromCommit - needs git show tests\n- [ ] hasChangesInWorktree - needs dirty state tests\n- [ ] commitInWorktree - needs commit scenario tests\n\n## Implementation Guide\n\n1. **Use testutil fixtures:**\n ```go\n import \"github.com/steveyegge/beads/internal/testutil/fixtures\"\n \n func TestCommitToSyncBranch(t *testing.T) {\n repo := fixtures.NewGitRepo(t)\n defer repo.Cleanup()\n // ... test scenarios\n }\n ```\n\n2. **Test scenarios for worktree.go:**\n - Clean commit (no conflicts)\n - Non-fast-forward push (diverged)\n - Merge conflict resolution\n - Empty changes (nothing to commit)\n\n3. **Mock storage for syncbranch.go:**\n ```go\n store := memory.New()\n // Set up test config\n syncbranch.Set(ctx, store, \"beads-sync\")\n ```\n\n## Success Criteria\n- Coverage ≥ 70%\n- All public functions have at least one test\n- Edge cases covered for git operations\n- Tests pass with `go test -race ./internal/syncbranch`\n\n## Run Tests\n```bash\ngo test -v -cover ./internal/syncbranch\ngo test -race ./internal/syncbranch\n```","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-13T20:43:02.079145-08:00","updated_at":"2025-12-23T12:31:35.147455-08:00"} +{"id":"bd-io8c","title":"Improve test coverage for internal/syncbranch (33.0% → 70%)","description":"Improve test coverage for internal/syncbranch package from 27% to 70%.\n\n## Current State\n- Coverage: 27.0%\n- Files: syncbranch.go, worktree.go\n- Tests: syncbranch_test.go (basic tests exist)\n\n## Functions Needing Tests\n\n### syncbranch.go (config management)\n- [x] ValidateBranchName - has tests\n- [ ] Get - needs store mock tests\n- [ ] GetFromYAML - needs YAML parsing tests\n- [ ] IsConfigured - needs file system tests\n- [ ] IsConfiguredWithDB - needs DB path tests\n- [ ] Set - needs store mock tests\n- [ ] Unset - needs store mock tests\n\n### worktree.go (git operations) - PRIORITY\n- [ ] CommitToSyncBranch - needs git repo fixture tests\n- [ ] PullFromSyncBranch - needs merge scenario tests\n- [ ] CheckDivergence - needs ahead/behind tests\n- [ ] ResetToRemote - needs reset scenario tests\n- [ ] performContentMerge - needs 3-way merge tests\n- [ ] extractJSONLFromCommit - needs git show tests\n- [ ] hasChangesInWorktree - needs dirty state tests\n- [ ] commitInWorktree - needs commit scenario tests\n\n## Implementation Guide\n\n1. **Use testutil fixtures:**\n ```go\n import \"github.com/steveyegge/beads/internal/testutil/fixtures\"\n \n func TestCommitToSyncBranch(t *testing.T) {\n repo := fixtures.NewGitRepo(t)\n defer repo.Cleanup()\n // ... test scenarios\n }\n ```\n\n2. **Test scenarios for worktree.go:**\n - Clean commit (no conflicts)\n - Non-fast-forward push (diverged)\n - Merge conflict resolution\n - Empty changes (nothing to commit)\n\n3. **Mock storage for syncbranch.go:**\n ```go\n store := memory.New()\n // Set up test config\n syncbranch.Set(ctx, store, \"beads-sync\")\n ```\n\n## Success Criteria\n- Coverage ≥ 70%\n- All public functions have at least one test\n- Edge cases covered for git operations\n- Tests pass with `go test -race ./internal/syncbranch`\n\n## Run Tests\n```bash\ngo test -v -cover ./internal/syncbranch\ngo test -race ./internal/syncbranch\n```","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-13T20:43:02.079145-08:00","updated_at":"2025-12-23T12:31:35.147455-08:00","dependencies":[{"issue_id":"bd-io8c","depends_on_id":"bd-iz5t","type":"parent-child","created_at":"2025-12-23T12:44:07.213092-08:00","created_by":"daemon"}]} {"id":"bd-ipj7","title":"enhance 'bd status' to show recent activity","description":"It would be nice to be able to quickly view the last N changes in the database, to see wha's recently been worked on. I'm imagining something like 'bd status activity'.","status":"closed","priority":3,"issue_type":"task","created_at":"2025-12-21T11:08:50.996541974-07:00","updated_at":"2025-12-21T17:54:00.279039-08:00","closed_at":"2025-12-21T17:54:00.279039-08:00","close_reason":"Already implemented - bd status includes Recent Activity section"} {"id":"bd-ipva","title":"Update go install bd to 0.33.2","description":"Rebuild and install bd to ~/go/bin:\n\n```bash\ngo install ./cmd/bd\n~/go/bin/bd version # Verify shows 0.33.2\n```\n\nNote: If ~/go/bin is in PATH before /opt/homebrew/bin, this is the version that runs by default.","status":"tombstone","priority":1,"issue_type":"task","created_at":"2025-12-21T16:10:13.760715-08:00","updated_at":"2025-12-21T17:29:31.791368-08:00","close_reason":"Local dev build used instead of go install","deleted_at":"2025-12-21T17:29:31.791368-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task","wisp":true} {"id":"bd-iq19","title":"Distill: promote ad-hoc epic to proto","description":"Extract a reusable proto from an existing ad-hoc epic.\n\nCOMMAND: bd mol distill \u003cepic-id\u003e [--as \u003cproto-name\u003e]\n\nBEHAVIOR:\n- Clone the epic and all children as a new proto\n- Set is_template=true on all cloned issues\n- Replace concrete values with {{variable}} placeholders (interactive or --var flags)\n- Add to proto catalog\n\nFLAGS:\n- --as NAME: Custom proto ID (default: proto-\u003cepic-id\u003e)\n- --var field=placeholder: Replace value with variable placeholder\n- --interactive: Prompt for each field that looks parameterizable\n- --dry-run: Preview the proto structure\n\nEXAMPLE:\n bd mol distill bd-o5xe --as proto-feature-workflow \\\n --var title=feature_name \\\n --var assignee=worker\n\nUSE CASES:\n- Team develops good workflow organically, wants to reuse it\n- Capture tribal knowledge as executable templates\n- Create starting point for similar future work\n\nThe reverse of spawn: instead of proto → molecule, it's molecule → proto.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-21T01:05:07.953538-08:00","updated_at":"2025-12-21T10:31:56.814246-08:00","closed_at":"2025-12-21T10:31:56.814246-08:00","close_reason":"Implemented distill command in mol.go","dependencies":[{"issue_id":"bd-iq19","depends_on_id":"bd-o5xe","type":"parent-child","created_at":"2025-12-21T01:05:16.495774-08:00","created_by":"daemon"},{"issue_id":"bd-iq19","depends_on_id":"bd-rnnr","type":"blocks","created_at":"2025-12-21T01:05:16.560404-08:00","created_by":"daemon"}]} {"id":"bd-iq7n","title":"Audit and fix JSONL filename mismatches across all repo clones","description":"## Problem\n\nMultiple clones of repos are configured with different JSONL filenames (issues.jsonl vs beads.jsonl), causing:\n1. JSONL files to be resurrected after deletion (one clone pushes issues.jsonl, another pushes beads.jsonl)\n2. Agents unable to see issues filed by other agents after sync\n3. Merge conflicts and data inconsistencies\n\n## Root Cause\n\nWhen repos were \"bd doctored\" or initialized at different times, some got issues.jsonl (old default) and others got beads.jsonl (Beads repo specific). These clones push their respective files, creating duplicates.\n\n## Task\n\nScan all repo clones under ~/src/ (1-2 levels deep) and standardize their JSONL configuration.\n\n### Step 1: Find all beads-enabled repos\n\n```bash\n# Find all directories named 'beads' at levels 1-2 under ~/src/\nfind ~/src -maxdepth 2 -type d -name beads\n```\n\n### Step 2: For each repo found, check configuration\n\nFor each directory from Step 1, check:\n- Does `.beads/metadata.json` exist?\n- What is the `jsonl_export` value?\n- What JSONL files actually exist in `.beads/`?\n- Are there multiple JSONL files (problem!)?\n\n### Step 3: Create audit report\n\nGenerate a report showing:\n```\nRepo Path | Config | Actual Files | Status\n----------------------------------- | ------------- | ---------------------- | --------\n~/src/beads | beads.jsonl | beads.jsonl | OK\n~/src/dave/beads | issues.jsonl | issues.jsonl | MISMATCH\n~/src/emma/beads | issues.jsonl | issues.jsonl, beads.jsonl | DUPLICATE!\n```\n\n### Step 4: Determine canonical name for each repo\n\nFor repos that are the SAME git repository (check `git remote -v`):\n- Group them together\n- Determine which JSONL filename should be canonical (majority wins, or beads.jsonl for the beads repo itself)\n- List which clones need to be updated\n\n### Step 5: Generate fix script\n\nCreate a script that for each mismatched clone:\n1. Updates `.beads/metadata.json` to use the canonical name\n2. If JSONL file needs renaming: `git mv .beads/old.jsonl .beads/new.jsonl`\n3. Removes any duplicate JSONL files: `git rm .beads/duplicate.jsonl`\n4. Commits the change\n5. Syncs: `bd sync`\n\n### Expected Output\n\n1. Audit report showing all repos and their config status\n2. List of repos grouped by git remote (same repository)\n3. Fix script or manual instructions for standardizing each repo\n4. Verification that after fixes, all clones of the same repo use the same JSONL filename\n\n### Edge Cases\n\n- Handle repos without metadata.json (use default discovery)\n- Handle repos with no git remote (standalone/local)\n- Handle repos that are not git repositories\n- Don't modify repos with uncommitted changes (warn instead)\n\n### Success Criteria\n\n- All clones of the same git repository use the same JSONL filename\n- No duplicate JSONL files in any repo\n- All configurations documented in metadata.json\n- bd doctor passes on all repos","status":"closed","priority":0,"issue_type":"task","created_at":"2025-11-21T23:58:35.044762-08:00","updated_at":"2025-12-17T23:13:40.531403-08:00","closed_at":"2025-12-17T16:50:59.510972-08:00"} {"id":"bd-is6m","title":"Add gate checking to Deacon patrol loop","description":"Integrate gate checking into Deacon's patrol cycle.\n\n## Patrol Integration\n```go\nfunc (d *Deacon) checkGates(ctx context.Context) {\n gates, _ := d.store.ListOpenGates(ctx)\n \n for _, gate := range gates {\n // Check timeout\n if time.Since(gate.CreatedAt) \u003e gate.Timeout {\n d.notifyWaiters(gate, \"timeout\")\n d.closeGate(gate, \"timed out\")\n continue\n }\n \n // Check condition\n if d.checkCondition(gate.AwaitType, gate.AwaitID) {\n d.notifyWaiters(gate, \"cleared\")\n d.closeGate(gate, \"condition met\")\n }\n }\n}\n```\n\n## Note\nThis task is in Gas Town (gt), not beads. May need to be moved there.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-23T11:44:36.839709-08:00","updated_at":"2025-12-23T12:19:44.204647-08:00","closed_at":"2025-12-23T12:19:44.204647-08:00","close_reason":"Moved to gastown: gt-dh65","dependencies":[{"issue_id":"bd-is6m","depends_on_id":"bd-udsi","type":"parent-child","created_at":"2025-12-23T11:44:52.909253-08:00","created_by":"daemon"},{"issue_id":"bd-is6m","depends_on_id":"bd-u66e","type":"blocks","created_at":"2025-12-23T11:44:56.428084-08:00","created_by":"daemon"}]} {"id":"bd-iw4z","title":"Compound visualization in bd mol show","description":"Enhance bd mol show to display compound structure.\n\nENHANCEMENTS:\n- Show constituent protos and how they're bonded\n- Display bond type (sequential/parallel) between components\n- Indicate attachment points\n- Show combined variable requirements across all protos\n\nEXAMPLE OUTPUT:\n\n Compound: proto-feature-with-tests\n Bonded from:\n └─ proto-feature (root)\n └─ proto-testing (sequential, after completion)\n \n Variables: {{name}}, {{version}}, {{test_suite}}\n \n Structure:\n proto-feature-with-tests\n ├─ Design feature {{name}}\n ├─ Implement core\n ├─ Write unit tests ← from proto-testing\n └─ Run test suite {{test_suite}} ← from proto-testing","status":"deferred","priority":2,"issue_type":"task","created_at":"2025-12-21T00:59:26.71318-08:00","updated_at":"2025-12-21T11:12:44.012871-08:00","dependencies":[{"issue_id":"bd-iw4z","depends_on_id":"bd-o5xe","type":"parent-child","created_at":"2025-12-21T00:59:51.500865-08:00","created_by":"daemon"},{"issue_id":"bd-iw4z","depends_on_id":"bd-rnnr","type":"blocks","created_at":"2025-12-21T00:59:51.891643-08:00","created_by":"daemon"}]} +{"id":"bd-iz5t","title":"Swarm: 13 beads backlog issues for polecat execution","description":"## Swarm Overview\n\n13 issues prepared for parallel polecat execution. All issues have been enhanced with concrete implementation guidance, file lists, and success criteria.\n\n## Issue List\n\n### Bug (1) - HIGH PRIORITY\n| ID | Priority | Title |\n|----|----------|-------|\n| bd-phtv | P1 | Pinned field overwritten by subsequent commands |\n\n### Test Coverage (3)\n| ID | Package | Target |\n|----|---------|--------|\n| bd-io8c | internal/syncbranch | 27% → 70% |\n| bd-thgk | internal/compact | 17% → 70% |\n| bd-tvu3 | internal/beads | 48% → 70% |\n\n### Code Quality (3)\n| ID | Task |\n|----|------|\n| bd-qioh | FatalError pattern standardization |\n| bd-rgyd | Split queries.go (1704 lines → 5 files) |\n| bd-u2sc.3 | Split cmd/bd files (sync/init/show/compact) |\n\n### Features (4)\n| ID | Task |\n|----|------|\n| bd-au0.5 | Search date/priority filters |\n| bd-ykd9 | Doctor --fix auto-repair |\n| bd-g4b4 | Close hooks system |\n| bd-likt | Gate daemon RPC |\n\n### Polish (2)\n| ID | Task |\n|----|------|\n| bd-4qfb | Doctor output formatting |\n| bd-u2sc.4 | slog structured logging |\n\n## Issue Details\n\nAll issues have been enhanced with:\n- Concrete file lists to modify\n- Code snippets and patterns\n- Success criteria\n- Test commands\n\nRun `bd show \u003cid\u003e` for full details on any issue.\n\n## Execution Notes\n\n- All issues are independent (no blockers between them)\n- bd-phtv (P1 bug) should get priority - affects bd pin functionality\n- Test coverage tasks are straightforward but time-consuming\n- File split tasks (bd-rgyd, bd-u2sc.3) are mechanical but important\n\n## Completed During Prep\n\n- bd-ucgz (P2 bug) - Fixed inline: external deps orphan check (commit f2db0a1d)\n- Moved 5 gastown issues out of beads backlog (gt-dh65, gt-ng6g, gt-fqcz, gt-gswn, gt-rw2z)\n- Deferred 4 premature/post-1.0 issues\n- Closed bd-udsi epic (core implementation complete)","status":"open","priority":1,"issue_type":"epic","created_at":"2025-12-23T12:43:58.427835-08:00","updated_at":"2025-12-23T12:43:58.427835-08:00"} {"id":"bd-j0tr","title":"Phase 1.3: Basic TOON read/write operations","description":"Add basic TOON read/write operations to bdt executable. Implement create, list, and show commands that use the internal/toon package for encoding/decoding to TOON format.\n\n## Subtasks\n1. Implement bdt create command - Create issues and serialize to TOON format\n2. Implement bdt list command - Read issues.toon and display all issues\n3. Implement bdt show command - Display single issue by ID\n4. Add file I/O operations for issues.toon\n5. Integrate internal/toon package (EncodeTOON, DecodeJSON)\n6. Write tests for create, list, show operations\n\n## Files to Create/Modify\n- cmd/bdt/create.go - Create command\n- cmd/bdt/list.go - List command \n- cmd/bdt/show.go - Show command\n- cmd/bdt/storage.go - File I/O helper\n\n## Success Criteria\n- bdt create \"Issue title\" creates and saves to issues.toon\n- bdt list displays all issues in human-readable format\n- bdt list --json shows JSON output\n- bdt show \u003cid\u003e displays single issue\n- Issues round-trip correctly: create → list → show\n- All tests passing with \u003e80% coverage","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-19T12:59:54.270296918-07:00","updated_at":"2025-12-19T13:09:00.196045685-07:00","closed_at":"2025-12-19T13:09:00.196045685-07:00"} {"id":"bd-j3il","title":"Add bd reset command for clean slate restart","description":"Implement a command to reset beads to a clean starting state.\n\n**Context:** GitHub issue #479 - users sometimes get beads into an invalid state after updates, and there's no clean way to start fresh. The git backup/restore mechanism that protects against accidental deletion also makes it hard to intentionally reset.\n\n**Current workaround** (from maphew):\n```bash\nbd daemons killall\ngit rm .beads/*.jsonl\ngit commit -m 'remove old issues'\nrm .beads/*\nbd init\nbd onboard\n```\n\n**Desired:** A proper `bd reset` command that handles this cleanly and safely.","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-13T08:41:34.956552+11:00","updated_at":"2025-12-13T08:43:49.970591+11:00","closed_at":"2025-12-13T08:43:49.970591+11:00"} {"id":"bd-j6lr","title":"GH#402: Add --parent flag documentation to bd onboard","description":"bd onboard output is missing --parent flag for epic subtasks. Agents guess wrong syntax (--deps parent:). See GitHub issue #402.","status":"tombstone","priority":2,"issue_type":"task","created_at":"2025-12-16T01:03:56.594829-08:00","updated_at":"2025-12-17T16:11:17.070763-08:00","deleted_at":"2025-12-17T16:11:17.070763-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"} @@ -293,7 +294,7 @@ {"id":"bd-kzda","title":"Implement conditional bond type for mol bond","description":"The mol bond command accepts 'conditional' as a bond type but doesn't implement any conditional-specific behavior. It currently behaves identically to 'parallel'.\n\n**Expected behavior:**\nConditional bonds should mean 'B runs only if A fails' per the help text (mol.go:318).\n\n**Implementation needed:**\n- Add failure-condition dependency handling\n- Possibly new dependency type or status-based blocking\n- Update bondProtoProto, bondProtoMol, bondMolMol to handle conditional\n\n**Alternative:**\nRemove 'conditional' from valid bond types until implemented.\n\nThis is new functionality, not a regression.","status":"closed","priority":3,"issue_type":"feature","assignee":"beads/toast","created_at":"2025-12-21T10:23:01.966367-08:00","updated_at":"2025-12-23T01:33:25.734264-08:00","closed_at":"2025-12-23T01:33:25.734264-08:00","close_reason":"Merged to main"} {"id":"bd-ldb0","title":"Rename ephemeral → wisp throughout codebase","description":"## The Change\n\nRename 'ephemeral' to 'wisp' throughout the beads codebase.\n\n## Why\n\n**Ephemeral** is:\n- 4 syllables (too long)\n- Greek/academic (doesn't match bond/burn/squash)\n- Overused in tech (K8s, networking, storage)\n- Passive/descriptive\n\n**Wisp** is:\n- 1 syllable (matches bond/burn/squash)\n- Evocative - you can SEE a wisp\n- Steam engine metaphor - Gas Town is engines, steam wisps rise and dissipate\n- Will-o'-the-wisp - transient spirits that guide then vanish\n- Unique - nobody else uses it\n\n## The Steam Engine Metaphor\n\n```\nEngine does work → generates steam\nSteam wisps rise → execution trace\nSteam condenses → digest (distillate)\nSteam dissipates → cleaned up (burned)\n```\n\n## Full Vocabulary\n\n| Term | Meaning |\n|------|---------|\n| bond | Attach proto to work (creates wisps) |\n| wisp | Temporary execution step |\n| squash | Condense wisps into digest |\n| burn | Destroy wisps without record |\n| digest | Permanent condensed record |\n\n## Changes Required\n\n### Code\n- `Ephemeral bool` → `Wisp bool` in types/issue.go\n- `--ephemeral` flag → remove (wisp is default)\n- `--persistent` flag → keep as opt-out\n- `bd cleanup --ephemeral` → `bd cleanup --wisps`\n- Update all references in mol_*.go files\n\n### Docs\n- Update all documentation\n- Update CLAUDE.md examples\n- Update CLI help text\n\n### Database Migration\n- Add migration to rename field (or keep internal name, just change API)\n\n## Example Usage After\n\n```bash\nbd mol bond mol-polecat-work # Creates wisps (default)\nbd mol bond mol-xxx --persistent # Creates permanent issues\nbd mol squash bd-xxx # Condenses wisps → digest\nbd cleanup --wisps # Clean old wisps\nbd list --wisps # Show wisp issues\n```","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-21T14:44:41.576068-08:00","updated_at":"2025-12-22T00:32:31.153738-08:00","closed_at":"2025-12-22T00:32:31.153738-08:00","close_reason":"Renamed ephemeral → wisp throughout codebase"} {"id":"bd-lfak","title":"bd preflight: PR readiness checks for contributors","description":"## Vision\n\nEncode project-specific institutional knowledge into executable checks. CONTRIBUTING.md is documentation that's read once and forgotten; `bd preflight` is documentation that runs at exactly the right moment.\n\n## Problem Statement\n\nContributors face a \"last mile\" problem - they do the work but stumble on project-specific gotchas at PR time:\n- Nix vendorHash gets stale when go.sum changes\n- Beads artifacts leak into PRs (see bd-umbf for namespace solution)\n- Version mismatches between version.go and default.nix\n- Tests/lint not run locally before pushing\n- Other project-specific checks that only surface when CI fails\n\nThese are too obscure to remember, exist in docs nobody reads end-to-end, and waste CI round-trips.\n\n## Why beads?\n\nBeads already has a foothold in the contributor workflow. It knows:\n- Git state (staged files, branch, dirty status)\n- Project structure\n- The specific issue being worked on\n- Project-specific configuration\n\n## Proposed Interface\n\n### Tier 1: Checklist Mode (v1)\n\n $ bd preflight\n PR Readiness Checklist:\n\n [ ] Tests pass: go test -short ./...\n [ ] Lint passes: golangci-lint run ./...\n [ ] No beads pollution: check .beads/issues.jsonl diff\n [ ] Nix hash current: go.sum unchanged or vendorHash updated\n [ ] Version sync: version.go matches default.nix\n\n Run 'bd preflight --check' to validate automatically.\n\n### Tier 2: Check Mode (v2)\n\n $ bd preflight --check\n ✓ Tests pass\n ✓ Lint passes\n ⚠ Beads pollution: 3 issues in diff - are these project issues or personal?\n ✗ Nix hash stale: go.sum changed, vendorHash needs update\n Fix: sha256-KRR6dXzsSw8OmEHGBEVDBOoIgfoZ2p0541T9ayjGHlI=\n ✓ Version sync\n\n 1 error, 1 warning. Run 'bd preflight --fix' to auto-fix where possible.\n\n### Tier 3: Fix Mode (v3)\n\n $ bd preflight --fix\n ✓ Updated vendorHash in default.nix\n ⚠ Cannot auto-fix beads pollution - manual review needed\n\n## Checks to Implement\n\n| Check | Description | Auto-fixable |\n|-------|-------------|--------------|\n| tests | Run go test -short ./... | No |\n| lint | Run golangci-lint | Partial (gofmt) |\n| beads-pollution | Detect personal issues in diff | No (see bd-umbf) |\n| nix-hash | Detect stale vendorHash | Yes (if nix available) |\n| version-sync | version.go matches default.nix | Yes |\n| no-debug | No TODO/FIXME/console.log | Warn only |\n| clean-stage | No unintended files staged | Warn only |\n\n## Future: Configuration\n\nMake checks configurable per-project via .beads/preflight.yaml:\n\n preflight:\n checks:\n - name: tests\n run: go test -short ./...\n required: true\n - name: no-secrets\n pattern: \"**/*.env\"\n staged: deny\n - name: custom-check\n run: ./scripts/validate.sh\n\nThis lets any project using beads define their own preflight checks.\n\n## Implementation Phases\n\n### Phase 1: Static Checklist\n- Implement bd preflight with hardcoded checklist for beads\n- No execution, just prints what to check\n- Update CONTRIBUTING.md to reference it\n\n### Phase 2: Automated Checks\n- Implement bd preflight --check\n- Run tests, lint, detect stale hashes\n- Clear pass/fail/warn output\n\n### Phase 3: Auto-fix\n- Implement bd preflight --fix\n- Fix vendorHash, version sync\n- Integrate with bd-umbf solution for pollution\n\n### Phase 4: Configuration\n- .beads/preflight.yaml support\n- Make it useful for other projects using beads\n- Plugin/hook system for custom checks\n\n## Dependencies\n\n- bd-umbf: Namespace isolation for beads pollution (blocking for full solution)\n\n## Success Metrics\n\n- Fewer CI failures on first PR push\n- Reduced \"fix nix hash\" commits\n- Contributors report preflight caught issues before CI","status":"open","priority":2,"issue_type":"epic","created_at":"2025-12-13T18:01:39.587078-08:00","updated_at":"2025-12-13T18:01:39.587078-08:00","dependencies":[{"issue_id":"bd-lfak","depends_on_id":"bd-umbf","type":"blocks","created_at":"2025-12-13T18:01:46.059901-08:00","created_by":"daemon","metadata":"{}"}]} -{"id":"bd-likt","title":"Add daemon RPC support for gate commands","description":"Add daemon RPC support for gate commands.\n\n## Current State\nGate commands require --no-daemon flag because they use direct SQLite access:\n- Gate create needs to write await_type, await_id, timeout_ns, waiters fields\n- Gate wait needs to update waiters JSON array\n- Daemon RPC doesnt have methods for these operations\n\n## Implementation\n\n### 1. Add RPC methods to internal/rpc/protocol.go\n\n```go\n// Gate operations\ntype GateCreateArgs struct {\n Title string \\`json:\"title\"\\`\n AwaitType string \\`json:\"await_type\"\\`\n AwaitID string \\`json:\"await_id\"\\`\n Timeout time.Duration \\`json:\"timeout\"\\`\n Waiters []string \\`json:\"waiters\"\\`\n}\n\ntype GateCreateResult struct {\n Issue *types.Issue \\`json:\"issue\"\\`\n}\n\ntype GateListArgs struct {\n All bool \\`json:\"all\"\\` // Include closed gates\n}\n\ntype GateListResult struct {\n Gates []*types.Issue \\`json:\"gates\"\\`\n}\n\ntype GateWaitArgs struct {\n GateID string \\`json:\"gate_id\"\\`\n Waiters []string \\`json:\"waiters\"\\` // Additional waiters to add\n}\n\ntype GateWaitResult struct {\n Gate *types.Issue \\`json:\"gate\"\\`\n AddedCount int \\`json:\"added_count\"\\`\n}\n```\n\n### 2. Add handler methods to internal/daemon/rpc_handler.go\n\n```go\nfunc (h *RPCHandler) GateCreate(ctx context.Context, args *rpc.GateCreateArgs) (*rpc.GateCreateResult, error) {\n now := time.Now()\n gate := \u0026types.Issue{\n Title: args.Title,\n IssueType: types.TypeGate,\n Status: types.StatusOpen,\n Priority: 1,\n Assignee: \"deacon/\",\n Wisp: true,\n AwaitType: args.AwaitType,\n AwaitID: args.AwaitID,\n Timeout: args.Timeout,\n Waiters: args.Waiters,\n CreatedAt: now,\n UpdatedAt: now,\n }\n gate.ContentHash = gate.ComputeContentHash()\n \n if err := h.store.CreateIssue(ctx, gate, h.actor); err != nil {\n return nil, err\n }\n \n return \u0026rpc.GateCreateResult{Issue: gate}, nil\n}\n\nfunc (h *RPCHandler) GateList(ctx context.Context, args *rpc.GateListArgs) (*rpc.GateListResult, error) {\n gateType := types.TypeGate\n filter := types.IssueFilter{IssueType: \u0026gateType}\n if !args.All {\n openStatus := types.StatusOpen\n filter.Status = \u0026openStatus\n }\n \n gates, err := h.store.SearchIssues(ctx, \"\", filter)\n if err != nil {\n return nil, err\n }\n \n return \u0026rpc.GateListResult{Gates: gates}, nil\n}\n\nfunc (h *RPCHandler) GateWait(ctx context.Context, args *rpc.GateWaitArgs) (*rpc.GateWaitResult, error) {\n gate, err := h.store.GetIssue(ctx, args.GateID)\n if err != nil {\n return nil, err\n }\n if gate.IssueType != types.TypeGate {\n return nil, fmt.Errorf(\"%s is not a gate\", args.GateID)\n }\n \n // Merge waiters (dedupe)\n waiterSet := make(map[string]bool)\n for _, w := range gate.Waiters {\n waiterSet[w] = true\n }\n added := 0\n for _, w := range args.Waiters {\n if !waiterSet[w] {\n gate.Waiters = append(gate.Waiters, w)\n waiterSet[w] = true\n added++\n }\n }\n \n if added \u003e 0 {\n // Update via store\n updates := map[string]interface{}{\n \"waiters\": gate.Waiters,\n }\n if err := h.store.UpdateIssue(ctx, args.GateID, updates, h.actor); err != nil {\n return nil, err\n }\n }\n \n return \u0026rpc.GateWaitResult{Gate: gate, AddedCount: added}, nil\n}\n```\n\n### 3. Register methods in daemon\n\nIn internal/daemon/server.go, register the new methods:\n```go\nrpc.RegisterMethod(\"gate.create\", h.GateCreate)\nrpc.RegisterMethod(\"gate.list\", h.GateList)\nrpc.RegisterMethod(\"gate.wait\", h.GateWait)\n```\n\n### 4. Add client methods to internal/rpc/client.go\n\n```go\nfunc (c *Client) GateCreate(ctx context.Context, args *GateCreateArgs) (*GateCreateResult, error) {\n var result GateCreateResult\n err := c.Call(ctx, \"gate.create\", args, \u0026result)\n return \u0026result, err\n}\n\nfunc (c *Client) GateList(ctx context.Context, args *GateListArgs) (*GateListResult, error) {\n var result GateListResult\n err := c.Call(ctx, \"gate.list\", args, \u0026result)\n return \u0026result, err\n}\n\nfunc (c *Client) GateWait(ctx context.Context, args *GateWaitArgs) (*GateWaitResult, error) {\n var result GateWaitResult\n err := c.Call(ctx, \"gate.wait\", args, \u0026result)\n return \u0026result, err\n}\n```\n\n### 5. Update cmd/bd/gate.go to use daemon\n\n```go\n// In gateCreateCmd Run:\nif daemonClient != nil {\n result, err := daemonClient.GateCreate(ctx, \u0026rpc.GateCreateArgs{\n Title: title,\n AwaitType: awaitType,\n AwaitID: awaitID,\n Timeout: timeout,\n Waiters: notifyAddrs,\n })\n if err != nil {\n FatalError(\"gate create: %v\", err)\n }\n gate = result.Issue\n} else {\n // Existing direct store code\n}\n```\n\n## Files to Modify\n\n1. **internal/rpc/protocol.go** - Add Gate*Args/Result types\n2. **internal/daemon/rpc_handler.go** - Add handler methods\n3. **internal/daemon/server.go** - Register methods\n4. **internal/rpc/client.go** - Add client methods\n5. **cmd/bd/gate.go** - Use daemon client when available\n\n## Testing\n\n```bash\n# Start daemon\nbd daemon start\n\n# Test via daemon (should work without --no-daemon)\nbd gate create --await timer:5m --notify beads/dave\nbd gate list\nbd gate wait \u003cid\u003e --notify beads/alice\n\n# Verify daemon handled it\nbd daemons logs . | grep gate\n```\n\n## Success Criteria\n- All gate commands work without --no-daemon\n- Same behavior in daemon vs direct mode\n- Waiters array updates correctly via RPC\n- Tests pass for RPC gate operations","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-23T12:13:25.778412-08:00","updated_at":"2025-12-23T12:35:04.230263-08:00","dependencies":[{"issue_id":"bd-likt","depends_on_id":"bd-udsi","type":"discovered-from","created_at":"2025-12-23T12:13:36.174822-08:00","created_by":"daemon"}]} +{"id":"bd-likt","title":"Add daemon RPC support for gate commands","description":"Add daemon RPC support for gate commands.\n\n## Current State\nGate commands require --no-daemon flag because they use direct SQLite access:\n- Gate create needs to write await_type, await_id, timeout_ns, waiters fields\n- Gate wait needs to update waiters JSON array\n- Daemon RPC doesnt have methods for these operations\n\n## Implementation\n\n### 1. Add RPC methods to internal/rpc/protocol.go\n\n```go\n// Gate operations\ntype GateCreateArgs struct {\n Title string \\`json:\"title\"\\`\n AwaitType string \\`json:\"await_type\"\\`\n AwaitID string \\`json:\"await_id\"\\`\n Timeout time.Duration \\`json:\"timeout\"\\`\n Waiters []string \\`json:\"waiters\"\\`\n}\n\ntype GateCreateResult struct {\n Issue *types.Issue \\`json:\"issue\"\\`\n}\n\ntype GateListArgs struct {\n All bool \\`json:\"all\"\\` // Include closed gates\n}\n\ntype GateListResult struct {\n Gates []*types.Issue \\`json:\"gates\"\\`\n}\n\ntype GateWaitArgs struct {\n GateID string \\`json:\"gate_id\"\\`\n Waiters []string \\`json:\"waiters\"\\` // Additional waiters to add\n}\n\ntype GateWaitResult struct {\n Gate *types.Issue \\`json:\"gate\"\\`\n AddedCount int \\`json:\"added_count\"\\`\n}\n```\n\n### 2. Add handler methods to internal/daemon/rpc_handler.go\n\n```go\nfunc (h *RPCHandler) GateCreate(ctx context.Context, args *rpc.GateCreateArgs) (*rpc.GateCreateResult, error) {\n now := time.Now()\n gate := \u0026types.Issue{\n Title: args.Title,\n IssueType: types.TypeGate,\n Status: types.StatusOpen,\n Priority: 1,\n Assignee: \"deacon/\",\n Wisp: true,\n AwaitType: args.AwaitType,\n AwaitID: args.AwaitID,\n Timeout: args.Timeout,\n Waiters: args.Waiters,\n CreatedAt: now,\n UpdatedAt: now,\n }\n gate.ContentHash = gate.ComputeContentHash()\n \n if err := h.store.CreateIssue(ctx, gate, h.actor); err != nil {\n return nil, err\n }\n \n return \u0026rpc.GateCreateResult{Issue: gate}, nil\n}\n\nfunc (h *RPCHandler) GateList(ctx context.Context, args *rpc.GateListArgs) (*rpc.GateListResult, error) {\n gateType := types.TypeGate\n filter := types.IssueFilter{IssueType: \u0026gateType}\n if !args.All {\n openStatus := types.StatusOpen\n filter.Status = \u0026openStatus\n }\n \n gates, err := h.store.SearchIssues(ctx, \"\", filter)\n if err != nil {\n return nil, err\n }\n \n return \u0026rpc.GateListResult{Gates: gates}, nil\n}\n\nfunc (h *RPCHandler) GateWait(ctx context.Context, args *rpc.GateWaitArgs) (*rpc.GateWaitResult, error) {\n gate, err := h.store.GetIssue(ctx, args.GateID)\n if err != nil {\n return nil, err\n }\n if gate.IssueType != types.TypeGate {\n return nil, fmt.Errorf(\"%s is not a gate\", args.GateID)\n }\n \n // Merge waiters (dedupe)\n waiterSet := make(map[string]bool)\n for _, w := range gate.Waiters {\n waiterSet[w] = true\n }\n added := 0\n for _, w := range args.Waiters {\n if !waiterSet[w] {\n gate.Waiters = append(gate.Waiters, w)\n waiterSet[w] = true\n added++\n }\n }\n \n if added \u003e 0 {\n // Update via store\n updates := map[string]interface{}{\n \"waiters\": gate.Waiters,\n }\n if err := h.store.UpdateIssue(ctx, args.GateID, updates, h.actor); err != nil {\n return nil, err\n }\n }\n \n return \u0026rpc.GateWaitResult{Gate: gate, AddedCount: added}, nil\n}\n```\n\n### 3. Register methods in daemon\n\nIn internal/daemon/server.go, register the new methods:\n```go\nrpc.RegisterMethod(\"gate.create\", h.GateCreate)\nrpc.RegisterMethod(\"gate.list\", h.GateList)\nrpc.RegisterMethod(\"gate.wait\", h.GateWait)\n```\n\n### 4. Add client methods to internal/rpc/client.go\n\n```go\nfunc (c *Client) GateCreate(ctx context.Context, args *GateCreateArgs) (*GateCreateResult, error) {\n var result GateCreateResult\n err := c.Call(ctx, \"gate.create\", args, \u0026result)\n return \u0026result, err\n}\n\nfunc (c *Client) GateList(ctx context.Context, args *GateListArgs) (*GateListResult, error) {\n var result GateListResult\n err := c.Call(ctx, \"gate.list\", args, \u0026result)\n return \u0026result, err\n}\n\nfunc (c *Client) GateWait(ctx context.Context, args *GateWaitArgs) (*GateWaitResult, error) {\n var result GateWaitResult\n err := c.Call(ctx, \"gate.wait\", args, \u0026result)\n return \u0026result, err\n}\n```\n\n### 5. Update cmd/bd/gate.go to use daemon\n\n```go\n// In gateCreateCmd Run:\nif daemonClient != nil {\n result, err := daemonClient.GateCreate(ctx, \u0026rpc.GateCreateArgs{\n Title: title,\n AwaitType: awaitType,\n AwaitID: awaitID,\n Timeout: timeout,\n Waiters: notifyAddrs,\n })\n if err != nil {\n FatalError(\"gate create: %v\", err)\n }\n gate = result.Issue\n} else {\n // Existing direct store code\n}\n```\n\n## Files to Modify\n\n1. **internal/rpc/protocol.go** - Add Gate*Args/Result types\n2. **internal/daemon/rpc_handler.go** - Add handler methods\n3. **internal/daemon/server.go** - Register methods\n4. **internal/rpc/client.go** - Add client methods\n5. **cmd/bd/gate.go** - Use daemon client when available\n\n## Testing\n\n```bash\n# Start daemon\nbd daemon start\n\n# Test via daemon (should work without --no-daemon)\nbd gate create --await timer:5m --notify beads/dave\nbd gate list\nbd gate wait \u003cid\u003e --notify beads/alice\n\n# Verify daemon handled it\nbd daemons logs . | grep gate\n```\n\n## Success Criteria\n- All gate commands work without --no-daemon\n- Same behavior in daemon vs direct mode\n- Waiters array updates correctly via RPC\n- Tests pass for RPC gate operations","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-23T12:13:25.778412-08:00","updated_at":"2025-12-23T12:35:04.230263-08:00","dependencies":[{"issue_id":"bd-likt","depends_on_id":"bd-udsi","type":"discovered-from","created_at":"2025-12-23T12:13:36.174822-08:00","created_by":"daemon"},{"issue_id":"bd-likt","depends_on_id":"bd-iz5t","type":"parent-child","created_at":"2025-12-23T12:44:07.891992-08:00","created_by":"daemon"}]} {"id":"bd-lk39","title":"Add composite index (issue_id, event_type) on events table","description":"GetCloseReason and GetCloseReasonsForIssues filter by both issue_id and event_type.\n\n**Query (queries.go:355-358):**\n```sql\nSELECT comment FROM events\nWHERE issue_id = ? AND event_type = ?\nORDER BY created_at DESC LIMIT 1\n```\n\n**Problem:** Currently uses idx_events_issue but must filter event_type in memory.\n\n**Solution:** Add migration:\n```sql\nCREATE INDEX IF NOT EXISTS idx_events_issue_type ON events(issue_id, event_type);\n```\n\n**Priority:** Low - events table is typically small relative to issues.","status":"closed","priority":4,"issue_type":"task","created_at":"2025-12-22T22:58:54.070587-08:00","updated_at":"2025-12-22T23:15:13.841988-08:00","closed_at":"2025-12-22T23:15:13.841988-08:00","close_reason":"Implemented in migration 026_additional_indexes.go","dependencies":[{"issue_id":"bd-lk39","depends_on_id":"bd-h0we","type":"discovered-from","created_at":"2025-12-22T22:58:54.071286-08:00","created_by":"daemon"}]} {"id":"bd-llfl","title":"Improve test coverage for cmd/bd CLI (26.2% → 50%)","description":"The main CLI package (cmd/bd) has only 26.2% test coverage. CLI commands should have at least 50% coverage to ensure reliability.\n\nKey areas with low/no coverage:\n- daemon_autostart.go (multiple 0% functions)\n- compact.go (several 0% functions)\n- Various command handlers\n\nCurrent coverage: 26.2%\nTarget coverage: 50%","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-13T20:43:03.123341-08:00","updated_at":"2025-12-13T21:01:18.901944-08:00"} {"id":"bd-lo4","title":"Test pinned issue","status":"tombstone","priority":2,"issue_type":"task","created_at":"2025-12-18T21:44:49.031385-08:00","updated_at":"2025-12-18T21:47:25.055109-08:00","deleted_at":"2025-12-18T21:47:25.055109-08:00","deleted_by":"daemon","delete_reason":"delete","original_type":"task"} @@ -367,13 +368,13 @@ {"id":"bd-pdr2","title":"Consider backwards compatibility for ready() and list() return type change","description":"PR #481 changed the return types of `ready()` and `list()` from `list[Issue]` to `list[IssueMinimal] | CompactedResult`. This is a breaking change for MCP clients.\n\n## Impact Assessment\nBreaking change affects:\n- Any MCP client expecting `list[Issue]` from ready()\n- Any MCP client expecting `list[Issue]` from list()\n- Client code that accesses full Issue fields (description, design, acceptance_criteria, timestamps, dependencies, dependents)\n\n## Current Behavior\n- ready() returns `list[IssueMinimal] | CompactedResult`\n- list() returns `list[IssueMinimal] | CompactedResult`\n- show() still returns full `Issue` (good)\n\n## Considerations\n**Pros of current approach:**\n- Forces clients to use show() for full details (good for context efficiency)\n- Simple mental model (always use show for full data)\n- Documentation warns about this\n\n**Cons:**\n- Clients expecting list[Issue] will break\n- No graceful degradation option\n- No migration period\n\n## Potential Solutions\n1. Add optional parameter `full_details=false` to ready/list (would increase payload)\n2. Create separate tools: ready_minimal/list_minimal + ready_full/list_full\n3. Accept breaking change and document upgrade path (current approach)\n4. Version the MCP server and document migration guide\n\n## Recommendation\nCurrent approach (solution 3) is reasonable if:\n- Changelog clearly documents the breaking change\n- Migration guide provided to clients\n- Error handling is graceful for clients expecting specific fields","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-14T14:24:56.460465-08:00","updated_at":"2025-12-14T14:24:56.460465-08:00","dependencies":[{"issue_id":"bd-pdr2","depends_on_id":"bd-otf4","type":"discovered-from","created_at":"2025-12-14T14:24:56.461959-08:00","created_by":"stevey","metadata":"{}"}]} {"id":"bd-pe4s","title":"JSON test issue","description":"Line 1\nLine 2\nLine 3","status":"tombstone","priority":2,"issue_type":"task","created_at":"2025-12-16T16:14:36.969074-08:00","updated_at":"2025-12-17T16:11:17.070763-08:00","deleted_at":"2025-12-17T16:11:17.070763-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"} {"id":"bd-pgcs","title":"Clean up orphaned child issues (bd-cb64c226.*, bd-cbed9619.*)","description":"## Problem\n\nEvery bd command shows warnings about 12 orphaned child issues:\n- bd-cb64c226.1, .6, .8, .9, .10, .12, .13\n- bd-cbed9619.1, .2, .3, .4, .5\n\nThese are hierarchical IDs (parent.child format) where the parent issues no longer exist.\n\n## Impact\n\n- Clutters output of every bd command\n- Confusing for users\n- Indicates incomplete cleanup of deleted parent issues\n\n## Proposed Solution\n\n1. Delete the orphaned issues since their parents no longer exist:\n ```bash\n bd delete bd-cb64c226.1 bd-cb64c226.6 bd-cb64c226.8 ...\n ```\n\n2. Or convert them to top-level issues if they contain useful content\n\n## Investigation Needed\n\n- What were the parent issues bd-cb64c226 and bd-cbed9619?\n- Why were they deleted without their children?\n- Should bd delete cascade to children automatically?","status":"tombstone","priority":2,"issue_type":"task","created_at":"2025-12-16T23:06:17.240571-08:00","updated_at":"2025-12-17T16:11:17.070763-08:00","deleted_at":"2025-12-17T16:11:17.070763-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"} -{"id":"bd-phtv","title":"bd pin: pinned field overwritten by subsequent bd commands","description":"## Summary\n\nThe `bd pin` command correctly sets `pinned=1` in SQLite, but any subsequent `bd` command (including read-only commands like `bd show`) resets `pinned` to 0.\n\n## Reproduction Steps\n\n```bash\nbd --no-daemon pin \u003cissue-id\u003e --for=max\nsqlite3 .beads/beads.db \"SELECT id, pinned FROM issues WHERE id=\\\"\u003cissue-id\u003e\\\"\"\n# Shows pinned=1 ✓\n\nbd --no-daemon show \u003cissue-id\u003e --json\nsqlite3 .beads/beads.db \"SELECT id, pinned FROM issues WHERE id=\\\"\u003cissue-id\u003e\\\"\"\n# Shows pinned=0 ✗ WRONG\n```\n\n## Root Cause Investigation\n\n### Prime Suspects\n\n1. **JSONL import overwrites DB** - The `pinned` field has `omitempty` so false values arent in JSONL. When JSONL is imported, it overwrites the DB pinned=1 with default pinned=0.\n\n2. **Files to check:**\n - `internal/importer/importer.go` - ImportIssue() may unconditionally set all fields\n - `internal/storage/sqlite/issues.go` - UpsertIssue() may not preserve pinned\n - `cmd/bd/main.go` - ensureStoreActive() may trigger import\n\n### Debug Steps\n\n```bash\n# Add debug logging to track what is writing pinned=0\ngrep -rn \"pinned\" internal/storage/sqlite/*.go\ngrep -rn \"Pinned\" internal/importer/*.go\n```\n\n## Likely Fix\n\nIn `internal/importer/importer.go` or `internal/storage/sqlite/issues.go`:\n\n```go\n// When upserting from JSONL, preserve pinned field if already set\nfunc (s *SQLiteStorage) UpsertIssue(ctx context.Context, issue *types.Issue) error {\n // Check if issue exists and is pinned\n existing, _ := s.GetIssue(ctx, issue.ID)\n if existing != nil \u0026\u0026 existing.Pinned \u0026\u0026 !issue.Pinned {\n // Preserve existing pinned status\n issue.Pinned = existing.Pinned\n }\n // ... rest of upsert\n}\n```\n\nOR the import should skip fields that are omitempty and not present in JSONL:\n\n```go\n// In importer, only update fields that are explicitly set in JSONL\n// Pinned with omitempty means absent = dont change, not absent = false\n```\n\n## Testing\n\n```bash\n# After fix:\nbd --no-daemon pin \u003cissue-id\u003e --for=max\nbd --no-daemon show \u003cissue-id\u003e --json # Should not reset pinned\nbd list --pinned # Should show the pinned issue\nbd hook --agent max # Should show pinned work\n```\n\n## Files to Modify\n\n1. **internal/importer/importer.go** - Preserve pinned on import\n2. **internal/storage/sqlite/issues.go** - UpsertIssue preserve pinned\n3. **Add test** in internal/importer/importer_test.go\n\n## Success Criteria\n- `bd pin` survives subsequent bd commands\n- `bd list --pinned` shows pinned issues\n- `bd hook --agent X` shows pinned work\n- Existing tests still pass","status":"open","priority":1,"issue_type":"bug","created_at":"2025-12-23T12:32:20.046988-08:00","updated_at":"2025-12-23T12:39:47.371978-08:00","labels":["export:pinned-field-fix"]} +{"id":"bd-phtv","title":"bd pin: pinned field overwritten by subsequent bd commands","description":"## Summary\n\nThe `bd pin` command correctly sets `pinned=1` in SQLite, but any subsequent `bd` command (including read-only commands like `bd show`) resets `pinned` to 0.\n\n## Reproduction Steps\n\n```bash\nbd --no-daemon pin \u003cissue-id\u003e --for=max\nsqlite3 .beads/beads.db \"SELECT id, pinned FROM issues WHERE id=\\\"\u003cissue-id\u003e\\\"\"\n# Shows pinned=1 ✓\n\nbd --no-daemon show \u003cissue-id\u003e --json\nsqlite3 .beads/beads.db \"SELECT id, pinned FROM issues WHERE id=\\\"\u003cissue-id\u003e\\\"\"\n# Shows pinned=0 ✗ WRONG\n```\n\n## Root Cause Investigation\n\n### Prime Suspects\n\n1. **JSONL import overwrites DB** - The `pinned` field has `omitempty` so false values arent in JSONL. When JSONL is imported, it overwrites the DB pinned=1 with default pinned=0.\n\n2. **Files to check:**\n - `internal/importer/importer.go` - ImportIssue() may unconditionally set all fields\n - `internal/storage/sqlite/issues.go` - UpsertIssue() may not preserve pinned\n - `cmd/bd/main.go` - ensureStoreActive() may trigger import\n\n### Debug Steps\n\n```bash\n# Add debug logging to track what is writing pinned=0\ngrep -rn \"pinned\" internal/storage/sqlite/*.go\ngrep -rn \"Pinned\" internal/importer/*.go\n```\n\n## Likely Fix\n\nIn `internal/importer/importer.go` or `internal/storage/sqlite/issues.go`:\n\n```go\n// When upserting from JSONL, preserve pinned field if already set\nfunc (s *SQLiteStorage) UpsertIssue(ctx context.Context, issue *types.Issue) error {\n // Check if issue exists and is pinned\n existing, _ := s.GetIssue(ctx, issue.ID)\n if existing != nil \u0026\u0026 existing.Pinned \u0026\u0026 !issue.Pinned {\n // Preserve existing pinned status\n issue.Pinned = existing.Pinned\n }\n // ... rest of upsert\n}\n```\n\nOR the import should skip fields that are omitempty and not present in JSONL:\n\n```go\n// In importer, only update fields that are explicitly set in JSONL\n// Pinned with omitempty means absent = dont change, not absent = false\n```\n\n## Testing\n\n```bash\n# After fix:\nbd --no-daemon pin \u003cissue-id\u003e --for=max\nbd --no-daemon show \u003cissue-id\u003e --json # Should not reset pinned\nbd list --pinned # Should show the pinned issue\nbd hook --agent max # Should show pinned work\n```\n\n## Files to Modify\n\n1. **internal/importer/importer.go** - Preserve pinned on import\n2. **internal/storage/sqlite/issues.go** - UpsertIssue preserve pinned\n3. **Add test** in internal/importer/importer_test.go\n\n## Success Criteria\n- `bd pin` survives subsequent bd commands\n- `bd list --pinned` shows pinned issues\n- `bd hook --agent X` shows pinned work\n- Existing tests still pass","status":"open","priority":1,"issue_type":"bug","created_at":"2025-12-23T12:32:20.046988-08:00","updated_at":"2025-12-23T12:39:47.371978-08:00","labels":["export:pinned-field-fix"],"dependencies":[{"issue_id":"bd-phtv","depends_on_id":"bd-iz5t","type":"parent-child","created_at":"2025-12-23T12:44:07.140151-08:00","created_by":"daemon"}]} {"id":"bd-phwd","title":"Add timeout message for long-running git push operations","description":"When git push hangs waiting for credential/browser auth, show a periodic message to the user instead of appearing frozen. Add timeout messaging after N seconds of inactivity during git operations.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-21T11:44:57.318984535-07:00","updated_at":"2025-12-21T11:46:05.218023559-07:00","closed_at":"2025-12-21T11:46:05.218023559-07:00"} {"id":"bd-pn0t","title":"Add 0.33.2 to versionChanges in info.go","description":"Add new entry at TOP of versionChanges in cmd/bd/info.go with release notes from CHANGELOG.md. Must do before bump-version.sh --commit.","status":"tombstone","priority":1,"issue_type":"task","created_at":"2025-12-21T16:10:13.760056-08:00","updated_at":"2025-12-21T17:29:31.791368-08:00","deleted_at":"2025-12-21T17:29:31.791368-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task","wisp":true} {"id":"bd-psg","title":"Add tests for dependency management","description":"Key dependency functions like mergeBidirectionalTrees, GetDependencyTree, and DetectCycles have low or no coverage. These are essential for maintaining data integrity in the dependency graph.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-18T07:00:43.458548462-07:00","updated_at":"2025-12-19T09:54:57.018745301-07:00","closed_at":"2025-12-18T10:24:56.271508339-07:00","dependencies":[{"issue_id":"bd-psg","depends_on_id":"bd-6ss","type":"discovered-from","created_at":"2025-12-18T07:00:43.463910911-07:00","created_by":"matt"}]} {"id":"bd-pvu0","title":"Merge: bd-4opy","description":"branch: polecat/angharad\ntarget: main\nsource_issue: bd-4opy\nrig: beads","status":"closed","priority":2,"issue_type":"merge-request","created_at":"2025-12-23T00:24:44.057267-08:00","updated_at":"2025-12-23T01:33:25.730271-08:00","closed_at":"2025-12-23T01:33:25.730271-08:00","close_reason":"Merged to main"} {"id":"bd-pzw7","title":"gt handoff deadlock at handoff.go:125","notes":"When running 'gt handoff -m \"message\"' after successful MR submit, go panics with 'fatal error: all goroutines are asleep - deadlock\\!' at handoff.go:125. The shutdown request still appears to be sent successfully but the command crashes. Stack trace shows issue is in runHandoff select statement.","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-19T23:22:12.46315-08:00","updated_at":"2025-12-21T17:51:25.817355-08:00","closed_at":"2025-12-21T17:51:25.817355-08:00","close_reason":"Moved to gastown: gt-dich"} -{"id":"bd-qioh","title":"Standardize error handling: replace direct fmt.Fprintf+os.Exit with FatalError","description":"Standardize error handling in cmd/bd/ using FatalError pattern.\n\n## Current State\n~200+ instances of direct `fmt.Fprintf(os.Stderr, ...) + os.Exit(1)` pattern scattered across cmd/bd/*.go files.\n\n## Target Pattern\n\nUse existing FatalError helper (or create if missing):\n\n```go\n// In cmd/bd/helpers.go or similar\nfunc FatalError(format string, args ...interface{}) {\n fmt.Fprintf(os.Stderr, \"Error: \"+format+\"\\n\", args...)\n os.Exit(1)\n}\n\nfunc FatalErrorf(err error, context string) {\n fmt.Fprintf(os.Stderr, \"Error: %s: %v\\n\", context, err)\n os.Exit(1)\n}\n```\n\n## Transformation\n\n```go\n// Before\nfmt.Fprintf(os.Stderr, \"Error: %v\\n\", err)\nos.Exit(1)\n\n// After\nFatalError(\"%v\", err)\n\n// Before\nfmt.Fprintf(os.Stderr, \"Error: invalid --since duration: %v\\n\", err)\nos.Exit(1)\n\n// After\nFatalError(\"invalid --since duration: %v\", err)\n```\n\n## Files to Update (by occurrence count)\nRun: `grep -c \"os.Exit(1)\" cmd/bd/*.go | sort -t: -k2 -rn | head -20`\n\nPriority files (highest occurrence):\n- close.go\n- show.go\n- sync.go\n- init.go\n- list.go\n- create.go\n- update.go\n\n## Implementation Steps\n\n1. **Check if FatalError exists** in cmd/bd/helpers.go or create it\n2. **Create migration script** or use sed:\n ```bash\n # Find patterns to replace\n grep -rn \"fmt.Fprintf(os.Stderr.*Error.*\\n.*os.Exit(1)\" cmd/bd/\n ```\n3. **Replace systematically** file by file\n4. **Run tests** after each file to verify behavior unchanged\n5. **Run linter** to catch any missed patterns\n\n## Verification\n```bash\n# Count remaining direct exits (should be near zero)\ngrep -c \"os.Exit(1)\" cmd/bd/*.go | awk -F: \"{sum+=\\$2} END {print sum}\"\n\n# Run tests\ngo test -short ./cmd/bd/...\n```\n\n## Success Criteria\n- All error exits use FatalError/FatalErrorf\n- Consistent \"Error: \" prefix on all error messages\n- Tests pass\n- No behavior changes (exit codes remain 1)","notes":"Investigation found ~1129 fmt.Fprintf(os.Stderr) calls in production code. Many are followed by os.Exit(1) and could use FatalError. Estimated: significant effort requiring mechanical but careful changes.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-16T18:17:19.309394-08:00","updated_at":"2025-12-23T12:32:37.686291-08:00"} +{"id":"bd-qioh","title":"Standardize error handling: replace direct fmt.Fprintf+os.Exit with FatalError","description":"Standardize error handling in cmd/bd/ using FatalError pattern.\n\n## Current State\n~200+ instances of direct `fmt.Fprintf(os.Stderr, ...) + os.Exit(1)` pattern scattered across cmd/bd/*.go files.\n\n## Target Pattern\n\nUse existing FatalError helper (or create if missing):\n\n```go\n// In cmd/bd/helpers.go or similar\nfunc FatalError(format string, args ...interface{}) {\n fmt.Fprintf(os.Stderr, \"Error: \"+format+\"\\n\", args...)\n os.Exit(1)\n}\n\nfunc FatalErrorf(err error, context string) {\n fmt.Fprintf(os.Stderr, \"Error: %s: %v\\n\", context, err)\n os.Exit(1)\n}\n```\n\n## Transformation\n\n```go\n// Before\nfmt.Fprintf(os.Stderr, \"Error: %v\\n\", err)\nos.Exit(1)\n\n// After\nFatalError(\"%v\", err)\n\n// Before\nfmt.Fprintf(os.Stderr, \"Error: invalid --since duration: %v\\n\", err)\nos.Exit(1)\n\n// After\nFatalError(\"invalid --since duration: %v\", err)\n```\n\n## Files to Update (by occurrence count)\nRun: `grep -c \"os.Exit(1)\" cmd/bd/*.go | sort -t: -k2 -rn | head -20`\n\nPriority files (highest occurrence):\n- close.go\n- show.go\n- sync.go\n- init.go\n- list.go\n- create.go\n- update.go\n\n## Implementation Steps\n\n1. **Check if FatalError exists** in cmd/bd/helpers.go or create it\n2. **Create migration script** or use sed:\n ```bash\n # Find patterns to replace\n grep -rn \"fmt.Fprintf(os.Stderr.*Error.*\\n.*os.Exit(1)\" cmd/bd/\n ```\n3. **Replace systematically** file by file\n4. **Run tests** after each file to verify behavior unchanged\n5. **Run linter** to catch any missed patterns\n\n## Verification\n```bash\n# Count remaining direct exits (should be near zero)\ngrep -c \"os.Exit(1)\" cmd/bd/*.go | awk -F: \"{sum+=\\$2} END {print sum}\"\n\n# Run tests\ngo test -short ./cmd/bd/...\n```\n\n## Success Criteria\n- All error exits use FatalError/FatalErrorf\n- Consistent \"Error: \" prefix on all error messages\n- Tests pass\n- No behavior changes (exit codes remain 1)","notes":"Investigation found ~1129 fmt.Fprintf(os.Stderr) calls in production code. Many are followed by os.Exit(1) and could use FatalError. Estimated: significant effort requiring mechanical but careful changes.","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-16T18:17:19.309394-08:00","updated_at":"2025-12-23T12:32:37.686291-08:00","dependencies":[{"issue_id":"bd-qioh","depends_on_id":"bd-iz5t","type":"parent-child","created_at":"2025-12-23T12:44:07.43514-08:00","created_by":"daemon"}]} {"id":"bd-qkw9","title":"Run bump-version.sh {{version}}","description":"Run ./scripts/bump-version.sh {{version}} to update version in all files","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-19T22:55:58.841424-08:00","updated_at":"2025-12-20T17:59:26.262877-08:00","closed_at":"2025-12-20T01:18:48.99813-08:00","dependencies":[{"issue_id":"bd-qkw9","depends_on_id":"bd-6s61","type":"parent-child","created_at":"2025-12-19T22:56:14.786027-08:00","created_by":"daemon"}]} {"id":"bd-qqc","title":"Release v{{version}}","description":"Version bump workflow for beads release {{version}}.\n\n## Variables\n- `{{version}}` - The new version number (e.g., 0.31.0)\n- `{{date}}` - Release date (YYYY-MM-DD format)\n\n## Workflow Steps\n1. Kill running daemons\n2. Run tests and linting\n3. Bump version in all files (10 files total)\n4. Update cmd/bd/info.go with release notes\n5. Commit and push version bump\n6. Create and push git tag\n7. Update Homebrew formula\n8. Upgrade local Homebrew installation\n9. Verify installation\n\n## Files Updated by bump-version.sh\n- cmd/bd/version.go\n- .claude-plugin/plugin.json\n- .claude-plugin/marketplace.json\n- integrations/beads-mcp/pyproject.toml\n- integrations/beads-mcp/src/beads_mcp/__init__.py\n- README.md\n- npm-package/package.json\n- cmd/bd/templates/hooks/* (4 files)\n- CHANGELOG.md\n\n## Manual Step Required\n- cmd/bd/info.go - Add versionChanges entry with release notes","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-12-18T12:59:00.610371-08:00","updated_at":"2025-12-20T17:59:26.263219-08:00","closed_at":"2025-12-20T01:18:46.71424-08:00","labels":["template"]} {"id":"bd-qqc.1","title":"Update version to {{version}} in version.go","description":"Edit cmd/bd/version.go line 17:\n\n```go\nVersion = \"{{version}}\"\n```\n\nVerify with: `grep 'Version =' cmd/bd/version.go`","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-18T12:59:13.887087-08:00","updated_at":"2025-12-18T23:34:18.630067-08:00","closed_at":"2025-12-18T22:41:41.82664-08:00","dependencies":[{"issue_id":"bd-qqc.1","depends_on_id":"bd-qqc","type":"parent-child","created_at":"2025-12-18T12:59:13.887655-08:00","created_by":"stevey"}]} @@ -401,7 +402,7 @@ {"id":"bd-r6a.5","title":"Update documentation for template system","description":"Update AGENTS.md and help text to document the new template system:\n\n- How to create a template (epic + template label + child issues)\n- How to define variables (just use {{name}} placeholders)\n- How to instantiate (bd template instantiate)\n- Migration from YAML workflows (if any users had custom ones)","status":"closed","priority":3,"issue_type":"task","created_at":"2025-12-17T22:43:55.461345-08:00","updated_at":"2025-12-18T17:42:26.001474-08:00","closed_at":"2025-12-18T13:46:53.446262-08:00","dependencies":[{"issue_id":"bd-r6a.5","depends_on_id":"bd-r6a","type":"parent-child","created_at":"2025-12-17T22:43:55.461763-08:00","created_by":"daemon","metadata":"{}"},{"issue_id":"bd-r6a.5","depends_on_id":"bd-r6a.3","type":"blocks","created_at":"2025-12-17T22:44:03.632404-08:00","created_by":"daemon","metadata":"{}"},{"issue_id":"bd-r6a.5","depends_on_id":"bd-r6a.4","type":"blocks","created_at":"2025-12-17T22:44:03.788517-08:00","created_by":"daemon","metadata":"{}"}]} {"id":"bd-rece","title":"Phase 1.1: TOON Library Integration - Add gotoon dependency","description":"Add gotoon (github.com/alpkeskin/gotoon) to go.mod and create internal/toon wrapper package for TOON encoding/decoding. This enables bdtoon to encode Issue structs to TOON format and decode TOON back to issues.\n\n## Subtasks\n1. Add gotoon dependency: go get github.com/alpkeskin/gotoon\n2. Create internal/toon package with wrapper functions\n3. Write encode tests for Issue struct round-trip conversion\n4. Write decode tests for TOON to Issue conversion\n5. Add gotoon API options to wrapper (indent, delimiter, length markers)\n\n## Success Criteria\n- go.mod includes gotoon dependency\n- internal/toon/encode.go exports EncodeTOON(issues) ([]byte, error)\n- internal/toon/decode.go exports DecodeTOON(data []byte) ([]Issue, error)\n- Round-trip tests verify Issue → TOON → Issue produces identical data\n- Tests pass with: go test ./internal/toon -v","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-19T11:48:30.018161133-07:00","updated_at":"2025-12-19T12:53:56.808833405-07:00","closed_at":"2025-12-19T12:53:56.808833405-07:00"} {"id":"bd-rgd7","title":"Update CHANGELOG.md with release notes","description":"Add release notes for 0.32.1: MCP output control params (#667), pin field fix","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-20T21:53:16.031879-08:00","updated_at":"2025-12-20T21:54:07.982164-08:00","closed_at":"2025-12-20T21:54:07.982164-08:00","close_reason":"Added 0.32.1 release notes","dependencies":[{"issue_id":"bd-rgd7","depends_on_id":"bd-an4s","type":"parent-child","created_at":"2025-12-20T21:53:16.034926-08:00","created_by":"daemon"}]} -{"id":"bd-rgyd","title":"Split internal/storage/sqlite/queries.go (1586 lines)","description":"Split internal/storage/sqlite/queries.go (1704 lines) into logical modules.\n\n## Current State\nqueries.go is 1704 lines with mixed responsibilities:\n- Issue CRUD operations\n- Search/filter operations\n- Delete operations (complex cascade logic)\n- Helper functions (parsing, formatting)\n\n## Proposed Split\n\n### 1. queries.go (keep ~400 lines) - Core CRUD\n```go\n// Core issue operations\nfunc (s *SQLiteStorage) CreateIssue(...)\nfunc (s *SQLiteStorage) GetIssue(...)\nfunc (s *SQLiteStorage) UpdateIssue(...)\nfunc (s *SQLiteStorage) CloseIssue(...)\n```\n\n### 2. queries_search.go (~300 lines) - Search/Filter\n```go\n// Search and filtering\nfunc (s *SQLiteStorage) SearchIssues(...)\nfunc (s *SQLiteStorage) GetIssueByExternalRef(...)\nfunc (s *SQLiteStorage) GetCloseReason(...)\nfunc (s *SQLiteStorage) GetCloseReasonsForIssues(...)\n```\n\n### 3. queries_delete.go (~400 lines) - Delete Operations\n```go\n// Delete operations with cascade logic\nfunc (s *SQLiteStorage) CreateTombstone(...)\nfunc (s *SQLiteStorage) DeleteIssue(...)\nfunc (s *SQLiteStorage) DeleteIssues(...)\nfunc (s *SQLiteStorage) resolveDeleteSet(...)\nfunc (s *SQLiteStorage) expandWithDependents(...)\nfunc (s *SQLiteStorage) validateNoDependents(...)\nfunc (s *SQLiteStorage) checkSingleIssueValidation(...)\nfunc (s *SQLiteStorage) trackOrphanedIssues(...)\nfunc (s *SQLiteStorage) collectOrphansForID(...)\nfunc (s *SQLiteStorage) populateDeleteStats(...)\nfunc (s *SQLiteStorage) executeDelete(...)\nfunc (s *SQLiteStorage) findAllDependentsRecursive(...)\n```\n\n### 4. queries_helpers.go (~100 lines) - Utilities\n```go\n// Helper functions (already at top of file)\nfunc parseNullableTimeString(...)\nfunc parseJSONStringArray(...)\nfunc formatJSONStringArray(...)\n```\n\n### 5. queries_rename.go (~100 lines) - ID/Prefix Operations\n```go\n// ID and prefix management\nfunc (s *SQLiteStorage) UpdateIssueID(...)\nfunc (s *SQLiteStorage) RenameDependencyPrefix(...)\nfunc (s *SQLiteStorage) RenameCounterPrefix(...)\nfunc (s *SQLiteStorage) ResetCounter(...)\n```\n\n## Implementation Steps\n\n1. **Create new files** with package declaration:\n ```go\n // queries_delete.go\n package sqlite\n \n import (...)\n ```\n\n2. **Move functions** - cut/paste, maintaining order within each file\n\n3. **Update imports** - each file needs its own imports\n\n4. **Run tests** after each file split:\n ```bash\n go test ./internal/storage/sqlite/...\n ```\n\n5. **Run linter** to catch any issues:\n ```bash\n golangci-lint run ./internal/storage/sqlite/...\n ```\n\n## File Organization\n```\ninternal/storage/sqlite/\n├── queries.go # Core CRUD (~400 lines)\n├── queries_search.go # Search/filter (~300 lines)\n├── queries_delete.go # Delete cascade (~400 lines)\n├── queries_helpers.go # Utilities (~100 lines)\n└── queries_rename.go # ID operations (~100 lines)\n```\n\n## Success Criteria\n- No file \u003e 500 lines\n- All tests pass\n- No functionality changes\n- Clear separation of concerns","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-16T18:17:23.85869-08:00","updated_at":"2025-12-23T12:32:59.427619-08:00"} +{"id":"bd-rgyd","title":"Split internal/storage/sqlite/queries.go (1586 lines)","description":"Split internal/storage/sqlite/queries.go (1704 lines) into logical modules.\n\n## Current State\nqueries.go is 1704 lines with mixed responsibilities:\n- Issue CRUD operations\n- Search/filter operations\n- Delete operations (complex cascade logic)\n- Helper functions (parsing, formatting)\n\n## Proposed Split\n\n### 1. queries.go (keep ~400 lines) - Core CRUD\n```go\n// Core issue operations\nfunc (s *SQLiteStorage) CreateIssue(...)\nfunc (s *SQLiteStorage) GetIssue(...)\nfunc (s *SQLiteStorage) UpdateIssue(...)\nfunc (s *SQLiteStorage) CloseIssue(...)\n```\n\n### 2. queries_search.go (~300 lines) - Search/Filter\n```go\n// Search and filtering\nfunc (s *SQLiteStorage) SearchIssues(...)\nfunc (s *SQLiteStorage) GetIssueByExternalRef(...)\nfunc (s *SQLiteStorage) GetCloseReason(...)\nfunc (s *SQLiteStorage) GetCloseReasonsForIssues(...)\n```\n\n### 3. queries_delete.go (~400 lines) - Delete Operations\n```go\n// Delete operations with cascade logic\nfunc (s *SQLiteStorage) CreateTombstone(...)\nfunc (s *SQLiteStorage) DeleteIssue(...)\nfunc (s *SQLiteStorage) DeleteIssues(...)\nfunc (s *SQLiteStorage) resolveDeleteSet(...)\nfunc (s *SQLiteStorage) expandWithDependents(...)\nfunc (s *SQLiteStorage) validateNoDependents(...)\nfunc (s *SQLiteStorage) checkSingleIssueValidation(...)\nfunc (s *SQLiteStorage) trackOrphanedIssues(...)\nfunc (s *SQLiteStorage) collectOrphansForID(...)\nfunc (s *SQLiteStorage) populateDeleteStats(...)\nfunc (s *SQLiteStorage) executeDelete(...)\nfunc (s *SQLiteStorage) findAllDependentsRecursive(...)\n```\n\n### 4. queries_helpers.go (~100 lines) - Utilities\n```go\n// Helper functions (already at top of file)\nfunc parseNullableTimeString(...)\nfunc parseJSONStringArray(...)\nfunc formatJSONStringArray(...)\n```\n\n### 5. queries_rename.go (~100 lines) - ID/Prefix Operations\n```go\n// ID and prefix management\nfunc (s *SQLiteStorage) UpdateIssueID(...)\nfunc (s *SQLiteStorage) RenameDependencyPrefix(...)\nfunc (s *SQLiteStorage) RenameCounterPrefix(...)\nfunc (s *SQLiteStorage) ResetCounter(...)\n```\n\n## Implementation Steps\n\n1. **Create new files** with package declaration:\n ```go\n // queries_delete.go\n package sqlite\n \n import (...)\n ```\n\n2. **Move functions** - cut/paste, maintaining order within each file\n\n3. **Update imports** - each file needs its own imports\n\n4. **Run tests** after each file split:\n ```bash\n go test ./internal/storage/sqlite/...\n ```\n\n5. **Run linter** to catch any issues:\n ```bash\n golangci-lint run ./internal/storage/sqlite/...\n ```\n\n## File Organization\n```\ninternal/storage/sqlite/\n├── queries.go # Core CRUD (~400 lines)\n├── queries_search.go # Search/filter (~300 lines)\n├── queries_delete.go # Delete cascade (~400 lines)\n├── queries_helpers.go # Utilities (~100 lines)\n└── queries_rename.go # ID operations (~100 lines)\n```\n\n## Success Criteria\n- No file \u003e 500 lines\n- All tests pass\n- No functionality changes\n- Clear separation of concerns","status":"open","priority":2,"issue_type":"task","created_at":"2025-12-16T18:17:23.85869-08:00","updated_at":"2025-12-23T12:32:59.427619-08:00","dependencies":[{"issue_id":"bd-rgyd","depends_on_id":"bd-iz5t","type":"parent-child","created_at":"2025-12-23T12:44:07.50733-08:00","created_by":"daemon"}]} {"id":"bd-rl5t","title":"Integration test: agent waits for CI via gate","description":"End-to-end test of the gate workflow.\n\n## Test Scenario\n1. Agent creates gate: bd gate create --await gh:run:123 --timeout 5m --notify beads/dave\n2. Agent writes handoff and exits\n3. Deacon patrol checks gate condition\n4. (Mock) GitHub run completes\n5. Deacon notifies waiter and closes gate\n6. New agent session reads mail and resumes\n\n## Test Requirements\n- Mock GitHub API responses\n- Test timeout path\n- Test multiple waiters\n- Verify mail notifications sent","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-23T11:44:41.725752-08:00","updated_at":"2025-12-23T12:24:08.346347-08:00","closed_at":"2025-12-23T12:24:08.346347-08:00","close_reason":"Moved to gastown: gt-gswn","dependencies":[{"issue_id":"bd-rl5t","depends_on_id":"bd-udsi","type":"parent-child","created_at":"2025-12-23T11:44:53.157037-08:00","created_by":"daemon"},{"issue_id":"bd-rl5t","depends_on_id":"bd-2l03","type":"blocks","created_at":"2025-12-23T11:44:56.674866-08:00","created_by":"daemon"},{"issue_id":"bd-rl5t","depends_on_id":"bd-ykqu","type":"blocks","created_at":"2025-12-23T11:44:56.753264-08:00","created_by":"daemon"}]} {"id":"bd-rnnr","title":"BondRef data model for compound lineage","description":"Add data model support for tracking compound molecule lineage.\n\nNEW FIELDS on Issue:\n bonded_from: []BondRef // For compounds: constituent protos\n\nNEW TYPE:\n type BondRef struct {\n ProtoID string // Source proto ID\n BondType string // sequential, parallel, conditional\n BondPoint string // Attachment site (issue ID or empty for root)\n }\n\nJSONL SERIALIZATION:\n {\n \"id\": \"proto-feature-tested\",\n \"title\": \"Feature with tests\",\n \"bonded_from\": [\n {\"proto_id\": \"proto-feature\", \"bond_type\": \"root\"},\n {\"proto_id\": \"proto-testing\", \"bond_type\": \"sequential\"}\n ],\n ...\n }\n\nQUERIES:\n- GetCompoundConstituents(id) → []BondRef\n- IsCompound(id) → bool\n- GetCompoundsUsing(protoID) → []Issue // Reverse lookup","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-21T00:59:38.582509-08:00","updated_at":"2025-12-21T01:19:43.922416-08:00","closed_at":"2025-12-21T01:19:43.922416-08:00","close_reason":"Added BondRef type to types.go with ProtoID, BondType, BondPoint fields; added BondedFrom field to Issue; added IsCompound() and GetConstituents() helpers; added BondType constants","dependencies":[{"issue_id":"bd-rnnr","depends_on_id":"bd-o5xe","type":"parent-child","created_at":"2025-12-21T00:59:51.234246-08:00","created_by":"daemon"}]} {"id":"bd-rp4o","title":"Deleted issues resurrect during bd sync (tombstones not propagating)","description":"## Problem\n\nWhen issues are deleted with bd delete --force, they get deleted from the local DB but resurrect during the next bd sync.\n\n## Reproduction\n\n1. Observe orphan warnings (bd-cb64c226.*, bd-cbed9619.*)\n2. Delete them: bd delete bd-cb64c226.1 ... --force\n3. Run bd sync\n4. Orphan warnings reappear - issues were resurrected!\n\n## Root Cause Hypothesis\n\nThe sync branch workflow (beads-sync) has the old state before deletions. When bd sync pulls from beads-sync and copies JSONL to main, the deleted issues are re-imported.\n\nTombstones may not be properly:\n1. Written to beads-sync during export\n2. Propagated during pull/merge\n3. Honored during import\n\n## Related\n\n- bd-7b7h: chicken-and-egg sync.branch bug (same workflow)\n- bd-ncwo: ID-based fallback matching to prevent ghost resurrection\n\n## Files to Investigate\n\n- cmd/bd/sync.go (export/import flow)\n- internal/syncbranch/worktree.go (PullFromSyncBranch, copyJSONLToMainRepo)\n- internal/importer/ (tombstone handling)","status":"tombstone","priority":1,"issue_type":"bug","created_at":"2025-12-16T23:09:43.072696-08:00","updated_at":"2025-12-17T16:11:17.070763-08:00","deleted_at":"2025-12-17T16:11:17.070763-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"bug"} @@ -422,19 +423,19 @@ {"id":"bd-t4u1","title":"False positive detection by Kaspersky Antivirus (Trojan)","description":"Kaspersky Antivirus falsely detects beads (bd.exe v0.23.1) as a Trojan (PDM:Trojan.Win32.Generic) and removes it.\nEvent: Malicious object detected\nComponent: System Watcher\nObject name: bd.exe\n","status":"open","priority":1,"issue_type":"task","created_at":"2025-11-20T18:56:12.498187-05:00","updated_at":"2025-11-20T18:56:12.498187-05:00"} {"id":"bd-tbz3","title":"bd init UX Improvements","description":"bd init leaves users with incomplete setup, requiring manual bd doctor --fix. Issues found: (1) git hooks not installed if user declines prompt, (2) no auto-migration when CLI is upgraded, (3) stale merge driver configs from old versions. Fix by making bd init more robust with better defaults and auto-migration.","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-11-21T23:16:00.333543-08:00","updated_at":"2025-12-23T04:20:51.88847-08:00","closed_at":"2025-12-23T04:20:51.88847-08:00","close_reason":"Already implemented in commits ec4117d0 and 3a36d0b9 - hooks/merge driver install by default, doctor runs at end of init"} {"id":"bd-tggf","title":"Code Health Review Dec 2025: Technical Debt Cleanup","description":"Epic grouping technical debt identified in the Dec 16, 2025 code health review.\n\n## Overall Health Grade: B (Solid foundation, needs cleanup)\n\n### P1 (High Priority):\n- bd-74w1: Consolidate duplicate path-finding utilities\n- bd-b6xo: Remove/fix ClearDirtyIssues() race condition\n- bd-b3og: Fix TestImportBugIntegration deadlock\n\n### P2 (Medium Priority):\n- bd-05a8: Split large files (doctor.go, sync.go)\n- bd-qioh: Standardize error handling patterns\n- bd-rgyd: Split queries.go (1586 lines)\n- bd-9g1z: Fix/remove TestFindJSONLPathDefault\n\n### P3 (Low Priority):\n- bd-ork0: Add comments to 30+ ignored errors\n- bd-4nqq: Remove dead test code in info_test.go\n- bd-dhza: Reduce global state in main.go\n\n## Key Areas:\n1. Code duplication in path utilities\n2. Large monolithic files (5 files \u003e1000 lines)\n3. Global state (25+ variables, 3 deprecated)\n4. Silent error suppression (30+ instances)\n5. Test gaps and dead test code\n6. Atomicity risks in batch operations","status":"open","priority":2,"issue_type":"epic","created_at":"2025-12-16T18:18:58.115507-08:00","updated_at":"2025-12-16T18:21:50.561709-08:00","dependencies":[{"issue_id":"bd-tggf","depends_on_id":"bd-74w1","type":"blocks","created_at":"2025-12-22T21:00:21.429274-08:00","created_by":"daemon"},{"issue_id":"bd-tggf","depends_on_id":"bd-05a8","type":"blocks","created_at":"2025-12-22T21:00:21.501589-08:00","created_by":"daemon"},{"issue_id":"bd-tggf","depends_on_id":"bd-9g1z","type":"blocks","created_at":"2025-12-22T21:00:21.571116-08:00","created_by":"daemon"},{"issue_id":"bd-tggf","depends_on_id":"bd-qioh","type":"blocks","created_at":"2025-12-22T21:00:21.640589-08:00","created_by":"daemon"},{"issue_id":"bd-tggf","depends_on_id":"bd-rgyd","type":"blocks","created_at":"2025-12-22T21:00:21.710912-08:00","created_by":"daemon"},{"issue_id":"bd-tggf","depends_on_id":"bd-4nqq","type":"blocks","created_at":"2025-12-22T21:00:21.781914-08:00","created_by":"daemon"},{"issue_id":"bd-tggf","depends_on_id":"bd-dhza","type":"blocks","created_at":"2025-12-22T21:00:21.852-08:00","created_by":"daemon"},{"issue_id":"bd-tggf","depends_on_id":"bd-ork0","type":"blocks","created_at":"2025-12-22T21:00:21.930168-08:00","created_by":"daemon"}]} -{"id":"bd-thgk","title":"Improve test coverage for internal/compact (18.2% → 70%)","description":"Improve test coverage for internal/compact package from 17% to 70%.\n\n## Current State\n- Coverage: 17.3%\n- Files: compactor.go, git.go, haiku.go\n- Tests: compactor_test.go (minimal tests)\n\n## Functions Needing Tests\n\n### compactor.go (core compaction)\n- [ ] New - needs config validation tests\n- [ ] CompactTier1 - needs single issue compaction tests\n- [ ] CompactTier1Batch - needs batch processing tests\n- [ ] compactSingleWithResult - internal, test via public API\n\n### git.go\n- [ ] GetCurrentCommitHash - needs git repo fixture tests\n\n### haiku.go (AI summarization) - MOCK REQUIRED\n- [ ] NewHaikuClient - needs API key validation tests\n- [ ] SummarizeTier1 - needs mock API response tests\n- [ ] callWithRetry - needs retry logic tests\n- [ ] isRetryable - needs error classification tests\n- [ ] renderTier1Prompt - needs template rendering tests\n\n## Implementation Guide\n\n1. **Mock the Anthropic API:**\n ```go\n // Create mock HTTP server\n server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n json.NewEncoder(w).Encode(map[string]interface{}{\n \"content\": []map[string]string{{\"text\": \"Summarized content\"}},\n })\n }))\n defer server.Close()\n \n // Point client at mock\n client.baseURL = server.URL\n ```\n\n2. **Test scenarios:**\n - Successful compaction with AI summary\n - API failure with retry\n - Rate limit handling\n - Empty issue handling\n - Large issue truncation\n\n3. **Use test database:**\n ```go\n store, cleanup := testutil.NewTestStore(t)\n defer cleanup()\n ```\n\n## Success Criteria\n- Coverage ≥ 70%\n- AI calls properly mocked (no real API calls in tests)\n- Retry logic verified\n- Error paths covered\n\n## Run Tests\n```bash\ngo test -v -cover ./internal/compact\ngo test -race ./internal/compact\n```","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-13T20:42:58.455767-08:00","updated_at":"2025-12-23T12:32:02.066107-08:00"} +{"id":"bd-thgk","title":"Improve test coverage for internal/compact (18.2% → 70%)","description":"Improve test coverage for internal/compact package from 17% to 70%.\n\n## Current State\n- Coverage: 17.3%\n- Files: compactor.go, git.go, haiku.go\n- Tests: compactor_test.go (minimal tests)\n\n## Functions Needing Tests\n\n### compactor.go (core compaction)\n- [ ] New - needs config validation tests\n- [ ] CompactTier1 - needs single issue compaction tests\n- [ ] CompactTier1Batch - needs batch processing tests\n- [ ] compactSingleWithResult - internal, test via public API\n\n### git.go\n- [ ] GetCurrentCommitHash - needs git repo fixture tests\n\n### haiku.go (AI summarization) - MOCK REQUIRED\n- [ ] NewHaikuClient - needs API key validation tests\n- [ ] SummarizeTier1 - needs mock API response tests\n- [ ] callWithRetry - needs retry logic tests\n- [ ] isRetryable - needs error classification tests\n- [ ] renderTier1Prompt - needs template rendering tests\n\n## Implementation Guide\n\n1. **Mock the Anthropic API:**\n ```go\n // Create mock HTTP server\n server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n json.NewEncoder(w).Encode(map[string]interface{}{\n \"content\": []map[string]string{{\"text\": \"Summarized content\"}},\n })\n }))\n defer server.Close()\n \n // Point client at mock\n client.baseURL = server.URL\n ```\n\n2. **Test scenarios:**\n - Successful compaction with AI summary\n - API failure with retry\n - Rate limit handling\n - Empty issue handling\n - Large issue truncation\n\n3. **Use test database:**\n ```go\n store, cleanup := testutil.NewTestStore(t)\n defer cleanup()\n ```\n\n## Success Criteria\n- Coverage ≥ 70%\n- AI calls properly mocked (no real API calls in tests)\n- Retry logic verified\n- Error paths covered\n\n## Run Tests\n```bash\ngo test -v -cover ./internal/compact\ngo test -race ./internal/compact\n```","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-13T20:42:58.455767-08:00","updated_at":"2025-12-23T12:32:02.066107-08:00","dependencies":[{"issue_id":"bd-thgk","depends_on_id":"bd-iz5t","type":"parent-child","created_at":"2025-12-23T12:44:07.287377-08:00","created_by":"daemon"}]} {"id":"bd-tj00","title":"Update local installation","description":"go build -o ~/.local/bin/bd ./cmd/bd \u0026\u0026 codesign -s - ~/.local/bin/bd","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-20T21:53:20.616907-08:00","updated_at":"2025-12-20T21:55:42.756171-08:00","closed_at":"2025-12-20T21:55:42.756171-08:00","close_reason":"Installed bd 0.32.1","dependencies":[{"issue_id":"bd-tj00","depends_on_id":"bd-an4s","type":"parent-child","created_at":"2025-12-20T21:53:20.619834-08:00","created_by":"daemon"},{"issue_id":"bd-tj00","depends_on_id":"bd-9l0h","type":"blocks","created_at":"2025-12-20T21:53:29.817989-08:00","created_by":"daemon"}]} {"id":"bd-tm2p","title":"Polecats get stuck on interactive shell prompts (cp/mv/rm -i)","description":"During swarm operations, polecats frequently get stuck waiting for interactive prompts from shell commands like:\n- cp prompting 'overwrite file? (y/n)'\n- mv prompting 'overwrite file? (y/n)' \n- rm prompting 'remove file?'\n\nThis happens because macOS aliases or shell configs may have -i flags set by default.\n\nRoot cause: Claude Code runs commands that trigger interactive confirmation prompts, but cannot respond to them, causing the agent to hang indefinitely.\n\nObserved in: Multiple polecats during GH issues swarm (Dec 2024)\n- Derrick, Roustabout, Prospector, Warboy all got stuck on y/n prompts\n\nSuggested fixes:\n1. AGENTS.md should instruct agents to always use -f flag with cp/mv/rm\n2. Polecat startup could set shell aliases to use non-interactive versions\n3. bd prime hook could include guidance about non-interactive commands\n4. Consider detecting stuck prompts and auto-recovering","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-14T16:51:24.572271-08:00","updated_at":"2025-12-17T23:13:40.536312-08:00","closed_at":"2025-12-17T19:13:04.074424-08:00"} {"id":"bd-to1u","title":"Run bump-version.sh test-squash","description":"Run ./scripts/bump-version.sh test-squash to update version in all files","status":"tombstone","priority":1,"issue_type":"task","created_at":"2025-12-21T13:52:33.06696-08:00","updated_at":"2025-12-21T13:53:41.841677-08:00","deleted_at":"2025-12-21T13:53:41.841677-08:00","deleted_by":"stevey","delete_reason":"manual delete","original_type":"task","wisp":true} {"id":"bd-toy3","title":"Test hook","status":"tombstone","priority":2,"issue_type":"task","created_at":"2025-12-16T18:33:39.717036-08:00","updated_at":"2025-12-17T16:11:17.070763-08:00","deleted_at":"2025-12-17T16:11:17.070763-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"} -{"id":"bd-tvu3","title":"Improve test coverage for internal/beads (48.1% → 70%)","description":"Improve test coverage for internal/beads package from 48% to 70%.\n\n## Current State\n- Coverage: 48.4%\n- Files: beads.go, fingerprint.go\n- Tests: beads_test.go (moderate coverage)\n\n## Functions Needing Tests\n\n### beads.go (database discovery)\n- [ ] followRedirect - needs redirect file tests\n- [ ] findDatabaseInBeadsDir - needs various dir structures\n- [x] NewSQLiteStorage - likely covered\n- [ ] FindDatabasePath - needs BEADS_DB env var tests\n- [ ] hasBeadsProjectFiles - needs file existence tests\n- [ ] FindBeadsDir - needs directory traversal tests\n- [ ] FindJSONLPath - needs path derivation tests\n- [ ] findGitRoot - needs git repo tests\n- [ ] findDatabaseInTree - needs nested directory tests\n- [ ] FindAllDatabases - needs multi-database tests\n- [ ] FindWispDir - needs wisp directory tests\n- [ ] FindWispDatabasePath - needs wisp path tests\n- [ ] NewWispStorage - needs wisp storage tests\n- [ ] EnsureWispGitignore - needs gitignore creation tests\n- [ ] IsWispDatabase - needs path classification tests\n\n### fingerprint.go (repo identification)\n- [ ] ComputeRepoID - needs various remote URL tests\n- [ ] canonicalizeGitURL - needs URL normalization tests\n- [ ] GetCloneID - needs clone identification tests\n\n## Implementation Guide\n\n1. **Use temp directories:**\n ```go\n func TestFindBeadsDir(t *testing.T) {\n tmpDir := t.TempDir()\n beadsDir := filepath.Join(tmpDir, \".beads\")\n os.MkdirAll(beadsDir, 0755)\n \n // Create test files\n os.WriteFile(filepath.Join(beadsDir, \"beads.db\"), []byte{}, 0644)\n \n // Change to tmpDir and test\n oldWd, _ := os.Getwd()\n os.Chdir(tmpDir)\n defer os.Chdir(oldWd)\n \n result := FindBeadsDir()\n assert.Equal(t, beadsDir, result)\n }\n ```\n\n2. **Test scenarios:**\n - BEADS_DB environment variable set\n - .beads/ in current directory\n - .beads/ in parent directory\n - Redirect file pointing elsewhere\n - No beads directory found\n - Wisp directory alongside main beads\n\n3. **Git remote URL tests:**\n ```go\n tests := []struct{\n input string\n expected string\n }{\n {\"git@github.com:user/repo.git\", \"github.com/user/repo\"},\n {\"https://github.com/user/repo\", \"github.com/user/repo\"},\n {\"ssh://git@github.com/user/repo.git\", \"github.com/user/repo\"},\n }\n ```\n\n## Success Criteria\n- Coverage ≥ 70%\n- All FindXxx functions have tests\n- Environment variable handling tested\n- Edge cases (missing dirs, redirects) covered\n\n## Run Tests\n```bash\ngo test -v -cover ./internal/beads\ngo test -race ./internal/beads\n```","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-13T20:42:59.739142-08:00","updated_at":"2025-12-23T12:32:03.42224-08:00"} +{"id":"bd-tvu3","title":"Improve test coverage for internal/beads (48.1% → 70%)","description":"Improve test coverage for internal/beads package from 48% to 70%.\n\n## Current State\n- Coverage: 48.4%\n- Files: beads.go, fingerprint.go\n- Tests: beads_test.go (moderate coverage)\n\n## Functions Needing Tests\n\n### beads.go (database discovery)\n- [ ] followRedirect - needs redirect file tests\n- [ ] findDatabaseInBeadsDir - needs various dir structures\n- [x] NewSQLiteStorage - likely covered\n- [ ] FindDatabasePath - needs BEADS_DB env var tests\n- [ ] hasBeadsProjectFiles - needs file existence tests\n- [ ] FindBeadsDir - needs directory traversal tests\n- [ ] FindJSONLPath - needs path derivation tests\n- [ ] findGitRoot - needs git repo tests\n- [ ] findDatabaseInTree - needs nested directory tests\n- [ ] FindAllDatabases - needs multi-database tests\n- [ ] FindWispDir - needs wisp directory tests\n- [ ] FindWispDatabasePath - needs wisp path tests\n- [ ] NewWispStorage - needs wisp storage tests\n- [ ] EnsureWispGitignore - needs gitignore creation tests\n- [ ] IsWispDatabase - needs path classification tests\n\n### fingerprint.go (repo identification)\n- [ ] ComputeRepoID - needs various remote URL tests\n- [ ] canonicalizeGitURL - needs URL normalization tests\n- [ ] GetCloneID - needs clone identification tests\n\n## Implementation Guide\n\n1. **Use temp directories:**\n ```go\n func TestFindBeadsDir(t *testing.T) {\n tmpDir := t.TempDir()\n beadsDir := filepath.Join(tmpDir, \".beads\")\n os.MkdirAll(beadsDir, 0755)\n \n // Create test files\n os.WriteFile(filepath.Join(beadsDir, \"beads.db\"), []byte{}, 0644)\n \n // Change to tmpDir and test\n oldWd, _ := os.Getwd()\n os.Chdir(tmpDir)\n defer os.Chdir(oldWd)\n \n result := FindBeadsDir()\n assert.Equal(t, beadsDir, result)\n }\n ```\n\n2. **Test scenarios:**\n - BEADS_DB environment variable set\n - .beads/ in current directory\n - .beads/ in parent directory\n - Redirect file pointing elsewhere\n - No beads directory found\n - Wisp directory alongside main beads\n\n3. **Git remote URL tests:**\n ```go\n tests := []struct{\n input string\n expected string\n }{\n {\"git@github.com:user/repo.git\", \"github.com/user/repo\"},\n {\"https://github.com/user/repo\", \"github.com/user/repo\"},\n {\"ssh://git@github.com/user/repo.git\", \"github.com/user/repo\"},\n }\n ```\n\n## Success Criteria\n- Coverage ≥ 70%\n- All FindXxx functions have tests\n- Environment variable handling tested\n- Edge cases (missing dirs, redirects) covered\n\n## Run Tests\n```bash\ngo test -v -cover ./internal/beads\ngo test -race ./internal/beads\n```","status":"open","priority":1,"issue_type":"task","created_at":"2025-12-13T20:42:59.739142-08:00","updated_at":"2025-12-23T12:32:03.42224-08:00","dependencies":[{"issue_id":"bd-tvu3","depends_on_id":"bd-iz5t","type":"parent-child","created_at":"2025-12-23T12:44:07.362967-08:00","created_by":"daemon"}]} {"id":"bd-u0g9","title":"GH#405: Prefix parsing with hyphens treats first segment as prefix","description":"Prefix me-py-toolkit gets parsed as just me- when detecting mismatches. Fix prefix parsing to handle multi-hyphen prefixes. See GitHub issue #405.","status":"tombstone","priority":2,"issue_type":"bug","created_at":"2025-12-16T01:03:18.354066-08:00","updated_at":"2025-12-17T16:11:17.070763-08:00","deleted_at":"2025-12-17T16:11:17.070763-08:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"bug"} {"id":"bd-u0sb","title":"Merge: bd-uqfn","description":"branch: polecat/cheedo\ntarget: main\nsource_issue: bd-uqfn\nrig: beads","status":"closed","priority":2,"issue_type":"merge-request","created_at":"2025-12-20T01:11:52.033964-08:00","updated_at":"2025-12-20T23:17:26.994875-08:00","closed_at":"2025-12-20T23:17:26.994875-08:00","close_reason":"Branches nuked, MRs obsolete"} {"id":"bd-u2sc","title":"GH#692: Code quality and refactoring improvements","description":"Epic for implementing refactoring suggestions from GitHub issue #692 (rsnodgrass). These are code quality improvements that don't change functionality but improve maintainability, type safety, and performance.\n\nOriginal issue: https://github.com/steveyegge/beads/issues/692\n\nHigh priority items:\n1. Replace map[string]interface{} with typed structs for JSON output\n2. Adopt slices.SortFunc instead of sort.Slice (Go 1.21+)\n3. Split large files (sync.go, init.go, show.go)\n4. Introduce slog for structured logging in daemon\n\nLower priority:\n5. Further CLI helper extraction\n6. Preallocate slices in hot paths\n7. Polish items (error wrapping, table-driven parsing)","status":"open","priority":3,"issue_type":"epic","created_at":"2025-12-22T14:26:31.630004-08:00","updated_at":"2025-12-22T14:43:08.562025-08:00","external_ref":"gh-692"} {"id":"bd-u2sc.1","title":"Replace map[string]interface{} with typed JSON response structs","description":"Many CLI commands use map[string]interface{} for JSON output which loses type safety and compile-time error detection.\n\nFiles with map[string]interface{}:\n- cmd/bd/compact.go (10+ instances)\n- cmd/bd/cleanup.go\n- cmd/bd/daemons.go\n- cmd/bd/daemon_lifecycle.go\n\nExample fix:\n```go\n// Before\nresult := map[string]interface{}{\n \"status\": \"ok\",\n \"count\": 42,\n}\n\n// After\ntype CompactResponse struct {\n Status string `json:\"status\"`\n Count int `json:\"count\"`\n}\nresult := CompactResponse{Status: \"ok\", Count: 42}\n```\n\nBenefits:\n- Compile-time type checking\n- IDE autocompletion\n- Easier refactoring\n- Self-documenting API","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-22T14:26:44.088548-08:00","updated_at":"2025-12-22T15:48:22.88824-08:00","closed_at":"2025-12-22T15:48:22.88824-08:00","close_reason":"Replaced map[string]interface{} with typed JSON response structs in compact.go, cleanup.go, daemons.go, and daemon_lifecycle.go. Created 15 typed response structs for improved type safety and compile-time error detection.","dependencies":[{"issue_id":"bd-u2sc.1","depends_on_id":"bd-u2sc","type":"parent-child","created_at":"2025-12-22T14:26:44.088931-08:00","created_by":"daemon"}]} {"id":"bd-u2sc.2","title":"Migrate sort.Slice to slices.SortFunc","description":"Go 1.21+ provides slices.SortFunc which is cleaner and slightly faster than sort.Slice.\n\nFound 15+ instances of sort.Slice in:\n- cmd/bd/autoflush.go\n- cmd/bd/count.go\n- cmd/bd/daemon_sync.go\n- cmd/bd/doctor.go\n- cmd/bd/export.go\n- cmd/bd/import.go\n- cmd/bd/integrity.go\n- cmd/bd/jira.go\n- cmd/bd/list.go\n- cmd/bd/migrate_hash_ids.go\n- cmd/bd/rename_prefix.go\n- cmd/bd/show.go\n\nExample migration:\n```go\n// Before\nsort.Slice(issues, func(i, j int) bool {\n return issues[i].Priority \u003c issues[j].Priority\n})\n\n// After\nslices.SortFunc(issues, func(a, b *types.Issue) int {\n return cmp.Compare(a.Priority, b.Priority)\n})\n```\n\nBenefits:\n- Cleaner 3-way comparison\n- Slightly better performance\n- Modern idiomatic Go","status":"closed","priority":3,"issue_type":"task","created_at":"2025-12-22T14:26:55.573524-08:00","updated_at":"2025-12-22T15:10:12.639807-08:00","closed_at":"2025-12-22T15:10:12.639807-08:00","close_reason":"Migrated all 19 instances of sort.Slice to slices.SortFunc across 17 files in cmd/bd/. Used cmp.Compare for orderable types, time.Compare for time comparisons, and cmp.Or for multi-field sorts.","dependencies":[{"issue_id":"bd-u2sc.2","depends_on_id":"bd-u2sc","type":"parent-child","created_at":"2025-12-22T14:26:55.573978-08:00","created_by":"daemon"}]} -{"id":"bd-u2sc.3","title":"Split large cmd/bd files into logical modules","description":"Split large cmd/bd files into logical modules for maintainability.\n\n## Current State\n| File | Lines | Status |\n|------|-------|--------|\n| sync.go | 2203 | Too large |\n| init.go | 1742 | Too large |\n| show.go | 1419 | Too large |\n| compact.go | 1190 | Borderline |\n\n## Proposed Splits\n\n### 1. sync.go (2203 lines) → 4 files\n\n**sync.go** (~500 lines) - Main command and coordination\n```go\nvar syncCmd = \u0026cobra.Command{...}\nfunc init() {...}\nfunc doSync(...) {...}\n```\n\n**sync_export.go** (~400 lines) - Export operations\n```go\nfunc exportToJSONL(...)\nfunc markDirtyAndScheduleFlush(...)\nfunc flushPendingChanges(...)\n```\n\n**sync_import.go** (~500 lines) - Import operations\n```go\nfunc importFromJSONL(...)\nfunc handleImportConflicts(...)\nfunc mergeImportedIssues(...)\n```\n\n**sync_branch.go** (~400 lines) - Git branch operations\n```go\nfunc commitToSyncBranch(...)\nfunc pullFromSyncBranch(...)\nfunc handleSyncBranchDivergence(...)\n```\n\n### 2. init.go (1742 lines) → 3 files\n\n**init.go** (~400 lines) - Main init command\n```go\nvar initCmd = \u0026cobra.Command{...}\nfunc runInit(...)\nfunc determinePrefix(...)\n```\n\n**init_wizard.go** (~500 lines) - Interactive setup\n```go\nfunc runContributorWizard(...)\nfunc runTeamWizard(...)\nfunc promptForConfig(...)\n```\n\n**init_hooks.go** (~400 lines) - Git hooks setup\n```go\nfunc installGitHooks(...)\nfunc configureAutosync(...)\nfunc setupMergeDriver(...)\n```\n\n### 3. show.go (1419 lines) → 3 files\n\n**show.go** (~400 lines) - Main show command\n```go\nvar showCmd = \u0026cobra.Command{...}\nfunc showIssue(...)\nfunc showMultipleIssues(...)\n```\n\n**show_format.go** (~400 lines) - Output formatting\n```go\nfunc formatIssueText(...)\nfunc formatIssueMarkdown(...)\nfunc formatDependencyTree(...)\n```\n\n**show_threads.go** (~300 lines) - Thread/conversation display\n```go\nfunc showThread(...)\nfunc formatThreadMessages(...)\n```\n\n### 4. compact.go (1190 lines) → 2 files\n\n**compact.go** (~600 lines) - Main compact command\n```go\nvar compactCmd = \u0026cobra.Command{...}\nfunc runCompact(...)\n```\n\n**compact_tiers.go** (~400 lines) - Tier-specific logic\n```go\nfunc compactTier1(...)\nfunc compactTier2(...)\nfunc squashWisps(...)\n```\n\n## Implementation Steps\n\n1. **Start with sync.go** (largest file)\n2. **Create new files** with same package declaration\n3. **Move functions** maintaining related code together\n4. **Update any file-local variables** that need to be shared\n5. **Run tests** after each split:\n ```bash\n go test -short ./cmd/bd/...\n ```\n\n## File Organization After Split\n```\ncmd/bd/\n├── sync.go (~500 lines)\n├── sync_export.go (~400 lines)\n├── sync_import.go (~500 lines)\n├── sync_branch.go (~400 lines)\n├── init.go (~400 lines)\n├── init_wizard.go (~500 lines)\n├── init_hooks.go (~400 lines)\n├── show.go (~400 lines)\n├── show_format.go (~400 lines)\n├── show_threads.go (~300 lines)\n├── compact.go (~600 lines)\n└── compact_tiers.go (~400 lines)\n```\n\n## Success Criteria\n- No file \u003e 600 lines\n- All tests pass\n- Related code stays together\n- Clear file naming indicates purpose","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-22T14:27:06.146343-08:00","updated_at":"2025-12-23T12:33:22.187826-08:00","dependencies":[{"issue_id":"bd-u2sc.3","depends_on_id":"bd-u2sc","type":"parent-child","created_at":"2025-12-22T14:27:06.146704-08:00","created_by":"daemon"}]} -{"id":"bd-u2sc.4","title":"Introduce slog for structured daemon logging","description":"Introduce slog for structured daemon logging.\n\n## Current State\nDaemon uses fmt.Fprintf for logging:\n```go\nfmt.Fprintf(os.Stderr, \"Warning: failed to detect user role: %v\\n\", err)\nfmt.Fprintf(logFile, \"[%s] %s\\n\", time.Now().Format(time.RFC3339), msg)\n```\n\nThis produces unstructured, hard-to-parse logs.\n\n## Target State\nUse Go 1.21+ slog for structured logging:\n```go\nslog.Warn(\"failed to detect user role\", \"error\", err)\nslog.Info(\"sync completed\", \"created\", 5, \"updated\", 3, \"duration_ms\", 150)\n```\n\n## Implementation\n\n### 1. Create logger setup (internal/daemon/logger.go)\n\n```go\npackage daemon\n\nimport (\n \"io\"\n \"log/slog\"\n \"os\"\n)\n\n// SetupLogger configures the daemon logger.\n// Returns a cleanup function to close the log file.\nfunc SetupLogger(logPath string, jsonFormat bool, level slog.Level) (func(), error) {\n var w io.Writer = os.Stderr\n var cleanup func()\n \n if logPath \\!= \"\" {\n f, err := os.OpenFile(logPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)\n if err \\!= nil {\n return nil, err\n }\n w = io.MultiWriter(os.Stderr, f)\n cleanup = func() { f.Close() }\n }\n \n var handler slog.Handler\n opts := \u0026slog.HandlerOptions{Level: level}\n \n if jsonFormat {\n handler = slog.NewJSONHandler(w, opts)\n } else {\n handler = slog.NewTextHandler(w, opts)\n }\n \n slog.SetDefault(slog.New(handler))\n \n return cleanup, nil\n}\n```\n\n### 2. Add log level flag\n\nIn cmd/bd/daemon.go:\n```go\ndaemonCmd.Flags().String(\"log-level\", \"info\", \"Log level (debug, info, warn, error)\")\ndaemonCmd.Flags().Bool(\"log-json\", false, \"Output logs in JSON format\")\n```\n\n### 3. Replace fmt.Fprintf with slog calls\n\n**Pattern 1: Simple messages**\n```go\n// Before\nfmt.Fprintf(os.Stderr, \"Starting daemon on %s\\n\", socketPath)\n\n// After\nslog.Info(\"starting daemon\", \"socket\", socketPath)\n```\n\n**Pattern 2: Errors**\n```go\n// Before\nfmt.Fprintf(os.Stderr, \"Error: failed to connect: %v\\n\", err)\n\n// After\nslog.Error(\"failed to connect\", \"error\", err)\n```\n\n**Pattern 3: Debug info**\n```go\n// Before\nif debug {\n fmt.Fprintf(os.Stderr, \"Received request: %s\\n\", method)\n}\n\n// After\nslog.Debug(\"received request\", \"method\", method)\n```\n\n**Pattern 4: Structured data**\n```go\n// Before\nfmt.Fprintf(logFile, \"Import: %d created, %d updated\\n\", created, updated)\n\n// After\nslog.Info(\"import completed\", \n \"created\", created,\n \"updated\", updated,\n \"unchanged\", unchanged,\n \"duration_ms\", duration.Milliseconds())\n```\n\n### 4. Files to Update\n\n| File | Changes |\n|------|---------|\n| cmd/bd/daemon.go | Add log flags, call SetupLogger |\n| internal/daemon/server.go | Replace fmt with slog |\n| internal/daemon/rpc_handler.go | Replace fmt with slog |\n| internal/daemon/sync.go | Replace fmt with slog |\n| internal/daemon/autoflush.go | Replace fmt with slog |\n| internal/daemon/logger.go | New file |\n\n### 5. Log output examples\n\n**Text format (default):**\n```\ntime=2025-12-23T12:30:00Z level=INFO msg=\"daemon started\" socket=/tmp/bd.sock pid=12345\ntime=2025-12-23T12:30:01Z level=INFO msg=\"import completed\" created=5 updated=3 duration_ms=150\ntime=2025-12-23T12:30:05Z level=WARN msg=\"sync branch diverged\" local_ahead=2 remote_ahead=1\n```\n\n**JSON format (--log-json):**\n```json\n{\"time\":\"2025-12-23T12:30:00Z\",\"level\":\"INFO\",\"msg\":\"daemon started\",\"socket\":\"/tmp/bd.sock\",\"pid\":12345}\n{\"time\":\"2025-12-23T12:30:01Z\",\"level\":\"INFO\",\"msg\":\"import completed\",\"created\":5,\"updated\":3,\"duration_ms\":150}\n```\n\n## Migration Strategy\n\n1. **Add logger.go** with SetupLogger\n2. **Update daemon startup** to initialize slog\n3. **Convert one file at a time** (start with server.go)\n4. **Test after each file**\n5. **Remove old logging code** once all converted\n\n## Testing\n\n```bash\n# Start daemon with debug logging\nbd daemon start --log-level debug\n\n# Check logs\nbd daemons logs . | head -20\n\n# Test JSON output\nbd daemon start --log-json --log-level debug\nbd daemons logs . | jq .\n```\n\n## Success Criteria\n- All daemon logging uses slog\n- --log-level controls verbosity\n- --log-json produces machine-parseable output\n- Log entries have consistent structure\n- No fmt.Fprintf to stderr in daemon code","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-22T14:27:16.47144-08:00","updated_at":"2025-12-23T12:36:19.089183-08:00","dependencies":[{"issue_id":"bd-u2sc.4","depends_on_id":"bd-u2sc","type":"parent-child","created_at":"2025-12-22T14:27:16.471878-08:00","created_by":"daemon"}]} +{"id":"bd-u2sc.3","title":"Split large cmd/bd files into logical modules","description":"Split large cmd/bd files into logical modules for maintainability.\n\n## Current State\n| File | Lines | Status |\n|------|-------|--------|\n| sync.go | 2203 | Too large |\n| init.go | 1742 | Too large |\n| show.go | 1419 | Too large |\n| compact.go | 1190 | Borderline |\n\n## Proposed Splits\n\n### 1. sync.go (2203 lines) → 4 files\n\n**sync.go** (~500 lines) - Main command and coordination\n```go\nvar syncCmd = \u0026cobra.Command{...}\nfunc init() {...}\nfunc doSync(...) {...}\n```\n\n**sync_export.go** (~400 lines) - Export operations\n```go\nfunc exportToJSONL(...)\nfunc markDirtyAndScheduleFlush(...)\nfunc flushPendingChanges(...)\n```\n\n**sync_import.go** (~500 lines) - Import operations\n```go\nfunc importFromJSONL(...)\nfunc handleImportConflicts(...)\nfunc mergeImportedIssues(...)\n```\n\n**sync_branch.go** (~400 lines) - Git branch operations\n```go\nfunc commitToSyncBranch(...)\nfunc pullFromSyncBranch(...)\nfunc handleSyncBranchDivergence(...)\n```\n\n### 2. init.go (1742 lines) → 3 files\n\n**init.go** (~400 lines) - Main init command\n```go\nvar initCmd = \u0026cobra.Command{...}\nfunc runInit(...)\nfunc determinePrefix(...)\n```\n\n**init_wizard.go** (~500 lines) - Interactive setup\n```go\nfunc runContributorWizard(...)\nfunc runTeamWizard(...)\nfunc promptForConfig(...)\n```\n\n**init_hooks.go** (~400 lines) - Git hooks setup\n```go\nfunc installGitHooks(...)\nfunc configureAutosync(...)\nfunc setupMergeDriver(...)\n```\n\n### 3. show.go (1419 lines) → 3 files\n\n**show.go** (~400 lines) - Main show command\n```go\nvar showCmd = \u0026cobra.Command{...}\nfunc showIssue(...)\nfunc showMultipleIssues(...)\n```\n\n**show_format.go** (~400 lines) - Output formatting\n```go\nfunc formatIssueText(...)\nfunc formatIssueMarkdown(...)\nfunc formatDependencyTree(...)\n```\n\n**show_threads.go** (~300 lines) - Thread/conversation display\n```go\nfunc showThread(...)\nfunc formatThreadMessages(...)\n```\n\n### 4. compact.go (1190 lines) → 2 files\n\n**compact.go** (~600 lines) - Main compact command\n```go\nvar compactCmd = \u0026cobra.Command{...}\nfunc runCompact(...)\n```\n\n**compact_tiers.go** (~400 lines) - Tier-specific logic\n```go\nfunc compactTier1(...)\nfunc compactTier2(...)\nfunc squashWisps(...)\n```\n\n## Implementation Steps\n\n1. **Start with sync.go** (largest file)\n2. **Create new files** with same package declaration\n3. **Move functions** maintaining related code together\n4. **Update any file-local variables** that need to be shared\n5. **Run tests** after each split:\n ```bash\n go test -short ./cmd/bd/...\n ```\n\n## File Organization After Split\n```\ncmd/bd/\n├── sync.go (~500 lines)\n├── sync_export.go (~400 lines)\n├── sync_import.go (~500 lines)\n├── sync_branch.go (~400 lines)\n├── init.go (~400 lines)\n├── init_wizard.go (~500 lines)\n├── init_hooks.go (~400 lines)\n├── show.go (~400 lines)\n├── show_format.go (~400 lines)\n├── show_threads.go (~300 lines)\n├── compact.go (~600 lines)\n└── compact_tiers.go (~400 lines)\n```\n\n## Success Criteria\n- No file \u003e 600 lines\n- All tests pass\n- Related code stays together\n- Clear file naming indicates purpose","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-22T14:27:06.146343-08:00","updated_at":"2025-12-23T12:33:22.187826-08:00","dependencies":[{"issue_id":"bd-u2sc.3","depends_on_id":"bd-u2sc","type":"parent-child","created_at":"2025-12-22T14:27:06.146704-08:00","created_by":"daemon"},{"issue_id":"bd-u2sc.3","depends_on_id":"bd-iz5t","type":"parent-child","created_at":"2025-12-23T12:44:07.583136-08:00","created_by":"daemon"}]} +{"id":"bd-u2sc.4","title":"Introduce slog for structured daemon logging","description":"Introduce slog for structured daemon logging.\n\n## Current State\nDaemon uses fmt.Fprintf for logging:\n```go\nfmt.Fprintf(os.Stderr, \"Warning: failed to detect user role: %v\\n\", err)\nfmt.Fprintf(logFile, \"[%s] %s\\n\", time.Now().Format(time.RFC3339), msg)\n```\n\nThis produces unstructured, hard-to-parse logs.\n\n## Target State\nUse Go 1.21+ slog for structured logging:\n```go\nslog.Warn(\"failed to detect user role\", \"error\", err)\nslog.Info(\"sync completed\", \"created\", 5, \"updated\", 3, \"duration_ms\", 150)\n```\n\n## Implementation\n\n### 1. Create logger setup (internal/daemon/logger.go)\n\n```go\npackage daemon\n\nimport (\n \"io\"\n \"log/slog\"\n \"os\"\n)\n\n// SetupLogger configures the daemon logger.\n// Returns a cleanup function to close the log file.\nfunc SetupLogger(logPath string, jsonFormat bool, level slog.Level) (func(), error) {\n var w io.Writer = os.Stderr\n var cleanup func()\n \n if logPath \\!= \"\" {\n f, err := os.OpenFile(logPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)\n if err \\!= nil {\n return nil, err\n }\n w = io.MultiWriter(os.Stderr, f)\n cleanup = func() { f.Close() }\n }\n \n var handler slog.Handler\n opts := \u0026slog.HandlerOptions{Level: level}\n \n if jsonFormat {\n handler = slog.NewJSONHandler(w, opts)\n } else {\n handler = slog.NewTextHandler(w, opts)\n }\n \n slog.SetDefault(slog.New(handler))\n \n return cleanup, nil\n}\n```\n\n### 2. Add log level flag\n\nIn cmd/bd/daemon.go:\n```go\ndaemonCmd.Flags().String(\"log-level\", \"info\", \"Log level (debug, info, warn, error)\")\ndaemonCmd.Flags().Bool(\"log-json\", false, \"Output logs in JSON format\")\n```\n\n### 3. Replace fmt.Fprintf with slog calls\n\n**Pattern 1: Simple messages**\n```go\n// Before\nfmt.Fprintf(os.Stderr, \"Starting daemon on %s\\n\", socketPath)\n\n// After\nslog.Info(\"starting daemon\", \"socket\", socketPath)\n```\n\n**Pattern 2: Errors**\n```go\n// Before\nfmt.Fprintf(os.Stderr, \"Error: failed to connect: %v\\n\", err)\n\n// After\nslog.Error(\"failed to connect\", \"error\", err)\n```\n\n**Pattern 3: Debug info**\n```go\n// Before\nif debug {\n fmt.Fprintf(os.Stderr, \"Received request: %s\\n\", method)\n}\n\n// After\nslog.Debug(\"received request\", \"method\", method)\n```\n\n**Pattern 4: Structured data**\n```go\n// Before\nfmt.Fprintf(logFile, \"Import: %d created, %d updated\\n\", created, updated)\n\n// After\nslog.Info(\"import completed\", \n \"created\", created,\n \"updated\", updated,\n \"unchanged\", unchanged,\n \"duration_ms\", duration.Milliseconds())\n```\n\n### 4. Files to Update\n\n| File | Changes |\n|------|---------|\n| cmd/bd/daemon.go | Add log flags, call SetupLogger |\n| internal/daemon/server.go | Replace fmt with slog |\n| internal/daemon/rpc_handler.go | Replace fmt with slog |\n| internal/daemon/sync.go | Replace fmt with slog |\n| internal/daemon/autoflush.go | Replace fmt with slog |\n| internal/daemon/logger.go | New file |\n\n### 5. Log output examples\n\n**Text format (default):**\n```\ntime=2025-12-23T12:30:00Z level=INFO msg=\"daemon started\" socket=/tmp/bd.sock pid=12345\ntime=2025-12-23T12:30:01Z level=INFO msg=\"import completed\" created=5 updated=3 duration_ms=150\ntime=2025-12-23T12:30:05Z level=WARN msg=\"sync branch diverged\" local_ahead=2 remote_ahead=1\n```\n\n**JSON format (--log-json):**\n```json\n{\"time\":\"2025-12-23T12:30:00Z\",\"level\":\"INFO\",\"msg\":\"daemon started\",\"socket\":\"/tmp/bd.sock\",\"pid\":12345}\n{\"time\":\"2025-12-23T12:30:01Z\",\"level\":\"INFO\",\"msg\":\"import completed\",\"created\":5,\"updated\":3,\"duration_ms\":150}\n```\n\n## Migration Strategy\n\n1. **Add logger.go** with SetupLogger\n2. **Update daemon startup** to initialize slog\n3. **Convert one file at a time** (start with server.go)\n4. **Test after each file**\n5. **Remove old logging code** once all converted\n\n## Testing\n\n```bash\n# Start daemon with debug logging\nbd daemon start --log-level debug\n\n# Check logs\nbd daemons logs . | head -20\n\n# Test JSON output\nbd daemon start --log-json --log-level debug\nbd daemons logs . | jq .\n```\n\n## Success Criteria\n- All daemon logging uses slog\n- --log-level controls verbosity\n- --log-json produces machine-parseable output\n- Log entries have consistent structure\n- No fmt.Fprintf to stderr in daemon code","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-22T14:27:16.47144-08:00","updated_at":"2025-12-23T12:36:19.089183-08:00","dependencies":[{"issue_id":"bd-u2sc.4","depends_on_id":"bd-u2sc","type":"parent-child","created_at":"2025-12-22T14:27:16.471878-08:00","created_by":"daemon"},{"issue_id":"bd-u2sc.4","depends_on_id":"bd-iz5t","type":"parent-child","created_at":"2025-12-23T12:44:08.048156-08:00","created_by":"daemon"}]} {"id":"bd-u66e","title":"Implement bd gate create/show/list/close/wait commands","description":"Implement the gate CLI commands.\n\n## Commands\n```bash\n# Create gate (returns gate ID)\nbd gate create --await \u003ctype\u003e:\u003cid\u003e --timeout \u003cduration\u003e --notify \u003caddr\u003e\n\n# Check gate status\nbd gate show \u003cid\u003e\n\n# List open gates\nbd gate list\n\n# Close gate (usually done by Deacon)\nbd gate close \u003cid\u003e --reason \"completed\"\n\n# Add waiter to existing gate\nbd gate wait \u003cid\u003e --notify \u003caddr\u003e\n```\n\n## Implementation\n- Add cmd/bd/gate.go with subcommands\n- Gate create creates wisp issue of type gate\n- Gate list filters for open gates\n- Gate wait adds to waiters[] array\n- All support --json output","status":"closed","priority":1,"issue_type":"task","created_at":"2025-12-23T11:44:34.022464-08:00","updated_at":"2025-12-23T12:06:18.550673-08:00","closed_at":"2025-12-23T12:06:18.550673-08:00","close_reason":"Implemented bd gate commands: create, show, list, close, wait. All support --json output and work with wisp/gate fields.","dependencies":[{"issue_id":"bd-u66e","depends_on_id":"bd-udsi","type":"parent-child","created_at":"2025-12-23T11:44:52.823353-08:00","created_by":"daemon"},{"issue_id":"bd-u66e","depends_on_id":"bd-lz49","type":"blocks","created_at":"2025-12-23T11:44:56.349662-08:00","created_by":"daemon"}]} {"id":"bd-ucgz","title":"Migration invariants should exclude external dependencies from orphan check","description":"## Summary\n\nThe `checkForeignKeys` function in `migration_invariants.go` flags external dependencies as orphaned because they dont exist in the local issues table.\n\n## Location\n\n`internal/storage/sqlite/migration_invariants.go` around line 150-162\n\n## Current Code (buggy)\n\n```go\n// Check for orphaned dependencies\nvar orphanCount int\nerr = db.QueryRowContext(ctx, \\`\n SELECT COUNT(*)\n FROM dependencies d\n WHERE NOT EXISTS (SELECT 1 FROM issues WHERE id = d.depends_on_id)\n\\`).Scan(\u0026orphanCount)\n```\n\n## Fix\n\nExclude external references (format: `external:\u003cproject\u003e:\u003ccapability\u003e`):\n\n```go\n// Check for orphaned dependencies (excluding external refs)\nvar orphanCount int\nerr = db.QueryRowContext(ctx, \\`\n SELECT COUNT(*)\n FROM dependencies d\n WHERE NOT EXISTS (SELECT 1 FROM issues WHERE id = d.depends_on_id)\n AND d.depends_on_id NOT LIKE 'external:%'\n\\`).Scan(\u0026orphanCount)\n```\n\n## Reproduction\n\n```bash\n# Add external dependency\nbd dep add bd-xxx external:other-project:some-capability\n\n# Try to sync - fails\nbd sync\n# Error: found 1 orphaned dependencies\n```\n\n## Files to Modify\n\n1. **internal/storage/sqlite/migration_invariants.go** - Add WHERE clause\n\n## Testing\n\n```bash\n# Create issue with external dep\nbd create \"Test external deps\" -t task\nbd dep add bd-xxx external:beads:release-workflow\n\n# Sync should succeed\nbd sync\n\n# Verify dep exists\nbd show bd-xxx --json | jq .dependencies\n```\n\n## Success Criteria\n- External dependencies dont trigger orphan check\n- `bd sync` succeeds with external deps\n- Regular orphan detection still works for internal deps","status":"closed","priority":2,"issue_type":"bug","created_at":"2025-12-23T12:37:08.99387-08:00","updated_at":"2025-12-23T12:42:03.722691-08:00","closed_at":"2025-12-23T12:42:03.722691-08:00","close_reason":"Fixed in commit 90196eca - added NOT LIKE 'external:%' exclusion"} {"id":"bd-udsi","title":"Async Gates for Agent Coordination","description":"Agents need an async primitive for waiting on external events (CI completion, API responses, human approval). Gates are wisp issues that block until external conditions are met, managed by the Deacon.\n\n## Core Concepts\n\n**Gate** = wisp issue that blocks until external condition is met\n- Type: gate\n- Phase: wisp (never synced, ephemeral)\n- Assignee: deacon/ (Deacon monitors it)\n- Fields: await_type, await_id, timeout, waiters[]\n\n**Await Types:**\n- gh:run:\u003cid\u003e - GitHub Actions run completion\n- gh:pr:\u003cid\u003e - PR merged/closed\n- timer:\u003cduration\u003e - Simple delay\n- human:\u003cprompt\u003e - Human approval required\n- mail:\u003cpattern\u003e - Wait for mail matching pattern\n\n## Open Questions\n- Should gates live in wisp storage or main storage with wisp flag?\n- Do we need a gate catalog (like molecule catalog)?\n- Should waits-for dep type work with gates?","status":"closed","priority":1,"issue_type":"epic","created_at":"2025-12-23T11:44:02.711062-08:00","updated_at":"2025-12-23T12:24:43.537615-08:00","closed_at":"2025-12-23T12:24:43.537615-08:00","close_reason":"Core beads implementation complete (types, migration, CLI). Remaining Deacon integration work moved to gastown (gt-dh65, gt-ng6g, gt-fqcz, gt-gswn)."} @@ -474,7 +475,7 @@ {"id":"bd-y8bj","title":"Auto-detect identity from directory context for bd mail","description":"Currently bd mail inbox defaults to git user name, requiring --identity flag with exact format.\n\n## Problem\n- Mail sent to `gastown/crew/max`\n- Max runs `bd mail inbox` → defaults to 'Steve Yegge' (git user)\n- Max must know to use `--identity 'gastown/crew/max'` with exact slashes\n\n## Proposed Fix\nAuto-detect identity from directory context when in a Gas Town workspace:\n- In `/Users/stevey/gt/gastown/crew/max`, infer identity = `gastown/crew/max`\n- Pattern: `\u003ctown\u003e/\u003crig\u003e/\u003crole\u003e/\u003cname\u003e` → `\u003crig\u003e/\u003crole\u003e/\u003cname\u003e`\n\n## Additional Improvements\n1. Support GT_IDENTITY env var (set by gt crew at / session spawning)\n2. Support identity in .beads/config.yaml\n3. Normalize format: accept both slashes and dashes as equivalent\n\n## Context\nDiscovered during crew-to-crew work assignment. Max couldn't see mail despite correct nudge because identity defaulted wrong.","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-12-20T17:22:53.938586-08:00","updated_at":"2025-12-20T18:12:58.472262-08:00","closed_at":"2025-12-20T17:58:51.034201-08:00"} {"id":"bd-y8tn","title":"Test Molecule","description":"A test molecule","status":"closed","priority":2,"issue_type":"molecule","created_at":"2025-12-19T18:30:24.491279-08:00","updated_at":"2025-12-19T18:31:12.49898-08:00","closed_at":"2025-12-19T18:31:12.49898-08:00","close_reason":"test molecule - deleting"} {"id":"bd-yck","title":"Fix checkExistingBeadsData to be worktree-aware","description":"The checkExistingBeadsData function in cmd/bd/init.go checks for .beads in the current working directory, but for worktrees it should check the main repository root instead. This prevents proper worktree compatibility.","status":"open","priority":2,"issue_type":"bug","created_at":"2025-12-07T16:48:32.082776345-07:00","updated_at":"2025-12-07T16:48:32.082776345-07:00"} -{"id":"bd-ykd9","title":"Add bd doctor --fix flag to automatically repair issues","description":"Add bd doctor --fix flag to automatically repair detected issues.\n\n## Current State\n- Doctor checks return issues with \"Fix\" field containing fix instructions\n- No automatic fix execution\n- User must manually follow fix instructions\n\n## Implementation\n\n### 1. Add --fix flag to doctor.go\n```go\n// In cmd/bd/doctor.go init()\ndoctorCmd.Flags().Bool(\"fix\", false, \"Automatically fix detected issues\")\ndoctorCmd.Flags().Bool(\"yes\", false, \"Skip confirmation prompts (use with --fix)\")\n```\n\n### 2. Create fix registry (cmd/bd/doctor/fix/registry.go)\n```go\npackage fix\n\n// Fixer can automatically repair an issue\ntype Fixer interface {\n // CanFix returns true if this fixer handles the given check name\n CanFix(checkName string) bool\n // Fix attempts to repair the issue, returns error if failed\n Fix(ctx context.Context, issue *CheckResult) error\n // Description returns human-readable description of what will be fixed\n Description() string\n}\n\nvar registry []Fixer\n\nfunc Register(f Fixer) {\n registry = append(registry, f)\n}\n\nfunc GetFixer(checkName string) Fixer {\n for _, f := range registry {\n if f.CanFix(checkName) {\n return f\n }\n }\n return nil\n}\n```\n\n### 3. Implement fixers (one per file)\n\n**cmd/bd/doctor/fix/hooks.go**\n```go\ntype HooksFixer struct{}\n\nfunc (f *HooksFixer) CanFix(name string) bool {\n return name == \"git-hooks\" || name == \"hooks-outdated\"\n}\n\nfunc (f *HooksFixer) Fix(ctx context.Context, issue *CheckResult) error {\n return hooks.Install(\".\", true) // force reinstall\n}\n\nfunc (f *HooksFixer) Description() string {\n return \"Reinstall git hooks\"\n}\n\nfunc init() {\n Register(\u0026HooksFixer{})\n}\n```\n\n**cmd/bd/doctor/fix/sync_branch.go**\n```go\ntype SyncBranchFixer struct{}\n\nfunc (f *SyncBranchFixer) CanFix(name string) bool {\n return name == \"sync-branch-missing\" || name == \"sync-branch-diverged\"\n}\n\nfunc (f *SyncBranchFixer) Fix(ctx context.Context, issue *CheckResult) error {\n // Reset to remote or create branch\n return syncbranch.ResetToRemote(ctx, repoRoot, branch, jsonlPath)\n}\n```\n\n**cmd/bd/doctor/fix/merge_driver.go**\n```go\ntype MergeDriverFixer struct{}\n\nfunc (f *MergeDriverFixer) CanFix(name string) bool {\n return name == \"merge-driver-missing\" || name == \"merge-driver-outdated\"\n}\n\nfunc (f *MergeDriverFixer) Fix(ctx context.Context, issue *CheckResult) error {\n return setupMergeDriver()\n}\n```\n\n### 4. Update doctor run logic\n\n```go\nfunc runDoctor(cmd *cobra.Command, args []string) {\n issues := runAllChecks()\n \n if \\!fixFlag {\n // Existing behavior - just display issues\n displayIssues(issues)\n return\n }\n \n // Collect fixable issues\n var fixable []FixableIssue\n for _, issue := range issues {\n if fixer := fix.GetFixer(issue.CheckName); fixer \\!= nil {\n fixable = append(fixable, FixableIssue{issue, fixer})\n }\n }\n \n if len(fixable) == 0 {\n fmt.Println(\"No automatically fixable issues found\")\n return\n }\n \n // Show what will be fixed\n fmt.Printf(\"Found %d fixable issues:\\n\", len(fixable))\n for i, f := range fixable {\n fmt.Printf(\" %d. [%s] %s\\n\", i+1, f.Issue.CheckName, f.Fixer.Description())\n }\n \n // Confirm unless --yes\n if \\!yesFlag {\n fmt.Print(\"\\nProceed with fixes? [Y/n] \")\n // ... read confirmation\n }\n \n // Apply fixes\n for _, f := range fixable {\n fmt.Printf(\"Fixing %s... \", f.Issue.CheckName)\n if err := f.Fixer.Fix(ctx, f.Issue); err \\!= nil {\n fmt.Printf(\"FAILED: %v\\n\", err)\n } else {\n fmt.Println(\"OK\")\n }\n }\n}\n```\n\n## Fixable Checks (Initial Set)\n\n| Check | Fixer | Action |\n|-------|-------|--------|\n| git-hooks | HooksFixer | Reinstall hooks |\n| hooks-outdated | HooksFixer | Update hooks |\n| merge-driver-missing | MergeDriverFixer | Configure merge driver |\n| sync-branch-diverged | SyncBranchFixer | Reset to remote |\n| daemon-version-mismatch | DaemonFixer | Restart daemon |\n\n## Testing\n```bash\n# Test with broken hooks\nrm .git/hooks/pre-commit\nbd doctor --fix --yes\n\n# Verify fix applied\nbd doctor # Should pass now\n```\n\n## Success Criteria\n- --fix flag triggers automatic repair\n- User prompted for confirmation (unless --yes)\n- Clear output showing what was fixed\n- Graceful handling of fix failures\n- At least 5 common issues auto-fixable","status":"open","priority":2,"issue_type":"feature","created_at":"2025-11-14T18:17:48.411264-08:00","updated_at":"2025-12-23T12:34:36.020111-08:00"} +{"id":"bd-ykd9","title":"Add bd doctor --fix flag to automatically repair issues","description":"Add bd doctor --fix flag to automatically repair detected issues.\n\n## Current State\n- Doctor checks return issues with \"Fix\" field containing fix instructions\n- No automatic fix execution\n- User must manually follow fix instructions\n\n## Implementation\n\n### 1. Add --fix flag to doctor.go\n```go\n// In cmd/bd/doctor.go init()\ndoctorCmd.Flags().Bool(\"fix\", false, \"Automatically fix detected issues\")\ndoctorCmd.Flags().Bool(\"yes\", false, \"Skip confirmation prompts (use with --fix)\")\n```\n\n### 2. Create fix registry (cmd/bd/doctor/fix/registry.go)\n```go\npackage fix\n\n// Fixer can automatically repair an issue\ntype Fixer interface {\n // CanFix returns true if this fixer handles the given check name\n CanFix(checkName string) bool\n // Fix attempts to repair the issue, returns error if failed\n Fix(ctx context.Context, issue *CheckResult) error\n // Description returns human-readable description of what will be fixed\n Description() string\n}\n\nvar registry []Fixer\n\nfunc Register(f Fixer) {\n registry = append(registry, f)\n}\n\nfunc GetFixer(checkName string) Fixer {\n for _, f := range registry {\n if f.CanFix(checkName) {\n return f\n }\n }\n return nil\n}\n```\n\n### 3. Implement fixers (one per file)\n\n**cmd/bd/doctor/fix/hooks.go**\n```go\ntype HooksFixer struct{}\n\nfunc (f *HooksFixer) CanFix(name string) bool {\n return name == \"git-hooks\" || name == \"hooks-outdated\"\n}\n\nfunc (f *HooksFixer) Fix(ctx context.Context, issue *CheckResult) error {\n return hooks.Install(\".\", true) // force reinstall\n}\n\nfunc (f *HooksFixer) Description() string {\n return \"Reinstall git hooks\"\n}\n\nfunc init() {\n Register(\u0026HooksFixer{})\n}\n```\n\n**cmd/bd/doctor/fix/sync_branch.go**\n```go\ntype SyncBranchFixer struct{}\n\nfunc (f *SyncBranchFixer) CanFix(name string) bool {\n return name == \"sync-branch-missing\" || name == \"sync-branch-diverged\"\n}\n\nfunc (f *SyncBranchFixer) Fix(ctx context.Context, issue *CheckResult) error {\n // Reset to remote or create branch\n return syncbranch.ResetToRemote(ctx, repoRoot, branch, jsonlPath)\n}\n```\n\n**cmd/bd/doctor/fix/merge_driver.go**\n```go\ntype MergeDriverFixer struct{}\n\nfunc (f *MergeDriverFixer) CanFix(name string) bool {\n return name == \"merge-driver-missing\" || name == \"merge-driver-outdated\"\n}\n\nfunc (f *MergeDriverFixer) Fix(ctx context.Context, issue *CheckResult) error {\n return setupMergeDriver()\n}\n```\n\n### 4. Update doctor run logic\n\n```go\nfunc runDoctor(cmd *cobra.Command, args []string) {\n issues := runAllChecks()\n \n if \\!fixFlag {\n // Existing behavior - just display issues\n displayIssues(issues)\n return\n }\n \n // Collect fixable issues\n var fixable []FixableIssue\n for _, issue := range issues {\n if fixer := fix.GetFixer(issue.CheckName); fixer \\!= nil {\n fixable = append(fixable, FixableIssue{issue, fixer})\n }\n }\n \n if len(fixable) == 0 {\n fmt.Println(\"No automatically fixable issues found\")\n return\n }\n \n // Show what will be fixed\n fmt.Printf(\"Found %d fixable issues:\\n\", len(fixable))\n for i, f := range fixable {\n fmt.Printf(\" %d. [%s] %s\\n\", i+1, f.Issue.CheckName, f.Fixer.Description())\n }\n \n // Confirm unless --yes\n if \\!yesFlag {\n fmt.Print(\"\\nProceed with fixes? [Y/n] \")\n // ... read confirmation\n }\n \n // Apply fixes\n for _, f := range fixable {\n fmt.Printf(\"Fixing %s... \", f.Issue.CheckName)\n if err := f.Fixer.Fix(ctx, f.Issue); err \\!= nil {\n fmt.Printf(\"FAILED: %v\\n\", err)\n } else {\n fmt.Println(\"OK\")\n }\n }\n}\n```\n\n## Fixable Checks (Initial Set)\n\n| Check | Fixer | Action |\n|-------|-------|--------|\n| git-hooks | HooksFixer | Reinstall hooks |\n| hooks-outdated | HooksFixer | Update hooks |\n| merge-driver-missing | MergeDriverFixer | Configure merge driver |\n| sync-branch-diverged | SyncBranchFixer | Reset to remote |\n| daemon-version-mismatch | DaemonFixer | Restart daemon |\n\n## Testing\n```bash\n# Test with broken hooks\nrm .git/hooks/pre-commit\nbd doctor --fix --yes\n\n# Verify fix applied\nbd doctor # Should pass now\n```\n\n## Success Criteria\n- --fix flag triggers automatic repair\n- User prompted for confirmation (unless --yes)\n- Clear output showing what was fixed\n- Graceful handling of fix failures\n- At least 5 common issues auto-fixable","status":"open","priority":2,"issue_type":"feature","created_at":"2025-11-14T18:17:48.411264-08:00","updated_at":"2025-12-23T12:34:36.020111-08:00","dependencies":[{"issue_id":"bd-ykd9","depends_on_id":"bd-iz5t","type":"parent-child","created_at":"2025-12-23T12:44:07.732505-08:00","created_by":"daemon"}]} {"id":"bd-ykqu","title":"Add gate timeout tracking and notification","description":"Implement timeout and notification logic for gates.\n\n## Timeout Behavior\n1. Gate created with timeout (e.g., 30m)\n2. Deacon tracks elapsed time during patrol\n3. If timeout reached:\n - Notify all waiters: \"Gate timed out\"\n - Close gate with timeout reason\n - Waiter can retry, escalate, or fail gracefully\n\n## Notification\n- Use gt mail send to notify waiters\n- Include gate ID, await type, and reason in message\n- Support multiple waiters notification\n\n## Escalation Path\n- Witness sees stuck worker, nudges them\n- Worker can escalate to human if needed","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-23T11:44:40.1825-08:00","updated_at":"2025-12-23T12:19:44.362527-08:00","closed_at":"2025-12-23T12:19:44.362527-08:00","close_reason":"Moved to gastown: gt-fqcz","dependencies":[{"issue_id":"bd-ykqu","depends_on_id":"bd-udsi","type":"parent-child","created_at":"2025-12-23T11:44:53.072862-08:00","created_by":"daemon"},{"issue_id":"bd-ykqu","depends_on_id":"bd-is6m","type":"blocks","created_at":"2025-12-23T11:44:56.595085-08:00","created_by":"daemon"}]} {"id":"bd-ymqn","title":"Code review: bd mol bond --ref and bd activity (bd-xo1o work)","description":"Review dave's recent commits for bd-xo1o (Dynamic Molecule Bonding):\n\n## Commits to Review\n- ee04b1ea: feat: add dynamic molecule bonding with --ref flag (bd-xo1o.1)\n- be520d90: feat: add bd activity command for real-time state feed (bd-xo1o.3)\n\n## Review Focus\n1. Code quality and correctness\n2. Error handling\n3. Edge cases\n4. Test coverage\n5. Documentation\n\n## Deliverables\n- File beads for any issues found\n- Note any concerns or suggestions\n- Verify the implementation matches the bd-xo1o epic requirements","status":"closed","priority":1,"issue_type":"task","assignee":"beads/ace","created_at":"2025-12-23T03:47:55.217363-08:00","updated_at":"2025-12-23T04:11:00.226326-08:00","closed_at":"2025-12-23T04:11:00.226326-08:00","close_reason":"Code review completed by ace"} {"id":"bd-yqhh","title":"bd list --parent: filter by parent issue","description":"Add --parent flag to bd list to filter issues by parent.\n\nExample:\n```bash\nbd list --parent=gt-h5n --status=open\n```\n\nWould show all open children of gt-h5n.\n\nUseful for:\n- Checking epic progress\n- Finding swarmable work within an epic\n- Molecule step listing","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-23T01:51:26.830952-08:00","updated_at":"2025-12-23T02:10:12.909803-08:00","closed_at":"2025-12-23T02:10:12.909803-08:00","close_reason":"Implemented --parent flag for bd list. Filters children by parent issue using parent-child dependencies."}