updated bd-135 epic
This commit is contained in:
@@ -38,7 +38,15 @@
|
||||
{"id":"bd-132","title":"GH#146: No color showing in terminal for some users","description":"User reports color not working in macOS (Taho 26.0.1) with iTerm 3.6.4 and Terminal.app, despite color working elsewhere in terminal. Python rich and printf escape codes work.\n\nNeed to investigate:\n- Is NO_COLOR env var set?\n- Terminal type detection?\n- fatih/color library configuration\n- Does bd list show colors? bd ready? bd init?\n- What's the output of: echo $TERM, echo $NO_COLOR","status":"open","priority":2,"issue_type":"bug","created_at":"2025-10-24T22:26:36.22163-07:00","updated_at":"2025-10-24T22:26:36.22163-07:00","external_ref":"github:146"}
|
||||
{"id":"bd-133","title":"Fix nil pointer crash in bd reopen command","description":"bd reopen crashes with SIGSEGV at reopen.go:30. Nil pointer dereference when trying to reopen an issue.","notes":"Fixed by adding daemon RPC support to reopen command. Pattern: check daemonClient != nil first, use RPC UpdateArgs with Status=open, fall back to direct store if daemon unavailable.","status":"closed","priority":0,"issue_type":"bug","created_at":"2025-10-25T10:30:31.602438-07:00","updated_at":"2025-10-25T10:33:39.016623-07:00","closed_at":"2025-10-25T10:33:39.016623-07:00"}
|
||||
{"id":"bd-134","title":"Address gosec security warnings (102 issues)","description":"Security linter warnings: file permissions (0755 should be 0750), G304 file inclusion via variable, G204 subprocess launches. Many are false positives but should be reviewed.","design":"Review each gosec warning. Add exclusions for legitimate cases to .golangci.yml. Fix real security issues (overly permissive file modes).","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-25T13:47:10.719134-07:00","updated_at":"2025-10-25T13:47:10.719134-07:00"}
|
||||
{"id":"bd-135","title":"Multi-project MCP context switching (GH#145)","description":"Enable MCP server to manage multiple beads projects in a single session with per-request workspace_root parameter.\n\nCurrent bug: set_context(workspace_root) doesn't actually switch sockets - all operations hit first initialized socket.\n\nUse case: Managing tasks across multiple organizations with different permission models (internal, partners, open source).\n\nArchitecture: Connection pool keyed by workspace_root, each maintaining its own daemon socket connection. Request-scoped routing using ContextVar to avoid global state races.\n\nSee GH#145 for full requirements and user context.","design":"✅ APPROVED WITH MODIFICATIONS by architectural review (2025-10-25)\n\nLSP-style model is correct: Single MCP server → per-project daemons → isolated databases.\n\nCRITICAL CHANGES from review:\n1. **Simplify connection pool**: No LRU eviction initially (typical user: 2-5 projects)\n2. **Add asyncio.Lock**: Prevent race conditions in pool access\n3. **Defer health checks**: Only retry on failure, not preemptive pings\n4. **Handle submodules**: Check local .beads BEFORE git toplevel\n5. **Path canonicalization**: realpath + git toplevel with caching\n\nRISKS MITIGATED:\n- Global _client bug: Replace with connection pool keyed by canonical path\n- Race conditions: Add asyncio.Lock for pool mutations\n- Submodule edge case: Check .beads directory first\n- Stale sockets: Retry once on connection failure\n\nEstimated effort: 2.5-3.5 days (simplified from 2.5-4.5 days)\nConfidence: 8/10","acceptance_criteria":"- Multiple projects can be accessed in single MCP session\n- Per-request workspace_root parameter works on all tools\n- No cross-project data leakage\n- Concurrent calls to different projects work correctly\n- Stale sockets auto-reconnect with retry/backoff\n- Integration tests verify isolation across 2+ temp repos\n- set_context() still works as default fallback","notes":"Review doc: docs/bd-135-architectural-review.md\n\nImplementation order:\n1. bd-139 (connection manager) - Foundation with pool + lock\n2. bd-136 (ContextVar routing) - Per-request workspace\n3. bd-138 (require_context) - Validation\n4. bd-140 (tests) - Concurrency + edge cases\n5. bd-141 (docs) - Usage guide\n6. bd-142 (health checks) - DEFERRED to Phase 2","status":"open","priority":1,"issue_type":"epic","created_at":"2025-10-25T13:59:57.231937-07:00","updated_at":"2025-10-25T14:23:57.985669-07:00"}
|
||||
{"id":"bd-136","title":"Implement request-scoped routing with ContextVar","description":"Add ContextVar-based routing to avoid global state races during concurrent multi-project calls.\n\nApproach:\n- Define current_workspace: ContextVar[str|None] in server.py\n- Add @with_workspace decorator that resolves workspace_root (via _resolve_workspace_root + realpath)\n- Set ContextVar for duration of tool call, reset after\n- Falls back to set_context default (BEADS_WORKING_DIR) if workspace_root not provided\n- beads_mcp.tools.get_client() reads current_workspace from ContextVar\n\nBlocks: bd-135 (connection manager must exist first)","design":"Decorator pattern with ContextVar for request-scoped workspace routing.\n\n@with_workspace decorator:\n- Extract workspace_root parameter from tool call\n- Resolve via _resolve_workspace_root + realpath\n- Set current_workspace ContextVar for request duration\n- Falls back to BEADS_WORKING_DIR if workspace_root not provided\n- Reset ContextVar after tool completes\n\nApplied to all tools in server.py. _get_client() reads current_workspace.\n\n⚠️ CONCURRENCY GOTCHA (from architectural review):\n- ContextVar doesn't propagate to asyncio.create_task() spawned tasks\n- SOLUTION: Keep tool calls synchronous, no background task spawning\n- If background tasks needed: use contextvars.copy_context()\n\nDocument this limitation in bd-141.","notes":"Blocks on bd-139 (connection pool must exist first).\n\nCRITICAL: Do NOT spawn background tasks within tool implementations.\nContextVar propagation to spawned tasks is unreliable.","status":"open","priority":1,"issue_type":"task","created_at":"2025-10-25T14:00:27.895512-07:00","updated_at":"2025-10-25T14:24:26.774466-07:00","dependencies":[{"issue_id":"bd-136","depends_on_id":"bd-135","type":"parent-child","created_at":"2025-10-25T14:00:27.896366-07:00","created_by":"daemon"}]}
|
||||
{"id":"bd-137","title":"Add health checks and reconnection logic for stale sockets","description":"Handle stale/broken socket connections after daemon restarts or upgrades.\n\nFeatures:\n- ping/health_check method on client\n- Check before use or periodic health check\n- On failure: drop from pool, attempt reconnect with exponential backoff\n- Clear error propagation if reconnect fails after retries\n- Handle version mismatch after daemon upgrade\n- Handle long-idle connections closed by daemon","design":"Add async ping() to DaemonClient. ConnectionManager.get_client() pings before returning cached client. On failure, del from pool and retry connect. Bounded retry (3-5 attempts) with backoff.","status":"open","priority":1,"issue_type":"task","created_at":"2025-10-25T14:00:27.8967-07:00","updated_at":"2025-10-25T14:00:27.8967-07:00"}
|
||||
{"id":"bd-138","title":"Fix require_context to support per-request workspace_root","description":"Update require_context decorator to pass if either:\n- workspace_root was provided on the tool call (via ContextVar), OR\n- BEADS_WORKING_DIR is set (from set_context)\n\nStop using BEADS_DB as router - treat it only as CLI fallback.\n\nEnsures backward compatibility with set_context() while supporting new per-request routing.","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-25T14:00:27.896646-07:00","updated_at":"2025-10-25T14:00:27.896646-07:00","dependencies":[{"issue_id":"bd-138","depends_on_id":"bd-135","type":"parent-child","created_at":"2025-10-25T14:00:27.89804-07:00","created_by":"daemon"}]}
|
||||
{"id":"bd-139","title":"Add connection manager for per-project daemon sockets","description":"Create connection pool for managing multiple daemon socket connections in beads_mcp.tools.\n\nSIMPLIFIED APPROACH (per architectural review):\n- Connection pool dict[str, BdClientBase] keyed by canonical path\n- Add asyncio.Lock for thread-safe pool access (CRITICAL)\n- Path canonicalization with @lru_cache for performance\n- NO LRU eviction (start simple, add if monitoring shows memory issues)\n- NO preemptive health checks (only retry on failure)\n\nFeatures:\n- get_client(workspace_root) → returns client for that project\n- Canonicalization: realpath + git toplevel (handles symlinks)\n- Submodule-aware: Check local .beads BEFORE git toplevel\n- Auto-start daemon if socket missing (existing behavior)\n- Validate version on connect (existing behavior)\n\nCRITICAL: Replace global _client singleton with pool to fix set_context() bug.","design":"Implementation in tools.py:\n\n```python\nfrom contextvars import ContextVar\nimport asyncio\nfrom functools import lru_cache\n\ncurrent_workspace: ContextVar[str | None] = ContextVar('workspace', default=None)\n_connection_pool: Dict[str, BdClientBase] = {}\n_pool_lock = asyncio.Lock() # Prevent race conditions\n\n@lru_cache(maxsize=128)\ndef _canonicalize_path(path: str) -\u003e str:\n # 1. realpath to resolve symlinks\n real = os.path.realpath(path)\n \n # 2. Check local .beads (submodule edge case)\n if os.path.exists(os.path.join(real, \".beads\")):\n return real\n \n # 3. Try git toplevel\n return _resolve_workspace_root(real)\n\nasync def _get_client() -\u003e BdClientBase:\n workspace = current_workspace.get() or os.environ.get(\"BEADS_WORKING_DIR\")\n if not workspace:\n raise BdError(\"No workspace set\")\n \n canonical = _canonicalize_path(workspace)\n \n async with _pool_lock: # CRITICAL: prevent concurrent mutations\n if canonical not in _connection_pool:\n _connection_pool[canonical] = create_bd_client(\n prefer_daemon=True,\n working_dir=canonical\n )\n return _connection_pool[canonical]\n```\n\n~200 LOC total (simplified from ~400-500)","status":"open","priority":1,"issue_type":"task","created_at":"2025-10-25T14:00:27.896896-07:00","updated_at":"2025-10-25T14:24:15.261895-07:00","dependencies":[{"issue_id":"bd-139","depends_on_id":"bd-135","type":"parent-child","created_at":"2025-10-25T14:00:27.89917-07:00","created_by":"daemon"}]}
|
||||
{"id":"bd-14","title":"Auto-flush writes test pollution and session work to git-tracked issues.jsonl","description":"Auto-flush exports ALL issues from DB to issues.jsonl every 5 seconds, including:\n- Test issues (bd-4053 through bd-4059 were version test junk)\n- Issues created during debugging sessions\n- Test pollution from stress tests\n- Temporary diagnostic issues\n\nThis pollutes the git-tracked issues.jsonl with garbage that shouldn't be committed.\n\nExample from today:\n- Git had 49 clean issues\n- Our DB grew to 100+ with test junk and session work\n- Auto-flush wrote all 100+ to issues.jsonl\n- Git status showed modified issues.jsonl with 50+ unwanted issues\n\nImpact:\n- Pollutes git history with test/debug garbage\n- Makes code review difficult (noise in diffs)\n- Can't distinguish real work from session artifacts\n- Other team members pull polluted issues\n\nSolutions to consider:\n1. Disable auto-flush by default (require explicit --enable-auto-flush)\n2. Add .beadsignore to exclude issue ID patterns\n3. Make auto-flush only export 'real' issues (exclude test-*)\n4. Require manual 'bd sync' for git commit\n5. Auto-flush to separate file (.beads/session.jsonl vs issues.jsonl)\n\nRelated: bd-117 (test pollution), isolation_test.go (test DB separation)","status":"closed","priority":1,"issue_type":"bug","created_at":"2025-10-22T00:05:10.788996-07:00","updated_at":"2025-10-24T13:51:54.437366-07:00","closed_at":"2025-10-22T01:05:59.459797-07:00"}
|
||||
{"id":"bd-140","title":"Add integration tests for multi-project MCP switching","description":"Comprehensive tests to verify multi-project isolation, concurrency, and edge cases.\n\nEXPANDED TEST COVERAGE (per architectural review):\n\n**Concurrency tests (CRITICAL):**\n- asyncio.gather() with calls to different workspace_root values\n- Verify no cross-project data leakage\n- Verify pool lock prevents race conditions\n\n**Edge case tests:**\n- Submodule handling: Parent repo vs submodule with own .beads\n- Symlink deduplication: Same physical path via different symlinks\n- Stale socket recovery: Kill daemon, verify retry on failure\n- Missing .beads directory handling\n\n**Isolation tests:**\n- Create 2+ temp repos with bd init\n- Verify operations in project A don't affect project B\n- Stress test: many parallel calls across 3-5 repos\n\nEstimated effort: M (1-2 days) including fixtures for temp repos and daemon process management","design":"Test structure:\n\n1. test_concurrent_multi_project.py:\n - asyncio.gather with 2+ projects\n - Verify pool lock prevents corruption\n \n2. test_path_canonicalization.py:\n - Submodule edge case (check .beads first)\n - Symlink deduplication (realpath normalization)\n \n3. test_stale_socket_recovery.py:\n - Kill daemon mid-session\n - Verify retry-on-failure works\n \n4. test_cross_project_isolation.py:\n - Create issues in project A\n - List from project B, verify empty\n - No data leakage\n\nUse pytest fixtures for temp repos and daemon lifecycle.","acceptance_criteria":"- All concurrency tests pass with asyncio.gather\n- Submodule edge case handled correctly\n- Symlinks deduplicated to same connection\n- Stale socket retry works\n- No cross-project data leakage in stress tests","status":"open","priority":1,"issue_type":"task","created_at":"2025-10-25T14:00:27.896623-07:00","updated_at":"2025-10-25T14:24:42.04792-07:00","dependencies":[{"issue_id":"bd-140","depends_on_id":"bd-135","type":"parent-child","created_at":"2025-10-25T14:00:27.90028-07:00","created_by":"daemon"}]}
|
||||
{"id":"bd-141","title":"Update MCP multi-project documentation","description":"Update documentation for multi-project workflow in README.md, AGENTS.md, and MCP integration docs.\n\nEXPANDED SECTIONS (per architectural review):\n\n**Usage examples:**\n- Per-request workspace_root parameter usage\n- Concurrent multi-project queries with asyncio.gather\n- Migration from set_context() to workspace_root parameter\n\n**Architecture notes:**\n- Connection pooling behavior (no limits initially)\n- set_context() as default fallback (still supported)\n- Library users NOT affected (all changes in MCP layer)\n\n**Concurrency gotchas (CRITICAL):**\n- ContextVar doesn't propagate to asyncio.create_task()\n- Do NOT spawn background tasks in tool implementations\n- All tool calls should be synchronous/sequential\n\n**Troubleshooting:**\n- Stale sockets (retry once on failure)\n- Version mismatches (auto-detected since v0.16.0)\n- Path aliasing via symlinks (deduplicated by realpath)\n- Submodules with own .beads (handled correctly)\n\n**Use cases:**\n- Multi-organization collaboration (GH#145)\n- Parallel project management scripts\n- Cross-project queries","design":"Documentation structure:\n\n1. integrations/beads-mcp/README.md:\n - Add \"Multi-Project Support\" section\n - workspace_root parameter examples\n - Connection pool behavior\n \n2. AGENTS.md:\n - Update MCP section with workspace_root usage\n - Add concurrency warning (no spawned tasks)\n - Document library non-impact\n \n3. New: docs/MCP_MULTI_PROJECT.md:\n - Detailed architecture explanation\n - Migration guide from set_context()\n - Troubleshooting guide\n - Edge cases (submodules, symlinks)","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-25T14:00:27.897025-07:00","updated_at":"2025-10-25T14:25:14.524235-07:00","dependencies":[{"issue_id":"bd-141","depends_on_id":"bd-135","type":"parent-child","created_at":"2025-10-25T14:00:27.901495-07:00","created_by":"daemon"}]}
|
||||
{"id":"bd-142","title":"Add health checks and reconnection logic for stale sockets","description":"⚠️ DEFERRED TO PHASE 2 (per architectural review)\n\nAdd health checks and reconnection logic for stale sockets ONLY IF monitoring shows it's needed.\n\nSIMPLIFIED APPROACH:\n- NO preemptive health checks (adds latency to every call)\n- NO periodic ping before use (daemon restarts are rare)\n- YES: Single retry on connection failure (DaemonConnectionError)\n- YES: Evict stale client from pool on failure\n\nRationale:\n- Stale sockets are rare (daemon auto-restart is uncommon)\n- Preemptive checks add latency with little benefit\n- Retry-on-failure is sufficient for most cases\n\nImplementation (if needed later):\n- Wrap tool calls with try/except\n- On DaemonConnectionError: evict from pool, retry once\n- Log failures for monitoring\n\nMonitor after Phase 1 launch:\n- Frequency of stale socket errors\n- User reports of connection issues\n- Decision point: Add if \u003e1% of calls fail","design":"Simplified retry wrapper (implement only if monitoring shows need):\n\n```python\nasync def _robust_client_call(func):\n try:\n client = await _get_client()\n return await func(client)\n except (DaemonConnectionError, DaemonNotRunningError):\n # Evict stale client and retry once\n workspace = current_workspace.get()\n canonical = _canonicalize_path(workspace)\n async with _pool_lock:\n _connection_pool.pop(canonical, None)\n # Retry\n client = await _get_client()\n return await func(client)\n```\n\nNO bounded backoff, NO health checks, NO version validation pings.","notes":"DEFERRED - Not needed for MVP.\n\nAdd to Phase 2 roadmap only if monitoring shows:\n- Stale socket errors \u003e1% of calls\n- User complaints about connection issues\n- Long-running MCP servers experiencing problems","status":"open","priority":3,"issue_type":"task","created_at":"2025-10-25T14:00:36.252409-07:00","updated_at":"2025-10-25T14:24:58.5637-07:00","dependencies":[{"issue_id":"bd-142","depends_on_id":"bd-135","type":"parent-child","created_at":"2025-10-25T14:00:42.132775-07:00","created_by":"daemon"}]}
|
||||
{"id":"bd-15","title":"Make merge command idempotent for safe retry after partial failures","description":"The merge command currently performs 3 operations without an outer transaction:\n1. Migrate dependencies from source → target\n2. Update text references across all issues\n3. Close source issues\n\nIf merge fails mid-operation (network issue, daemon crash, etc.), a retry will fail or produce incorrect results because some operations already succeeded.\n\n**Goal:** Make merge idempotent so retrying after partial failure is safe and completes the remaining work.\n\n**Idempotency checks needed:**\n- Skip dependency migration if target already has the dependency\n- Skip text reference updates if already updated\n- Skip closing source issues if already closed\n- Report which operations were skipped vs performed\n\n**Example output:**\n```\n✓ Merged 2 issue(s) into bd-63\n - Dependencies: 3 migrated, 2 already existed\n - Text references: 5 updated, 0 already correct\n - Source issues: 1 closed, 1 already closed\n```\n\n**Related:** bd-115 originally requested transaction support, but idempotency is a better solution for this use case since individual operations are already atomic.","design":"Current merge code already has some idempotency:\n- Dependency migration checks `alreadyExists` before adding (line ~145-151 in merge.go)\n- Text reference updates are naturally idempotent (replacing bd-X with bd-Y twice has same result)\n\nMissing idempotency:\n- CloseIssue fails if source already closed\n- Error messages don't distinguish \"already done\" from \"real failure\"\n\nImplementation:\n1. Check source issue status before closing - skip if already closed\n2. Track which operations succeeded/skipped\n3. Return detailed results for user visibility\n4. Consider adding --dry-run output showing what would be done vs skipped","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-10-22T00:47:43.165434-07:00","updated_at":"2025-10-24T13:51:54.437619-07:00","closed_at":"2025-10-22T11:56:36.526276-07:00"}
|
||||
{"id":"bd-16","title":"Global daemon should warn/reject --auto-commit and --auto-push","description":"When user runs 'bd daemon --global --auto-commit', it's unclear which repo the daemon will commit to (especially after fixing bd-62 where global daemon won't open a DB).\n\nOptions:\n1. Warn and ignore the flags in global mode\n2. Error out with clear message\n\nLine 87-91 already checks autoPush, but should skip check entirely for global mode. Add user-friendly messaging about flag incompatibility.","status":"closed","priority":3,"issue_type":"feature","created_at":"2025-10-22T00:47:43.165645-07:00","updated_at":"2025-10-24T13:51:54.437812-07:00","closed_at":"2025-10-17T23:04:30.223432-07:00"}
|
||||
{"id":"bd-17","title":"Add cross-repo issue references (future enhancement)","description":"Support referencing issues across different beads repositories. Useful for tracking dependencies between separate projects.\n\nProposed syntax:\n- Local reference: bd-63 (current behavior)\n- Cross-repo by path: ~/src/other-project#bd-456\n- Cross-repo by workspace name: @project2:bd-789\n\nUse cases:\n1. Frontend project depends on backend API issue\n2. Shared library changes blocking multiple projects\n3. System administrator tracking work across machines\n4. Monorepo with separate beads databases per component\n\nImplementation challenges:\n- Storage layer needs to query external databases\n- Dependency resolution across repos\n- What if external repo not available?\n- How to handle in JSONL export/import?\n- Security: should repos be able to read others?\n\nDesign questions to resolve first:\n1. Read-only references vs full cross-repo dependencies?\n2. How to handle repo renames/moves?\n3. Absolute paths vs workspace names vs git remotes?\n4. Should bd-38 auto-discover related repos?\n\nRecommendation: \n- Gather user feedback first\n- Start with read-only references\n- Implement as plugin/extension?\n\nContext: This is mentioned in bd-38 as approach #2. Much more complex than daemon multi-repo approach. Only implement if there's strong user demand.\n\nPriority: Backlog (4) - wait for user feedback before designing","status":"closed","priority":4,"issue_type":"feature","created_at":"2025-10-22T00:47:43.165857-07:00","updated_at":"2025-10-24T13:51:54.438011-07:00","closed_at":"2025-10-20T22:00:31.966891-07:00"}
|
||||
|
||||
Reference in New Issue
Block a user