Commit graph build-cran-binaries/local/patches
Author SHA1 Message Date
fe2289af56 fix(patches): force RcppParallel to use its bundled oneTBB (#146)
Some checks failed
ci/crow/cron/process-updates/16 Pipeline failed
ci/crow/cron/process-updates/11 Pipeline failed
ci/crow/cron/process-updates/18 Pipeline failed
ci/crow/cron/process-updates/17 Pipeline failed
ci/crow/cron/process-updates/12 Pipeline failed
ci/crow/cron/process-updates/6 Pipeline failed
ci/crow/cron/process-updates/5 Pipeline was successful
## Why

`RcppParallel` binaries built on ubuntu 22.04 still record `NEEDED libtbb.so.12` and cannot `dyn.load` without the distro TBB, although #145 landed the link-order patch and build-env-images dropped `TBB_INC`/`TBB_LIB` the same morning.

The link-order fix does not survive a build image that exports those variables, and the published images still do: `.crow/images.yaml` in build-env-images triggers on `cron`/`manual` only, so every image in the registry is still the 2026-07-21 build.
With `TBB_LIB` set, `configure.R` never reaches the bundled-oneTBB branch at all, `install.libs.R` fills `RcppParallel/lib` with absolute symlinks into the image's library dir, and the plain `-L` from #145 just points at the system TBB instead of the bundled one.
Nothing on the pipeline side can undo this: R reads `~/.Renviron` **after** the process environment, so neither a Crow `environment:` block nor the registry `env` field wins.

```
# the image, not the Containerfile
docker run --rm reg.devxy.io/rpkgs/build-env-ubuntu:jammy sh -lc 'grep TBB ~/.Renviron'
TBB_INC=/usr/include/oneapi
TBB_LIB=/usr/lib/x86_64-linux-gnu
```

Rather than couple correctness to image freshness for a third round-trip, the patch now forces the bundled TBB itself.

## Changes

- `local/patches/RcppParallel/bundled-tbb-link-order.patch` -> `local/patches/RcppParallel/force-bundled-tbb.patch`.
  Keeps the `-Wl,-L` -> `-L` link-order hunk (the bundled branch has the same problem when a distro libtbb is installed) and adds an opt-out, `BINCRAFT_ALLOW_SYSTEM_TBB=TRUE`, for the ambient `TBB_ROOT`/`TBB_LIB`/`TBB_INC`.
- All four read sites have to move together; patching only `configure.R` builds the bundled TBB and then **segfaults R on the post-install load test**:
  - `tools/config/configure.R`: branch selection.
  - `src/install.libs.R`: `R CMD INSTALL` runs it outside the `tbb` rule in `src/Makevars` that passes the configured values, so at install time it still sees the image environment.
  - `R/tbb.R` `tbbLibraryPath()`: run time. `.onLoad()` would `dyn.load` the system TBB into the global scope next to the bundled one that `RcppParallel.so` needs - two copies of the same symbols, hence the segfault.
  - `R/tbb.R` `tbbCxxFlags()` / `tbbLdFlags()`: otherwise dependents such as `rstan` get the system TBB handed back to them.
- `local/patches/registry.json`: new patch path and a `reason` describing both failure modes.

## Verification

Built through `bincraft:::prepare_patched_repo()` on the **current, unfixed** images (all three still export `TBB_INC`/`TBB_LIB`), with bincraft v5.0.1, i.e. what CI deploys:

| image | `NEEDED` | rpath | `RcppParallel/lib` | load, all system `libtbb*` moved away |
| --- | --- | --- | --- | --- |
| `build-env-ubuntu:jammy` | `libtbb.so` | `$ORIGIN/../lib` | real `libtbb.so.2`, 5.0 MB | OK, 12 threads |
| `build-env-redhat:9` | `libtbb.so` | `$ORIGIN/../lib` | real `libtbb.so.2`, 5.1 MB | OK, 12 threads |
| `build-env-alpine:3.24` | `libtbb.so` | `$ORIGIN/../lib` | real `libtbb.so.2`, 4.7 MB | OK, 12 threads |

For contrast, the same jammy build without this change:

```
NEEDED   libtbb.so.12
RUNPATH  /usr/lib/x86_64-linux-gnu:$ORIGIN/../lib
lib/     libtbb.so.12.5 -> /usr/lib/x86_64-linux-gnu/libtbb.so.12.5   (dangling off the image)
load     libtbb.so.12: cannot open shared object file: No such file or directory
```

With `TBB_LIB` still exported, `RcppParallel::tbbLibraryPath()` resolves to the package's own `lib`, and `CxxFlags()` emits the package's own `include` plus `-DTBB_INTERFACE_NEW` - not `/usr/include/oneapi`.

## Behaviour change

Published `RcppParallel` binaries carry their own oneTBB on every platform, whichever image version CI pulls. The patch content changed, so the patched-binary cache key changes with it and no stale entry is reused.

The build-env-images fix is still worth rolling out (those images also carry the broken el8 `uvr` and uvr 0.4.1), but RcppParallel no longer waits on it.

## Follow-up, not in this PR

The binaries already in B2 are the broken ones; they need a rebuild, and a Bunny `/purge` does not evict Perma-Cache.

Reviewed-on: #146
2026-07-31 09:48:41 +00:00
98371fb9c5 fix(patches): replace the RcppParallel system-TBB patch with a link-order fix (#145)
Some checks failed
ci/crow/cron/process-updates/9 Pipeline was successful
ci/crow/cron/process-updates/3 Pipeline was successful
ci/crow/cron/process-updates/4 Pipeline was successful
ci/crow/cron/process-updates/10 Pipeline was successful
ci/crow/cron/process-updates/13 Pipeline was successful
ci/crow/cron/process-updates/14 Pipeline failed
ci/crow/cron/process-updates/15 Pipeline failed
## Why

Every build logs:

```
! Patch for RcppParallel 6.2.0 did not apply cleanly; skipping patched build.
```

RcppParallel 6.x rewrote its build system. `src/Makevars.in` no longer contains the `USE_TBB=Linux` block that `system-tbb.patch` edited (it is now a short `@VAR@` template driven by `tools/config/configure.R`), so the patch can never apply again.

The workaround it implemented is obsolete too. 6.x bundles **oneTBB 2022** and builds it with cmake, which works on musl and with g++ 8-15, so the 5.x reason for linking a system TBB is gone. Linking one is now harmful: `install.libs.R` symlinks the system libraries into `RcppParallel/lib`, so the published binary depends on a TBB the consumer does not have — the same failure mode as `fs` and libuv.

What is still broken upstream is the **link order**. `configure.R` names the TBB directory with `-Wl,-L`, and gcc expands its own search dirs into `-L` options ahead of anything forwarded verbatim with `-Wl,`:

```
$ gcc -v -o t t.c -Wl,-L/usr/local/lib64 -ltbb
-L/usr/lib/gcc/x86_64-redhat-linux/8  -L/lib/../lib64  -L/usr/lib/../lib64  ...  -L/usr/local/lib64
```

So on any build host with a distro TBB installed, `-ltbb` resolves to that library and `RcppParallel.so` records *its* SONAME (`libtbb.so.12`, or `libtbb.so.2` for the classic Intel TBB on el8/el9) instead of the bundled `libtbb.so`.

## Changes

- Replace `RcppParallel/system-tbb.patch` with `RcppParallel/bundled-tbb-link-order.patch`, which changes the three `-Wl,-L` occurrences in `tools/config/configure.R` to plain `-L`.
- Scope the RcppParallel entry to `>=6.0.0` and widen `platforms` to `*`.
- Drop the `rstan` entry: its `-DTBB_INTERFACE_NEW` is already emitted by `RcppParallel::CxxFlags()` once the bundled oneTBB is used, and its `-I/usr/local/include` was an el8/el9 path applied on every platform.
- `.pre-commit-config.yaml`: exclude `local/patches/*.patch` from `trailing-whitespace`, `end-of-file-fixer` and `editorconfig-checker`. They rewrite blank context lines (` ` -> ``) in every diff in the registry; `git apply` happens to tolerate it today, but a patch with meaningful trailing whitespace would be silently corrupted. The excludes are per-hook so `validate patch registry` still runs.
- README: the RcppParallel example described the 5.x problem.

## Verification

Built in the published images:

| image | NEEDED | RPATH | loads with system libtbb removed |
| --- | --- | --- | --- |
| `build-env-alpine:3.24` | `libtbb.so` | `$ORIGIN/../lib` | yes |
| `build-env-redhat:8` | `libtbb.so`, `libtbbmalloc.so` | `$ORIGIN/../lib` | yes |
| `build-env-ubuntu:noble` | `libtbb.so` | `$ORIGIN/../lib` | yes |

Without the patch the same builds record `libtbb.so.12` (alpine, ubuntu) or `libtbb.so.2` (el8, the classic 2018 TBB) and fall back to the system library at load time.

rstan 2.32.7 compiles and loads against the patched RcppParallel on ubuntu noble with no makevars override and with the system libtbb moved away. On el8 it also compiles; loading it there is blocked by an unrelated image bug (see below).

## Behaviour change

Requires the paired build-env-images PR (drops `TBB_INC`/`TBB_LIB`) and an image rebuild — with those env vars set, configure still takes the system-TBB branch. After that, RcppParallel binaries ship their own oneTBB and are self-contained.

## Also found, not fixed here

- The `uvr lock failed ... GLIBC_2.29 not found` errors in the same log are the `-gnu` uvr artifact on el8's glibc 2.28; fixed in build-env-images.
- `build-env-redhat:8` R 4.5.3/4.6.0 cannot load `stats` (`libRlapack.so: undefined symbol: dgemmtr_`): the el8 R RPM symlinks `libRblas.so` to openblas 0.3.15, which predates that symbol. R 4.4.3 and el9 are fine. Belongs in the R RPM build.

Reviewed-on: #145
2026-07-31 06:41:46 +00:00
8d0c4b7ccd fix(patches): widen RcppParallel system-tbb scope to the whole redhat family (#139)
Some checks failed
ci/crow/cron/process-updates/4 Pipeline was successful
ci/crow/cron/process-updates/10 Pipeline was successful
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
ci/crow/cron/process-updates/11 Pipeline was successful
ci/crow/cron/process-updates/18 Pipeline was successful
ci/crow/cron/process-updates/17 Pipeline was successful
ci/crow/cron/process-updates/12 Pipeline was successful
ci/crow/cron/process-updates/6 Pipeline was successful
ci/crow/cron/process-updates/5 Pipeline was successful
ci/crow/cron/process-updates/1 Pipeline was successful
ci/crow/cron/process-updates/2 Pipeline was successful
ci/crow/cron/process-updates/7 Pipeline was successful
ci/crow/cron/process-updates/8 Pipeline was successful
ci/crow/cron/process-updates/9 Pipeline failed
ci/crow/cron/process-updates/3 Pipeline was successful
Extends #137. el8/el9 were excluded because they ship only classic Intel TBB. build-env-images now vendors oneTBB 2021 into `/usr/local` on el8/el9, so every platform has oneTBB 2021+. Widen the scope `redhat-10` → `redhat` (family token covers el8/el9/el10) and fix the reason text (no longer 'left on bundled'). **Requires the el8/el9 images rebuilt with the vendored oneTBB (build-env-images PR).** Verified: patched RcppParallel builds + links the vendored oneTBB on both el8 (g++ 8.5) and el9 (g++ 11.5).

Reviewed-on: #139
2026-07-28 06:57:08 +00:00
3cc323d99e fix(patches): widen RcppParallel system-tbb scope to all oneTBB-2021 platforms (#137)
Some checks failed
ci/crow/manual/weekly-audit-missing/15 Pipeline was successful
ci/crow/manual/weekly-audit-missing/17 Pipeline was successful
ci/crow/manual/weekly-audit-missing/18 Pipeline was successful
ci/crow/manual/weekly-audit-missing/16 Pipeline was successful
ci/crow/manual/weekly-audit-missing/14 Pipeline was successful
ci/crow/manual/weekly-audit-missing/12 Pipeline was successful
ci/crow/manual/weekly-audit-missing/2 Pipeline was successful
ci/crow/cron/process-updates/9 Pipeline was successful
ci/crow/cron/process-updates/3 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/1 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/3 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/4 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/7 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/9 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/12 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/13 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/14 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/15 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/16 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/2 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/10 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/8 Pipeline was successful
ci/crow/cron/process-updates/4 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/11 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/6 Pipeline was canceled
ci/crow/manual/weekly-rebuild-missing/17 Pipeline was canceled
ci/crow/manual/weekly-rebuild-missing/5 Pipeline was canceled
ci/crow/manual/weekly-rebuild-missing/18 Pipeline was canceled
ci/crow/cron/process-updates/10 Pipeline was successful
ci/crow/cron/process-updates/13 Pipeline was successful
## What

Widen the RcppParallel `system-tbb` registry patch scope from `[alpine, ubuntu-2604]` to `[alpine, ubuntu, redhat-10]`.

## Why

#135 fixed the TBB cluster on alpine + ubuntu-2604 only. A per-image audit showed the problem is broader: RcppParallel's bundled Intel TBB build fails on **every** build image with a modern toolchain (alpine/musl, ubuntu jammy/noble/resolute g++ 11/13/15, redhat-10 GCC 14), and only redhat-8/9 (older toolchains + pre-oneTBB system TBB) tolerate the bundled build.

The system-TBB fix applies wherever the image ships **oneTBB 2021+** (has the `tbb::detail::r1` namespace rstan needs):

| platform | system TBB | in scope? |
|---|---|---|
| alpine (all) | oneTBB 2022 |  |
| ubuntu 2204/2404/2604 | oneTBB 2021.5 / 2021.11 / 2022 |  (via `ubuntu` family token) |
| redhat-10 | oneTBB 2021.11 |  |
| redhat-8 | TBB 2018 (classic) |  pre-oneTBB → bundled |
| redhat-9 | TBB 2020.3 (classic) |  pre-oneTBB, bundled works → bundled |

## Requires

The matching build-env-images env (`TBB_INC`/`TBB_LIB`) extended to all ubuntu + el10: build-env-images #13. Ship together.

## Verified

RcppParallel + rstan build and link (no `symbol not found`) on alpine, redhat-10, ubuntu noble and ubuntu resolute; RcppParallel builds on ubuntu jammy.

Reviewed-on: #137
2026-07-20 20:59:47 +00:00
6df945d147 fix(patches): build RcppParallel against system oneTBB instead of disabling TBB (#135)
All checks were successful
ci/crow/manual/weekly-audit-missing/6 Pipeline was successful
ci/crow/manual/weekly-audit-missing/7 Pipeline was successful
ci/crow/manual/weekly-audit-missing/8 Pipeline was successful
ci/crow/manual/weekly-audit-missing/9 Pipeline was successful
ci/crow/manual/weekly-audit-missing/11 Pipeline was successful
ci/crow/manual/weekly-audit-missing/12 Pipeline was successful
ci/crow/manual/weekly-audit-missing/13 Pipeline was successful
ci/crow/manual/weekly-audit-missing/14 Pipeline was successful
ci/crow/manual/weekly-audit-missing/15 Pipeline was successful
ci/crow/manual/weekly-audit-missing/16 Pipeline was successful
ci/crow/manual/weekly-audit-missing/2 Pipeline was successful
ci/crow/manual/weekly-audit-missing/10 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/4 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/5 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/3 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/7 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/6 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/11 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/13 Pipeline was successful
ci/crow/cron/process-updates/12 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/8 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/12 Pipeline was successful
ci/crow/cron/process-updates/1 Pipeline was successful
ci/crow/cron/process-updates/18 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/10 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/14 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/2 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/1 Pipeline was successful
ci/crow/cron/process-updates/17 Pipeline was successful
ci/crow/manual/weekly-rebuild-missing/9 Pipeline was successful
## What

Replace RcppParallel's `disable-tbb` registry patch with `system-tbb`: build RcppParallel against the system oneTBB instead of stripping TBB entirely.

## Why

`disable-tbb` skipped the bundled Intel TBB build by forcing the TinyThread backend, which also removed RcppParallel's TBB linkage.
That broke every dependent that links TBB through `RcppParallelLibs()` -- `rstan` and the whole Stan cluster -- with `symbol not found: tbb::detail::r1::observe`.
This is the root cause behind the large "blocked on RcppParallel" / rstan clusters (issues #115, #120): the packages were not individually broken, they were all waiting on one TBB-linkage regression.

## Change

`system-tbb.patch` leaves `USE_TBB` unset (so the bundled build is still skipped on musl and g++ 15) but keeps the TBB backend and links the system oneTBB now shipped in the build-env images:

```
PKG_CXXFLAGS += -DRCPP_PARALLEL_USE_TBB=1 -DTBB_SUPPRESS_DEPRECATED_MESSAGES=1 -DTBB_INTERFACE_NEW
PKG_LIBS += -ltbb -ltbbmalloc
```

Scoped to `alpine` + `ubuntu-2604`, the only platforms where the bundled build fails; redhat and older ubuntus keep the bundled TBB.

## Requires

The companion image change that exports `TBB_INC`/`TBB_LIB` so `RcppParallelLibs()` hands the system-TBB flags to dependents: build-env-images PR #12. Both must ship together.

## Verified

In `build-env-alpine:3.24` with the new images:
- RcppParallel builds against oneTBB 2022, no ABI errors, patch applies cleanly to the target clone.
- `RcppParallelLibs()` returns `-L/usr/lib -Wl,-rpath,/usr/lib -ltbb -ltbbmalloc`.
- rstan links (`-ltbb -ltbbmalloc`) and loads with no missing symbol -- `* DONE (rstan)`, trial build exit 0.

Reviewed-on: #135
2026-07-20 09:56:21 +00:00
1bca6004fd chore(local): reuse FORGEJO_TOKEN for the auto-patch push (#125)
Follow-up to #124 (merged): the auto-patch pipeline required a separate write-scoped `REPO_RW_TOKEN` to push the branch. Reuse the existing `FORGEJO_TOKEN` instead.

- `propose-patches.R --open-pr` now pushes `auto/registry-patch-proposals` over HTTPS with `FORGEJO_TOKEN` (the same token used for the PR API); the read-only `origin` clone URL can't push, so it builds an authenticated URL explicitly.
- Drop `REPO_RW_TOKEN` from `.crow/auto-apply-patches.yaml` and the docs.

No new secret needed: the pipeline's secrets are now `PGPASS`, `REPO_RO_TOKEN`, and `FORGEJO_TOKEN` (all existing). `FORGEJO_TOKEN` must have repository write scope for the push to succeed.

105 tests pass; all pre-commit hooks pass.

Reviewed-on: #125
2026-07-15 14:50:19 +00:00
4bca17e4ac feat(local): auto-apply registry patches with a build-env trial-build gate (#124)
All checks were successful
ci/crow/cron/process-updates/14 Pipeline was successful
ci/crow/cron/process-updates/13 Pipeline was successful
ci/crow/cron/process-updates/16 Pipeline was successful
ci/crow/cron/process-updates/18 Pipeline was successful
ci/crow/cron/process-updates/15 Pipeline was successful
ci/crow/cron/process-updates/12 Pipeline was successful
ci/crow/cron/process-updates/17 Pipeline was successful
ci/crow/cron/process-updates/6 Pipeline was successful
ci/crow/cron/process-updates/5 Pipeline was successful
ci/crow/cron/process-updates/11 Pipeline was successful
ci/crow/cron/process-updates/1 Pipeline was successful
ci/crow/cron/process-updates/2 Pipeline was successful
ci/crow/cron/process-updates/7 Pipeline was successful
ci/crow/cron/process-updates/8 Pipeline was successful
Closes the classifier loop (issue #115, step 3): turns the auto-proposable candidates into an actual PR, gated by a real trial build **in our own build-env images**.

Chosen model (from the design discussion): **autonomous PR**, **PR-first with a CI trial-build gate**, **bounded top-N batch** per run.

## Creating the patch PR

- `propose-patches.R` gains:
  - `--limit N` -- act on the top-N candidates by failure volume; the rest defer to the next run (logged).
  - `--open-pr` -- write the entries onto the reused `auto/registry-patch-proposals` branch, push (with `REPO_RW_TOKEN`), and open/update **one** PR via the Forgejo API (so re-runs update the same PR instead of piling up).
- `.crow/auto-apply-patches.yaml` -- a single job that runs `--open-pr --limit` on a cron/manual trigger. Needs `FORGEJO_TOKEN` + a write-scoped `REPO_RW_TOKEN`.

Novel source diffs and unknown signatures are still never proposed; nothing merges.

## The merge gate (our build-env images)

- `.crow/trial-build-registry.yaml` -- matrixed over the real `OS/IMG` build-env matrix (alpine:3.24, redhat:8/9/10, ubuntu:jammy/noble/**resolute** for ubuntu-2604). Each platform runs `local/trial-build-registry.R`, which diffs the branch registry against `main` and trial-builds **only the entries the branch adds** that apply to that platform, inside `reg.devxy.io/rpkgs/build-env-*`. Green only if every new entry builds; a platform with no new entries is a fast no-op. Nothing is uploaded/archived/recorded.
- The base-registry read **fails loud** if it can't read `registry.json` at `main`, rather than silently treating the base as empty and trial-building the whole registry.

## Notes / follow-up

- The repo uses **no `pull_request` triggers**, so the gate runs manually or on a cron against the branch (`--var patch_branch=...`). Wiring it to fire automatically on the PR needs `event: pull_request` enabled on the Forgejo webhook -- a one-line addition once that's on.
- Two new crons to register in the crow UI: `auto-apply-patches` and `trial-build-registry`. New secret needed: `REPO_RW_TOKEN` (write scope) for the push.

## Verification

- New pure helpers `entry_applies_to_os()` / `new_registry_packages()` covered by tests (platform codename/family/wildcard matching; added-vs-unchanged entry detection; per-platform filtering).
- `--limit` smoke (stubbed DB): top-2 by volume proposed, 3 deferred, candidate registry validates.
- Full suite: 105 tests pass; all pre-commit hooks pass (air, prettier, markdownlint, yamllint, validate-patches).

Reviewed-on: #124
2026-07-15 08:28:15 +00:00
21a2fe9e6c feat(local): report unclassified and dependency-blocked failures for discovery (#122)
All checks were successful
ci/crow/manual/weekly-patch-proposals Pipeline was successful
ci/crow/cron/process-updates/4 Pipeline was successful
ci/crow/cron/process-updates/10 Pipeline was successful
## Why

Issue #120 (the auto-proposed-patches issue) only lists **auto-proposable** fixes -- currently just the TBB signatures. So a reasonable read of it was "TBB is our only build failure", when in fact three whole categories are simply not shown there:

- **Unclassified failures** -- anything that doesn't match a seeded signature is routed to human triage and never appears (we've only seeded TBB and libuv signatures).
- **Dependency-blocked failures** -- the ~800 RcppParallel dependents (post #121) are still failing; they only show as a log line.
- Human-only signatures (libuv).

These blind spots are exactly where the *next* signatures should come from, so they deserve the same visibility as the proposals.

## What

Extend the feedback-loop tracker to surface the classifier's blind spots:

- **`unclassified_summary()`** -- groups every unknown-signature failure by normalised fingerprint, ranked by build count, capped with an explicit `dropped_groups` count (no silent truncation), each with example packages + platforms. These are the candidates for new `build_signatures()` rules.
- **`blocked_summary()`** -- lists each dependency (e.g. RcppParallel) and how many dependents wait on it.
- `proposal-tracking.R` prints both sections, and a new **`--open-issue`** mode posts/updates a *"Unclassified build failures (needs signatures) (#115)"* Forgejo issue.
- The weekly crow pipeline now runs the tracker with `--open-issue`, so it maintains a second tracking issue alongside the proposals one. Read-only on the DB; the only writes are the two issues.

## Verification

- New tests cover `unclassified_summary` (ranking + both caps) and `blocked_summary`.
- Tracker smoke with a stubbed DB (proposable + blocked + unclassified mix) prints the hit rate, `Blocked on a dependency: RcppParallel: 2 dependent(s)`, and `Unclassified failures ... [2 builds | 2 pkgs] ld: undefined reference ...`.
- Full suite: 95 tests pass; all pre-commit hooks pass (air, prettier, markdownlint, yamllint, validate-patches).

Reviewed-on: #122
2026-07-15 07:46:29 +00:00
af481f5129 ci(crow): add weekly patch-proposal + feedback-loop pipeline (#118)
All checks were successful
ci/crow/cron/process-updates/3 Pipeline was successful
ci/crow/cron/process-updates/4 Pipeline was successful
ci/crow/cron/process-updates/10 Pipeline was successful
Adds the scheduled pipeline for the failure-triage proposer/tracker landed in #117 (issue #115, steps 3 + 4).

## `.crow/weekly-patch-proposals.yaml`

A single (non-matrix) job -- the classifier groups over all of `single_builds`, so unlike the per-platform `weekly-audit-missing` it runs once globally. Weekly it:

1. `propose-patches.R --open-issue` -- posts/updates a Forgejo tracking issue with the auto-proposable registry entries (human reviews, trial-builds, opens the PR; nothing merges).
2. `proposal-tracking.R` -- logs the feedback-loop metrics (signature hit rate, proposed-vs-merged, retirement candidates).

Low blast radius: clones read-only (`REPO_RO_TOKEN`); the only write is the tracking issue via `FORGEJO_TOKEN`. It builds nothing (no B2 keys, no bincraft).

Triggers: `cron: weekly-patch-proposals` (register the cron in the crow UI) or manual with `task=weekly-patch-proposals`, matching the `archive-missed-packages` convention. Documented under "Scheduled run" in `local/patches/README.md`.

`yamllint` and the other pre-commit hooks pass.

Reviewed-on: #118
2026-07-14 15:17:16 +00:00
1c297c01bf 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
2026-07-14 15:03:24 +00:00
f11ba7172f feat(local): classify failing binary builds and pre-fill registry suggestions (#116)
All checks were successful
ci/crow/cron/process-updates/9 Pipeline was successful
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
2026-07-14 14:45:25 +00:00
04059f20e3 fix(patches): add rstan entry to skip removed tbb_stddef.h (#114)
All checks were successful
ci/crow/cron/process-updates/7 Pipeline was successful
ci/crow/cron/process-updates/8 Pipeline was successful
## Motivation

`rstan` fails to compile when it is pulled in as a transitive dependency (e.g. of `ctsem`), aborting the dependency install:

```
/mnt/cache/R-pkgs/StanHeaders/include/stan/math/prim/core/init_threadpool_tbb.hpp:9:10:
  fatal error: tbb/tbb_stddef.h: No such file or directory
    9 | #include <tbb/tbb_stddef.h>
```

StanHeaders' `init_threadpool_tbb.hpp` unconditionally includes the legacy `<tbb/tbb_stddef.h>` to detect the TBB version, but that header was removed in oneTBB 2021+, which is the TBB that StanHeaders now bundles.

## Fix

Add an `rstan` entry to `local/patches/registry.json` that pre-defines `TBB_INTERFACE_NEW` via `CPPFLAGS`.
The header's own guard skips the removed include when that macro is already defined and selects the modern `tbb/global_control.h` + `tbb/task_arena.h` path that the bundled oneTBB actually provides:

```cpp
#ifndef TBB_INTERFACE_NEW
#include <tbb/tbb_stddef.h>          // removed in oneTBB 2021+
#if TBB_VERSION_MAJOR >= 2020
#define TBB_INTERFACE_NEW
#endif
#endif
#ifdef TBB_INTERFACE_NEW
#include <tbb/global_control.h>      // taken when we pre-define the macro
#include <tbb/task_arena.h>
```

This is a makevars-tier override (no source diff), applied through bincraft's `R_MAKEVARS_USER`, so `CPPFLAGS` stays additive and does not clobber rstan's own `PKG_CPPFLAGS`.
`-I/usr/local/include` is kept because the override replaces the default `CPPFLAGS`.

## Files changed

- `local/patches/registry.json`: add the `rstan` entry (`versions: "*"`, `platforms: ["*"]`, `makevars.CPPFLAGS = "-DTBB_INTERFACE_NEW -I/usr/local/include"`).

## Behaviour change

When bincraft resolves `rstan` (direct or transitive) it builds a patched binary with `TBB_INTERFACE_NEW` defined, so `rstan` (and dependents such as `ctsem`) compile against the bundled oneTBB.
The scope is `["*"]` because the failure is bound to StanHeaders' bundled TBB version, not the OS or toolchain.

Reviewed-on: #114
2026-07-14 13:52:17 +00:00
36d3bc8604 fix(patches): force fs to build vendored static libuv (#111)
Some checks failed
ci/crow/cron/process-updates/11 Pipeline was successful
ci/crow/cron/process-updates/17 Pipeline was successful
ci/crow/cron/process-updates/18 Pipeline was successful
ci/crow/cron/process-updates/6 Pipeline was successful
ci/crow/cron/process-updates/12 Pipeline was successful
ci/crow/cron/process-updates/5 Pipeline was successful
ci/crow/manual/build-all-versions-install-deps/2 Pipeline was successful
ci/crow/cron/process-updates/7 Pipeline was successful
ci/crow/cron/process-updates/1 Pipeline was successful
ci/crow/cron/process-updates/2 Pipeline was successful
ci/crow/cron/process-updates/9 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/build-all-versions/8 Pipeline was canceled
ci/crow/manual/build-all-versions/7 Pipeline was canceled
ci/crow/manual/build-all-versions/6 Pipeline was canceled
ci/crow/manual/build-all-versions/5 Pipeline was canceled
ci/crow/cron/process-updates/10 Pipeline was canceled
## Problem

The `fs` 2.1.0 binary links **system libuv** (`readelf -d fs.so` shows `NEEDED libuv.so.1`).
fs's `configure` prefers system libuv whenever `pkg-config` resolves it, and our build images ship `libuv-devel` (installed as a pak build-time system requirement), so the resulting binary is dynamically linked against `libuv.so.1`.

That binary fails to load on any consumer machine without runtime libuv:

```text
unable to load shared object '.../fs/libs/fs.so':
  libuv.so.1: cannot open shared object file: No such file or directory
```

`install.packages()`/renv do **not** install `SystemRequirements` (only `pak` does, and only inside the build container), so most consumers hit this.
Older fs 1.6.x always vendored libuv, so only the 2.x binaries regressed.
Reproduced in a clean `reg.devxy.io/r/r-alma:4.5-9`.

## Fix

Add `local/patches/fs/force-vendored-libuv.patch`, registered for all platforms.
It short-circuits `configure` to `cp -f src/Makevars.vendor src/Makevars; exit 0` before the pkg-config detection, forcing the bundled static libuv build (`tools/libuv-v1.52.0.tar.gz`, built via cmake).
An env/pkg-config override (`PKG_CONFIG_LIBDIR`) was tried first but the rebuilt binary still linked `libuv.so.1` (the registry `env` tier does not reach fs's configure step), so a source patch is used instead.

## Verification

Built end-to-end inside the real `build-env-redhat:9` image (system libuv present):

- patch fires (`Building static libuv (bincraft: forced vendored)`),
- cmake compiles the vendored libuv,
- resulting `fs.so` has **no `libuv.so.1`** in `NEEDED` (only libR, libstdc++, libm, libgcc_s, libc).

`Rscript local/validate-patches.R` passes (2 entries).
cmake confirmed present in the build-env images.

## Follow-up (not in this PR)

- Rebuild `fs 2.1.0` on every affected platform (rhel8/9/10, ubuntu jammy/noble, alpine 3.22/3.23; amd64 + arm64) and purge the CDN binary paths.
- CDN delivery gap: `purge_cdn_cache.sh` only purges `PACKAGES*`, never package binaries, so rebuilt binaries stay masked until their `.tar.gz` path is purged.

Reviewed-on: #111
2026-07-07 18:12:36 +00:00
e59590e2d6 fix: RcppParallel disable-TBB source patch (env var was a no-op) (#106)
All checks were successful
ci/crow/cron/process-updates/2 Pipeline was successful
## Summary

Fixes the RcppParallel patch, which was a **no-op** and left the build hanging.

The previous registry entry set `env: { RCPP_PARALLEL_USE_TBB: "0" }`. But `RCPP_PARALLEL_USE_TBB` is a **compile-time `-D` flag** in RcppParallel's Makevars — it is never read from the environment. So the override did nothing: `USE_TBB=Linux` (hardcoded from `uname`) still triggered the **bundled Intel TBB build**, which hangs/fails on musl (Alpine) and newer toolchains (g++ 15 on ubuntu-2604). The `Applying patch …` log only meant the env was set, not that it had any effect.

## Fix

Replace the env entry with a **source patch** (`local/patches/RcppParallel/disable-tbb.patch`) on `src/Makevars.in` that, on Linux:

- leaves `USE_TBB` unset → the whole bundled-TBB build/link path is skipped (no hang), and
- forces `PKG_CXXFLAGS += -DRCPP_PARALLEL_USE_TBB=0` → the sources compile the **TinyThread** backend (needed because `RcppParallel.h` otherwise auto-defaults TBB on for glibc Linux).

## Verification

In a Linux container, applying the patch and running `R CMD INSTALL RcppParallel`:

```
bundled_TBB_build=0      # bundled TBB build never runs
* DONE (RcppParallel)    # installs via TinyThread
```

## Note

bincraft's `apply_source_patch` shells out to `patch`. If a build-env image lacks the `patch` tool (common on Alpine), the patch will report "did not apply cleanly" and fall back to an unpatched (hanging) build. If that happens, the follow-up is to switch bincraft's patch application to `git apply` (git is always present) — happy to do that if needed.

Reviewed-on: #106
2026-06-30 13:27:01 +00:00
55a18fd87d feat: patch registry + wiring for per-package patching (#103)
Some checks failed
ci/crow/cron/process-updates/15 Pipeline failed
ci/crow/cron/process-updates/14 Pipeline failed
ci/crow/cron/process-updates/16 Pipeline failed
ci/crow/cron/process-updates/18 Pipeline failed
ci/crow/cron/process-updates/11 Pipeline failed
ci/crow/cron/process-updates/12 Pipeline failed
ci/crow/cron/process-updates/17 Pipeline failed
ci/crow/cron/process-updates/6 Pipeline failed
ci/crow/cron/process-updates/5 Pipeline failed
## 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
2026-06-30 08:50:19 +00:00