Add `.crow/weekly-patch-proposals.yaml`, a single (non-matrix) job that runs
the classifier over all of `single_builds` weekly: it posts/updates a Forgejo
tracking issue with the auto-proposable registry entries via
`propose-patches.R --open-issue`, then logs the step-4 metrics via
`proposal-tracking.R`. Clones read-only; the only write is the issue.
Register the `weekly-patch-proposals` cron in the crow UI, or trigger manually
with `task=weekly-patch-proposals`.
Implement steps 3 + 4 of issue #115 on top of the failure classifier, now
that bincraft v4.4.3 applies registry patches/makevars/configure_args to the
target build (not just dependencies), so a trial patched build is meaningful.
- refactor the classify helpers to expose a pure build_triage_report() and a
list-returning entry builder; failing-builds-report.R now renders from it
- add local/propose-patches.R (step 3, "propose, do not apply"): emit a
pre-filled registry.json entry for each classified, safe, unregistered
failure, validate the candidate set against a temporary merged registry,
and (only on request) --write it plus a proposals ledger, or --open-issue a
Forgejo tracking issue; the human gate and validator/trial-build acceptance
stay, and novel source diffs / unknown signatures are never proposed
- add local/trial-build-patch.R: isolated bincraft build of one package with
the registry applied (no upload/archive/metadata) as the pre-merge gate
- add local/proposal-tracking.R + local/proposal-tracking-lib.R (step 4):
signature hit rate, proposed-vs-merged, and retirement candidates, with the
pure helpers covered by tests
- teach validate-patches.R optional PATCH_DIR/REGISTRY_FILE overrides so a
candidate registry can be validated without touching the real one
- document the propose/trial-build/tracking workflow in local/patches/README.md
Implements steps 1 + 2 of #115: turn recorded build failures into triaged patch suggestions instead of hand-scraping Crow logs.
## What this adds
A **read-only** reporting pipeline over the `single_builds` metadata table. It never writes to the DB or the registry.
- `local/failing-builds-classify.R` — pure, DB-free helpers:
- `normalise_error()` strips temp paths, version numbers, hex addresses, and the package name so the same root cause collapses to one fingerprint.
- `fingerprint_error()` extracts the salient error line and normalises it.
- `classify_error()` matches against a seed signature set; unmatched errors are never guessed at.
- `propose_registry_entry()` renders a schema-valid `registry.json` entry.
- `local/failing-builds-report.R` — entrypoint: queries `single_builds WHERE error_occurred = TRUE AND removed = FALSE`, groups by root cause (signature when classified, fingerprint otherwise), classifies each group, and prints a triaged report. Flags: `--platform`, `--arch`, `--min`, `--json`; `PLATFORM`/`ARCH` env fallbacks.
- `local/tests/test-failing-builds-classify.R` — unit tests for the helpers.
- `local/patches/README.md` — documents the workflow.
## Seed signatures
Each rule carries a fix tier, confidence, and an auto/human-only flag:
| Signature | Fix | Disposition |
| --- | --- | --- |
| `tbb/tbb_stddef.h: No such file` | makevars `-DTBB_INTERFACE_NEW` | auto-proposable |
| RcppParallel bundled TBB (musl / new g++) | curated `disable-tbb.patch` | auto-proposable |
| system `libuv.so` link leak | force vendored/static lib | **human triage** (novel source diff) |
| unmatched | none | **human triage** |
## Guardrails honored
- No autonomous novel source diffs: only known env/makevars levers and already-curated package patches are auto-proposable; anything needing a brand-new diff, and any unknown signature, is routed to human triage.
- No DB or registry writes; no change to the public `src/contrib` index.
- Reuses `single_builds.error_text`; no new failure-capture pipeline.
## Verification
- All helper unit tests pass under the Dockerized R 4.5.3 build env.
- Pre-commit hooks pass (`air-format`, `validate-patches`, prettier, etc.).
- Smoke-tested the full report path with a stubbed DB; generated proposals pass the real `local/validate-patches.R`.
Steps 3 (auto-open PRs) and 4 (feedback loop) are intentionally deferred, per the issue's suggestion to validate the signature set first.
Closes#115
Reviewed-on: #116
## Summary
Adds the curated **patch registry** and wiring that drives bincraft's new package-patching mechanism (see bincraft PR `feat/package-patching`).
Lets specific packages be patched (env/configure/Makevars overrides or source diffs) before pak installs them — including as transitive dependencies — so compiler-/OS-specific failures like RcppParallel's bundled TBB stop cascading.
## What's included
- `local/patches/registry.json` — initial entry: RcppParallel with `RCPP_PARALLEL_USE_TBB=0` for alpine / ubuntu-2604, plus `local/patches/README.md` schema docs.
- `local/validate-patches.R` — validates schema, referenced patch files, and ambiguous overlaps; clean failure + exit 1 (no stacktrace).
- `.pre-commit-config.yaml` — a `validate-patches` hook (re-runs when the registry or the validator changes).
- `local/build-one.R` / `local/build-all.R` — pass `patches = "local/patches"` to `bincraft::build_binary_package()`.
- `specs/2026-06-30-package-patching-design.md` and `plans/2026-06-30-package-patching-implementation.md`.
## ⚠️ Merge ordering (blocker)
This PR adds a `patches = ...` argument to `build_binary_package()` calls.
The `.crow/*.yaml` workflows currently pin bincraft **v4.2.3**, which does not accept that argument — CI will error with `unused argument (patches=...)` until:
1. bincraft **v4.3.0** is released (PR `feat/package-patching`), and
2. the pin is bumped in `.crow/build-all-versions-install-deps.yaml`, `.crow/build-all-versions.yaml`, and `.crow/process-updates.yaml`.
The `.crow` pin bump will be added to this PR once bincraft v4.3.0 is tagged. Do not merge before then.
Reviewed-on: #103