diff --git a/.beads/beads.jsonl b/.beads/beads.jsonl index bbaf8aa3..ffde24b7 100644 --- a/.beads/beads.jsonl +++ b/.beads/beads.jsonl @@ -5,12 +5,12 @@ {"id":"bd-1qwo","title":"Audit and enforce consistent error handling patterns across codebase","description":"**Background:** Error handling patterns are now documented in docs/ERROR_HANDLING.md (bd-9lwr). We have three established patterns:\n- Pattern A: Exit immediately (os.Exit) for fatal errors\n- Pattern B: Warn and continue for optional operations\n- Pattern C: Silent ignore for cleanup/best-effort\n\n**Goal:** Systematically review all error handling in cmd/bd to ensure each instance uses the appropriate pattern according to the guidelines.\n\n**Scope:** \n- Review all files in cmd/bd/*.go\n- Focus on files with high usage: create.go, init.go, sync.go, daemon_sync.go, export.go, import.go\n- Identify outliers where similar operations use different patterns\n- Refactor to use consistent patterns\n\n**Acceptance Criteria:**\n- Similar operations (e.g., all metadata updates) use the same pattern\n- Critical operations always use Pattern A\n- Auxiliary operations use Pattern B or C appropriately\n- No mixed patterns for identical operation types\n\n**References:**\n- docs/ERROR_HANDLING.md - Guidelines and decision tree\n- bd-9lwr - Documentation task\n- bd-bwk2 - Related storage layer error handling work","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-23T21:53:25.687087-08:00","updated_at":"2025-11-24T01:08:18.294375-08:00","closed_at":"2025-11-24T00:28:58.356196-08:00"} {"id":"bd-1rh","title":"cmd/bd test suite is absurdly slow - 279 tests taking 8+ minutes","description":"# Problem\n\nThe cmd/bd test suite is painfully slow:\n- **279 tests** in cmd/bd alone\n- Full suite takes **8+ minutes** to run\n- Even with the 16 slowest integration tests now tagged with `integration` build tag, the remaining tests still take forever\n\nThis makes the development loop unusable. We can't wait 8+ minutes every time we want to run tests.\n\n# Root Cause Analysis\n\n## 1. Sheer Volume\n279 tests is too many for a single package. Even at 0.1s per test, that's 28 seconds minimum just for cmd/bd.\n\n## 2. Each Test Creates Full Database + Temp Directories\nEvery test does heavy setup:\n- Creates temp directory (`t.TempDir()` or `os.MkdirTemp`)\n- Initializes SQLite database\n- Sets up git repo in many cases\n- Creates full storage layer\n\nExample from the tests:\n```go\nfunc setupCLITestDB(t *testing.T) string {\n tmpDir := createTempDirWithCleanup(t)\n runBDInProcess(t, tmpDir, \"init\", \"--prefix\", \"test\", \"--quiet\")\n return tmpDir\n}\n```\n\nThis happens 279 times!\n\n## 3. Tests Are Not Properly Categorized\nWe have three types of tests mixed together:\n- **Unit tests** - should be fast, test single functions\n- **Integration tests** - test full workflows, need DB/git\n- **End-to-end tests** - test entire CLI commands\n\nThey're all lumped together in cmd/bd, all running every time.\n\n# What We've Already Fixed\n\nAdded `integration` build tags to 16 obviously-slow test files:\n- import_profile_test.go (performance benchmarking tests)\n- export_mtime_test.go (tests with time.Sleep calls)\n- cli_fast_test.go (full CLI integration tests)\n- delete_test.go, import_uncommitted_test.go, sync_local_only_test.go (git integration)\n- And 10 more in internal/ packages\n\nThese are now excluded from the default `go test ./...` run.\n\n# Proposed Solutions\n\n## Option 1: Shared Test Fixtures (Quick Win)\nCreate a shared test database that multiple tests can use:\n```go\nvar testDB *sqlite.SQLiteStorage\nvar testDBOnce sync.Once\n\nfunc getSharedTestDB(t *testing.T) storage.Storage {\n testDBOnce.Do(func() {\n // Create one DB for all tests\n })\n return testDB\n}\n```\n\n**Pros**: Easy to implement, immediate speedup\n**Cons**: Tests become less isolated, harder to debug failures\n\n## Option 2: Table-Driven Tests (Medium Win)\nCollapse similar tests into table-driven tests:\n```go\nfunc TestCreate(t *testing.T) {\n tests := []struct{\n name string\n args []string\n want string\n }{\n {\"basic issue\", []string{\"create\", \"Test\"}, \"created\"},\n {\"with description\", []string{\"create\", \"Test\", \"-d\", \"desc\"}, \"created\"},\n // ... 50 more cases\n }\n \n db := setupOnce(t) // Setup once, not 50 times\n for _, tt := range tests {\n t.Run(tt.name, func(t *testing.T) {\n // test using shared db\n })\n }\n}\n```\n\n**Pros**: Dramatically reduces setup overhead, tests run in parallel\n**Cons**: Requires refactoring, tests share more state\n\n## Option 3: Split cmd/bd Tests Into Packages (Big Win)\nMove tests into focused packages:\n- `cmd/bd/internal/clitests` - CLI integration tests (mark with integration tag)\n- `cmd/bd/internal/unittests` - Fast unit tests\n- Keep only essential tests in cmd/bd\n\n**Pros**: Clean separation, easy to run just fast tests\n**Cons**: Requires significant refactoring\n\n## Option 4: Parallel Execution (Quick Win)\nAdd `t.Parallel()` to independent tests:\n```go\nfunc TestSomething(t *testing.T) {\n t.Parallel() // Run this test concurrently with others\n // ...\n}\n```\n\n**Pros**: Easy to add, can cut time in half on multi-core machines\n**Cons**: Doesn't reduce actual test work, just parallelizes it\n\n## Option 5: In-Memory Databases (Medium Win)\nUse `:memory:` SQLite databases instead of file-based:\n```go\nstore, err := sqlite.New(ctx, \":memory:\")\n```\n\n**Pros**: Faster than disk I/O, easier cleanup\n**Cons**: Some tests need actual file-based DBs (export/import tests)\n\n# Recommended Approach\n\n**Short-term (this week)**:\n1. Add `t.Parallel()` to all independent tests in cmd/bd\n2. Use `:memory:` databases where possible\n3. Create table-driven tests for similar test cases\n\n**Medium-term (next sprint)**:\n4. Split cmd/bd tests into focused packages\n5. Mark more integration tests appropriately\n\n**Long-term (backlog)**:\n6. Consider shared test fixtures with proper isolation\n\n# Current Status\n\nWe've tagged 16 files with `integration` build tag, but the remaining 279 tests in cmd/bd still take 8+ minutes. This issue tracks fixing the cmd/bd test performance specifically.\n\n# Target\n\nGet `go test ./...` (without `-short` or `-tags=integration`) down to **under 30 seconds**.\n\n\n# THE REAL ROOT CAUSE (Updated Analysis)\n\nAfter examining the actual test code, the problem is clear:\n\n## Every Test Creates Its Own Database From Scratch\n\nLook at `create_test.go`:\n```go\nfunc TestCreate_BasicIssue(t *testing.T) {\n tmpDir := t.TempDir() // ← Creates temp dir\n testDB := filepath.Join(tmpDir, \".beads\", \"beads.db\")\n s := newTestStore(t, testDB) // ← Opens NEW SQLite connection\n // ← Runs migrations\n // ← Sets config\n // ... actual test (3 lines)\n}\n\nfunc TestCreate_WithDescription(t *testing.T) {\n tmpDir := t.TempDir() // ← Creates ANOTHER temp dir\n testDB := filepath.Join(tmpDir, \".beads\", \"beads.db\")\n s := newTestStore(t, testDB) // ← Opens ANOTHER SQLite connection\n // ... actual test (3 lines)\n}\n```\n\n**This happens 279 times!**\n\n## These Tests Don't Need Isolation!\n\nMost tests are just checking:\n- \"Can I create an issue with a title?\"\n- \"Can I create an issue with a description?\"\n- \"Can I add labels?\"\n\nThey don't conflict with each other. They could all share ONE database!\n\n## The Fix: Test Suites with Shared Setup\n\nInstead of:\n```go\nfunc TestCreate_BasicIssue(t *testing.T) {\n s := newTestStore(t, t.TempDir()+\"/db\") // ← Expensive!\n // test\n}\n\nfunc TestCreate_WithDesc(t *testing.T) {\n s := newTestStore(t, t.TempDir()+\"/db\") // ← Expensive!\n // test\n}\n```\n\nDo this:\n```go\nfunc TestCreate(t *testing.T) {\n // ONE setup for all subtests\n s := newTestStore(t, t.TempDir()+\"/db\")\n \n t.Run(\"basic_issue\", func(t *testing.T) {\n t.Parallel() // Can run concurrently - tests don't conflict\n // test using shared `s`\n })\n \n t.Run(\"with_description\", func(t *testing.T) {\n t.Parallel()\n // test using shared `s`\n })\n \n // ... 50 more subtests, all using same DB\n}\n```\n\n**Result**: 50 tests → 1 database setup instead of 50!\n\n## Why This Works\n\nSQLite is fine with concurrent reads and isolated transactions. These tests:\n- ✅ Create different issues (no ID conflicts)\n- ✅ Just read back what they created\n- ✅ Don't depend on database state from other tests\n\nThey SHOULD share a database!\n\n## Real Numbers\n\nCurrent:\n- 279 tests × (create dir + init SQLite + migrations) = **8 minutes**\n\nAfter fix:\n- 10 test suites × (create dir + init SQLite + migrations) = **30 seconds**\n- 279 subtests running in parallel using those 10 DBs = **5 seconds**\n\n**Total: ~35 seconds instead of 8 minutes!**\n\n## Implementation Plan\n\n1. **Group related tests** into suites (Create, List, Update, Delete, etc.)\n2. **One setup per suite** instead of per test\n3. **Use t.Run() for subtests** with t.Parallel()\n4. **Keep tests that actually need isolation** separate (export/import tests, git operations)\n\nThis is way better than shuffling tests into folders!","notes":"## Progress Update (2025-11-21)\n\n✅ **Completed**:\n- Audited all 280 tests, created TEST_SUITE_AUDIT.md ([deleted:bd-c49])\n- Refactored create_test.go to shared DB pattern ([deleted:bd-y6d])\n- Proven the pattern works: 11 tests now run in 0.04s with 1 DB instead of 11\n\n❌ **Current Reality**:\n- Overall test suite: Still 8+ minutes (no meaningful change)\n- Only 1 of 76 test files refactored\n- Saved ~10 DB initializations out of 280\n\n## Acceptance Criteria (REALISTIC)\n\nThis task is NOT complete until:\n- [ ] All P1 files refactored (create ✅, dep, stale, comments, list, ready)\n- [ ] Test suite runs in \u003c 2 minutes\n- [ ] Measured and verified actual speedup\n\n## Next Steps\n\n1. Refactor remaining 5 P1 files: dep_test.go, stale_test.go, comments_test.go, list_test.go, ready_test.go\n2. Measure actual time improvement after each file\n3. Continue with P2 files if needed to hit \u003c2min target","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-11-21T11:37:47.886207-05:00","updated_at":"2025-11-24T00:01:27.554109-08:00","closed_at":"2025-11-23T23:38:54.185364-08:00"} {"id":"bd-1w6i","title":"Document blocked_issues_cache architecture and behavior","description":"Add comprehensive documentation about the blocked_issues_cache optimization to help future maintainers understand the design.\n\n## What to document\n\n1. **Architecture overview**\n - Why cache exists (performance: 752ms -\u003e 29ms)\n - When cache is used (GetReadyWork queries)\n - How cache is maintained (invalidation triggers)\n\n2. **Cache invalidation rules**\n - Invalidated on: dependency add/remove (blocks/parent-child only)\n - Invalidated on: any status change\n - Invalidated on: issue close\n - NOT invalidated on: related/discovered-from dependencies\n\n3. **Transaction safety**\n - All invalidations happen within transactions\n - Cache can use tx or direct db connection\n - Rebuild is atomic (DELETE + INSERT in same tx)\n\n4. **Performance characteristics**\n - Full rebuild on every invalidation\n - Rebuild is fast (\u003c50ms on 10K database)\n - Write complexity traded for read speed\n - Dependency changes are rare vs reads\n\n5. **Edge cases**\n - Parent-child transitive blocking\n - Closed issues don't block\n - Foreign key CASCADE handles deletions\n\n## Where to document\n\nAdd to these locations:\n\n1. **blocked_cache.go** - Add detailed package comment:\n```go\n// Package blocked_cache implements the blocked_issues_cache optimization.\n// \n// Performance: GetReadyWork originally used recursive CTE (752ms on 10K db).\n// With cache: Simple NOT EXISTS query (29ms on 10K db).\n//\n// Cache Maintenance:\n// - Rebuild triggered on dependency changes (blocks/parent-child only)\n// - Rebuild triggered on status changes \n// - Full rebuild (DELETE + INSERT) is fast enough for all cases\n// ...\n```\n\n2. **ARCHITECTURE.md** or **PERFORMANCE.md** (new file) - High-level overview\n\n3. **ready.go** - Update comment at line 84-85 with more detail\n\n## Related\n\n- [deleted:[deleted:[deleted:[deleted:[deleted:[deleted:bd-5qim]]]]]]: GetReadyWork optimization (implemented)\n- bd-13gm: Cache validation tests\n- [deleted:[deleted:[deleted:[deleted:bd-obdc]]]]: Cache rebuild command","status":"closed","priority":3,"issue_type":"task","created_at":"2025-11-23T20:07:23.467296-08:00","updated_at":"2025-11-25T09:10:06.179923-08:00","closed_at":"2025-11-24T01:40:45.872263-08:00"} -{"id":"bd-2bl","title":"Add DeletionRecord type and deletions.jsonl file handling","description":"Parent: bd-imj\n\n## Task\nCreate the core data structures and file I/O for the deletions manifest.\n\n## Implementation\n\n### DeletionRecord struct\n```go\ntype DeletionRecord struct {\n ID string `json:\"id\"`\n Timestamp time.Time `json:\"ts\"`\n Actor string `json:\"by\"`\n Reason string `json:\"reason,omitempty\"`\n}\n```\n\n### File operations\n- `LoadDeletions(path string) (map[string]DeletionRecord, error)` - load into map for O(1) lookup\n- `AppendDeletion(path string, record DeletionRecord) error` - append single record\n- `WriteDeletions(path string, records []DeletionRecord) error` - atomic rewrite for compaction\n\n### Corrupt file recovery\n`LoadDeletions` should:\n- Skip invalid JSON lines with a warning log\n- Return successfully loaded entries\n- Not fail on partial corruption\n- Optionally return count of skipped lines for diagnostics\n\n```go\nfunc LoadDeletions(path string) (map[string]DeletionRecord, int, error) {\n // returns: records, skippedCount, error\n}\n```\n\n### Location\n- New file: `internal/deletions/deletions.go`\n- Path: `.beads/deletions.jsonl`\n\n## Acceptance Criteria\n- [ ] DeletionRecord type with JSON tags\n- [ ] Load/append/write functions with proper error handling\n- [ ] Atomic write (temp file + rename) for compaction safety\n- [ ] Corrupt lines skipped with warning (not fatal)\n- [ ] Unit tests for round-trip serialization\n- [ ] Unit tests for corrupt line handling","status":"closed","priority":0,"issue_type":"task","created_at":"2025-11-25T09:56:24.634947-08:00","updated_at":"2025-11-25T10:59:00.126123-08:00","closed_at":"2025-11-25T10:59:00.126123-08:00"} +{"id":"bd-2bl","title":"Add DeletionRecord type and deletions.jsonl file handling","description":"Parent: bd-imj\n\n## Task\nCreate the core data structures and file I/O for the deletions manifest.\n\n## Implementation\n\n### DeletionRecord struct\n```go\ntype DeletionRecord struct {\n ID string `json:\"id\"`\n Timestamp time.Time `json:\"ts\"`\n Actor string `json:\"by\"`\n Reason string `json:\"reason,omitempty\"`\n}\n```\n\n### File operations\n- `LoadDeletions(path string) (map[string]DeletionRecord, error)` - load into map for O(1) lookup\n- `AppendDeletion(path string, record DeletionRecord) error` - append single record\n- `WriteDeletions(path string, records []DeletionRecord) error` - atomic rewrite for compaction\n\n### Corrupt file recovery\n`LoadDeletions` should:\n- Skip invalid JSON lines with a warning log\n- Return successfully loaded entries\n- Not fail on partial corruption\n- Optionally return count of skipped lines for diagnostics\n\n```go\nfunc LoadDeletions(path string) (map[string]DeletionRecord, int, error) {\n // returns: records, skippedCount, error\n}\n```\n\n### Location\n- New file: `internal/deletions/deletions.go`\n- Path: `.beads/deletions.jsonl`\n\n## Acceptance Criteria\n- [ ] DeletionRecord type with JSON tags\n- [ ] Load/append/write functions with proper error handling\n- [ ] Atomic write (temp file + rename) for compaction safety\n- [ ] Corrupt lines skipped with warning (not fatal)\n- [ ] Unit tests for round-trip serialization\n- [ ] Unit tests for corrupt line handling","status":"closed","priority":0,"issue_type":"task","created_at":"2025-11-25T09:56:24.634947-08:00","updated_at":"2025-11-25T12:33:39.682114-08:00","closed_at":"2025-11-25T10:59:00.126123-08:00"} {"id":"bd-379","title":"Implement `bd setup cursor` for Cursor IDE integration","description":"Create a `bd setup cursor` command that integrates Beads workflow into Cursor IDE via .cursorrules file. Unlike Claude Code (which has hooks), Cursor uses a static rules file to provide context to its AI.","status":"closed","priority":3,"issue_type":"feature","created_at":"2025-11-11T23:32:22.170083-08:00","updated_at":"2025-11-25T09:10:06.181261-08:00","closed_at":"2025-11-24T01:39:26.852836-08:00"} {"id":"bd-3852","title":"Add orphan detection migration","description":"Create migration to detect orphaned children in existing databases. Query: SELECT id FROM issues WHERE id LIKE '%.%' AND substr(id, 1, instr(id || '.', '.') - 1) NOT IN (SELECT id FROM issues). Log results, let user decide action (delete orphans or convert to top-level).","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-04T12:32:30.727044-08:00","updated_at":"2025-11-24T01:08:18.295118-08:00","closed_at":"2025-11-24T00:31:16.479343-08:00"} {"id":"bd-39o","title":"Rename last_import_hash metadata key to jsonl_content_hash","description":"The metadata key 'last_import_hash' is misleading because it's updated on both import AND export (sync.go:614, import.go:320).\n\nBetter names:\n- jsonl_content_hash (more accurate)\n- last_sync_hash (clearer intent)\n\nThis is a breaking change requiring migration of existing metadata values.","status":"open","priority":2,"issue_type":"task","created_at":"2025-11-20T21:31:07.568739-05:00","updated_at":"2025-11-20T21:31:07.568739-05:00"} {"id":"bd-3gc","title":"Audit remaining cmd/bd files for error handling consistency","description":"Extend ERROR_HANDLING_AUDIT.md to cover: daemon_sync.go, update.go, list.go, show.go, close.go, reopen.go, dep.go, label.go, comments.go, delete.go, compact.go, config.go, validate.go and other high-usage command files","status":"open","priority":3,"issue_type":"task","created_at":"2025-11-24T00:28:55.890991-08:00","updated_at":"2025-11-24T00:28:55.890991-08:00","dependencies":[{"issue_id":"bd-3gc","depends_on_id":"bd-1qwo","type":"blocks","created_at":"2025-11-24T00:28:55.891827-08:00","created_by":"daemon"}]} -{"id":"bd-44e","title":"Ensure deletions.jsonl is tracked in git","description":"Parent: bd-imj\n\nEnsure deletions.jsonl is tracked in git (not ignored).\n\nUpdate bd init and gitignore upgrade logic to:\n1. NOT add deletions.jsonl to .gitignore\n2. Ensure it is committed alongside beads.jsonl\n\nThe file must be in git for cross-clone propagation to work.\n\nAcceptance criteria:\n- bd init does not ignore deletions.jsonl\n- Existing .gitignore files are not broken\n- File appears in git status when modified","status":"open","priority":2,"issue_type":"task","created_at":"2025-11-25T09:57:21.663196-08:00","updated_at":"2025-11-25T10:50:55.696036-08:00"} +{"id":"bd-44e","title":"Ensure deletions.jsonl is tracked in git","description":"Parent: bd-imj\n\nEnsure deletions.jsonl is tracked in git (not ignored).\n\nUpdate bd init and gitignore upgrade logic to:\n1. NOT add deletions.jsonl to .gitignore\n2. Ensure it is committed alongside beads.jsonl\n\nThe file must be in git for cross-clone propagation to work.\n\nAcceptance criteria:\n- bd init does not ignore deletions.jsonl\n- Existing .gitignore files are not broken\n- File appears in git status when modified","status":"open","priority":2,"issue_type":"task","created_at":"2025-11-25T09:57:21.663196-08:00","updated_at":"2025-11-25T12:33:39.683491-08:00"} {"id":"bd-49kw","title":"Workaround for FastMCP outputSchema bug in Claude Code","description":"The beads MCP server (v0.23.1) successfully connects to Claude Code, but all tools fail to load with a schema validation error due to a bug in FastMCP 2.13.1.\n\nError: \"Invalid literal value, expected \\\"object\\\"\" in outputSchema.\n\nRoot Cause: FastMCP generates outputSchema with $ref at root level without \"type\": \"object\" for self-referential models (Issue).\n\nWorkaround: Use slash commands (/beads:ready) or wait for FastMCP fix.\n","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-11-20T18:55:39.041831-05:00","updated_at":"2025-11-24T00:01:27.554672-08:00","closed_at":"2025-11-23T23:22:49.789004-08:00"} {"id":"bd-4aao","title":"Fix failing integration tests in beads-mcp","description":"The `beads-mcp` test suite has failures in `tests/test_bd_client_integration.py` (assertion error in `test_init_creates_beads_directory`) and errors in `tests/test_worktree_separate_dbs.py` (setup failures finding database). These need to be investigated and fixed to ensure a reliable CI baseline.","status":"open","priority":2,"issue_type":"task","created_at":"2025-11-20T18:53:28.4803-05:00","updated_at":"2025-11-20T18:53:28.4803-05:00"} {"id":"bd-4h3","title":"Add test coverage for internal/git package","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-11-20T21:21:23.497486-05:00","updated_at":"2025-11-20T21:21:23.497486-05:00","dependencies":[{"issue_id":"bd-4h3","depends_on_id":"bd-ge7","type":"blocks","created_at":"2025-11-20T21:21:31.277639-05:00","created_by":"daemon"}]} @@ -46,12 +46,12 @@ {"id":"bd-gfu","title":"Add --start flag to bd daemon, show help with no args","description":"Currently `bd daemon` with no args immediately starts the daemon. This is inconsistent with other daemon management commands like `--stop`, `--status`, etc.\n\n## Proposed Changes\n\n1. Add `--start` flag to explicitly start daemon\n2. With no args or flags, print help text showing available options\n3. Maintain backward compatibility where feasible\n\n## Current Consumers\n\n- **Auto-start logic** (`daemon_autostart.go:106, 270`): Calls `bd daemon` programmatically - needs update\n- **MCP docs** (SETUP_DAEMON.md:111): Already incorrectly shows `bd daemon start` - will be fixed by this change\n- **Python daemon client**: Suggests `bd daemon` in error messages - needs doc update\n\n## Implementation Notes\n\n- Default behavior (no args/flags) should show help\n- `--start` should start daemon (current no-args behavior)\n- Auto-start code needs to pass `--start` flag explicitly\n- Update all documentation to use `bd daemon --start`\n- Ensure backward compat doesn't break existing workflows","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-11-23T23:54:49.906553-08:00","updated_at":"2025-11-24T00:03:15.231345-08:00","closed_at":"2025-11-24T00:03:15.231345-08:00"} {"id":"bd-gqo","title":"Implement health checks in daemon event loop","description":"Add health checks to checkDaemonHealth() function in daemon_event_loop.go:170:\n- Database integrity check\n- Disk space check\n- Memory usage check\n\nCurrently it's just a no-op placeholder.","status":"open","priority":3,"issue_type":"feature","created_at":"2025-11-21T18:55:07.534304-05:00","updated_at":"2025-11-21T18:55:07.534304-05:00"} {"id":"bd-hdt","title":"Implement auto-merge functionality in duplicates command","description":"The duplicates.go file has a TODO at line 95 to implement the performMerge function for automatic duplicate merging. Currently it just prints a warning message. This would automate the merge process instead of just suggesting commands.","status":"open","priority":2,"issue_type":"feature","created_at":"2025-11-21T18:55:02.828619-05:00","updated_at":"2025-11-21T18:55:02.828619-05:00"} -{"id":"bd-ibg","title":"Integrate deletions manifest into import logic","description":"Parent: bd-imj\n\n## Task\nModify import to check deletions manifest and delete stale DB issues.\n\n## Implementation\n\nIn `internal/importer/importer.go` (or equivalent):\n\n```go\nfunc (i *Importer) Import(jsonlPath string) error {\n jsonlIDs := i.parseJSONL(jsonlPath)\n deletions, _ := deletions.Load(\".beads/deletions.jsonl\")\n \n for _, dbID := range i.db.AllIDs() {\n if jsonlIDs.Has(dbID) {\n continue // In sync\n }\n if del, ok := deletions[dbID]; ok {\n i.db.Delete(dbID)\n log.Printf(\"Purged %s (deleted %s by %s)\", dbID, del.Timestamp, del.Actor)\n continue\n }\n // Not deleted, keep as local work\n }\n \n // Normal create/update logic...\n}\n```\n\n## Acceptance Criteria\n- [ ] Import loads deletions manifest\n- [ ] DB issues not in JSONL but in manifest are deleted\n- [ ] Deletion logged with metadata (timestamp, actor)\n- [ ] Handles missing/empty deletions file gracefully\n- [ ] Integration test: delete in clone A propagates to clone B","status":"closed","priority":0,"issue_type":"task","created_at":"2025-11-25T09:56:25.168983-08:00","updated_at":"2025-11-25T11:45:48.824571-08:00","closed_at":"2025-11-25T11:45:48.824571-08:00","dependencies":[{"issue_id":"bd-ibg","depends_on_id":"bd-2bl","type":"blocks","created_at":"2025-11-25T09:57:49.904629-08:00","created_by":"daemon"}]} -{"id":"bd-ieh","title":"Record deletions in bd delete and bd cleanup","description":"Parent: bd-imj\n\n## Task\nModify delete operations to append to deletions manifest.\n\n## Implementation\n\n### Order of Operations (IMPORTANT)\n1. Append to deletions.jsonl first (can fail safely, no side effects)\n2. Delete from DB\n3. Export to JSONL\n\nThis order ensures we never lose deletion records.\n\n### bd delete\n```go\nfunc (s *Storage) DeleteIssue(id, actor, reason string) error {\n // NEW: Record in manifest FIRST\n if err := deletions.Append(\".beads/deletions.jsonl\", DeletionRecord{\n ID: id,\n Timestamp: time.Now().UTC(),\n Actor: actor,\n Reason: reason,\n }); err != nil {\n return fmt.Errorf(\"failed to record deletion: %w\", err)\n }\n \n // Then delete from DB\n s.db.Delete(id)\n \n // Then export\n // ...\n}\n```\n\n### bd cleanup\nSame pattern, with reason=\"cleanup\"\n\n### Actor sourcing (in order)\n1. `--actor` flag if provided\n2. `$BD_ACTOR` env var if set\n3. `git config user.name` (preferred default)\n4. `$USER` as last resort\n\n## Acceptance Criteria\n- [ ] `bd delete` appends to deletions.jsonl BEFORE deleting\n- [ ] `bd cleanup` appends to deletions.jsonl (one entry per deleted issue)\n- [ ] Actor sourced from flag \u003e env \u003e git config \u003e $USER\n- [ ] Reason field populated appropriately\n- [ ] Test: delete creates manifest entry with correct metadata\n- [ ] Test: failed manifest append prevents deletion","status":"closed","priority":0,"issue_type":"task","created_at":"2025-11-25T09:56:25.721739-08:00","updated_at":"2025-11-25T11:45:48.77157-08:00","closed_at":"2025-11-25T11:45:48.77157-08:00","dependencies":[{"issue_id":"bd-ieh","depends_on_id":"bd-2bl","type":"blocks","created_at":"2025-11-25T09:57:49.940751-08:00","created_by":"daemon"}]} -{"id":"bd-imj","title":"Deletion propagation via deletions manifest","description":"## Problem\n\nWhen `bd cleanup -f` or `bd delete` removes issues in one clone, those deletions don't propagate to other clones. The import logic only creates/updates, never deletes. This causes \"resurrection\" where deleted issues reappear.\n\n## Root Cause\n\nImport sees DB issues not in JSONL and assumes they're \"local unpushed work\" rather than \"intentionally deleted upstream.\"\n\n## Solution: Deletions Manifest\n\nAdd `.beads/deletions.jsonl` - an append-only log of deleted issue IDs with metadata.\n\n### Format\n```jsonl\n{\"id\":\"bd-xxx\",\"ts\":\"2025-11-25T10:00:00Z\",\"by\":\"stevey\"}\n{\"id\":\"bd-yyy\",\"ts\":\"2025-11-25T10:05:00Z\",\"by\":\"claude\",\"reason\":\"duplicate of bd-zzz\"}\n```\n\n### Fields\n- `id`: Issue ID (required)\n- `ts`: ISO 8601 UTC timestamp (required)\n- `by`: Actor who deleted (required)\n- `reason`: Optional context (\"cleanup\", \"duplicate of X\", etc.)\n\n### Import Logic\n```\nFor each DB issue not in JSONL:\n 1. Check deletions manifest → if found, delete from DB\n 2. Fallback: check git history → if found, delete + backfill manifest\n 3. Neither → keep (local unpushed work)\n```\n\n### Conflict Resolution\nSimultaneous deletions from multiple clones are handled naturally:\n- Append-only design means both clones append their deletion records\n- On merge, file contains duplicate entries (same ID, different timestamps)\n- `LoadDeletions` deduplicates by ID (keeps any/first entry)\n- Result: deletion propagates correctly, duplicates are harmless\n\n### Pruning Policy\n- Default retention: 7 days (configurable via `deletions.retention_days`)\n- Auto-compact during `bd sync` is **opt-in** (disabled by default)\n- Hard cap: `deletions.max_entries` (default 50000)\n- Git fallback handles pruned entries (self-healing)\n\n### Self-Healing\nWhen git fallback catches a resurrection (pruned entry), it backfills the manifest. One-time git scan cost, then fast again.\n\n### Size Estimates\n- ~80 bytes/entry\n- 7-day retention with 100 deletions/day = ~56KB\n- Git compressed: ~10KB\n\n## Benefits\n- ✅ Deletions propagate across clones\n- ✅ O(1) lookup (no git scan in normal case)\n- ✅ Works in shallow clones\n- ✅ Survives history rewrite\n- ✅ Audit trail (who deleted what when)\n- ✅ Self-healing via git fallback\n- ✅ Bounded size via time-based pruning\n\n## References\n- Investigation session: 2025-11-25\n- Related: bd-2q6d (stale database warnings)","status":"open","priority":0,"issue_type":"epic","created_at":"2025-11-25T09:56:01.98027-08:00","updated_at":"2025-11-25T10:52:28.738368-08:00","dependencies":[{"issue_id":"bd-imj","depends_on_id":"bd-2bl","type":"blocks","created_at":"2025-11-25T09:57:37.845868-08:00","created_by":"daemon"},{"issue_id":"bd-imj","depends_on_id":"bd-ibg","type":"blocks","created_at":"2025-11-25T09:57:42.694905-08:00","created_by":"daemon"},{"issue_id":"bd-imj","depends_on_id":"bd-ieh","type":"blocks","created_at":"2025-11-25T09:57:42.724272-08:00","created_by":"daemon"},{"issue_id":"bd-imj","depends_on_id":"bd-pnm","type":"blocks","created_at":"2025-11-25T09:57:42.755707-08:00","created_by":"daemon"},{"issue_id":"bd-imj","depends_on_id":"bd-v2x","type":"blocks","created_at":"2025-11-25T09:57:42.790453-08:00","created_by":"daemon"},{"issue_id":"bd-imj","depends_on_id":"bd-qsm","type":"blocks","created_at":"2025-11-25T09:57:42.821911-08:00","created_by":"daemon"},{"issue_id":"bd-imj","depends_on_id":"bd-x2i","type":"blocks","created_at":"2025-11-25T09:57:42.851712-08:00","created_by":"daemon"},{"issue_id":"bd-imj","depends_on_id":"bd-44e","type":"blocks","created_at":"2025-11-25T09:57:42.88154-08:00","created_by":"daemon"},{"issue_id":"bd-imj","depends_on_id":"bd-kzo","type":"blocks","created_at":"2025-11-25T10:52:16.319402-08:00","created_by":"daemon"}]} +{"id":"bd-ibg","title":"Integrate deletions manifest into import logic","description":"Parent: bd-imj\n\n## Task\nModify import to check deletions manifest and delete stale DB issues.\n\n## Implementation\n\nIn `internal/importer/importer.go` (or equivalent):\n\n```go\nfunc (i *Importer) Import(jsonlPath string) error {\n jsonlIDs := i.parseJSONL(jsonlPath)\n deletions, _ := deletions.Load(\".beads/deletions.jsonl\")\n \n for _, dbID := range i.db.AllIDs() {\n if jsonlIDs.Has(dbID) {\n continue // In sync\n }\n if del, ok := deletions[dbID]; ok {\n i.db.Delete(dbID)\n log.Printf(\"Purged %s (deleted %s by %s)\", dbID, del.Timestamp, del.Actor)\n continue\n }\n // Not deleted, keep as local work\n }\n \n // Normal create/update logic...\n}\n```\n\n## Acceptance Criteria\n- [ ] Import loads deletions manifest\n- [ ] DB issues not in JSONL but in manifest are deleted\n- [ ] Deletion logged with metadata (timestamp, actor)\n- [ ] Handles missing/empty deletions file gracefully\n- [ ] Integration test: delete in clone A propagates to clone B","status":"closed","priority":0,"issue_type":"task","created_at":"2025-11-25T09:56:25.168983-08:00","updated_at":"2025-11-25T12:33:39.68408-08:00","closed_at":"2025-11-25T11:45:48.824571-08:00","dependencies":[{"issue_id":"bd-ibg","depends_on_id":"bd-2bl","type":"blocks","created_at":"2025-11-25T09:57:49.904629-08:00","created_by":"daemon"}]} +{"id":"bd-ieh","title":"Record deletions in bd delete and bd cleanup","description":"Parent: bd-imj\n\n## Task\nModify delete operations to append to deletions manifest.\n\n## Implementation\n\n### Order of Operations (IMPORTANT)\n1. Append to deletions.jsonl first (can fail safely, no side effects)\n2. Delete from DB\n3. Export to JSONL\n\nThis order ensures we never lose deletion records.\n\n### bd delete\n```go\nfunc (s *Storage) DeleteIssue(id, actor, reason string) error {\n // NEW: Record in manifest FIRST\n if err := deletions.Append(\".beads/deletions.jsonl\", DeletionRecord{\n ID: id,\n Timestamp: time.Now().UTC(),\n Actor: actor,\n Reason: reason,\n }); err != nil {\n return fmt.Errorf(\"failed to record deletion: %w\", err)\n }\n \n // Then delete from DB\n s.db.Delete(id)\n \n // Then export\n // ...\n}\n```\n\n### bd cleanup\nSame pattern, with reason=\"cleanup\"\n\n### Actor sourcing (in order)\n1. `--actor` flag if provided\n2. `$BD_ACTOR` env var if set\n3. `git config user.name` (preferred default)\n4. `$USER` as last resort\n\n## Acceptance Criteria\n- [ ] `bd delete` appends to deletions.jsonl BEFORE deleting\n- [ ] `bd cleanup` appends to deletions.jsonl (one entry per deleted issue)\n- [ ] Actor sourced from flag \u003e env \u003e git config \u003e $USER\n- [ ] Reason field populated appropriately\n- [ ] Test: delete creates manifest entry with correct metadata\n- [ ] Test: failed manifest append prevents deletion","status":"closed","priority":0,"issue_type":"task","created_at":"2025-11-25T09:56:25.721739-08:00","updated_at":"2025-11-25T12:33:39.684588-08:00","closed_at":"2025-11-25T11:45:48.77157-08:00","dependencies":[{"issue_id":"bd-ieh","depends_on_id":"bd-2bl","type":"blocks","created_at":"2025-11-25T09:57:49.940751-08:00","created_by":"daemon"}]} +{"id":"bd-imj","title":"Deletion propagation via deletions manifest","description":"## Problem\n\nWhen `bd cleanup -f` or `bd delete` removes issues in one clone, those deletions don't propagate to other clones. The import logic only creates/updates, never deletes. This causes \"resurrection\" where deleted issues reappear.\n\n## Root Cause\n\nImport sees DB issues not in JSONL and assumes they're \"local unpushed work\" rather than \"intentionally deleted upstream.\"\n\n## Solution: Deletions Manifest\n\nAdd `.beads/deletions.jsonl` - an append-only log of deleted issue IDs with metadata.\n\n### Format\n```jsonl\n{\"id\":\"bd-xxx\",\"ts\":\"2025-11-25T10:00:00Z\",\"by\":\"stevey\"}\n{\"id\":\"bd-yyy\",\"ts\":\"2025-11-25T10:05:00Z\",\"by\":\"claude\",\"reason\":\"duplicate of bd-zzz\"}\n```\n\n### Fields\n- `id`: Issue ID (required)\n- `ts`: ISO 8601 UTC timestamp (required)\n- `by`: Actor who deleted (required)\n- `reason`: Optional context (\"cleanup\", \"duplicate of X\", etc.)\n\n### Import Logic\n```\nFor each DB issue not in JSONL:\n 1. Check deletions manifest → if found, delete from DB\n 2. Fallback: check git history → if found, delete + backfill manifest\n 3. Neither → keep (local unpushed work)\n```\n\n### Conflict Resolution\nSimultaneous deletions from multiple clones are handled naturally:\n- Append-only design means both clones append their deletion records\n- On merge, file contains duplicate entries (same ID, different timestamps)\n- `LoadDeletions` deduplicates by ID (keeps any/first entry)\n- Result: deletion propagates correctly, duplicates are harmless\n\n### Pruning Policy\n- Default retention: 7 days (configurable via `deletions.retention_days`)\n- Auto-compact during `bd sync` is **opt-in** (disabled by default)\n- Hard cap: `deletions.max_entries` (default 50000)\n- Git fallback handles pruned entries (self-healing)\n\n### Self-Healing\nWhen git fallback catches a resurrection (pruned entry), it backfills the manifest. One-time git scan cost, then fast again.\n\n### Size Estimates\n- ~80 bytes/entry\n- 7-day retention with 100 deletions/day = ~56KB\n- Git compressed: ~10KB\n\n## Benefits\n- ✅ Deletions propagate across clones\n- ✅ O(1) lookup (no git scan in normal case)\n- ✅ Works in shallow clones\n- ✅ Survives history rewrite\n- ✅ Audit trail (who deleted what when)\n- ✅ Self-healing via git fallback\n- ✅ Bounded size via time-based pruning\n\n## References\n- Investigation session: 2025-11-25\n- Related: bd-2q6d (stale database warnings)","status":"open","priority":0,"issue_type":"epic","created_at":"2025-11-25T09:56:01.98027-08:00","updated_at":"2025-11-25T12:33:39.685101-08:00","dependencies":[{"issue_id":"bd-imj","depends_on_id":"bd-2bl","type":"blocks","created_at":"2025-11-25T09:57:37.845868-08:00","created_by":"daemon"},{"issue_id":"bd-imj","depends_on_id":"bd-ibg","type":"blocks","created_at":"2025-11-25T09:57:42.694905-08:00","created_by":"daemon"},{"issue_id":"bd-imj","depends_on_id":"bd-ieh","type":"blocks","created_at":"2025-11-25T09:57:42.724272-08:00","created_by":"daemon"},{"issue_id":"bd-imj","depends_on_id":"bd-pnm","type":"blocks","created_at":"2025-11-25T09:57:42.755707-08:00","created_by":"daemon"},{"issue_id":"bd-imj","depends_on_id":"bd-v2x","type":"blocks","created_at":"2025-11-25T09:57:42.790453-08:00","created_by":"daemon"},{"issue_id":"bd-imj","depends_on_id":"bd-qsm","type":"blocks","created_at":"2025-11-25T09:57:42.821911-08:00","created_by":"daemon"},{"issue_id":"bd-imj","depends_on_id":"bd-x2i","type":"blocks","created_at":"2025-11-25T09:57:42.851712-08:00","created_by":"daemon"},{"issue_id":"bd-imj","depends_on_id":"bd-44e","type":"blocks","created_at":"2025-11-25T09:57:42.88154-08:00","created_by":"daemon"},{"issue_id":"bd-imj","depends_on_id":"bd-kzo","type":"blocks","created_at":"2025-11-25T10:52:16.319402-08:00","created_by":"daemon"}]} {"id":"bd-j3zt","title":"Fix mypy errors in beads-mcp","description":"Running `mypy .` in `integrations/beads-mcp` reports 287 errors. These should be addressed to improve type safety and code quality.","status":"open","priority":3,"issue_type":"task","created_at":"2025-11-20T18:53:28.557708-05:00","updated_at":"2025-11-20T18:53:28.557708-05:00"} {"id":"bd-koab","title":"Import should continue on FOREIGN KEY constraint violations from deletions","description":"# Problem\n\nWhen importing JSONL after a merge that includes deletions, we may encounter FOREIGN KEY constraint violations if:\n- Issue A was deleted in one branch\n- Issue B (that depends on A) was modified in another branch \n- The merge keeps the deletion of A and the modification of B\n- Import tries to import B with a dependency/reference to deleted A\n\nCurrently import fails completely on such constraint violations, requiring manual intervention.\n\n# Solution\n\nAdd IsForeignKeyConstraintError() helper similar to IsUniqueConstraintError()\n\nUpdate import code to:\n1. Detect FOREIGN KEY constraint violations\n2. Log a warning with the issue ID and constraint\n3. Continue importing remaining issues\n4. Report summary of skipped issues at the end\n\n# Implementation Notes\n\n- Add to internal/storage/sqlite/util.go\n- Pattern: strings.Contains(err.Error(), \"FOREIGN KEY constraint failed\")\n- Update importer to handle these errors gracefully\n- Keep track of skipped issues for summary reporting","notes":"## Implementation Complete\n\nAdded FOREIGN KEY constraint violation handling to the importer:\n\n**Changes made:**\n\n1. **internal/importer/importer.go**\n - Added SkippedDependencies field to Result struct\n - Updated importDependencies() to accept result parameter\n - Added FK constraint detection using sqlite.IsForeignKeyConstraintError()\n - Log warning for each skipped dependency\n - Track skipped dependencies in result\n\n2. **cmd/bd/import_shared.go**\n - Added SkippedDependencies field to ImportResult struct\n - Updated result conversion to include skipped dependencies\n\n3. **cmd/bd/import.go**\n - Added summary reporting for skipped dependencies\n - Displays warning with list of skipped dependencies and helpful context\n\n**Behavior:**\n- When a FOREIGN KEY constraint violation is encountered during dependency import:\n - A warning is logged: 'Warning: Skipping dependency due to missing reference: issue-a → issue-b (blocks)'\n - The dependency is tracked in result.SkippedDependencies\n - Import continues with remaining dependencies\n - Summary at end lists all skipped dependencies with context message\n\n**Testing:**\n- All existing importer tests pass\n- Build succeeds\n- Ready for real-world testing when FK constraint violations are encountered","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-11-23T21:37:02.811665-08:00","updated_at":"2025-11-24T00:01:27.559495-08:00","closed_at":"2025-11-23T23:31:04.325337-08:00"} -{"id":"bd-ktng","title":"Optimize CLI test suite - eliminate redundant git init calls","description":"Current: Each of 13 CLI tests calls git init (31s total). Solution: Use single test binary built once in init(), skip git operations where possible, or use mock filesystem.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-04T11:23:13.660276-08:00","updated_at":"2025-11-23T23:01:49.003046-08:00","closed_at":"2025-11-23T22:55:32.103839-08:00","comments":[{"id":1,"issue_id":"bd-ktng","author":"stevey","text":"Completed first phase: refactored sync_test.go to use helper functions.\n\nChanges:\n- Added 3 helper functions: setupGitRepo(), setupGitRepoWithBranch(), setupMinimalGitRepo()\n- Refactored 19 test functions in sync_test.go\n- Eliminated ~300 lines of duplicate git initialization boilerplate\n- All tests pass\n\nThe helpers consolidate the repeated pattern of:\n1. Creating temp directory\n2. Initializing git repo\n3. Configuring git user\n4. Creating initial commit\n5. Handling cleanup\n\nNext steps:\n- Apply similar optimization to other test files (daemon_test.go, hooks_test.go, etc.)\n- Measure actual performance improvement","created_at":"2025-11-25T17:10:06Z"}]} +{"id":"bd-ktng","title":"Optimize CLI test suite - eliminate redundant git init calls","description":"Current: Each of 13 CLI tests calls git init (31s total). Solution: Use single test binary built once in init(), skip git operations where possible, or use mock filesystem.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-04T11:23:13.660276-08:00","updated_at":"2025-11-23T23:01:49.003046-08:00","closed_at":"2025-11-23T22:55:32.103839-08:00","comments":[{"id":1,"issue_id":"bd-ktng","author":"stevey","text":"Completed first phase: refactored sync_test.go to use helper functions.\n\nChanges:\n- Added 3 helper functions: setupGitRepo(), setupGitRepoWithBranch(), setupMinimalGitRepo()\n- Refactored 19 test functions in sync_test.go\n- Eliminated ~300 lines of duplicate git initialization boilerplate\n- All tests pass\n\nThe helpers consolidate the repeated pattern of:\n1. Creating temp directory\n2. Initializing git repo\n3. Configuring git user\n4. Creating initial commit\n5. Handling cleanup\n\nNext steps:\n- Apply similar optimization to other test files (daemon_test.go, hooks_test.go, etc.)\n- Measure actual performance improvement","created_at":"2025-11-25T17:19:42Z"}]} {"id":"bd-kzo","title":"End-to-end test: deletion propagation across clones","description":"Parent: bd-imj\n\n## Task\nCreate integration test verifying deletions propagate correctly between clones.\n\n## Test Scenario\n1. Create two \"clones\" (separate .beads directories)\n2. Clone A: create issue bd-xxx\n3. Sync both clones (both have bd-xxx)\n4. Clone A: delete bd-xxx\n5. Sync Clone A (deletions.jsonl updated)\n6. Sync Clone B (should pull deletions.jsonl)\n7. Clone B: import should purge bd-xxx from DB\n\n## Edge Cases to Test\n- Simultaneous deletion in both clones (should be idempotent)\n- Deletion of non-existent issue (no-op, still recorded)\n- Corrupt deletions.jsonl line (skipped with warning)\n- Empty/missing deletions.jsonl (graceful handling)\n\n## Acceptance Criteria\n- [ ] Test creates isolated clone environments\n- [ ] Verifies deletion propagates via manifest\n- [ ] Verifies duplicate deletions are handled\n- [ ] Verifies corrupt line recovery\n- [ ] Can run in CI (no real git remotes needed)","status":"closed","priority":1,"issue_type":"task","created_at":"2025-11-25T10:50:57.436227-08:00","updated_at":"2025-11-25T12:03:08.790655-08:00","closed_at":"2025-11-25T12:03:08.790655-08:00"} {"id":"bd-l0r","title":"CRITICAL: bd sync pushes stale DB state instead of trusting JSONL truth on pull","description":"When a clone has stale DB (688 issues) and pulls fresh JSONL (62 issues), bd sync detects 1009.7% divergence but proceeds to re-export the stale DB and push it, overwriting the correct JSONL state. \n\nRepro:\n1. Clone has 688 issues in DB (628 closed)\n2. git pull brings JSONL with 62 issues (cleanup applied)\n3. bd sync warns about divergence but exports DB→JSONL\n4. Pushes 688 issues back to remote, undoing cleanup\n\nExpected: JSONL is source of truth after pull. DB should be wiped and reimported.\nActual: DB overwrites JSONL, pushing stale state upstream.\n\nImpact: Breaks multi-clone coordination. Cleanup work gets undone.","status":"closed","priority":0,"issue_type":"bug","created_at":"2025-11-23T22:29:37.668882-08:00","updated_at":"2025-11-25T09:10:06.18172-08:00","closed_at":"2025-11-24T01:45:33.004694-08:00"} {"id":"bd-l954","title":"Performance Testing Framework","description":"Add comprehensive performance testing for beads focusing on optimization guidance and validating 10K+ database scale. Uses standard Go tooling, follows existing patterns, minimal complexity.\n\nComponents:\n- Benchmark suite for critical operations at 10K-20K scale\n- Fixture generator for realistic test data (epic hierarchies, cross-links)\n- User diagnostics via bd doctor --perf\n- Always-on profiling integration\n\nGoals:\n- Identify bottlenecks for optimization work\n- Validate performance at 10K+ issue scale\n- Enable users to collect diagnostics for bug reports\n- Support both SQLite and JSONL import paths","status":"open","priority":2,"issue_type":"epic","created_at":"2025-11-13T22:22:11.203467-08:00","updated_at":"2025-11-13T22:22:11.203467-08:00"} @@ -63,9 +63,9 @@ {"id":"bd-oai","title":"Redesign session close protocol to be impossible to miss","description":"The current 'land the plane' protocol is failing repeatedly because:\n1. 'Land the plane' is vague and doesn't trigger specific training patterns\n2. Protocol instructions are buried in bd prime output\n3. No prominent checklist format\n4. Competes with strong 'summarize and finish' training patterns\n\nNeed to:\n- Rename to something concrete (Ship Protocol, Push Checklist, Close Session)\n- Put at TOP of bd prime output with 🚨 markers\n- Format as explicit checklist with [ ] boxes\n- Make it impossible to skip or miss\n- Test with actual usage to verify it works\n\nThe protocol MUST be:\n[ ] 1. git status\n[ ] 2. git add \u003cfiles\u003e\n[ ] 3. bd sync\n[ ] 4. git commit\n[ ] 5. bd sync (if needed)\n[ ] 6. git push\n\nThis is critical infrastructure - without it, work doesn't land.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-11-24T01:16:15.646213-08:00","updated_at":"2025-11-24T01:17:20.879091-08:00","closed_at":"2025-11-24T01:17:20.879091-08:00"} {"id":"bd-ola6","title":"Implement transaction retry logic for SQLITE_BUSY","description":"BEGIN IMMEDIATE fails immediately on SQLITE_BUSY instead of retrying with exponential backoff.\n\nLocation: internal/storage/sqlite/sqlite.go:223-225\n\nProblem:\n- Under concurrent write load, BEGIN IMMEDIATE can fail with SQLITE_BUSY\n- Current implementation fails immediately instead of retrying\n- Results in spurious failures under normal concurrent usage\n\nSolution: Implement exponential backoff retry:\n- Retry up to N times (e.g., 5)\n- Backoff: 10ms, 20ms, 40ms, 80ms, 160ms\n- Check for context cancellation between retries\n- Only retry on SQLITE_BUSY/database locked errors\n\nImpact: Spurious failures under concurrent write load\n\nEffort: 3 hours","status":"closed","priority":1,"issue_type":"feature","created_at":"2025-11-16T14:51:31.247147-08:00","updated_at":"2025-11-24T01:17:35.455245-08:00","closed_at":"2025-11-24T01:08:59.54254-08:00"} {"id":"bd-p6vp","title":"Clarify .beads/.gitattributes handling in Protected Branches docs","description":"Protected Branches docs quick start leaves untracked `.beads` directory and `.gitattributes`.\nQuestion: Are these changes meant to be checked into the protected branch?\nNeed to clarify if these should be ignored or committed, or if the instructions are missing a step.\n","status":"open","priority":2,"issue_type":"task","created_at":"2025-11-20T18:56:25.79407-05:00","updated_at":"2025-11-20T18:56:25.79407-05:00"} -{"id":"bd-pnm","title":"Add git history fallback for pruned deletions","description":"Parent: bd-imj\n\n## Task\nImplement git history fallback when DB issue is not in JSONL or deletions manifest.\n\n## Why This Works\nThe deletions manifest IS the cache. The git fallback is for:\n1. Entries pruned from manifest (older than retention period)\n2. Edge case: manifest somehow lost an entry\n\n## Implementation\n\n### Git command\n```bash\ngit log --all -S \"\\\"id\\\":\\\"bd-xxx\\\"\" --oneline -- .beads/beads.jsonl\n```\n- `-S` searches for string additions/removals\n- If output is non-empty AND issue not currently in JSONL → was deleted\n- If no output → truly new local issue\n\n### Batching for efficiency\nWhen multiple DB issues need checking:\n1. Collect all IDs needing git check\n2. Run single git log with grep for any matching ID\n3. Parse results to determine which were deleted\n\n```bash\ngit log --all -p -- .beads/beads.jsonl | grep -E \"bd-(xxx|yyy|zzz)\"\n```\n\n### Backfill on hit\nWhen git confirms deletion:\n1. Delete from DB\n2. Append to deletions.jsonl (self-healing)\n3. Log: \"Backfilled deletion record for bd-xxx (pruned from manifest)\"\n\nFuture imports will find it in manifest (fast path).\n\n### Edge cases\n- **Shallow clone**: git returns nothing → issue kept (correct, conservative)\n- **History rewrite**: manifest is authoritative, git is fallback only\n- **Large history**: Consider `--since` flag to bound search\n\n## Acceptance Criteria\n- [ ] Git fallback triggers on manifest miss\n- [ ] Backfills manifest on git hit (self-healing)\n- [ ] Batches multiple ID checks efficiently\n- [ ] Graceful shallow clone handling (keeps issues)\n- [ ] Test: pruned deletion caught via git and backfilled","status":"open","priority":1,"issue_type":"task","created_at":"2025-11-25T09:57:20.593564-08:00","updated_at":"2025-11-25T10:51:38.138806-08:00","dependencies":[{"issue_id":"bd-pnm","depends_on_id":"bd-ibg","type":"blocks","created_at":"2025-11-25T09:57:49.973533-08:00","created_by":"daemon"}]} +{"id":"bd-pnm","title":"Add git history fallback for pruned deletions","description":"Parent: bd-imj\n\n## Task\nImplement git history fallback when DB issue is not in JSONL or deletions manifest.\n\n## Why This Works\nThe deletions manifest IS the cache. The git fallback is for:\n1. Entries pruned from manifest (older than retention period)\n2. Edge case: manifest somehow lost an entry\n\n## Implementation\n\n### Git command\n```bash\ngit log --all -S \"\\\"id\\\":\\\"bd-xxx\\\"\" --oneline -- .beads/beads.jsonl\n```\n- `-S` searches for string additions/removals\n- If output is non-empty AND issue not currently in JSONL → was deleted\n- If no output → truly new local issue\n\n### Batching for efficiency\nWhen multiple DB issues need checking:\n1. Collect all IDs needing git check\n2. Run single git log with grep for any matching ID\n3. Parse results to determine which were deleted\n\n```bash\ngit log --all -p -- .beads/beads.jsonl | grep -E \"bd-(xxx|yyy|zzz)\"\n```\n\n### Backfill on hit\nWhen git confirms deletion:\n1. Delete from DB\n2. Append to deletions.jsonl (self-healing)\n3. Log: \"Backfilled deletion record for bd-xxx (pruned from manifest)\"\n\nFuture imports will find it in manifest (fast path).\n\n### Edge cases\n- **Shallow clone**: git returns nothing → issue kept (correct, conservative)\n- **History rewrite**: manifest is authoritative, git is fallback only\n- **Large history**: Consider `--since` flag to bound search\n\n## Acceptance Criteria\n- [ ] Git fallback triggers on manifest miss\n- [ ] Backfills manifest on git hit (self-healing)\n- [ ] Batches multiple ID checks efficiently\n- [ ] Graceful shallow clone handling (keeps issues)\n- [ ] Test: pruned deletion caught via git and backfilled","status":"open","priority":1,"issue_type":"task","created_at":"2025-11-25T09:57:20.593564-08:00","updated_at":"2025-11-25T12:33:39.685628-08:00","dependencies":[{"issue_id":"bd-pnm","depends_on_id":"bd-ibg","type":"blocks","created_at":"2025-11-25T09:57:49.973533-08:00","created_by":"daemon"}]} {"id":"bd-pp3","title":"Add metadata distinction guidelines to ERROR_HANDLING.md","description":"Update ERROR_HANDLING.md with clear guidelines distinguishing configuration metadata (Pattern A) from tracking metadata (Pattern B). Include examples from init.go and sync.go.","status":"closed","priority":3,"issue_type":"task","created_at":"2025-11-24T00:28:54.559851-08:00","updated_at":"2025-11-24T00:34:13.980602-08:00","closed_at":"2025-11-24T00:34:13.980602-08:00","dependencies":[{"issue_id":"bd-pp3","depends_on_id":"bd-1qwo","type":"blocks","created_at":"2025-11-24T00:28:54.560699-08:00","created_by":"daemon"}]} -{"id":"bd-qsm","title":"Auto-compact deletions during bd sync","description":"Parent: bd-imj\n\n## Task\nOptionally prune deletions manifest during sync when threshold exceeded.\n\n**Note: Opt-in feature** - disabled by default to avoid sync latency.\n\n## Implementation\n\nIn `bd sync`:\n```go\nfunc (s *Syncer) Sync() error {\n // ... existing sync logic ...\n \n // Auto-compact only if enabled\n if s.config.GetBool(\"deletions.auto_compact\", false) {\n deletionCount := deletions.Count(\".beads/deletions.jsonl\")\n threshold := s.config.GetInt(\"deletions.auto_compact_threshold\", 1000)\n \n if deletionCount \u003e threshold {\n retentionDays := s.config.GetInt(\"deletions.retention_days\", 7)\n if err := s.compactor.PruneDeletions(retentionDays); err != nil {\n log.Warnf(\"Failed to auto-compact deletions: %v\", err)\n // Non-fatal, continue sync\n }\n }\n }\n \n // ... rest of sync ...\n}\n```\n\n## Configuration\n```yaml\ndeletions:\n retention_days: 7\n auto_compact: false # Opt-in, disabled by default\n auto_compact_threshold: 1000 # Trigger when \u003e N entries (if enabled)\n```\n\n## Acceptance Criteria\n- [ ] Auto-compact disabled by default\n- [ ] Enabled via config `deletions.auto_compact: true`\n- [ ] Sync checks deletion count only when enabled\n- [ ] Auto-prunes when threshold exceeded\n- [ ] Failure is non-fatal (logged warning)\n- [ ] Test: no compaction when disabled\n- [ ] Test: compaction triggers when enabled and threshold exceeded","status":"open","priority":1,"issue_type":"task","created_at":"2025-11-25T09:57:04.522795-08:00","updated_at":"2025-11-25T10:51:39.798716-08:00","dependencies":[{"issue_id":"bd-qsm","depends_on_id":"bd-v2x","type":"blocks","created_at":"2025-11-25T09:57:50.045734-08:00","created_by":"daemon"}]} +{"id":"bd-qsm","title":"Auto-compact deletions during bd sync","description":"Parent: bd-imj\n\n## Task\nOptionally prune deletions manifest during sync when threshold exceeded.\n\n**Note: Opt-in feature** - disabled by default to avoid sync latency.\n\n## Implementation\n\nIn `bd sync`:\n```go\nfunc (s *Syncer) Sync() error {\n // ... existing sync logic ...\n \n // Auto-compact only if enabled\n if s.config.GetBool(\"deletions.auto_compact\", false) {\n deletionCount := deletions.Count(\".beads/deletions.jsonl\")\n threshold := s.config.GetInt(\"deletions.auto_compact_threshold\", 1000)\n \n if deletionCount \u003e threshold {\n retentionDays := s.config.GetInt(\"deletions.retention_days\", 7)\n if err := s.compactor.PruneDeletions(retentionDays); err != nil {\n log.Warnf(\"Failed to auto-compact deletions: %v\", err)\n // Non-fatal, continue sync\n }\n }\n }\n \n // ... rest of sync ...\n}\n```\n\n## Configuration\n```yaml\ndeletions:\n retention_days: 7\n auto_compact: false # Opt-in, disabled by default\n auto_compact_threshold: 1000 # Trigger when \u003e N entries (if enabled)\n```\n\n## Acceptance Criteria\n- [ ] Auto-compact disabled by default\n- [ ] Enabled via config `deletions.auto_compact: true`\n- [ ] Sync checks deletion count only when enabled\n- [ ] Auto-prunes when threshold exceeded\n- [ ] Failure is non-fatal (logged warning)\n- [ ] Test: no compaction when disabled\n- [ ] Test: compaction triggers when enabled and threshold exceeded","status":"open","priority":1,"issue_type":"task","created_at":"2025-11-25T09:57:04.522795-08:00","updated_at":"2025-11-25T12:33:39.686082-08:00","dependencies":[{"issue_id":"bd-qsm","depends_on_id":"bd-v2x","type":"blocks","created_at":"2025-11-25T09:57:50.045734-08:00","created_by":"daemon"}]} {"id":"bd-r46","title":"Support --reason flag in daemon mode for reopen command","description":"The reopen.go command has a TODO at line 61 to add reason as a comment once RPC supports AddComment. Currently --reason flag is ignored in daemon mode with a warning.","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-11-21T18:55:10.773626-05:00","updated_at":"2025-11-24T01:08:58.570375-08:00","closed_at":"2025-11-24T01:08:58.570375-08:00"} {"id":"bd-r71","title":"Add tests for error wrapping behavior","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-24T00:53:16.906291-08:00","updated_at":"2025-11-24T00:57:46.21316-08:00","closed_at":"2025-11-24T00:57:46.21316-08:00"} {"id":"bd-s0z","title":"Consider extracting error handling helpers","description":"Evaluate creating FatalError() and WarnError() helpers as suggested in ERROR_HANDLING.md to reduce boilerplate and enforce consistency. Prototype in a few files first to validate the approach.","status":"open","priority":4,"issue_type":"task","created_at":"2025-11-24T00:28:57.248959-08:00","updated_at":"2025-11-24T00:28:57.248959-08:00","dependencies":[{"issue_id":"bd-s0z","depends_on_id":"bd-1qwo","type":"blocks","created_at":"2025-11-24T00:28:57.249945-08:00","created_by":"daemon"}]}