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

@@ -12,6 +12,7 @@ package sqlite
import (
"context"
"database/sql"
"os"
"path/filepath"
"strings"
@@ -73,14 +74,27 @@ func CheckExternalDep(ctx context.Context, ref string) *ExternalDepStatus {
dbPath := cfg.DatabasePath(beadsDir)
// Open the external database (read-only)
db, err := sql.Open("sqlite3", dbPath+"?mode=ro")
// Verify database file exists
if _, err := os.Stat(dbPath); err != nil {
status.Reason = "database file not found: " + dbPath
return status
}
// Open the external database
// Use regular mode to ensure we can read from WAL-mode databases
db, err := sql.Open("sqlite3", dbPath)
if err != nil {
status.Reason = "cannot open project database"
status.Reason = "cannot open project database: " + err.Error()
return status
}
defer func() { _ = db.Close() }()
// Verify we can ping the database
if err := db.Ping(); err != nil {
status.Reason = "cannot connect to project database: " + err.Error()
return status
}
// Check for a closed issue with provides:<capability> label
providesLabel := "provides:" + status.Capability
var count int
@@ -92,7 +106,7 @@ func CheckExternalDep(ctx context.Context, ref string) *ExternalDepStatus {
`, providesLabel).Scan(&count)
if err != nil {
status.Reason = "database query failed"
status.Reason = "database query failed: " + err.Error()
return status
}