feat(local): aggregate blocked-on-dependency reporting by dependency (#131)
Some checks failed
ci/crow/cron/process-updates/1 Pipeline was successful
ci/crow/cron/process-updates/2 Pipeline failed
ci/crow/cron/process-updates/7 Pipeline was successful
ci/crow/cron/process-updates/8 Pipeline was successful
ci/crow/cron/process-updates/3 Pipeline was successful
ci/crow/cron/process-updates/4 Pipeline was successful
ci/crow/manual/weekly-patch-proposals Pipeline was successful
ci/crow/cron/process-updates/9 Pipeline was successful
ci/crow/cron/process-updates/10 Pipeline was successful
ci/crow/manual/trial-build-registry/7 Pipeline failed
ci/crow/manual/trial-build-registry/5 Pipeline failed
ci/crow/manual/trial-build-registry/3 Pipeline failed
ci/crow/manual/trial-build-registry/13 Pipeline failed
ci/crow/manual/trial-build-registry/1 Pipeline failed
ci/crow/manual/trial-build-registry/11 Pipeline failed
ci/crow/manual/trial-build-registry/4 Pipeline failed
ci/crow/manual/trial-build-registry/2 Pipeline failed
ci/crow/manual/trial-build-registry/6 Pipeline failed
ci/crow/manual/trial-build-registry/9 Pipeline failed
ci/crow/manual/trial-build-registry/17 Pipeline failed
ci/crow/manual/trial-build-registry/15 Pipeline failed
ci/crow/manual/trial-build-registry/10 Pipeline failed
ci/crow/manual/trial-build-registry/14 Pipeline failed
ci/crow/manual/trial-build-registry/16 Pipeline failed
ci/crow/manual/trial-build-registry/12 Pipeline failed
ci/crow/manual/trial-build-registry/8 Pipeline failed
ci/crow/manual/trial-build-registry/18 Pipeline failed
ci/crow/cron/process-updates/13 Pipeline was successful
ci/crow/cron/process-updates/14 Pipeline was successful
ci/crow/cron/process-updates/15 Pipeline was successful
ci/crow/cron/process-updates/16 Pipeline was successful

## Why

With cascade detection (#128) live, the latest `auto-apply-patches` run did exactly the right thing — **proposed nothing** (`No auto-proposable candidates`) because every failure is a dependency cascade, and it surfaced the ~30 root-cause dependencies to fix.

But the "Blocked on a dependency" list printed **one line per fingerprint group**, so the same dependency repeated (rstan ×4, lpsymphony ×4, salso ×2, BH ×2, GO.db ×2, RcppCWB ×2, …), burying the priority.

## What

Aggregate blocked packages across all groups **by the dependency they wait on**:

- Expose `blocked_map` (package → dependency) from `build_triage_report()`.
- Add `blocked_by_dependency()` — dedupes dependents (a package in two groups counts once) and ranks dependencies by how many distinct dependents they block.
- Proposer and tracker (log + issue) now print one line per dependency, sorted by impact. Replaces the per-group `blocked_summary`.

## Result (same data, aggregated)

```
Blocked on a dependency (3 dependencies block 6 dependents; fix the dependency, not each dependent):
  RcppParallel             3 dependent(s)
  rstan                    2 dependent(s)
  sf                       1 dependent(s)
```

So the real run becomes a crisp, ranked worklist: RcppParallel (894), sf (128), rstan (~96), Rfast (33), clarabel/DescTools (26), Rglpk (22), xgboost (18), …

## Verified

New test covers cross-group aggregation, dedup (a dependent in two groups counted once), the example cap, and ranking. 112 tests pass; hooks pass.

Reviewed-on: #131
This commit is contained in:
Patrick Schratz 2026-07-16 21:24:03 +00:00 committed by Patrick Schratz
commit 6c03f278ec

View file

@ -127,13 +127,13 @@ if (length(retire) > 0L) {
# ---------------------------------------------------------------------------
# Blind spots: failures the classifier could not auto-propose.
# ---------------------------------------------------------------------------
blocked <- blocked_summary(report)
blocked <- blocked_by_dependency(report)
unmatched <- unclassified_summary(report)
cat("\nBlocked on a dependency (fix the dependency, not each dependent):\n")
if (length(blocked) > 0L) {
for (b in blocked) {
cat(sprintf(" %s: %d dependent(s) waiting\n", b$blocked_on, b$n_packages))
cat(sprintf(" %-20s %5d dependent(s)\n", b$dependency, b$n_packages))
}
} else {
cat(" (none)\n")
@ -207,8 +207,8 @@ if (do_issue) {
body_lines <- c(
body_lines,
sprintf(
"- **%s**: %d dependent(s) waiting (e.g. %s%s)",
b$blocked_on,
"- **%s**: %d dependent(s) (e.g. %s%s)",
b$dependency,
b$n_packages,
toString(b$packages),
if (isTRUE(b$packages_truncated)) ", ..." else ""