Skip to content

ci(release): P0-1 — four apr binaries on every tag ({cuda,cpu} × {x86_64,aarch64}), verify-apr-assets requires all four, check_release_assets.sh + C13 + a post-publish dogfood row (67-A1, #3082, PMAT-1098) - #3092

Open
noahgift wants to merge 34 commits into
mainfrom
PMAT-1098-67-A1-four-apr-assets

Conversation

@noahgift

@noahgift noahgift commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Row 67-A1 (P0-1, #3082) — spec docs/specifications/06x-release-schedule.md §2 A · epic #3078 · ticket PMAT-1098 · stacked on #3088 (67-C2)

v0.66.0 was cut with zero apr binaries because no gate asserted the asset set. Live today (bash scripts/check_release_assets.sh v0.66.0): the two CUDA tarballs read ok, the two -cpu tarballs and their checksums are MISSING — exit 1. That is the P0 made visible by a command.

  • build-apr-cpu in binary-release.yml: same two disposable runners as the CUDA lane (gx10-eph aarch64, yoga-eph x86_64; labels …, ephemeral, docker), preflight first, DEFAULT features (crates/apr-cli/Cargo.toml: default carries no cuda; cuda is opt-in), asset apr-<tag>-<target>-cpu.tar.gz + .sha256, glibc floor recorded; the ci(release): CUDA apr binaries for x86_64 and aarch64 on every release — a hard requirement, verified in the bytes and on both GPU hosts (PMAT-1096, #2869) #3072 discriminator inverted — grep -ac libcuda.so apr must be 0 or the job fails.
  • verify-cuda-assetsverify-apr-assets: all four apr tarballs + four .sha256 + the eight pv assets, by calling scripts/check_release_assets.sh <tag> (0 complete · 1 missing-by-name · 2 ENV; gh when present else REST with the job token; --assets-from/RELEASE_ASSETS_FIXTURE offline seam; 11-row --selftest incl. the registered mutation: remove the aarch64 cpu asset → RED). scripts/tests/check_release_assets_test.sh 13 rows.
  • smoke-cpu: the x86_64 CPU asset on a GPU-less clean-room runner and each arch's CPU asset on its GPU host — checksum, apr --version carries the tag, libcuda.so count 0, glibc floor from objdump -T.
  • scripts/release_criteria.sh C13 reads the tag from the root manifest and runs the checker; three new self-test rows (GREEN/RED/ENV). scripts/dogfood.sh gains release-assets: PASS/FAIL post-publish, SKIP-with-reason pre-publish (assets exist only after the tag; PREPUBLISH_DEFERRABLE is outside this row's scope, growing it is a follow-up).

Orchestrator re-runs: --selftest 11/11, release_criteria.sh --self-test 10/10, YAML parses, runner-label / path-filter / guards-wired PASS, bashrs 0 gating, live v0.66.0 check exit 1 naming the two -cpu assets, make gate 41 checks 0 failed.

Note: this branch predates #3086 (bullseye container builds); build-apr-cpu mirrors the lane's shape at branch time. The merge reconciles the two lanes; the smoke's floor assertion arms only when APR_ASSET_GLIBC_FLOOR is set, which #3086's containerised lane should export.

🤖 Generated with Claude Code

Closes #3073.

noahgift and others added 10 commits September 10, 2026 12:27
Pmat-Ticket: PMAT-1098

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjhtNUSensCYpQb3mCYLod
…RED at row 0

Ten rows, both polarities: a scratch PATH with every default tool is GREEN and
the same PATH minus jq is RED naming MISSING jq; the JSON side-channel parses and
records the missing tool; a usage error is exit 2 and a missing tool is exit 1;
and the wiring itself is checked — every self-hosted job in fleet-toolset.yml,
binary-release.yml and cuda-nightly.yml must run the preflight immediately after
checkout, mutation-verified by deleting the step from a workflow copy.

Run 34448908554 built a 21 MB CUDA asset on gx10 and died on the upload with
`gh: command not found`. Nothing checked the toolset a workflow assumes.

Pmat-Ticket: PMAT-1098

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…build

scripts/ci_self_hosted_preflight.sh — defaults jq curl git python3 tar sha256sum
rustup cargo; --cuda adds nvidia-smi AND asserts a device is actually visible;
--need takes the extras a job knows it wants. `gh` is deliberately NOT a default:
the CUDA lane moved to REST with curl+python3 because fleet boxes have no gh
(#3074), and defaulting it would re-assert the assumption that failed.

Exit 1 is a finding about the BOX, exit 2 a finding about the STEP that called
this — merged codes send the wrong person. Identity (runner/labels/arch/glibc,
driver under --cuda) is printed and, when PREFLIGHT_OUT or RUNNER_TEMP is set,
written as JSON: that file is the fleet probe output.

13-row hermetic case table, both polarities, every tool a stub in a scratch PATH
so the verdict does not depend on this box. Two rows were RED for the wrong
reason first and are recorded as comments: the script called dirname/basename
(now parameter expansion) and the nvidia-smi stub catted a fixture, so --cuda
reported zero devices because `cat` was not on the scratch PATH.

Pmat-Ticket: PMAT-1098

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…spends an hour

The preflight is now the step immediately after checkout in all seven
self-hosted jobs: binary-release.yml build-apr-cuda (--cuda --need docker
objdump), smoke-cuda (same; both GPU boxes carry `docker` as a runner LABEL, so
a pool that stopped matching its own labels is worth one second), and the two
clean-room jobs verify-cuda-assets and summary, which had no checkout at all;
plus the gx10 lane in cuda-nightly.yml. Not before `decide` and not before the
checkout there: on a yield night the box has no working tree, and a step that
bashes a repo path would exit 127 and turn a deliberately-yielded night RED.

fleet-toolset.yml probes all three pools daily at 04:47 UTC and uploads
toolset-<label>/preflight.json, 90-day retention, plus one table in the run
summary. ARTIFACT + SUMMARY, NOT A PR: a PR per day against evidence/ is ~365
machine-written PRs a year through a queue that runs at ~1 PR/hour, so the cost
of the record would exceed the record. A zero-row roll-up exits 1 — no
preflight.json downloaded is a broken probe, not a clean fleet.

The falsifier built its universe from the wrong side and said so: `runs-on`
alone saw 3 jobs, missing build-apr-cuda and smoke-cuda, whose selector is
`${{ fromJSON(matrix.labels) }}` — the two GPU jobs this row exists for. It now
reads the strategy block too, and sees 7.

Pmat-Ticket: PMAT-1098

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Pmat-Ticket: PMAT-1098

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…0 is missing four

Pmat-Ticket: PMAT-1098

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…d smoke it

Pmat-Ticket: PMAT-1098

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…h polarities in the table

Pmat-Ticket: PMAT-1098

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… SEC011)

Pmat-Ticket: PMAT-1098

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ur apr binaries

Pmat-Ticket: PMAT-1098

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@noahgift noahgift added this to the 0.67.0 milestone Sep 10, 2026
@noahgift
noahgift enabled auto-merge September 10, 2026 12:58
@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown

§13.11 rung 1 — quorum shadow verdict

S13-SHADOW pr=3092 head=3604e2fe5833cd087b4df8cf160c200466d3d282 verdict=REFUSE class=Q1 arm_rc=1

Shadow mode: this records a verdict and merges nothing. A refusal
to arm is not a block (§13 adds zero rows to §7) — the pull request is
exactly as green as it was.

noahgift and others added 2 commits September 10, 2026 16:27
…apr-assets

# Conflicts:
#	.github/workflows/binary-release.yml
…oint in the workspace — pre-create it as the runner (rebuild 34488955316 died at mkdir after a green 1m54s build)

Pmat-Ticket: PMAT-1096

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
noahgift and others added 14 commits September 10, 2026 17:48
…rst 'Host layout' step exports CI_TARGETS_ROOT / CI_CARGO_ROOT / SCCACHE_HOST_DIR / CI_REGISTRY / IMAGE with the intel clean-room defaults, 33 hardcoded sites read them; byte-identical on intel (#3100)

Also: check_runner_labels.sh accepts the `build` pool label. No job changes where it runs yet (BP-3).

Pmat-Ticket: PMAT-1098

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… box carrying `build` (intel clean-room or yoga-eph); gate on any pool box (operator 2026-09-10: requeue to available capacity; #3100)

Pmat-Ticket: PMAT-1096

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… box carrying `build` (intel clean-room or yoga-eph); gate on any pool box (operator 2026-09-10: requeue to available capacity; #3100)

Pmat-Ticket: PMAT-1096

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…red review job held yoga's only pool slot for 15 min while required jobs queued (#3100)

Pmat-Ticket: PMAT-1096

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… so gx10-build's idle cores may take it (#3100)

Pmat-Ticket: PMAT-1098

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…too (runs_on input, paiml/.github#67; operator: move now)

Pmat-Ticket: PMAT-1096

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tpoint' into PMAT-1098-build-pool-any-of-three

# Conflicts:
#	.github/workflows/ci.yml
…pool; guard-tree/guard-cargo/mutants/vendored-schemas/pr-review-* and the reusable ci jobs back on clean-room until measured (ci / security refuses to run without pmat, absent on yoga; #3100)

Pmat-Ticket: PMAT-1098

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ls (#3100); guard_tree_test.sh rejected BP-1's guard-cargo env move

Pmat-Ticket: PMAT-1098

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e the cuda lane, and smoke-cpu GATES their glibc floor at GLIBC_2.31 — the floor variable it read was defined nowhere

check_workflow_env_defined (guard-cargo) failed #3092: smoke-cpu interpolated APR_ASSET_GLIBC_FLOOR, which no job defines, so the floor check recorded a number and gated nothing. It could not honestly be set while build-apr-cpu built NATIVELY: the asset's floor was whatever the build box ran (0.66.0's native x86_64 asset needed GLIBC_2.43 and did not start on 22.04). build-apr-cpu now uses the cuda lane's bullseye sibling-container step verbatim minus --features cuda and the device lines (own cache dir, pre-created target mountpoint), and smoke-cpu declares the floor, so an asset importing a newer GLIBC_* symbol fails the release. Guards: check_workflow_env_defined OK (15 workflows), guard_tree_test 23/0, check_lockfile_current PASS.

Pmat-Ticket: PMAT-1098

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
noahgift and others added 4 commits September 11, 2026 01:32
…r left in binary-release.yml (#3073)

binary-release.yml fires on `release: published`, and its pv job ran on ubuntu-latest (aarch64 via
cross). Under the operator rule of 2026-09-10 ("WE DO NOT USE HOSTED GITHUB RUNNERS") a tag cut that
way is not done. Each pv target now builds natively on the box of its architecture, x86_64 on yoga
and aarch64 on gx10, inside rust:1.93.0-bullseye: the sibling-container pattern build-apr-cpu already
uses. gnu assets carry a GLIBC_2.31 floor (cross's aarch64 image was 2.31 too; x86_64 came from
ubuntu-latest's 2.39, so its floor drops), and musl assets are static, with musl-gcc named as their
C compiler.

New checks that can fail: the asset runs on its builder, a musl asset has no NEEDED entry, and a gnu
asset imports nothing above GLIBC_2.31. Upload is the apr lanes' REST path, since these boxes carry no
gh. Asset names are unchanged, so verify-apr-assets' eight pv names still hold. CROSS_VERSION, used
only by the old pv job, is gone, and the header describes the new build.

Pmat-Ticket: PMAT-1098
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…'s Release file has expired

Rehearsal run 34543016302 (v0.67.0-rc.0): both gnu pv lanes built, both musl lanes died at
`apt-get update` inside rust:1.93.0-bullseye, exit 100: "Release file for .../bullseye-security/
InRelease is expired". Only the musl lanes run apt (for musl-tools), which is why the gnu lanes on
the same two boxes were green. The matrix now names each target's image: gnu stays on
rust:1.93.0-bullseye (glibc 2.31 is its floor), musl moves to rust:1.93.0-bookworm, where apt still
installs musl-tools. A static musl binary carries no builder glibc, so the floor promise is unchanged.

Pmat-Ticket: PMAT-1098
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… guard that refuses one (#3073)

Operator rule (2026-09-10, verbatim): "WE DO NOT USE HOSTED GITHUB RUNNERS. WE USE GX10 AND
LAMBDA-LABS OR YOGA". Three workflows still ran on GitHub-hosted images:

- nightly.yml: ubuntu-latest, ubuntu-24.04-arm, macos-latest x2, windows-latest. The Linux builds now
  run on the fleet (x86_64 on yoga, aarch64 natively on gx10) inside rust:1.93.0-bullseye, so the
  nightly's glibc floor drops from 2.39 to 2.31. check-activity and the release job run on the
  clean-room pool, and the release job deletes the old nightly over REST, because the fleet has no gh.
  No macOS or Windows self-hosted runner is registered, so those targets leave the nightly until one is.
- book.yml (build + Pages deploy) and book-contracts.yml (six jobs) move to the clean-room pool. Both are
  path-filtered (0 and 1 runs in the last 24 h), so the pool barely notices.

scripts/check_no_hosted_runners.sh refuses a hosted image token on any non-comment workflow line, not
only on `runs-on:` lines: nightly.yml hid `ubuntu-latest` in a matrix value, which a runs-on-only scan
never reads (and check_runner_labels.sh exempts hosted jobs by design). A shrink-only `file:job`
baseline can excuse a job while its migration is in flight, and a stale entry is RED. The case table
has 12 rows: both polarities, a stale-baseline row and an ENV row. Mutation: re-inserting
`runs-on: ubuntu-latest` into nightly.yml turns the real-tree run RED (rc=1); restoring it turns it
green. The guard is wired through guard_tree.sh's dispatch (check_guards_are_wired.sh PASS), and bashrs
reports 0 errors.

Pmat-Ticket: PMAT-1098
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…n the fleet

With the pv lane on gx10 + yoga (this branch), no workflow names a GitHub-hosted image, so the
baseline check_no_hosted_runners.sh reads carries no entry. From here a hosted image anywhere is RED.

Pmat-Ticket: PMAT-1098
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@noahgift

Copy link
Copy Markdown
Contributor Author

Folded the pv lane and the rest of #3073 into this branch. It is now one PR that takes every workflow off GitHub-hosted runners: binary-release.yml, nightly.yml, book.yml and book-contracts.yml. That lets the new guard ship with an empty baseline. As two PRs, a shrink-only baseline entry would have stranded whichever one landed second.

  • 7157e5f The pv lane is on the fleet: x86_64 on yoga, aarch64 natively on gx10, in build-apr-cpu's sibling-container pattern. gnu assets have a GLIBC_2.31 floor, and asset names are unchanged.
  • d8bff4a The musl pv lanes build on bookworm. Rehearsal rc.0 (run 34543016302) found both musl lanes dying at apt-get update in bullseye with exit 100, because bullseye-security's Release file has expired. Local proof on lambda afterwards: a static pv (NEEDED=0) built in 41 s.
  • 7c520b2 nightly, book and book-contracts run on the fleet. The nightly's Linux builds run on yoga and gx10 with a 2.31 floor; its macOS and Windows targets leave, because no self-hosted runner exists for them. This commit also adds scripts/check_no_hosted_runners.sh: a 12-row case table, mutation-proven, and wired through guard_tree.sh's dispatch.
  • ceaeb2b The guard's baseline is empty.

Release-pipeline rehearsal: v0.67.0-rc.1 is a throwaway prerelease, made of this branch's pv fix plus a version bump. It runs binary-release.yml from the tag (run 34543976049): 8 build jobs, none with a hosted label. The result will be posted here, and the prerelease and tag will be deleted after.

Closes #3073 on merge.

🤖 Generated with Claude Code

@noahgift

Copy link
Copy Markdown
Contributor Author

Release-pipeline rehearsal: green end to end. v0.67.0-rc.1 was a throwaway prerelease: this branch's pv fix (d8bff4a) plus a version bump. Run 34543976049 was triggered by release: published and ran binary-release.yml from the tag's own commit. Every job ran on the self-hosted fleet; none used a GitHub-hosted runner.

job result runner
pv x86_64-unknown-linux-musl on yoga success yoga-gpu
pv aarch64-unknown-linux-gnu on gx10 success gx10-blackwell
pv x86_64-unknown-linux-gnu on yoga success yoga-gpu
apr (cuda) x86_64-unknown-linux-gnu on yoga success yoga-eph
apr (cpu) x86_64-unknown-linux-gnu on yoga success yoga-gpu
apr (cuda) aarch64-unknown-linux-gnu on gx10 success gx10-eph
apr (cpu) aarch64-unknown-linux-gnu on gx10 success gx10-blackwell
pv aarch64-unknown-linux-musl on gx10 success gx10-blackwell
All four apr assets are on the release success yoga-build
smoke apr (cuda) on gx10 success gx10-eph
smoke apr (cuda) on yoga success yoga-eph
smoke apr (cpu) on gx10 success gx10-eph
smoke apr (cpu) on yoga success yoga-gpu
smoke apr (cpu) on clean-room success yoga-eph
Summary success yoga-eph

scripts/check_release_assets.sh v0.67.0-rc.1 reports all 16 expected assets (4 apr + 4 sha256 + 8 pv). That was checked twice: in CI by verify-apr-assets on yoga-build, and in a local re-run.

The earlier rehearsal, rc.0 (run 34543016302), found the musl apt defect that d8bff4a fixes. For this run, gx10-eph was restored: it is the only runner the aarch64 CUDA lane can use. The prerelease and its tag are now deleted.

🤖 Generated with Claude Code

noahgift and others added 2 commits September 11, 2026 03:26
…ivate root, not the shared home

Moving book.yml onto the clean-room pool (#3073) put its three `cargo install` steps (bashrs, the
local apr-cli, pmat) under check_cargo_install_private_root.sh, which guard-cargo ran red on
yoga-eph (run 34544213071): a self-hosted `cargo install` into the shared ~/.cargo/bin replaces a
binary another job on the same box may be exec'ing (aprender#2353). Each install now passes
`--root "$RUNNER_TEMP/cargo-tools"`, and that root's bin is added to PATH last, so it resolves first.
mdBook, which the guard does not cover, had the same race in ~/bin; it now unpacks into
$RUNNER_TEMP/bin.

Checked locally: check_cargo_install_private_root.sh OK (43 jobs, 9 installs, none shared) and its
case table; check_no_hosted_runners, check_runner_labels, check_workflow_env_defined,
check_workflow_path_filters and check_apr_bin_pinned all green.

Pmat-Ticket: PMAT-1098
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…fied baseline file is RED

check_baseline_ratchets.sh refuses any baseline file it cannot classify as a ratchet or as a stated
exemption ("no rule is how a baseline arrives that nothing ever compares"). It turned guard-tree red on
this PR for scripts/hosted_runner_baseline.txt, which had held nothing since the pv lane moved to the
fleet. check_no_hosted_runners.sh already treats a missing baseline as zero exemptions, the strictest
state, so the file goes. A later migration that needs an entry must add the file and classify it in the
same change. Checked locally: check_baseline_ratchets, check_no_hosted_runners (real tree + 12-row case
table) and check_guards_are_wired are all green.

Pmat-Ticket: PMAT-1098
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@noahgift
noahgift added this pull request to the merge queue Sep 11, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 11, 2026
@noahgift
noahgift added this pull request to the merge queue Sep 11, 2026
@noahgift
noahgift removed this pull request from the merge queue due to a manual request Sep 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant