feat: bd ready filters by external dep satisfaction (bd-zmmy)

GetReadyWork now lazily resolves external dependencies at query time:
- External refs (external:project:capability) checked against target DB
- Issues with unsatisfied external deps are filtered from ready list
- Satisfaction = closed issue with provides:<capability> label in target

Key changes:
- Remove FK constraint on depends_on_id to allow external refs
- Add migration 025 to drop FK and recreate views
- Filter external deps in GetReadyWork, not in blocked_issues_cache
- Add application-level validation for orphaned local deps
- Comprehensive tests for external dep resolution

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-21 23:41:44 -08:00
parent a9bfce7f6e
commit 1cfb23487b
9 changed files with 633 additions and 64 deletions

View File

@@ -113,27 +113,19 @@ func (s *SQLiteStorage) rebuildBlockedCache(ctx context.Context, exec execer) er
}
// Rebuild using the recursive CTE logic
// Includes both local blockers (open issues) and external refs (bd-om4a)
// Only includes local blockers (open issues) - external refs are resolved
// lazily at query time by GetReadyWork (bd-zmmy supersedes bd-om4a)
query := `
INSERT INTO blocked_issues_cache (issue_id)
WITH RECURSIVE
-- Step 1: Find issues blocked directly by dependencies
-- Includes both local blockers (open issues) and external references
-- Step 1: Find issues blocked directly by LOCAL dependencies
-- External refs (external:*) are excluded - they're resolved lazily by GetReadyWork
blocked_directly AS (
-- Local blockers: issues with open status
SELECT DISTINCT d.issue_id
FROM dependencies d
JOIN issues blocker ON d.depends_on_id = blocker.id
WHERE d.type = 'blocks'
AND blocker.status IN ('open', 'in_progress', 'blocked', 'deferred')
UNION
-- External blockers: always blocking until resolved (bd-om4a)
SELECT DISTINCT d.issue_id
FROM dependencies d
WHERE d.type = 'blocks'
AND d.depends_on_id LIKE 'external:%'
),
-- Step 2: Propagate blockage to all descendants via parent-child