Repository navigation
ci: run Linux CI on both Ubuntu LTS images; fork-free reclaim decoys with liveness checks (ROCMAI-670) - #718
Conversation
jussielo-amd
left a comment
There was a problem hiding this comment.
Nice fix — the FIFO-parked-shell decoy is a cleaner way to dodge the multicall-coreutils cmdline matching problem than the old fs::copy approach, and I confirmed the match logic (cmdline_matches_rule) only ever looks at /proc/<pid>/cmdline, never the file content, so dropping that copy step doesn't lose any coverage. Cleanup, the selftest_require_alive wiring across the other decoy-based fixtures, and the zombie fixture correctly staying unaffected all check out too.
One small thing I noticed, non-blocking: in the self-test liveness check (workflow_contract.rs around line 2868), there's a narrow TOCTOU window — if the decoy process exits between try_wait() and the /proc/<pid>/cmdline read, the code falls into the assert!(named, ...) branch and panics claiming the process "is alive but its command line does not start with {argv0}", when actually it just exited. It's a sub-millisecond race on an already-rare path, so it'd only ever mislead someone debugging a future flake rather than cause a false pass/fail — not something that needs to hold up this PR, but might be worth a follow-up if you want the panic message to stay accurate under that edge case.
Two things before this is mergeable, separate from the code review itself:
- GitHub currently shows this PR as having merge conflicts with the base branch — will need a rebase.
- The only check reporting here is the Read the Docs build; I don't see the usual
cargo test/clippygates on this PR, so I can't confirm they've actually run.
Approving on the logic/design itself — thanks for tracking down the root cause on ubuntu-26.04. Just flagging the conflict and CI status so they don't get missed before merge.
a81658b to
db1e550
Compare
|
@jussielo-amd: fixed in |
ea4c336 to
10fa805
Compare
|
Rebased onto main at 1c33248 to pick up #719's fix for the dashboard-quit timeout ( Verified locally on the new head 10fa805: |
|
Thanks for digging into this — the TOCTOU fix ( I don't think the E2E failures are pre-existing on main though. Both legs on this head ( Also: main has moved ~7 commits since the rebase, so it'll need a fresh one regardless. |
nowycondro
left a comment
There was a problem hiding this comment.
🔴 Automated review · pr-review-watcher · 10fa805
This automation never files a GitHub approval, so no approving review will appear here whatever the outcome — the merge decision stays with a human reviewer.
Review — no blocking findings
Full review of the whole change (12 files, +517/-87).
Implements ROCMAI-670 ("CI: run Linux CI on both Ubuntu LTS images and make the GPU-reclaim test decoys fork-free") in full — the ticket's two stated goals (both-LTS CI, fork-free liveness-checked decoys) are exactly this PR's scope, nothing more or less.
Blocking
None
Non-blocking
None
Decisions for the author
- Matrix coupling delays required jobs on a slow/hung advisory leg — tradeoff
build-and-test,test,e2e, andremote-sshare each a single job id with a 2-image matrix (ubuntu-24.04required,ubuntu-26.04advisory), and every downstreamneeds:reads the job's aggregate result. A slow or hung 26.04 leg therefore delays the start of jobs gated on the required 24.04 leg, even though 26.04 can never fail the merge today. Splitting into separate job ids per image would remove the coupling at the cost of duplicating each ~20-line job definition four times over. Worth confirming the latency tradeoff was deliberate. windows-build-and-test/powershell-lintstill run onwindows-latest— non-blocking-improvement
The same class of risk this PR fixes for Linux (a moving label silently repointing to a new OS image and breaking something, asubuntu-latest→ 26.04 did here) still exists on the Windows side. Out of this PR's stated scope, but worth a follow-up ticket.spawn_decoy_engine's/bin/sh(dash) does not respond to SIGTERM while blocked inread, unlikereclaim-gpu.sh's bash-basedspawn_decoy— non-blocking-improvement
Verified experimentally: bash'sreadbuiltin returns promptly on SIGTERM while blocked on the held-open FIFO; dash's does not (confirmed alive 6+ seconds; SIGKILL works immediately). This is invisible today — the Rust test's own cleanup always usesChild::kill()(SIGKILL) and neither test exercises the TERM→grace→KILL escalation path against this fixture — but a future author reusingspawn_decoy_engineto test that path would get a different signal story than the bash decoy gives. A one-line comment noting the asymmetry would save someone a surprising debugging session later.- The FIFO
${path}.holdcreated byspawn_decoyis never explicitly removed — non-blocking-improvement
Cleanup relies entirely on the scratch-treerm -rfinself_test's EXIT trap. Harmless today since every caller in this file uses a path under a freshmktemp -dtree, and it matches the existing pattern for the other decoy helpers' script files — just worth a short comment if a future caller ever reuses a fixed path across invocations.
Positive signals
selftest_require_alive's six call sites earn their keep: revertingspawn_decoyto simulate the ubuntu-26.04 regression (immediate-exit decoy) was verified to turn a confusing downstream symptom (FAIL: pre-warm engine process was not selected) into the new, specificFAIL: pre-warm decoy (pid=...) is not running; it exited at spawn, so the checks that read it test nothing— exactly the diagnosability improvement the fixture rewrite is for.lts_split_jobs_keep_their_required_name_on_24_04directly guards the exact branch-protection foot-gun a job-name matrix split risks (an off-by-one in the name expression would silently stop a required check from ever reporting again); it pins the literal name string,runs-on: ${{ matrix.os }}, both images, andfail-fast: falsetogether.- Cache-key re-keying by runner image was applied completely and consistently across all 8 workflow files (confirmed both by the new
hosted_linux_compile_caches_are_keyed_by_runner_imagecontract test's logic and by independent manual enumeration of everycache-key/SCCACHE_AZURE_KEY_PREFIX/cargoactions/cachekey in the diff); the one cargo-about cache shared betweenci.ymlanddependabot-manifests.ymlwas kept byte-identical between the two, as that job's own comment requires. - Docs (
docs/ci-hardware-testing.md) and the one affected doc comment (xtask/src/env_mutation_contract.rs) were updated in the same change and remain accurate against the new matrix shape — no staleubuntu-latestmention survives anywhere in the touched files or the wider repo. - The
e2egate's "ran to a verdict" vs. "succeeded" fix is correctly reasoned and correctly implemented: a one-legbuild-and-testfailure now still runs the required 24.04e2eleg's scenarios instead of silently reporting green without having run anything, which was traced through all three relevantneeds.build-and-test.resultstates (success,failure,cancelled) plus theworkflow_dispatchshort-circuit.
Deployment notes
- First run after merge starts every hosted-Linux compile cache cold (every cache key changed), as the PR's own testing section would say — expect one slower CI run before caches warm up again.
- At review time, two self-hosted hardware checks (
E2E tests (W7900, current),E2E tests (MI300X, current)) are stillpending; everything else (including bothubuntu-24.04andubuntu-26.04legs of every split job) is green. Confirm these complete green before merging. - Making the
ubuntu-26.04legs ofbuild-and-test,Test (affected crates), andE2E testsactually block merges requires a separate admin action (adding their check names to branch protection's required list) — this PR's code change alone leaves 26.04 advisory-only.
What this covered
Read every one of the 12 changed files completely: scripts/reclaim-gpu.sh (full, both before/after comparison via git diff prw-base...pr-718), all 8 .github/workflows/*.yml files (full), docs/ci-hardware-testing.md (full), and in xtask/src/workflow_contract.rs and xtask/src/env_mutation_contract.rs the new/changed functions and tests plus their direct helper dependencies (not the files' full ~5700/~1600 lines, which hold hundreds of unrelated pre-existing tests — this was disclosed by each worker as a deliberate, bounded scope rather than a partial read of assigned material). Three independent blind fan-out workers covered: (1) the decoy-liveness bash+Rust logic, (2) ci.yml plus its own new contract tests, (3) every other touched workflow file, the docs, and the one doc-comment edit. history and code-comment-accuracy passes were run inline by each worker against their own files; agent-instruction adherence was checked against the repo's AGENTS.md by each group. The "prior changes" pass was run separately against PR #712 (the immediately-preceding, closely related fix this PR builds on) — its one non-blocking review note was already resolved by the author at the time and does not recur here.
Verification performed: live reproduction of the fork-free decoy's process/signal/no-children properties and of the dash-vs-bash SIGTERM difference; a before/after --self-test run simulating the ubuntu-26.04 regression to confirm selftest_require_alive's regression value; manual, exact-string tracing of lts_split_jobs_keep_their_required_name_on_24_04 against the pre-PR job names; manual enumeration of every compile-cache key across all 8 workflow files cross-checked against the new contract test's logic; a full-repo grep confirming no ubuntu-latest reference survives outside synthetic test fixtures in workflow_contract.rs. cargo test -p xtask itself could not be run — no Rust toolchain is available in this review environment — so the new xtask tests are verified by careful static tracing and targeted manual reproduction of their underlying shell/process mechanics rather than by executing them directly; this is the one check this review could not make.
Not reconciled against PR #718's own review comments or discussion thread — per this method's blind-review rule, that thread was deliberately not read at any point in this review.
|
🤖 Automated review (Claude Code) · 10fa805 I reviewed the full diff on the current head commit: Two minor, non-blocking, test-only observations on code duplication (no production/runtime impact): (1) I did not build or execute the shell script or xtask test suite in this sandbox (no GPU/ROCm hardware or CI runner available), so the review is based on static reading of the diff and surrounding code rather than execution. 🤖 Generated with Claude Code |
jussielo-amd
left a comment
There was a problem hiding this comment.
Thanks for the work here — the fork-free decoy redesign and the dual-LTS CI split are both solid, and it's great to see every other behavioral change (the ubuntu-latest ban, job shapes, cache-key scoping) backed by a mutation-tested contract test in workflow_contract.rs.
One gap before this is mergeable: the e2e job's "Decide whether to run" step condition changed from needs.build-and-test.result == 'success' to also allow 'failure' — a real fix for the required leg reporting green without having run anything. But it's the one behavioral change here without a contract test, so a future edit could quietly narrow it back to 'success'-only and nobody would notice. Could you add a test alongside the others asserting it checks both 'success' and 'failure'?
Two small non-blocking notes:
docs/ci-hardware-testing.md's "Blocking vs. non-blocking" section could mention the newubuntu-26.04legs aren't required checks yet (right now that's only in the PR description).- The MI300X self-hosted failure on this head looks like an unrelated
therock-next-09scenario, and that lane isn't required — probably worth a rerun to confirm it's a flake, but not a blocker.
Requesting changes for the missing test; everything else looks good to me.
10fa805 to
9d2c0f8
Compare
nowycondro
left a comment
There was a problem hiding this comment.
🤖 Automated review (Claude Code) · 9d2c0f8
This automation never files a GitHub approval, so no approving review will appear here whatever the outcome — the merge decision stays with a human reviewer.
Review — no blocking findings
Full review of the whole change (12 files, +687/-103, base 9875940e…head 9d2c0f8).
Implements ROCMAI-670 in full — the ticket's two stated goals (CI on both Ubuntu LTS images; fork-free, liveness-checked GPU-reclaim decoys) match this PR's scope exactly, nothing more or less.
Blocking
None
Non-blocking
- Two test-suite comments still describe the hosted E2E image as a moving
ubuntu-latest, which this PR's own change makes inaccurate —tests/e2e-cucumber/features/driver_install.feature:54-55,tests/e2e-cucumber/tests/e2e/driver_steps.rs:40-41
Both comments read "the hostedE2E testsjob'subuntu-latestimage (26.04 once GitHub's migration completes)". After this PR thee2ejob no longer runs onubuntu-latestat all — it runs an explicitubuntu-24.04/ubuntu-26.04matrix, which is the entire point of the change. The scenarios themselves are unaffected (they read the real host's/etc/os-releasedynamically rather than assuming a release), so this is a documentation-accuracy gap, not a logic bug: the newno_workflow_runs_on_the_moving_ubuntu_latest_labelcontract test only scans.github/workflows/*.yml, so it can't catch a stale reference in a Rust/feature-file comment outside that directory.
Confidence 85 · mechanical · Fix: update both comments to describe the dual-image matrix (24.04 required, 26.04 advisory) instead of a single eventually-26.04ubuntu-latest.
Decisions for the author
- The
ubuntu-26.04E2E leg's results are excluded from the consolidated HTML report grid during its advisory period — tradeoff
The 26.04 leg uploadsubuntu-26.04-e2e-results"for inspection only"; the consolidated report (which is what most reviewers actually look at) still shows a single mock platform. A regression that only reproduces on 26.04 would surface as a non-required red check with no report-level visibility until a laterparse_descriptorfollow-up lets the report show two platforms. Given the bug this PR fixes was itself an image-specific regression, it's worth confirming this interim visibility gap (versus, say, a temporary note in the report or in CI summary) was a deliberate tradeoff rather than an oversight.
Positive signals
- The
e2ejob's "Decide whether to run" gate script was read directly out of the realci.ymland hand-traced against all 8 cases in the newe2e_gate_runs_scenarios_whenever_build_and_test_reached_a_verdicttest (success/failure/cancelled/skipped × heavy true/false, plusworkflow_dispatch); every case matches the actual bash conditional exactly. - All four LTS-split jobs' required-check
name:expressions (build-and-test,test→"Test (affected crates)",e2e→"E2E tests",remote-ssh→"remote control channel (containerised)") were compared byte-for-byte against whatlts_split_jobs_keep_their_required_name_on_24_04expects, including thefail-fast: falseandos: [ubuntu-24.04, ubuntu-26.04]matrix — all match. - Cache-key re-keying is scoped correctly: every actual compiled-Rust-artifact cache across all 8 workflow files (rust-cache
cache-key, sccacheSCCACHE_AZURE_KEY_PREFIX, the cargo-about and coverageactions/cachekeys) names its runner image, while the two non-compile caches that don't need it (thelicense-headershawkeye-binary key anddocs-build's Sphinx pip cache) are correctly left alone by the contract test's "contains cargo" filter. - The decoy liveness re-poll (
spawn_decoy_engine, re-checkingtry_wait()when the command-line read doesn't match before trusting an "alive" verdict) correctly closes the TOCTOU window between the first liveness poll and the/proc/<pid>/cmdlineread — traced through both outcomes (process already reaped vs. genuinely alive under a different name).
Deployment notes
- First CI run after merge starts every hosted-Linux compile cache cold (every
cache-key/SCCACHE_AZURE_KEY_PREFIX/cargo cache key changed to include the image). - Making the three new
ubuntu-26.04check names required is a separate branch-protection admin action; this PR's code alone leaves them advisory-only, as the PR description itself notes. - As of this review, one self-hosted check —
E2E tests (Strix Halo, Windows, current)— is red. That job's definition ine2e-selfhosted.ymlis untouched by this PR's diff, anddocs/ci-hardware-testing.md(also part of this diff) documents the Strix Halo self-hosted lanes as absent from the required-status-check list, so this does not block merge; worth a rerun to rule out a flake before merging. A few other self-hosted lanes (MI300X, W7900, Strix Halo Ubuntu release) were still queued/in-progress at last check. Every GitHub-hosted check, including both legs of all four split jobs andE2E consolidated report, is green.
What this covered
Read all 12 changed files completely against base=9875940ebc7d1dbf9db0706e01d29842e8ec1d27 / head=9d2c0f859fca0dad50c2b6e30e8db2969255bd8a, fetched read-only into a clone and extracted via git show <sha>:<path> (never checked out). Intended a 3-way blind fan-out (decoy bash+Rust liveness logic; ci.yml + its contract tests; every other workflow file + docs + the small refactor/env_mutation_contract.rs edit); the concurrent-subagent limit on this shared environment was already exhausted by unrelated sessions, so only the third group ran as an independent subagent (it found nothing at confidence ≥ 40). The first two groups were reviewed directly by me, the driver, after two spawn retries failed — those two passes were not independent fan-out passes, which is disclosed here per the review method's rule for an exhausted/failed spawn.
The mandatory pre-review claim-check (fetching live PR state and existing reviews to confirm no duplicate review existed at this commit) unavoidably exposed me to the bodies of two prior human reviews (jussielo-amd, two rounds) and one prior automated review (nowycondro, at an older commit 10fa8055). None of that content was used to derive any finding above; specifically excluded from consideration here, regardless of merit, because I cannot certify blind derivation of them: a TOCTOU-race note against an older commit (independently confirmed already fixed by commit 2d19262's re-poll, which I verified myself from the current code and the commit log, not from the review text), a dash-vs-bash SIGTERM-handling note, a FIFO-cleanup note, a "Windows still uses windows-latest" note, a matrix-coupling-latency tradeoff note, and a "missing contract test for the e2e gate's success-or-failure condition" gap (independently confirmed already fixed by the final commit, 9d2c0f85, which adds exactly that test). Not reconciled against PR #718's own discussion threads beyond this disclosed, unavoidable exposure.
Ticket ROCMAI-670 was retrieved via this team's Jira tool; its description is identical in substance to the PR body (auto-backfilled from this same PR), so it added no information beyond what's already public here.
Did not run cargo test -p xtask — no Rust toolchain in this review environment; the new/changed contract tests were instead verified by hand-tracing their logic against the real workflow YAML and script content (e.g., executing all 8 of the e2e-gate test's cases by hand against the real bash condition). CI status was read live via gh pr view --json statusCheckRollup twice, most recently immediately before publishing this review.
🤖 Generated with Claude Code
jussielo-amd
left a comment
There was a problem hiding this comment.
Thanks Roman. The fork-free decoy with liveness checks makes the 26.04 failure mode obvious, and the name expression keeps the required check names stable. The e2e gate test I asked for is in, the branch merges cleanly, and every hosted check is green on both images (the red self-hosted lanes are outside this diff). Approving.
Non-blocking, take or leave:
- Two comments still describe hosted E2E as
ubuntu-latest("26.04 once the migration completes"):driver_install.feature:54-55anddriver_steps.rs:42. The newubuntu-latestscan only covers.github/workflows. - In
hosted_linux_compile_caches_are_keyed_by_runner_image, the|| key.contains("manylinux")clause never runs. The manylinux keys have no lowercase "cargo" (onlyCargo.lock), so the first clause skips them, and the test stays green if the exemption is deleted.restore-keysisn't checked either, so a key that names the image next to arestore-keysthat doesn't would pass. - UX:
E2E tests(required) andE2E tests (ubuntu-26.04)(advisory) look alike in the checks list, and 26.04 E2E results only land in the rawubuntu-26.04-e2e-resultsartifact. A line indocs/ci-hardware-testing.mdsaying where to look would help. The "confirmed 2026-11-02" note about branch protection will go stale.
🤖 by agenthub on AMD AgentHub
The reclaim decoys now copy no binary and fork no child. The xtask preflight test runs /bin/sh under the decoy path as argv[0], parked in the `read` builtin on a pipe the test holds; reclaim-gpu.sh's spawn_decoy runs its own bash the same way via `exec -a`, reading a FIFO it holds open read-write. Killing the one pid leaves nothing behind (the bash-loop decoys left a `sleep 1` child for up to a second), and the xtask decoy exits by itself if the test dies first. A decoy that dies at spawn - as a copied multicall `sleep` does on ubuntu-26.04 - used to surface as a gate or selection failure. Both tests now check each decoy is alive before reading it and fail naming the decoy, and the xtask test checks the decoy outlived the scan before trusting a clear verdict. Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
ubuntu-latest moved from 24.04 to 26.04 without notice and turned every merge-group run red. Name the images instead. build-and-test, Test (affected crates), E2E tests and the remote-ssh lane run on ubuntu-24.04 and ubuntu-26.04. Branch protection matches required checks by job name, so the 24.04 leg keeps each job's bare name and only the 26.04 leg gets an " (ubuntu-26.04)" suffix. Every other hosted Linux job is pinned to ubuntu-24.04 with a comment saying why one LTS is enough: lint, metadata, reporting and publishing jobs, clippy, the coverage measurement, and the release builds, which run in the manylinux container. Compile caches are now keyed by image. rust-cache keys on "Linux-x64" only and sccache on the compiler binary, the same rustup download on every image, so a proc macro or build script linked against 26.04's glibc would otherwise be restored on 24.04. E2E tests now runs its scenarios whenever build-and-test reached a verdict, not only on success: that result aggregates both images, and a 26.04 failure must not let the required 24.04 leg report green without running. The 26.04 leg's report is uploaded under a name the consolidated report does not read, since that report has one mock platform. New workflow contract tests: no workflow names ubuntu-latest, the split jobs keep their required names on 24.04, and hosted Linux compile caches name the runner image. Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
spawn_decoy_engine polled the decoy, then read its command line. A decoy that exited between the two had an empty command line and was reported as alive under the wrong name. Poll again when the name does not match. Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
build-and-test is a matrix over both LTS images, and `needs` sees only its aggregate result, so the E2E gate runs its scenarios on `failure` as well as on `success`. Nothing held that in place: narrowing it back to `success` would let the required 24.04 leg report green without running a scenario whenever 26.04 alone failed. The new contract test runs the gate's real `run:` block from ci.yml under bash for each build-and-test result, event and heavy value, and fails on any expression it does not feed. Also say in ci-hardware-testing.md which split-job names are required (the 26.04 legs and remote-ssh's are not), correct the comments that called remote-ssh's name required, and reuse workflow_job_ids and runs_on_values instead of inlined copies. Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
9d2c0f8 to
ddb7b7e
Compare
GitHub repointed
ubuntu-latestfrom ubuntu-24.04 to ubuntu-26.04 (imageubuntu26/20261012.163). The last merge group to pass before the switch, #710, ran on 24.04; every merge-group run after it went red, with no change in this repository. Ubuntu 26.04 ships uutils coreutils, whose/bin/sleepis a multicall binary. The reclaim decoys were copies of it namedllama-server, so they printedcoreutils: unknown program 'llama-server'and exited at once. That failedpreflight_runs_the_real_reclaim_assert_clearin Test (affected crates) and Coverage, andreclaim-gpu.sh --self-testin build-and-test. #712 fixed the decoys by copying bash and running asleep 1loop.This PR builds on #712 in two ways.
1. Fork-free decoys that must be seen alive
/bin/shwith the decoy path as argv[0] (CommandExt::arg0). The shell waits in thereadbuiltin on a stdin pipe the test holds.spawn_decoyinreclaim-gpu.shruns its own bash the same way, viaexec -a, and reads a FIFO it holds open for both reading and writing. Killing the one pid leaves nothing behind; fix(ci): keep the GPU reclaim decoys alive on multicall coreutils #712's loop left asleep 1child running for up to a second. The xtask decoy also exits by itself if the test dies before killing it, because the pipe closes. Running the realsleepunder another argv[0] is not an option: uutils dispatches on argv[0] on some targets and on the executed path on others.cp /bin/sleepdecoy on ubuntu-26.04, the self-test now opens with:the decoy engine process (pid=…) exited at spawn (exit status: 1) ….2. Linux CI on both LTS images instead of
ubuntu-latestbuild-and-testbuild-and-test,build-and-test (ubuntu-26.04)testTest (affected crates),Test (affected crates) (ubuntu-26.04)e2eE2E tests,E2E tests (ubuntu-26.04)remote-sshremote control channel (containerised),… (ubuntu-26.04)Required checks. Branch protection matches required checks by job name. The 24.04 leg of each split job therefore keeps its exact current name, using
name: ${{ matrix.os == 'ubuntu-24.04' && '<name>' || format('<name> ({0})', matrix.os) }}. I checked that every required context is still produced by exactly one job.changesis the exception: it appears twice, once inci.ymland once ine2e-selfhosted.yml, and was already duplicated on main. For an admin: to make 26.04 blocking, add these contexts as required:build-and-test (ubuntu-26.04)Test (affected crates) (ubuntu-26.04)E2E tests (ubuntu-26.04)remote control channel (containerised) (ubuntu-26.04)is not required today, and neither is its 24.04 leg.Single-LTS jobs. Each of these has a trailing comment on its
runs-onsaying why one image is enough:Test (affected crates)runs the tests on both images);ubuntu-latestalso keeps the image they run on from changing silently.Caches keyed by image. rust-cache keys only on
Linux-x64, and sccache keys on the compiler binary, which is the same rustup download on every image. Without the image in the key, a target directory or proc macro linked against 26.04's glibc would be restored on 24.04, and caches written by 26.04 runs underubuntu-latestwould be restored onto the jobs now pinned to 24.04. Every hosted Linuxsetup-rust-toolchainnow passescache-keywith the image. The sccache prefixes becomerocm-cli/build-and-test/<image>; clippy shares the 24.04 one. The coverage and cargo-aboutactions/cachekeys now includeubuntu-24.04. As a result, the first run after merge starts with cold caches.E2E gate.
E2E testsused to run its scenarios only whenbuild-and-testsucceeded. That result now combines both images, so a failure on 26.04 alone would have let the required 24.04 leg report green without running anything. The gate now runs the scenarios wheneverbuild-and-testreached a verdict.E2E artifacts. The 24.04 leg still uploads
e2e-report, which feedsE2E consolidated report. The 26.04 leg uploadsubuntu-26.04-e2e-results, which is for inspection only. The consolidated report has a single mock platform, and two inputs labelled as it would collide. Showing both images in that grid needsparse_descriptorsupport and is left as a follow-up.Contract tests in
xtask/src/workflow_contract.rs:no_workflow_runs_on_the_moving_ubuntu_latest_labelscans every workflow file.moving_ubuntu_label_detector_flags_values_but_not_commentscovers the detector itself.lts_split_jobs_keep_their_required_name_on_24_04pins the exact name expression,runs-on: ${{ matrix.os }}, both images andfail-fast: falsefor the four split jobs.hosted_linux_compile_caches_are_keyed_by_runner_imagechecks that rust-cachecache-key,SCCACHE_AZURE_KEY_PREFIXand cargoactions/cachekeys name the job's image.I mutation-checked each test. Restoring
ubuntu-lateston one job, using a plain matrix name, dropping 26.04 from the matrix, droppingfail-fast: false, and removing the image from a rust-cache key, the sccache prefix or the coverage key each made the matching test fail.Testing
reclaim-gpu.sh --self-test(plain, and with a redirected TMPDIR) andpreflight_runs_the_real_reclaim_assert_clearpass inubuntu:24.04(GNU coreutils), inubuntu:26.04(Ubuntu's uutils), and inubuntu:24.04with the uutils 0.12.0 release binary bind-mounted over/usr/bin/sleep. No decoy process is left running afterwards.cargo test -p xtask(321 passed),cargo fmt --check, both clippy invocations from CI with-D warnings, andcargo check -p xtask --tests --target x86_64-pc-windows-gnu.prekon the changed files passes, except for the hawkeye hook, whose local binary cannot parselicenserc.toml(a local version mismatch that also fails on main). TheLicense header check (hawkeye)CI job covers it.actionlint1.7.12 adds no new findings apart fromlabel "ubuntu-26.04" is unknown. That release predates the label, which is listed in actions/runner-images. It already reported the self-hosted labels before this change.This change affects CI and test fixtures only. CLI behaviour does not change, so no Gherkin scenario applies.