feat(local): auto-propose registry patches and track the feedback loop (#117)

Implements steps 3 + 4 of #115, building on the classifier merged in #116. Now that bincraft **v4.4.3** applies registry `patch`/`makevars`/`configure_args` to the *target* package build (previously deps-only), a trial patched build is a meaningful acceptance gate, so the "propose" half is viable.

## Step 3 — propose, do not apply

- **`local/propose-patches.R`** — for each classified, safe fix affecting a package with no current registry entry, emits a pre-filled `registry.json` entry and validates the candidate set against a *temporary* merged registry (the real one is never touched unless asked).
  - default: print candidates + validation, **take no action**
  - `--write`: append entries to `registry.json` + the proposals ledger (you commit + open the PR)
  - `--open-issue`: post/update a Forgejo tracking issue (reuses the weekly-audit `httr2` + `FORGEJO_TOKEN` pattern)
- **`local/trial-build-patch.R`** — isolated bincraft build of one package with the registry applied (no upload/archive/metadata; `patchhash` keeps it out of the real cache). Exit 0/1, so it gates a CI step or manual pre-merge check.

The human gate stays: nothing merges. Acceptance = `validate-patches.R` passes (checked automatically) **and** the trial build succeeds. Novel source diffs and unknown signatures are never proposed (they carry `auto = FALSE`).

## Step 4 — feedback loop

- **`local/proposal-tracking.R`** (read-only) — signature hit rate (builds/pkgs/addressed/open per signature), proposed-vs-merged (a proposal counts merged once its package is in the registry), and retirement candidates (registry entries whose package no longer fails, i.e. likely fixed upstream).
- **`local/proposal-tracking-lib.R`** — the pure metric/ledger helpers.

## Supporting changes

- Refactored the classify helpers to expose a pure `build_triage_report()` + a list-returning entry builder; `failing-builds-report.R` now renders from the shared function (no behaviour change).
- `validate-patches.R` gains optional `PATCH_DIR`/`REGISTRY_FILE` overrides (backward-compatible) so a candidate registry can be validated in isolation.
- Documented the propose/trial-build/tracking workflow in `local/patches/README.md`.

## Verification

- 71 unit tests pass (incl. new `test-proposal-tracking-lib.R`) under the Dockerized R 4.5.3 build env.
- All pre-commit hooks pass (`air-format`, `validate-patches`, prettier, etc.).
- Smoke-tested all three entrypoints end-to-end with a stubbed DB: dry-run, `--write` (produces a registry that passes the canonical validator + a valid ledger, then reverted), and the tracker.

Closes #115

Reviewed-on: #117
This commit is contained in:
Patrick Schratz 2026-07-14 15:03:24 +00:00 committed by Patrick Schratz
commit 1c297c01bf

View file

@ -59,3 +59,37 @@ For auto-proposable groups it prints a ready-to-review registry entry; still run
Novel source diffs and unknown signatures stay human-reviewed by design.
Add a new signature by appending a rule to `build_signatures()` in `local/failing-builds-classify.R`; the pure helpers are covered by `local/tests/test-failing-builds-classify.R`.
### Proposing entries (step 3: propose, do not apply)
`local/propose-patches.R` takes the auto-proposable candidates one step further: for each classified, safe fix affecting a package with no current entry, it emits a pre-filled `registry.json` entry and validates the candidate set against a temporary merged registry (the real registry is never touched unless you ask).
The human gate stays: it never merges.
```bash
# Default: print candidates + validation, take no action.
PGPASS=... Rscript local/propose-patches.R
# Append the candidates to registry.json + the proposals ledger (you commit + open the PR).
PGPASS=... Rscript local/propose-patches.R --write
# Or post/update a Forgejo tracking issue instead (needs FORGEJO_TOKEN).
PGPASS=... FORGEJO_TOKEN=... Rscript local/propose-patches.R --open-issue
```
The acceptance criteria before merging a proposal are: `validate-patches.R` passes (checked automatically), and an isolated trial build succeeds.
Run the trial build inside the failing platform's build-env image; it uploads/archives nothing and writes no metadata:
```bash
Rscript local/trial-build-patch.R <package>
```
`--write` and `--open-issue` also append to `local/patches/proposals-log.json`, a ledger of what was proposed.
### Feedback loop (step 4)
`local/proposal-tracking.R` reports the signature hit rate, proposed-vs-merged status (a proposal counts as merged once its package appears in the registry), and retirement candidates (registry entries whose package no longer appears in any current failure, so the upstream cause was likely fixed).
It is read-only.
```bash
PGPASS=... Rscript local/proposal-tracking.R --json metrics.json
```
The pure metric/ledger helpers live in `local/proposal-tracking-lib.R` and are covered by `local/tests/test-proposal-tracking-lib.R`.