The propagation gate: what bioc-registry publishes, and why
r-universe builds Bioconductor packages continuously. Only some of those builds become what bioc-registry serves as an installable repository and as the data behind the new site’s package pages. This page states the rule that decides, read from the code that runs it.
Bioconductor’s production propagation to bioconductor.org is a different gate, run in core infrastructure next to BBS and known here only from conversation. It is described on Propagation in the legacy system, together with the push hooks and the measured agreement between r-universe’s checks and nebbiolo1’s. The two are compared at the end of this page.
The gate, check by check
Everything below is in src/index.ts (evaluate) and src/repo.ts (passingFamilies, approveByDeps) at commit c614a8a, 2026-08-29. A package observed from r-universe propagates when all of the following hold:
- The build succeeded. r-universe’s
_statusissuccessand the package has at least one check job._failureis not read: a green status on a stale commit is caught by rule 5 in practice, because the stale version has already propagated. - Checks are judged on the gating R version, resolved at evaluation time from
bioconductor.org/config.yaml(r_ver_for_bioc_verfor the universe’s Bioconductor version) and matched against each job’s actual R version, not its label. Jobs on other R lines are ignored. - At least one platform family passes. Three families are gated:
linux(thesourcejob pluslinux-*x86_64),win(windows-*x86_64) andmac(macos-*arm64). A family passes when it has at least one gating job on the gating R and none of them isERROR,FAILorFAILURE.NOTEandWARNINGpass. Everything else gates nothing:bioc-checks(BiocCheck),wasm, linux arm64, mac x86_64. - The version is well-formed (digits separated by
.or-) and a source sha256 exists. - The version is a strict bump over the version already in the propagation index. A push without a bump never propagates. A seeded entry (below) counts as the current version for this comparison.
- Hard dependencies this registry publishes are satisfiable. Every
Depends,ImportsandLinkingToentry naming a package that is itself in the propagation index must be present at a version meeting its>=or>constraint. Dependencies not published here (CRAN, base R, and annotation, experiment and workflow packages until they have a build home) pass untouched. Candidates are approved in waves until a fixpoint, so a package lands as soon as what it waits on does. Blocked candidates are written toprop/{universe}/blocked/with the unmet requirement spelled out.
What propagates for an eligible package: the source tarball always; binaries only for the families that passed in rule 3, and only those built at the same version with status success. wasm binaries ride along with no gate of their own. The passing families are recorded on the index entry as archs, the BiocCheck verdict as bioccheck.
What this gate does not check, and the legacy production gate or the push hooks do:
- The
nebbiolo1sha match. The pilot gate has no provenance check of its own. - The push-hook rules: version parity,
xandyunchanged, the 5 MiB file limit, merge markers. It inherits them by trusting that the commit reachedgit.bioconductor.org.
Two ways into the index
About 300 packages build in BBS and fail in r-universe’s environment, usually because an example reaches a network service the runner cannot. Those are seeded from Bioconductor’s own release repositories so the registry matches what Bioconductor ships. Each index entry carries an origin: r-universe means it passed the gate above and archs says on which platforms; bioconductor means it was seeded, never faced the gate, and has empty archs and null bioccheck. Seeding only fills holes; a seeded package is replaced the ordinary way when the maintainer bumps the version and r-universe builds it.
What r-universe already computes
A common assumption — encouraged by “r-universe doesn’t care about R CMD check” — is that r-universe skips checking. It does not. It does not gate on the result, which is a different claim. It runs R CMD check on every platform in its matrix, and for Bioconductor packages it also runs BiocCheck.
All of it is exposed in the per-package API, e.g. https://bioc.r-universe.dev/api/packages/limma (verified live, 2026-08-04):
| Field | Contents |
|---|---|
_jobs[] |
one entry per matrix leg: {job, time, config, r, check, artifact}. config values include source, bioc-checks, linux-{devel,release}-{arm64,x86_64}, macos-*, windows-*, wasm-release. check is OK / NOTE / WARNING / ERROR / FAIL. |
_bioccheck |
BiocCheck summary as counts — {error, warning, note}. First-class API field; no download required. |
_commit |
{id, author, committer, message, time} — the source commit sha and metadata. |
_status |
overall deploy status for the package. |
_failure |
present when the newest commit failed to produce a source package. |
_buildurl |
the GitHub Actions run URL, in r-universe/{universe}. |
_binaries[] |
per-artifact provenance: {r, os, arch, distro, version, date, commit, fileid, status, check, buildurl}. |
Build logs and the full BiocCheck artifact are retrievable without authentication via /api/actions/logs/{job} and /api/actions/artifacts/{id} (both 302 to a signed blob URL).
The gate therefore does not need to run checks. It needs to read them.
There is no build notification to subscribe to
The natural design is event-driven: r-universe finishes a build, something fires, the gate runs. That is not available.
r-universe emits no webhook, no repository_dispatch, and no subscribable status endpoint. The feature request for build notifications (r-universe-org/help#101) has been open and uncommented since 2021.
The one push-style signal it does emit is a GitHub commit status on the upstream repo (context r-universe/{universe}/{package}/deploy), which an on: status workflow could catch — but only if the r-universe GitHub App is installed on that repo. Checked live against bioc/limma at the exact sha r-universe built: zero statuses, no contexts. That path is closed for Bioconductor packages today.
So: poll, and don’t poll fast
r-universe discovers new commits through its own hourly cron (41 * * * *, in r-universe-org/control-room), which then dispatches per-monorepo sync and build. Polling faster than hourly buys nothing — it re-reads the same state.
The cheapest useful endpoint (measured against bioc-release, 2026-08-04):
GET /api/files?type=src&fields=_created,_buildurl,_commit
# 1.1 MB · 0.27 s · 2,399 NDJSON rowsEach row carries package, version, commit id and time, build time, and the Actions run URL — everything a delta needs. Two comparisons for scale: the same universe via /api/packages with a field list is 6.4 MB / 0.9 s, and with no field list at all it is ~69 MB. Always pass fields=.
Polling /api/files this way is the sanctioned pattern — r-universe’s own rebuilds package does exactly this. If a sync is needed ahead of the cron, PATCH /api/sync is public, unauthenticated, and self-throttles to 60 seconds.
Two traps in the data
_status can be green on a stale commit. When the newest commit fails to produce a source package, the package keeps its previous successful _status and the failure appears separately in _failure. A gate reading only _status will propagate while the current head is broken. Read both.
Key the delta on _commit.id, not build time. 453 release packages were rebuilt in the 24 hours before measurement, but r-universe also rebuilds on dependency changes, R-version changes, a nightly retry cron (30 1 * * *), and a reverse-dependency cascade after any version change. Most of that churn carries no new source.
Relation to the legacy production gate
| Legacy production gate | This gate | |
|---|---|---|
| Feeds | bioconductor.org/packages/... (the legacy site and mirrors) |
bioc-registry (/repo/..., the propagation index, the new site’s data) |
| Runs in | Bioconductor core infrastructure, alongside BBS | a Cloudflare Worker |
| Described from | conversation with a core team member, 2026-08-04, not re-verified since | the code, pinned at commit c614a8a |
| Provenance check | source sha must match what nebbiolo1 built |
none: pushes to git.bioconductor.org are assumed hook-checked upstream |
| Check verdict | “the check is clean”, all-or-nothing | per platform family; at least one family must pass |
| Version rule | version must match nebbiolo1’s build |
strict bump over the last propagated version |
| Dependency rule | unknown | hard deps that this registry publishes must be satisfiable |
| BiocCheck | advisory | advisory, recorded on the index entry |
Whether WARNINGS counts as clean in the legacy gate is still undetermined; this gate answers it explicitly (rule 3: yes).
Sources
seandavi/bioc-registryatc614a8a—src/index.ts(evaluate,gatingRMinor),src/repo.ts(passingFamilies,approveByDeps,verGt)r-universe-org/frontend—routes/packages.js,routes/universe.js;r-universe-org/workflows—build.yml,deploy.yml- Live endpoints, 2026-08-04:
bioc-release.r-universe.dev/api/{packages,files},bioc.r-universe.dev/api/packages/limma - Related: Propagation in the legacy system, Builder Transition