Repository navigation
feat: nightly release-readiness summary (wiki page) - #655
juhovainio wants to merge 2 commits into
Conversation
rominf
left a comment
There was a problem hiding this comment.
🔴 Automated review · pr-review-watcher · 7235dd9
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.
Round 1 — every finding below is new this round.
Review — needs work
Full review of the whole change.
No ticket named. The description misstates the change: see the last two blocking findings.
Blocking
-
The GO verdict fails open when the E2E evidence is missing —
xtask/src/release_readiness.rs:312-319,xtask/src/release_readiness.rs:495-498,.github/workflows/nightly.yml:1735-1781
total_e2e_failuresreturns.unwrap_or(0)when there is no| **Total**row. That is whatconsolidated_summary_markdown(crates/e2e-report/src/consolidated.rs:966-970) writes when no lane uploaded a report. It is also what an empty or partialsupport-matrix.mdyields when thee2e-report-nightlystep fails, because the artifact upload there runsif: always(). A probe run returned 0 failures for an empty file, for the real "no report.json files" output, and for anEMPTY-status-only table, so each of these publishes "✅ GO". A lane that times out before uploading is not counted as a failure either, and the six lanes are allcontinue-on-error. The verdict never receivesneeds.e2e-report-nightly.resultor any per-lane result. The testtotal_e2e_failures_defaults_to_zero_without_a_totals_rowpins this fail-open behaviour.
Confidence 100 · logic · Fix: return NO-GO (or UNKNOWN) when the Total row is missing or fewer platforms are present than expected. Pass the e2e-report and lane job results into the xtask. Invert the test. -
Nights with no new commits publish a false NO-GO —
xtask/src/release_readiness.rs:91-93,.github/workflows/nightly.yml:207
Whennightlysetsskip=true, thenightlyjob succeeds,windows-nightlyisskipped, andwindows_result != "success"adds the failing gate "windows build (skipped)". The verdict is then NO-GO, andpublish-release-readiness-wikipublishes it becauserelease-readiness-nightlysucceeded.
Confidence 85 · logic · Fix: treatskippedas "no build this night" (or skip the whole release-readiness chain whenneeds.nightly.outputs.skip == 'true'). Do not count it as a failed gate. -
Text from issues any GitHub user can file reaches the public wiki unescaped and unreviewed —
xtask/src/release_readiness.rs:253-291,xtask/src/release_readiness.rs:390-395,.github/workflows/nightly.yml:1826-1858,.github/workflows/nightly.yml:1916-1925.github/ISSUE_TEMPLATE/bug_report.md:11auto-applieslabels: bug, so any GitHub user controls entries inknown_issues.- The raw titles go into
table.mdas unescaped markdown. A probe rendered injected image and link markdown verbatim. That file is written to$GITHUB_STEP_SUMMARYand to the publicRelease-Readinesswiki page. - Titles and 240-character body excerpts are also the LLM prompt. At the pinned SHA, ai-inference joins the system prompt and the user prompt into one
copilot -pstring, so the prompt's "Hard rules" carry no more weight than injected text. - The model's response is published with
cat, with no filtering. An attacker can therefore put links, a fake "GO" claim, or text that drops real blockers from "Known Issues" on the project wiki. - Command execution is not possible, because
copilot-allow-toolsis unset.
Confidence 85 · architectural · Fix: escape titles or render issues as
#NNNlinks only. Either keep the narrative as a run artifact or step summary, or feed the model only maintainer-authored inputs (commit subjects), never issue bodies. -
The verdict, gate assembly and table rendering are untested —
xtask/src/release_readiness.rs:71-116,xtask/src/release_readiness.rs:321-400
These mutations all left the 6 tests green (cargo test -p xtask release_readiness, 6 run):- nightly gate removed
- E2E gate removed
- verdict inverted
- Build-row check broken
- issue sort reversed
rfind→findinextract_pr_number- the
excerptboundary<=→<
No test reaches
run,render_tableorcollect_known_issues. That includes the GO/NO-GO logic the page exists to publish.Test verdicts:
- All six tests are in a new module, so verdict (1) does not apply.
- (2) The categorize tests catch the scope-filter,
!and suffix-strip mutations. - (2)
extract_pr_number_reads_trailing_referencemissesrfind→find. - (2)
excerpt_…misses the boundary mutation. - (2)
total_e2e_failures_reads_the_totals_rowcatches column-index mutations. - (2) The defaults-to-zero test pins the defect above.
Confidence 100 · logic · Fix: extract the gate/verdict computation into a pure function and test it, including skipped and cancelled results and a missing report. Snapshot-test
render_table. -
The wiki "degenerate-output guard" can never trip —
.github/workflows/nightly.yml:1903-1909,.github/workflows/nightly.yml:1866-1868
The guard grepstable.mdfor| Area | Evidence | Status |, butrender_tablewrites that header unconditionally (release_readiness.rs:335). The publish job also only runs whenrelease-readiness-nightlysucceeded, so the file always exists. The comment "same degenerate-output guard" aspublish-e2e-wikiis false: that guard catches an empty consolidation and is covered bywiki_publish_guard_stops_on_the_generators_empty_outputinxtask/src/workflow_contract.rs. The new job has no contract test, unlikepublish-e2e-wiki'snightly_publishes_the_consolidated_matrix_to_the_wiki. Together with the first finding, an empty E2E night publishes GO.
Confidence 85 · logic · Fix: make the guard test real evidence (e.g. that a Total row was parsed, carried as a field in facts.json). Add a workflow-contract test like the one #590 added. -
The Strix Windows nightly runner move is bundled in, unmentioned, and incomplete —
.github/workflows/nightly.yml:917-926,.github/workflows/nightly.yml:1115-1118,docs/ci-hardware-testing.md:60-74- Out of scope: commit 51d125a moves
e2e-gpu-nightly-strix-windowsfromnativetodevlab-dispatch, a behaviour change unrelated to the release-readiness feature (AGENTS.md §11: one logical change per PR). - Docs left stale:
docs/ci-hardware-testing.mdstill says both nightly Strix lanes "still pin thenativelabel" and that moving them "is a separate, not-yet-made decision" (AGENTS.md §5 docs-sync). - Comment left stale: the moved job still says "The persistent runner workspace keeps those paths valid across scenarios and subsequent runs". That is false on the fresh-per-job pool, and the per-PR twin in
e2e-selfhosted.ymlsays "RUNNER_WORKSPACE is fresh every job". - No contract test: no test pins the moved lane's labels. The WSL lane has
nightly_wsl_lane_shares_the_per_pr_pool_labels. - Contradicted commit claim: the message says "no registered runner carries native anymore", but
e2e-gpu-nightly-strix(nightly.yml:716) still pinsnative.
Confidence 85 · architectural · Fix: move this to its own PR, with the doc, comment and contract-test updates.
- Out of scope: commit 51d125a moves
-
Three commits use a placeholder identity and fail the required signature gate — commits 49070b1, e654b4c, 7235dd9
These three are authored, committed and signed off astest <test@example.com>. CI "Commit signatures + sign-off" fails on exactly these three ("signature is not GitHub-Verified"). AGENTS.md §2 requires the employer identity and a valid DCO sign-off. The squash body would also carry the bogusSigned-off-by.
Confidence 100 · mechanical · Fix: re-author and re-sign these commits under the real identity. -
"Test (affected crates)" fails — CI on 7235dd9
It fails atcargo nextest run --locked -p rocm-core --lib --features e2e-test-hookswith "the package 'rocm-core' does not contain this feature". The merge-base 34fbdfe is 24 commits behind the base tip, which added that feature (#510). The cause is the stale base, not this code, but the required check is red.
Confidence 100 · mechanical · Fix: rebase onto the current base and re-run. -
The description claims "no new secret — uses
permissions: models: read", and the code does the opposite —.github/workflows/nightly.yml:1786-1800,.github/workflows/nightly.yml:1836-1840
The narrative job needs a newsecrets.COPILOT_PAT(a Copilot-licensed PAT, per its own comment) and declares onlycontents: read. Nothing in the diff hasmodels: read. A maintainer reading the description would not know a new PAT must be provisioned.
Confidence 100 · mechanical · Fix: correct the description. Also document the secret and its minimum scope. -
The description omits work the change does — PR description
It does not mention:- the Strix Windows runner-label move;
- the comment rewrites in
e2e-wsl-nightly; - the new
COPILOT_PATdependency; - the unpinned npm install.
Its "degenerate-output guard" claim is not delivered (see above).
Confidence 85 · mechanical · Fix: narrow the PR to the feature and update the description to match the code.
Non-blocking
-
Unpinned
npm install -g @github/copilotruns withCOPILOT_PATin the environment —.github/workflows/nightly.yml:1818,.github/workflows/nightly.yml:1836-1840
Each night installs the latest version with no version pin, and the job holds a secret. This is the same supply-chain reasoning as AGENTS.md §6's action pinning. Separately, the checkout at:1805keeps its credentials, which this job does not need.
Confidence 85 · architectural · Fix: pin@github/copilot@x.y.z, and setpersist-credentials: falseon that checkout. -
actions/ai-inferencepin comment is# v3, not# vX.Y.Z—.github/workflows/nightly.yml:1836
The SHA matches upstream'sv3tag, which is the release name. Nov3.x.ytag exists, so §6's comment form cannot be met literally. Note thatv3is the kind of tag upstream re-points.
Confidence 70 · mechanical · Fix: keep the SHA. Note in the comment that no patch tag exists, or wait for one. -
always()where!cancelled()fits —.github/workflows/nightly.yml:1746,.github/workflows/nightly.yml:1801
Both jobs run after a workflow cancel, and the narrative job uses the PAT when it does. The publish job is already gated with!cancelled().
Confidence 85 · mechanical · Fix: use!cancelled(). -
Commit classification misses user-facing changes without saying so —
xtask/src/release_readiness.rs:181-224
These are dropped:- ticket-prefixed subjects such as
EAI-7404: make runtime activation and rollback transactional (#424)(32 of the 374 commits on the base tip); - capitalised
Fix …/Feat:and spacedfeat (cli):; Revert "…"subjects, so the revertedfeatstill lists as new.
extract_pr_numberalso takes the last(#anywhere in the subject, not only a trailing one, despite its doc. Probe:fix: handle (#12) refsrenders… (#12)twice. The table never reports how many commits went unclassified, and--limit 100caps the issue list while the header says "(N open)".
Confidence 85 · logic · Fix: match the type case-insensitively, report the unclassified count, anchor the PR match to the end of the subject, and note any truncation. - ticket-prefixed subjects such as
-
The two wiki publish jobs can race —
.github/workflows/nightly.yml:1880-1882
publish-e2e-wikiandpublish-release-readiness-wikipush to the same wiki repo in the same run. They use different concurrency groups and neither retries, so a non-fast-forward rejection fails one of them.
Confidence 70 · logic · Fix: share one concurrency group, orpull --rebaseand retry on push. -
The prompt contradicts itself —
.github/prompts/release-readiness-narrative.txt:25-35
Section 3 asks the model to highlight "failing gates", and the hard rules forbid "any pass/fail/status judgment".
Confidence 70 · mechanical · Fix: allow restatingfailing_gatesverbatim and forbid only the model's own verdict. -
Commit history would put false or noisy claims into the squash body — commits 834ae24, e654b4c, 300abb0, cc75a79
834ae24 ("temporarily narrow") and its revert are noise. 300abb0's "switch … to gpt-4.1" is undone by cc75a79, and the two give different reasons for the model choice. 51d125a's "no registered runner carries native anymore" is contradicted by the Linux nightly lane, as noted above.
Confidence 85 · mechanical · Fix: squash or reword these before merge so the squash body describes the final state.
Decisions for the author
-
Overlapping "release readiness" name — non-blocking-improvement
scripts/release_readiness.pyis already the release gate under that name: AGENTS.md §8,docs/release-trust.md, andnightly.yml:15,154,246,294. The new "GO" ignores what that gate checks:publish-nightly;- the production-trust gate state;
- clippy, smoke, and the required GPU-lane set.
A different name ("nightly health / release-notes preview"), or listing the unchecked gates on the page, would stop readers mistaking it for the gate.
-
New xtask module vs. extending
crates/e2e-report— tradeoff
e2e-reportalready owns per-platform PASS/FAIL and expected-vs-actual reconciliation. Having it emit a machine-readable verdict would remove the scraping of rendered markdown, which is the root of the first finding. A separate module and page keep the release-preview concern apart from the support matrix. Both are reasonable; confirm the choice was deliberate. -
Release baseline includes pre-release tags — tradeoff
git describe --match 'v*'matches-rcand-previewtags, so "since last release" can mean "since the last RC". It resolves tov0.2.0today.
Positive signals
- The verdict, counts and PR numbers are computed outside the LLM step, and the publish job tolerates a missing narrative.
- The
GITHUB_OUTPUTheredoc uses a random delimiter for content holding untrusted data, and the LLM response reaches the shell throughenv:, not${{ }}interpolation. - ai-inference is used without
copilot-allow-tools, so injected text cannot run commands.
Deployment notes
- Needs a new repository secret,
COPILOT_PAT: a Copilot-licensed PAT. Its minimum scope is undocumented. - Creates and overwrites a new
Release-Readinesswiki page nightly. The wiki must already be initialised. - Moves the nightly Strix Windows lane onto the DevLab Dispatch pool, which adds load to the pool the per-PR lanes use. Commit 834ae24's message reports this lane hanging to its 90-minute timeout there.
What this covered
All four changed files read completely (.github/prompts/release-readiness-narrative.txt, .github/workflows/nightly.yml, xtask/src/main.rs, xtask/src/release_readiness.rs), plus crates/e2e-report consolidation, xtask/src/workflow_contract.rs, e2e-selfhosted.yml, docs/ci-hardware-testing.md, scripts/release_readiness.py, and ai-inference's source at the pinned SHA. Reviewed against prw-base, merge-base 34fbdfe…7235dd94 (merges cleanly with the base tip). What ran: cargo test -p xtask release_readiness (6 tests ran), cargo clippy -p xtask --all-targets -D warnings (clean), and 16 single-branch mutations plus probe inputs. All four passes ran, fanned out to independent workers alongside a design pass.
Did not run:
- Signature verification locally (no keys); CI's result was used instead.
- actionlint (not installed).
- The xtask coverage measurement (the CI coverage check is green).
- Confirmation that the
claude-sonnet-5.5slug is valid, or of the claim that the native Windows runner is unregistered.
"E2E tests (MI300X)" was still queued.
Not reconciled against any PR discussion here.
7235dd9 to
367acd3
Compare
|
Thanks for the thorough review — addressed everything below. Branch was rebased onto current Blocking, addressed in
|
rominf
left a comment
There was a problem hiding this comment.
🔴 Automated review · pr-review-watcher · 367acd3
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.
Round 2 — full review again: the round-1 head (7235dd9) no longer exists after the force-push. Each finding is marked new or standing. Round 1's change request was dismissed by the author. This change request restates every objection that still holds.
Round-1 objections settled this round:
- Skipped nights publish a false NO-GO: resolved. The review did not re-find it. When asked afterwards, the reviewer confirmed
assemble_factsnow passesskipped, and a test pins that. - Strix Windows runner move: resolved. It is gone from the diff (moved to #682).
- Placeholder commit identity: resolved. All 9 commits are GitHub-Verified under the author's own identity.
- "Test (affected crates)" failure: resolved. It is green after the rebase.
- Description scope and secrets claim: resolved. The rewritten description was not re-found as a mismatch.
- Dead wiki guard: resolved. The guard now trips. What it now causes is the new finding about NO-GO never being published.
- The untrusted-issue-text objection still holds; it is restated at the end of Blocking.
- Unpinned
npm install(non-blocking in round 1): mostly resolved. The CLI is now pinned to an exact version, no secret is present at install time, and the checkout no longer keeps credentials. One gap remains: the CLI's dependencydetect-libc ^2.1.2still floats. It is loaded inside the step that holdsCOPILOT_PAT, so a bad future release would run with that token in reach. Pinning it,--ignore-scripts, or a committed lockfile would close the gap. Not gating.
Self-hosted lanes not reporting at this head: "E2E tests (MI300X)" and "E2E tests (Strix Halo, Ubuntu) (release)" are queued, and "E2E tests (Strix Halo, Ubuntu) (nightly)" is in progress. That is missing evidence, not a defect. Every hosted check passed.
Review — needs work
Full review of the whole change.
The description names ROCMAI-568, which cannot be retrieved from here. The PR adds cargo xtask release-readiness and three nightly jobs that publish a GO/NO-GO page to the repo wiki.
Blocking
- standing (narrowed) — The E2E gate gives GO when most platforms reported nothing —
xtask/src/release_readiness.rs:347-353,xtask/src/release_readiness.rs:126-130,.github/workflows/nightly.yml(release-readiness-nightlyneeds:)total_e2e_failuresreads only theFailcell of the consolidated**Total**row.consolidated_summary_markdown_from_reports(crates/e2e-report/src/consolidated.rs:1032-1076) adds up only the reports that were actually downloaded.- The GPU lanes are
continue-on-error. A lane that dies before uploadingreport.json(preflight failure, timeout, runner loss) simply drops out of the Total. A platform with zero scenarios (EMPTY) adds 0. - The job never looks at the lane results or at
needs.e2e-report-nightly.result. So one passing platform out of six gives GO, and the table says "0 failure(s) across all platforms". - This contradicts the module's own rule for
e2e_fail_total("treated as a failing gate rather than silently reading as zero failures"). It also contradicts the description's claim that "E2E evidence that's missing … now fails the gate explicitly". Only a missing Total row fails it. - Confidence 85 · logic · Fix: gate on every platform row's
Statusbeing PASS and on an expected platform set, or pass the lane job results in as gates.
- new — A NO-GO is never published, so the wiki keeps showing the previous verdict —
xtask/src/release_readiness.rs:386-391,.github/workflows/nightly.yml(publish-release-readiness-wikiguard and itsresult == 'success'condition),xtask/src/workflow_contract.rs(release_readiness_wiki_publish_guard_stops_on_unreadable_e2e_evidence)- When E2E evidence is unreadable,
render_tableleaves out the| Area | Evidence | Status |header on purpose. The guard then exits 0 without publishing, and the new contract test requires exactly that. - Every other failure has the same effect, because the publish job only runs when
release-readiness-nightlysucceeded. Examples: the artifact download fails,git describefails, orghfails. - An empty report is a realistic case:
e2e-report-nightlyredirects intosupport-matrix.mdbefore cargo runs, so a cargo failure leaves an empty file. - On exactly the nights when readiness is unknown, the page keeps the last GO. Keeping the last good page makes sense for the support-matrix page this job copies. For a verdict page it fails in the wrong direction.
- Confidence 85 · logic · Fix: publish a NO-GO / "evidence unavailable" page in these cases. Keep the guard only for a truly empty file, keyed on a line
render_tablealways emits.
- When E2E evidence is unreadable,
- standing (narrowed) — Several of the new gate checks have no test that fails when they break —
xtask/src/release_readiness.rs:120,:128,:397,:406- The reviewer made four changes at once and all 14
release_readinesstests still passed:- the
Some(n) if n > 0E2E check made to never fire - the
nightly_resultcheck removed - the Build cell forced to ✅
- the E2E cell forced to ✅
- the
- No test covers E2E failures > 0 giving NO-GO, a Linux nightly failure giving NO-GO, or either ❌ cell. The description says assembly was extracted so "the GO/NO-GO logic is directly unit-tested". That is only partly true.
- Confidence 100 · mechanical · Fix: add one test for each case, so that each of the four changes above fails a test.
- The reviewer made four changes at once and all 14
- standing — round 1 rated this non-blocking. This change request does not rest on it, and it gates nothing on its own. — The
actions/ai-inferencepin is commented# v3, not# vX.Y.Z—.github/workflows/nightly.yml(Draft narrativestep)- AGENTS.md §6 requires a full SHA "with a trailing
# vX.Y.Zcomment, never a moving tag". - Checked via
gh api:2c43c91…is the lightweight major tagv3. Nov3.x.yexists yet, so neither the reader nor Dependabot can tell which release is pinned. - Confidence 85 · mechanical · Fix: pin to the latest semver release (
v2.1.1), or get a maintainer to accept the exception explicitly and note it in the comment.
- AGENTS.md §6 requires a full SHA "with a trailing
- standing (round 1) — Text from outsiders' issues still reaches the public wiki, and the escaping fix can be bypassed —
xtask/src/release_readiness.rs(escape_markdown,collect_known_issues),.github/workflows/nightly.yml(Read facts for prompt,Draft narrative,Push page to the wiki)
This round's blind review did not report it as blocking: it rated the narrative path non-blocking at 70. It was put to the reviewer afterwards as a standing objection, with the author's defence ("addedescape_markdown(), applied to every known-issue title"). The verdict below comes from that follow-up question and is anchored evidence, not a blind finding. Most of the objection still holds:- Titles: the escaping can be bypassed.
escape_markdowndoes not escape\. A title written as\[click\](https://evil.example)comes out with doubled backslashes, and under CommonMark that renders as a live link intable.md. The reviewer ranescape_markdownon it and read the result against the escape rules, but could not render it (no renderer installed). Bare URLs also pass through and are autolinked. Raw HTML is neutralised. - The narrative path is unchanged. Issue titles and 240-character body excerpts are still the LLM prompt. At the pinned SHA the system prompt and the user prompt are one string.
narrative.mdis stillcat'd onto the wiki unfiltered. An outsider can therefore try, every night and unattended, to get links, prose implying GO, or a "Known Issues" list that drops real blockers onto the official project wiki. - Limits: whether it works depends on the model following the injected text. The mechanical verdict line and the raw issue list above it cannot be changed. There is no command execution.
- Unverified: whether GitHub honours the bug template's automatic
buglabel, since a licence comment comes before it. If it does not, a triager applyingbugis the gate. That narrows who can do this but does not remove it. - Fix: escape
\first, or render issues as#NNNlinks only. Either keep the narrative as a run artifact or step summary, or feed the model only maintainer-authored inputs, never issue bodies.
- Titles: the escaping can be bypassed.
Non-blocking
- new — Untrusted issue text reaches the LLM, and its output is published to the wiki unescaped —
xtask/src/release_readiness.rs:288-327,.github/workflows/nightly.yml(Read facts for prompt,Push page to the wiki)- Open
bugissues come from outside contributors (the bug template asks forlabels: bug). Their titles and 240-character body excerpts are inlined as the prompt. - At this SHA the action joins the system prompt and the user prompt into one string, so the prompt's rules can be overridden.
narrative.mdiscat'd onto the official wiki. Issue titles are escaped in the table, but the narrative is not escaped at all.- The token and permissions are scoped well, so this is limited to content: links, images, or prose contradicting the mechanical verdict.
- Confidence 70 · architectural · Fix: restrict input to issues from maintainers, or drop the body excerpts. Strip links and images from the narrative before publishing.
- Open
- new — Commit subjects are written to the wiki unescaped —
xtask/src/release_readiness.rs:419,:422. Squash subjects are contributor-written PR titles, while issue titles already go throughescape_markdown. Confidence 70 · logic · Fix: escapee.subjecttoo. - new — "👍 N" counts every reaction type —
xtask/src/release_readiness.rs:321,:446. It sumsusers.totalCountover allreactionGroups, including 👎 and confused. Confidence 85 · logic · Fix: filter toTHUMBS_UP, or label the number "reactions". - standing (round 1) — The known-issue count is silently capped at 100 —
xtask/src/release_readiness.rs:299-300,:432-435. With--limit 100, the header shows "(100 open)" once there are more than 100 open bugs. Confidence 70 · logic · Fix: paginate, or print "100+" when the cap is hit. - new —
!counts as a breaking change for any commit type —xtask/src/release_readiness.rs:216,:236. Unscopedchore!:,docs!:,ci!:andtest!:subjects land in the end-user "Breaking Changes" list. The scope filter does not catch them. Confidence 70 · logic · Fix: count as breaking only for user-facing types. - standing (round 1) — A PR suffix without a leading space is printed twice —
xtask/src/release_readiness.rs:229-233. Forfix: x(#12),pr = 12is extracted, but only" (#12)"is stripped, so it renders asx(#12) (#12). Confidence 70 · mechanical · Fix: strip the exact span thatextract_pr_numbermatched. - standing (round 1) — The baseline tag pattern also matches prerelease tags —
.github/workflows/nightly.yml(since_tag=$(git describe --tags --abbrev=0 --match 'v*')). The repo hasv0.2.0-rc.1,v0.1.0-preview.1and similar tags. Once an RC is tagged on main, "since the last release" becomes "since the RC". Confidence 70 · logic · Fix: add--exclude 'v*-*'. - new — The new page and its secret are not documented —
docs/ci-hardware-testing.md(thepublish-e2e-wikiparagraph). The doc covers the E2E wiki page and its one-time setup. Nothing underdocs/mentions theRelease-Readinesspage, theCOPILOT_PATprerequisite, orcargo xtask release-readiness. Confidence 70 · mechanical · Fix: extend that paragraph.
Decisions for the author
- Name collision with the existing release gate — tradeoff
scripts/release_readiness.py(documented indocs/release-trust.mdanddocs/testing.md, and referenced innightly.yml's env comments) is the signing/trust release gate.cargo xtask release-readinessis a nightly summary with its own GO/NO-GO.- Sharing the name is convenient. The cost is that readers may take one for the other, especially since both say "release readiness".
- The Build row shows ✅ on a no-op night — non-blocking-improvement
- On a skip night,
nightlysucceeds without building andwindows-nightlyis skipped. - The row "Build (Linux + Windows nightly) ✅" reads as "built and passed". "Not built (no new commits)" would be more accurate.
- Accepting
skippedfornightly_resultdoes nothing, because that job has noif:.
- On a skip night,
Positive signals
- The verdict, counts and PR numbers are computed without the LLM, and the narrative job is
continue-on-errorwith an optional artifact download. A broken LLM step cannot change the table. - The new contract test runs the real bash guard from
nightly.ymlagainst realrender_tableoutput instead of a pinned string. Removingexit 0from the guard made it fail. - The LLM response reaches the shell only through
env:andprintf '%s\n', never inline${{ }}.GITHUB_OUTPUTuses a random heredoc delimiter. - Each job has narrow permissions (
contents: read/issues: read), and both checkouts usepersist-credentials: false. Every other new action pin was verified viagh apito match its# vX.Y.Zcomment. - A failing
git logorgh issue listaborts the run instead of producing an empty list.
Deployment notes
- Requires a
COPILOT_PATrepo secret holding a Copilot-licensed PAT. Without it the narrative job fails (non-blocking) and the page has no narrative. - Requires at least one non-prerelease
v*tag reachable from main. Without one,git describefails and the page is never published. - Uses the existing repo wiki, which must already be initialized (same prerequisite as
publish-e2e-wiki).
What this covered
- Read every changed file completely against
21be0034…367acd35:xtask/src/release_readiness.rs,xtask/src/main.rs,xtask/src/workflow_contract.rs,.github/workflows/nightly.ymland.github/prompts/release-readiness-narrative.txt. Also read the jobs these depend on innightly.yml, the consolidated-report generator incrates/e2e-report, the bug issue template, and the docs that mention the nightly wiki. - Two read-only workers covered the code passes (xtask module; workflow + contract test). The reviewer ran the agent-instruction, history and code-comment passes and the design questions itself, so those are not independent.
- CI at this head: every hosted check passed, including commit signatures/DCO. Three self-hosted GPU lanes (MI300X, Strix Halo Ubuntu nightly and release) were still queued or running. That is missing evidence, not a defect.
- Did not run:
- prior-changes pass — no access to earlier review threads, by design.
- workflow end-to-end and
actionlint— not runnable here. - ROCMAI-568 — no tracker access.
- Not checked: whether Copilot CLI 1.0.97 accepts the
claude-sonnet-5.5slug. - Not reconciled against the PR's discussion or earlier reviews. Standing objections were settled separately, as noted above.
|
Addressed the blocking findings from @rominf's review (5522757577):
Also fixed the two non-blocking findings: On the two "decisions for the author" items, left as-is deliberately:
|
7cfce6a to
1cb356d
Compare
rominf
left a comment
There was a problem hiding this comment.
🔴 Automated review · pr-review-watcher · 1cb356d
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.
Round 3 — full review again: the round-2 head (367acd3) no longer exists after the force-push. Each finding is marked new or standing. Round 2's change request was dismissed by the author. This change request restates every objection that still holds.
Round-2 objections settled this round:
- E2E gate gives GO when lanes upload no report: still holds. It was re-found blind (first item under Blocking). The new
--e2e-lane-resultgate does not close it. Commit 40f5d62's message also says "a lane that dies before producing a report still fails the verdict", and that sentence would land on main with the squash. - New gate checks have no test that fails when they break: still holds. It was re-found blind with a new set of surviving mutations (fourth and third items under Blocking).
- NO-GO never published: partly resolved. The blind review reported only a related non-blocking item. When asked afterwards (anchored evidence, not a blind finding), the reviewer confirmed:
- An empty or unreadable E2E report now publishes a NO-GO page. That part is resolved.
- Any failure of
release-readiness-nightlystill publishes nothing, and the wiki keeps the last page, which may say GO. The causes are a missing report artifact or file,git describe,gh issue list, or the xtask build. This part is restated as the last item under Blocking.
- Untrusted issue text reaches the public wiki: partly resolved.
- The narrative half still holds and was re-found blind (second item under Blocking).
- The title half was put to the reviewer afterwards (anchored). The backslash bypass is fixed:
escape_markdownnow doubles\, and a test catches removing that. Commit subjects now go through the same escaping. - Bare URLs in titles and subjects still pass through unescaped and would likely be autolinked. This was not rendered to confirm. It is folded into the narrative item rather than gated on separately.
# v3pin comment (not gating): still holds. It was re-found as non-blocking below.- Copilot CLI's floating
detect-libc ^2.1.2dependency in theCOPILOT_PATstep (not gating): not re-checked this round.
CI at this head (from the driver, compared against main's own runs):
E2E testsfails with the same dashboard-quit and full-model-name scenarios on main's latest CI run (6186f15), so it does not gate.E2E tests (Strix Halo, Windows, current)failed on a 404 downloading an external llama.cpp release. The same lane also failed on main at c7a0d65. This PR does not touch that path.- The consolidated report failed downstream of the lanes, and MI300X was cancelled.
- The method lists these under Blocking. This change request does not rest on them; re-running after a rebase should settle them.
Review — needs work
Full review of the whole change.
The description names Jira ROCMAI-568, which could not be retrieved from here (no tracker tool). The description's account of the change is out of date in places (see Blocking).
Blocking
-
standing — The new per-lane E2E gate can never fire for a failed lane, and it leaves out the W7900 lane —
.github/workflows/nightly.yml:1701-1710,.github/workflows/nightly.yml:1747-1752,xtask/src/release_readiness.rs:134-144,xtask/src/main.rs(--e2e-lane-resulthelp),docs/ci-hardware-testing.md:199-203- Why it never fires: every lane job sets
continue-on-error: true, soneeds.<lane>.resultreadssuccesseven when the lane failed. Nightly run 39103436218 shows this:- Both Strix Halo Windows lane jobs concluded
failure. - In the same run,
e2e-report-nightly's annotation says:lane e2e-gpu-nightly-strix-windows ran (needs result: success) but uploaded no e2e-gpu-strix-windows-release-report. - Run 39554529729 shows all 7 lanes reporting
"success"inNEEDSeven though three of them failed.
- Both Strix Halo Windows lane jobs concluded
- Consequence: a lane that dies before uploading drops out of the
**Total**row, and the verdict can still read GO. The code comment, the--e2e-lane-resulthelp and the docs sentence all say this case fails the gate. The code governs here: those statements describe a gate that does not work. - W7900: separately,
e2e-gpu-nightly-w7900is ine2e-report-nightly'sneedsandLANE_REPORTS, but not in the new job'sneedsor in its lane list. - The signal that does work:
needs.e2e-report-nightly.result. That job's "Check every lane that ran uploaded its report" step exists for exactly this case. Release readiness already lists the job inneedsbut never gates on its result. - Confidence 100 · logic · Fix: gate on
needs.e2e-report-nightly.result, or reuse its artifact-list check. Drop the--e2e-lane-resultplumbing or correct what it claims. Add a contract test that ties any lane list toLANE_REPORTS.
- Why it never fires: every lane job sets
-
standing (narrative half) — The link-stripping
seddoes not stop what its comment says it stops, and its input is attacker-controllable —.github/workflows/nightly.yml(publish step,sed -E 's/!?\[([^]]*)\]\([^)]*\)/\1/g')- The comment says the narrative "can't smuggle a redirect or tracking pixel onto the public wiki page".
- I ran the sed. These pass through unchanged:
<img src="https://example.com/p.png">- reference-style
[a][1]with[1]: https://example.com/x - bare URLs and autolinks
- Nested
[outer [inner](https://a.example)](https://b.example)comes out as a live link:outer [inner](https://b.example). - Who can inject:
.github/ISSUE_TEMPLATE/bug_report.mdsetslabels: bug, so any GitHub user's issue title plus a 240-character body excerpt goes into the LLM prompt. The LLM output is then published to the public wiki. - Confidence 100 · logic (security) · Fix: render the narrative as plain text (escape it, or put it in a fenced block), or allowlist its output. At minimum, correct the comment so it does not claim protection that is not there.
-
new —
collect_known_issues_counts_only_thumbs_up_reactionsnever calls production logic —xtask/src/release_readiness.rs:816-828- The test re-implements
filter(THUMBS_UP).map(..).sum()inline. The only thing it shares with production is the serde shape. - Replacing the production filter with "count all reactions", or reversing the sort, still passes. Both mutations were run.
- Confidence 100 · mechanical · Fix: extract the
RawIssue→KnownIssuemapping and sort into a pure function, and test that function.
- The test re-implements
-
standing — Several branches the change introduces are not exercised by any test. Each mutation below was run alone against
cargo test -p xtask release_readiness(24 tests) and survived:- The verdict headline.
## Release Readiness — ✅ GOvs❌ NO-GOis never asserted; swapping them passes. (release_readiness.rs:406-413) - PR-number rendering. Dropping the
(#N)suffix from rendered entries passes. (:452-459) - Treating
skippedas passing for the nightly job and for lanes. Only the Windows twin is tested. (:123,:141) - The
n > 0boundary. Changing it ton > 1passes. (:131) escape_markdown's newline collapsing and its</>escaping. Removing either passes. (:394,:396)- The
ends_with(')')guard insplit_trailing_pr_reference. Removing it passes, and it is not equivalent: thelen()-1slice can then panic on a subject ending in a multibyte character. (:269) - Confidence 100 · mechanical · Fix: add an assertion for each.
- The verdict headline.
-
new — The PR description misstates the change
- It says "the table header is omitted when E2E evidence is unreadable, so the guard can actually trip". The code does the opposite.
render_tablealways emits the header (release_readiness.rs:416-427), and the new contract test asserts that unreadable evidence reaches the publish step. - It does not mention the per-lane
--e2e-lane-resultgate added in 40f5d62, which is the main gating change in the later commits. - It says the job mirrors the E2E wiki job's guards.
publish-e2e-wikialso skips whene2e-report-nightlyfails; this job does not. - Confidence 90 · Fix: update the description to match the code. Narrow it; don't add work to fit it.
- It says "the table header is omitted when E2E evidence is unreadable, so the guard can actually trip". The code does the opposite.
-
new — does not gate (see the CI note above) — Required CI checks are failing at 1cb356d
E2E tests(CI):dash-esc-menu-quit-row-confirms-when-servingregressed (TUI quit timeout).E2E tests (Strix Halo, Windows, current): 404/403 errors downloading external llama.cpp and TheRock artifacts.E2E consolidated report (self-hosted)is failing, andE2E tests (MI300X, current)was cancelled.- These failures are in code paths this PR does not touch, so they may be shared with main. I did not compare against main.
- Confidence 100 · Fix: re-run or rebase once main is green.
-
standing (round 2, narrowed), unverified blind this round — When
release-readiness-nightlyfails, nothing is published, so the wiki keeps the previous verdict —.github/workflows/nightly.yml(publish-release-readiness-wiki'sneeds.release-readiness-nightly.result == 'success'condition),xtask/src/release_readiness.rs:89-90,:332-337- This round's blind review rated a related item non-blocking at 75 (the second item under Non-blocking). The rest comes from the follow-up question and is anchored evidence.
- The job fails outright, rather than producing a NO-GO page, when:
- the E2E report artifact or file is missing (the
?at:89); git describefails;gh issue listfails (:332-337), which runs before the E2E report is read, so a flaky issue listing alone suppresses the page;- the xtask build fails.
- the E2E report artifact or file is missing (the
- In each case the page keeps whatever the last successful night published, which may be GO, and nothing on the page marks it stale. For a verdict page that fails in the wrong direction.
- Fix: publish a NO-GO / "evidence unavailable" page when the job cannot reach a verdict. A fallback publish step on
failure()would do it, as would a staleness line naming the run that produced the page.
Non-blocking
- new — The docs paragraph is inaccurate beyond the lane sentence —
docs/ci-hardware-testing.md:199-211- It opens "On
main", butrelease-readiness-nightlyandrelease-readiness-narrativehave no ref guard. They run on any branch dispatch and use theCOPILOT_PATsecret there. - It says "same … guards as
publish-e2e-wiki", which is not true (see the description finding). - Confidence 85.
- It opens "On
- new — A missing E2E report publishes nothing, and the wiki keeps the previous verdict, possibly GO —
xtask/src/release_readiness.rs:89-90- A missing artifact or file fails the job via
read_to_string(...)?. Only a report that is present but unparseable becomes NO-GO. - The comment at
:416-420("a NO-GO page must still publish") holds only for that second case. - Confidence 75.
- A missing artifact or file fails the job via
- new — The status table hides which gate failed —
xtask/src/release_readiness.rs:421-441failing_gatesis never rendered intable.md.- When a lane is cancelled, the row reads
| E2E | 0 failure(s) across all platforms | ❌ |: the evidence says zero failures while the status is red. - Confidence 85.
- standing — The
actions/ai-inferencepin comment is# v3, not the# vX.Y.Zform AGENTS.md §6 requires —.github/workflows/nightly.yml(Draft narrative step)- The SHA does match the lightweight
v3tag, and nov3.x.ytag exists, so this is a reasoned exception. - But the comment's statement that the action "tags its releases as bare majors" is wrong: v2.x.y and v1.x.y semver tags exist.
- Confidence 85.
- The SHA does match the lightweight
- new —
extract_pr_numberis a#[cfg(test)]wrapper that only forwards tosplit_trailing_pr_reference—xtask/src/release_readiness.rs:281-284. The tests could call the real function directly. Confidence 80. - new — The
--since-taghelp text is out of step with the workflow —xtask/src/main.rs. The help cites--match 'v*', but the workflow also passes--exclude 'v*-*'. Confidence 85.
Decisions for the author
- LLM-drafted prose from public issue text, published to a public wiki — tradeoff. The prose makes the page more readable. The cost is a narrative that any issue filer can steer, which the prompt cannot fully prevent. The alternative is the mechanical table alone, or a narrative restricted to commit data. Confirm that this exposure is deliberate.
- Both wiki publish jobs push to the same wiki under different concurrency groups, with no retry — tradeoff. They seldom overlap in practice (seconds-long jobs, minutes apart). Overlap needs runner queueing, and the loser then fails non-fast-forward. A shared group such as
nightly-wikiremoves the race.
Positive signals
- Keeping the verdict and counts fully mechanical (
assemble_facts), with the LLM only drafting prose, cleanly separates what is trusted from what is not. - The new contract test runs the real publish guard, extracted from
nightly.yml, againstrender_table's real output rather than a pinned string. Both guard mutations were caught. - The facts are inlined through a
with:expression with a random delimiter rather than through a shell, so there is no shell injection from issue titles.
Deployment notes
- The
COPILOT_PATrepo secret must exist (a Copilot-licensed PAT), or the narrative job fails and the page shows its placeholder line. - The wiki must already be initialised, the same prerequisite as
publish-e2e-wiki. - The
@github/copilot@1.0.97install and theclaude-sonnet-5.5model slug are pinned inrun:andwith:, so Dependabot will not bump them. - A non-prerelease
v*tag must be reachable from main, or the job fails before publishing.v0.2.0satisfies this today.
What this covered
All 6 changed files were read completely, along with the existing nightly.yml lane jobs, e2e-report-nightly and publish-e2e-wiki, crates/e2e-report's consolidated markdown format, and the issue templates. Range: prw-base (6186f15)…1cb356de.
Ran two independent read-only workers: an xtask code pass with 27 single-branch mutations plus 2 workflow-guard mutations, and a workflow/docs/policy pass. I re-checked their key claims myself against run annotations and by running the sed.
cargo test -p xtask release_readiness(24 tests) andcargo clippy -p xtask --all-targets -D warningspass, as the worker ran them.- The design questions (Step 7) were judged inline by me, not by independent workers.
Did not run:
- Prior-changes pass: would need other PRs' review threads.
- The new jobs end to end: no run at this head has exercised them.
- Copilot model slug and CLI version: not checked.
- Ticket: ROCMAI-568 is not retrievable here.
The PR's discussion was not reconciled here. Standing objections were settled separately, as noted above.
|
Addressed the latest review (id 5600395338) at Blocking
Non-blocking
All fixes verified via |
Addressed in 2ada576 (see summary comment). All blocking findings fixed and pinned by new tests; non-blocking items fixed except two explicit judgment calls called out in the PR description.
rominf
left a comment
There was a problem hiding this comment.
🔴 Automated review · pr-review-watcher · 2ada576
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.
Round 4 — full review of the whole change at 2ada576 (not narrowed: some round-3 objections, such as the commit messages and missing tests, cannot be scoped to a file). Each finding is marked new or standing. Round 3's change request was dismissed by the author. This change request restates every objection that still holds.
Round-3 objections settled this round:
- Per-lane E2E gate never fires; W7900 missing. The blind review did not re-find it in this form. When asked afterwards (anchored evidence, not a blind finding), the reviewer confirmed:
- Code: resolved.
--e2e-lane-resultis gone. The job now gates one2e-report-nightly, whoseLANE_REPORTSincludes W7900. The blind review raised a separate new gap (only thereleaseleg of each lane is checked, fourth item under Blocking). - Commit messages: still holds. It is restated as the last item under Blocking.
- Code: resolved.
- Link-stripping
sedlets links through; narrative built from outsiders' issue text: still holds. It was re-found blind in a new form: the escapingsedthat replaced it never doubles\(second item under Blocking). - Thumbs-up test never calls production code: resolved. The blind review did not re-find it. Asked afterwards (anchored), the reviewer re-ran both named mutations against
build_known_issues, and each now fails the test. - Branches that survive mutation: still holds. It was re-found blind with a new set of surviving mutations (seventh item under Blocking). The round-3 specific set (headline,
(#N),skipped,n > 0, newline/angle escaping,ends_with(')')) is now covered. - Failed
release-readiness-nightlypublishes nothing, wiki keeps the old (maybe GO) page: resolved. The blind review did not re-find it. Asked afterwards (anchored), the reviewer confirmed that each listed cause now reaches the EVIDENCE UNAVAILABLE page. Its residue (no test pins the new!= 'skipped'/!cancelled()conditions) is already part of the seventh item under Blocking. - PR description misstates the change: still holds, in new places. It is folded into the first, second and fourth items under Blocking. On its own a description mismatch does not gate, because the squash body is built from commit messages.
CI at this head (from the driver, compared against main's latest CI run at 32834b2):
windows-build-and-testfails on this PR's own new test, and passes on main. This gates (first item under Blocking).Test (affected crates)fails inpreflight_runs_the_real_reclaim_assert_clear, a pre-existing test this PR does not touch. It passes on main, and the branch predates the main commit that addresses it, so a rebase should settle it. Does not gate.E2E testsfails with the samedash-gen-tps-expiry-boundary/dash-esc-menu-quit-row-confirms-when-servingregressions as main's own run. Does not gate.- The
actions/ai-inferencepin item (sixth under Blocking) was rated non-blocking last round. Nothing new makes it a defect beyond what was known, so this change request does not rest on it.
Review — needs work
Full review of the whole change.
Ticket ROCMAI-568 is named but could not be retrieved from here, because no tracker tool is available. The description was compared against the diff; Blocking items 1, 2 and 4 cover where it overstates.
Blocking
-
new — A test this PR adds fails on the required Windows lane, and the description says the CI failures are unrelated —
xtask/src/workflow_contract.rs:3557(publish_release_readiness_wiki_escapes_markdown_link_and_html_syntax)- The test runs
Command::new("bash")but has no#[cfg(unix)]. Its siblingrelease_readiness_wiki_publish_guard_handles_missing_degenerate_and_well_formed_tables(:3431) and the other bash-driven tests in the file have that guard. - On
windows-build-and-test(job 119771938583),bashresolves to the WSL launcher, which has no distro installed. The run ended:panicked at xtask\src\workflow_contract.rs:3602:13: sed command failed … "Windows Subsystem for Linux has no installed distributions"and340 passed; 1 failed. - The description says "The CI check failures showing on this PR are pre-existing flakes unrelated to this diff". For this check, that is wrong.
- Confidence 100 · mechanical · Fix: add
#[cfg(unix)]like the sibling test, and correct the description.
- The test runs
-
standing (narrative escaping, re-found blind in a new form) — The wiki escaping of the LLM narrative can be bypassed with a backslash, and the description claims a backslash step that is not there —
.github/workflows/nightly.yml(thesed … release-readiness/narrative.mdline inpublish-release-readiness-wiki)- The actual command is
sed -e 's/\[/\\[/g' -e 's/\]/\\]/g' -e 's/</\</g' -e 's/>/\>/g'. It never doubles\. - The input
\[click\](https://evil.example)comes out as\\[click\\](https://evil.example). GitHub's markdown renderer (gh api markdown) turns that into a live<a href="https://evil.example">, and!\[img\](url)behaves the same way. - The narrative is LLM output drafted from untrusted issue and commit text, so a prompt injection can reach this.
- The description says the sed "doubles
\and escapes[,],<,>". The code governs here: the doubling is not in the command. - Confidence 100 · logic · Fix: add
-e 's/\\/\\\\/g'as the first expression, and add this input to the escape test.
- The actual command is
-
new — A lane report that is empty or unparseable still gives GO —
xtask/src/release_readiness.rs:381-387(total_e2e_failures),:129-133- The verdict reads only the
Failcell of the**Total**row and never reads any platform'sStatus. - When a report has no
platform.json,crates/e2e-reportuses its legacy counts. For areport.jsonof[]ornot json, it renders| … | 0 | 0 | 0 | 0 | n/a | EMPTY |. The lane did upload an artifact, so the lane-report check passes too. - Verified with a temporary test through
load_platform_reports→consolidated_summary_markdown→total_e2e_failures→assemble_facts. Both inputs gaveSome(0)andVerdict::Go. - Also,
total_e2e_failures_reads_the_totals_rowuses a hand-copied table, although the doc comment says parsing the rendered row "keeps this in lockstep" with the renderer. A column reorder inconsolidated.rs:1083would feed the wrong column into the gate, and no test would notice. - Confidence 100 · logic · Fix: fail on any platform row whose status is not
PASS. Better: havee2e-reportemit a machine-readable summary and gate on that, and test it against real renderer output.
- The verdict reads only the
-
new — The lane-report gate covers only the
releaseleg of each lane, but the code, docs and description say it covers every lane —release_readiness.rs:134-143,xtask/src/main.rs(--e2e-report-job-resulthelp),docs/ci-hardware-testing.md:199-204,nightly.yml:1588-1596- Every E2E lane runs
channel: [release, nightly].LANE_REPORTSlists only*-release-report, and the job's own comment says the release leg "stands for the whole lane". - So a
nightly-channel leg that dies before uploading drops out of the Total row, ande2e-report-nightlystill succeeds, giving GO. - That weakness was already in
e2e-report-nightly. This PR makes its result release-gate evidence, and states "a lane that dies before uploading a report still fails the gate" in the description, the docs and the help text. - Confidence 85 · logic · Fix: check both channel legs in
LANE_REPORTS, or narrow the three claims to match.
- Every E2E lane runs
-
new — The stale-evidence notice describes content that is not on the page —
nightly.yml:1895- The notice ends "Any verdict below this line predates this run and does not reflect it."
- The job then runs
cp "/tmp/${page}" "wiki/${page}", which replaces the whole page. Nothing is below the line, and the previous verdict is deleted rather than kept. - Confidence 85 · mechanical · Fix: reword it (e.g. "No verdict was computed for this run"), or append the previous page body if keeping it is the intent.
-
standing (rated non-blocking last round; this change request does not rest on it) — The
actions/ai-inferencepin has a# v3comment, not the required# vX.Y.Z, and the comment explaining it is wrong for v3 —nightly.yml(theDraft narrativestep)- AGENTS.md §6: "pin … to a full commit SHA with a trailing
# vX.Y.Zcomment, never a moving tag". gh api repos/actions/ai-inference/git/matching-refs/tags/vlistsv2.1.1and earlier, plus only the floatingv3→2c43c91a…. There is nov3.x.ytag.- The workflow comment and the description say the action tags full semver releases alongside the major alias. That does not hold for the line pinned here.
- Confidence 85 · mechanical · Fix: pin a semver-tagged release (e.g.
v2.1.1,a7805884…) if its interface fits, or record a documented exception and correct the comment.
- AGENTS.md §6: "pin … to a full commit SHA with a trailing
-
standing (untested branches, re-found blind with a new set) — Much of the new workflow gating has no test that fails when it breaks
- Each of these mutations, made alone, left
cargo test -p xtask workflow_contractgreen (70 passed):- removing
if: ${{ !cancelled() }}fromrelease-readiness-nightly. A failede2e-report-nightlythen skips the readiness job and the publish job, so the wiki keeps a stale GO.release_readiness_nightly_gates_on_the_e2e_report_job_not_individual_lanesstill passes, so its name claims more than it checks. - changing the publish job's
!= 'skipped'to== 'success', or dropping its!cancelled() - dropping the
refs/heads/mainguard - feeding
--windows-resultfromneeds.nightly.result - inlining the narrative as
${{ }}inrun:(expression injection) - replacing the random
GITHUB_OUTPUTdelimiter with a fixedEOF - dropping
persist-credentials: false
- removing
- In
release_readiness.rs, these survivedcargo test -p xtask release_readiness(36 passed):- taking the scope from
headinstead ofhead_no_bang(:237-239), sofix(ci)!:leaks into Breaking Changes - dropping
.trim()inis_internal_only_scope(:215), sofix(core, ci)is misclassified - removing
*,_and the backtick, or'\r', fromescape_markdown(:395-397)
- taking the scope from
- With the production change reverted, every new test fails, because the jobs and functions it calls no longer exist.
- Confidence 100 · mechanical · Fix: assert these conditions, and add the two categorisation cases (bang with an internal scope; a space after the comma).
- Each of these mutations, made alone, left
-
standing (round 3, commit-message half), settled by putting it to the reviewer: anchored evidence, not a blind finding — Commit messages that would form the squash body still describe behaviour this PR no longer has
- This repository squash-merges, and the squash body is built from the commit messages, so these land on
main. No later message in the range corrects them:fc76d6aand40f5d62describe the per-lane--e2e-lane-resultgate, including "a lane that dies before producing a report still fails the verdict".1cb356dsays the gate uses "each GPU/WSL lane's own result".e20b631removed that gate, but its message does not say so, and no message says the gate moved toe2e-report-nightly.40f5d62says links and images are stripped from the narrative. The code now escapes brackets and angle brackets instead, and no message says so.fc76d6asaysextract_pr_number"is kept test-only". The function no longer exists.- On the
actions/ai-inferencetags,40f5d62says "bare-major tags" ande20b631says "full semver releases". Neither holds for the pinnedv3line.
- I checked the
e20b631message myself and confirmed it has no correction. - Fix: reword the commits (rebase), or have the squash body rewritten to describe the final behaviour before merge.
- This repository squash-merges, and the squash body is built from the commit messages, so these land on
Non-blocking
- new — The LLM narrative can render headings and tables, so it can print a fake verdict —
nightly.yml(the## Narrativesection)- Only
[ ] < >are escaped. A prompt-injected narrative could render## Release Readiness — ✅ GOor a fake| Area | Evidence | Status |table under a real NO-GO. That undercuts the guarantee that the verdict stays mechanical. - Confidence 70 · logic · Fix: put the narrative in a blockquote or
<details>labelled as LLM-drafted, or escape a leading#,|and>on each line.
- Only
- standing (was a tradeoff last round) — The two wiki publish jobs can race on the same wiki repo —
publish-e2e-wikiandpublish-release-readiness-wiki- They use separate concurrency groups, and each does a depth-1 clone, a commit and a
git push. If the pushes overlap, the second is rejected as non-fast-forward and its job goes red. - The description treats this as a retry question, but sharing one concurrency group (e.g.
wiki-push) would prevent it outright. - Confidence 70 · logic.
- They use separate concurrency groups, and each does a depth-1 clone, a commit and a
- new — A broken consolidated-report upload shows "EVIDENCE UNAVAILABLE" rather than NO-GO — the
release-readiness-nightly"Download consolidated nightly report" step has nocontinue-on-error. Ife2e-report-nightlydies before writingconsolidated/, the page shows the notice, although the failed report job is itself a known NO-GO. Confidence 70 · logic.
Decisions for the author
- The name
release-readinessis already in use — non-blocking-improvement.scripts/release_readiness.pyis the release trust gate (AGENTS.md §8,docs/release-trust.md, thenightly.ymlenv comments). The newcargo xtask release-readiness/release_readiness.rsis an unrelated nightly summary. A distinct name (e.g.readiness-report) would stop contributors and agents confusing the two. - A skipped build counts as a pass — tradeoff.
skippedis accepted fornightly,windows-nightlyande2e-report-nightly. That is safe today, becauseskip=trueonly happens when there are no commits since the last published nightly, and that nightly exists only if both builds succeeded. The cost is that the Build row shows ✅ on nights with no build at all. - Dropped scopes include
deps— tradeoff.fix(deps): …commits, such as a security bump to a shipped dependency, can affect end users but are left out of Bug Fixes.
Positive signals
assemble_factsis a pure function, so the GO/NO-GO rules are unit-tested at their boundaries (e.g. exactly one failure).- A missing Total row and an empty bundle both fail safe to NO-GO, and
git/ghnon-zero exits arebail!ed rather than swallowed. - The narrative reaches the shell through
env: NARRATIVErather than${{ }}inrun:. The facts are inlined with a random 16-hexGITHUB_OUTPUTdelimiter. - At this SHA,
ai-inferencepasses no--allow-tool, so an injection through the facts can only affect the text output. - The guard test runs the workflow's real shell block against real
render_tableoutput rather than a pinned string, which closes the hand-copied-guard gap from the sibling wiki job.
Deployment notes
- Needs a Copilot-licensed PAT in the
COPILOT_PATrepository secret. Without it, the narrative job fails (it is allowed to) and the page publishes without prose. - Publishes a new
Release-Readinesswiki page onmain, and needs the wiki already initialised (it is a prerequisite shared withpublish-e2e-wiki).
What this covered
- Read: every changed file in full,
32834b29…2ada5764(prw-base...HEAD). That includesxtask/src/release_readiness.rs, thextask/src/main.rscommand, the three newnightly.ymljobs, the newworkflow_contract.rstests, the prompt file and the docs paragraph. Also read: thee2e-report-nightly,publish-e2e-wiki, lane and trigger sections ofnightly.yml, and the renderer incrates/e2e-report/src/consolidated.rs. - Passes: code passes in two coupled groups (Rust logic; workflow plus contract tests) as independent read-only workers. Agent-instruction adherence, history, code-comment and design passes ran in the driver's context because of the 2-worker cap, so they were not independent. The prior-changes pass read the earlier automated reviews on #590, the sibling wiki-publish PR.
- CI, as data:
windows-build-and-testfails on this PR's own test (Blocking item 1).Test (affected crates)fails inpreflight_runs_the_real_reclaim_assert_clear(coreutils: unknown program 'llama-server'). The branch does not include5dfa9a8(#712, which addresses multicall coreutils and is onmain).E2E testsfails on twodash-*scenarios and on missingresolved modellines.
- Not done: ROCMAI-568 was not retrievable here. Not reconciled against this PR's review discussion.
|
Addressed in c333065 (round 3 of feedback): Blocking — fixed:
Non-blocking — fixed this round (not just acknowledged):
Judgment call — deliberately left as-is:
Other non-blocking items (LLM narrative fake-heading/table risk, two wiki-publish jobs racing, the "EVIDENCE UNAVAILABLE" download-failure edge case, and the three "decisions for the author" items — Build/test/lint: |
rominf
left a comment
There was a problem hiding this comment.
🔴 Automated review · pr-review-watcher · c333065
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.
Round 5 — full review of the whole change at c333065 (not narrowed: the commit-message and missing-test objections cannot be scoped to a file). Each finding is marked new or standing. Round 4's change request was dismissed by the author. This change request restates every objection that still holds, and answers the author's round-4 summary comment.
Round-4 objections settled this round. None was re-found blind. Each was then put to the reviewer (anchored evidence, not a blind finding):
- Windows-lane failure of the new bash test: resolved. The test now has
#[cfg(unix)], andwindows-build-and-testpasses at this head (350 passed, 0 failed). - Backslash bypass of the narrative
sed: resolved. The line now starts with-e 's/\\/\\\\/g'. The reviewer ran it on the three bypass inputs and rendered the result withgh api markdown; no attacker link or image survives. Bare-URL autolinks remain, as the workflow comment says. - Empty or unparseable lane report gives GO: half resolved. An
EMPTYplatform row now counts as a failure. The other half still holds: the parser is tested only against hand-written tables, never againste2e-report's real output. That is folded into the first item under Blocking, whose fix already asks for exactly that test. - Only the
releaseleg of each lane is checked: resolved. All 7 channel-matrixed lanes now list both legs inLANE_REPORTS, and the help text, code comment and docs match. One stale comment remains (first item under Non-blocking). The PR description does not mention the change (fourth item under Blocking). - Stale-evidence notice refers to a verdict below the line: resolved. It now ends "No verdict was computed for this run."
# v3pin: resolved. The driver checked that it is nowactions/ai-inference@a7805884… # v2.1.1.- Untested workflow and gate branches and commit messages describing removed behaviour both still hold, and both were re-found blind (second, third and fifth items under Blocking). On the commit messages, the author proposes fixing them at merge time. Until the history or the squash body is actually reworded, the false claims land on
main, so this still gates.
CI at this head, compared by the driver against main (latest CI run at ea1d871):
Test (affected crates)fails inpreflight_runs_the_real_reclaim_assert_clear. This PR's code does not cause it: the branch predates5dfa9a8(#712), which fixes it, andmainpasses this job. A rebase settles it. This was rated non-gating last round, so this change request does not rest on it (sixth item under Blocking).E2E testsfails on the samedash-*quit scenarios asmain's own run. Does not gate.E2E tests (Strix Halo, Windows, current)failed to download the TheRock tarball on the runner (infrastructure).E2E consolidated report (self-hosted)fails because of that lane, which is correct. Neither gates.
Review — needs work
Full review of the whole change.
Implements ROCMAI-568 (nightly release-readiness wiki page). The ticket and the description agree on the feature. The description leaves out the LANE_REPORTS change, and the commit messages describe behaviour the code no longer has (both under Blocking).
Blocking
-
new — The E2E failure count is inflated about 90-fold, so the verdict can never be GO and the wiki prints a false failure count. —
xtask/src/release_readiness.rs:399-417(total_e2e_failures); its tests atxtask/src/release_readiness.rs:658-682- The "silently empty platform" pass checks every
|line in the document, not only the summary table. consolidated_summary_markdownwrites more tables after the summary: the expectation grid and command coverage. Their rows (| Scenario | …,| automations-01 … | ✅ |) have a non-numeric cell 5 and a non-PASS cell 8, so each row counts as one failure.- I ran the parser logic on the real
support-matrix.mdfrom nightly run 39554529729. The**Total**row has Fail = 7, and the function adds 620 phantom failures, for 627. - The parser tests only feed hand-written 4-line summary snippets, so they never see the producer's real output. The same blind spot would also miss a column reorder, which could silently read 0 failures and give a false GO.
- Confidence 100 · logic · Fix: scan only the rows between the
| Platformheader and the| **Total**row. Add a test that runse2e-report's realconsolidated_summary_markdown(with sidecars, so the grid renders) and parses its output.
- The "silently empty platform" pass checks every
-
standing (untested branches, re-found blind with a new set) — The gate logic has no tests that can fail: a cancelled or timed-out build reads as GO with every test green. —
xtask/src/release_readiness.rs:123-128,175-192,311-339- I replaced both build gates with
== "failure". All 46release_readinesstests still pass, socancelledortimed_outonnightly/windows-nightlywould yield GO. - A worker's mutation run also found that making
git log/gh issue listfailures return empty lists survives.run,collect_commit_subjectsandcollect_known_issueshave no tests. - Confidence 100 · logic · Fix: add
cancelledandtimed_outcases for both build gates, and failure-path tests for the two subprocess collectors (behind a seam if needed).
- I replaced both build gates with
-
standing (untested branches, re-found blind with a new set) — The workflow contract tests leave load-bearing wiring unpinned, and one test claims more than it checks. —
.github/workflows/nightly.yml:1736,:1867,:1874;xtask/src/workflow_contract.rs:3760-3770- I removed
continue-on-errorfrom both publish-job downloads and also hard-coded--nightly-result "success". All 75workflow_contracttests still pass. - Without
continue-on-error, download-artifact fails whenever there is no narrative, which is every night untilCOPILOT_PATexists. That fails the publish job and makes the "evidence unavailable" path unreachable. - The worker also found that these single mutations survive:
- dropping
release-readiness-narrativefrom the publish job'sneeds(the publish job then races the narrative); - dropping
fetch-depth: 0(git describethen fails every night); - renaming the page so it overwrites
E2E-Support-Matrix.md.
- dropping
release_readiness_narrative_reaches_the_shell_only_through_envonly checks that the safe lines are present (job.contains(...)). A second step that inlines${{ steps.draft.outputs.response }}intorun:would still pass, so the "only" in its name is a claim the test cannot fail on.- Confidence 100 · logic · Fix: pin these items in the contract tests. Make the env test also assert that
steps.draft.outputs.responseappears nowhere in the job except theNARRATIVE:env line (and theif:).
- I removed
-
new — The description does not mention the LANE_REPORTS change, which changes when a per-PR check fails. —
.github/workflows/nightly.yml:1575-1619,.github/workflows/e2e-selfhosted.yml:1691-1734,xtask/src/workflow_contract.rs(LANE_REPORTS tests)- Both workflows now require the
nightlyleg's artifact as well as thereleaseleg's for every channel-matrixed lane. Ine2e-selfhosted.ymlthat is the per-PR "E2E consolidated report (self-hosted)" check. - None of the Summary or "Changes since…" sections mentions it, so a reader of the description would not know a PR-gating check got stricter.
- Confidence 85 · architectural · Fix: describe it in the PR body, or split it into its own PR as was done with #682.
- Both workflows now require the
-
standing (round 3, re-found blind) — Commit messages that will land in the squash body describe behaviour the code no longer has. — commits
40f5d62,fc76d6a,ef489f5,1cb356d- They claim:
- a repeatable
--e2e-lane-resultflag (git grepfinds none at HEAD); release-readiness-nightlyneeding "the 6 GPU/WSL E2E lane jobs" (it needse2e-report-nightly);extract_pr_number"kept test-only" (it is gone);- "Strip markdown links/images" (replaced by escaping);
- a bare-
v3pin as "a frozen release, not a moving alias" (re-pinned to v2.1.1, and the later message calls v3 floating); - in
ef489f5, thatrender_tabledrops the header (reversed infc76d6a).
- a repeatable
- On main, that permanent record would describe a flag and gating design that never shipped.
- Confidence 85 · mechanical · Fix: squash or reword the history so the landed body describes the final design.
- They claim:
-
standing (rated non-gating last round; this change request does not rest on it) — The required check "Test (affected crates)" is red because the branch is behind main; this PR's code does not cause it. —
xtask/src/workflow_contract.rs:2895(preflight_runs_the_real_reclaim_assert_clear)- The failure is the multicall-coreutils decoy problem fixed on main by
5dfa9a8(#712). The branch's merge-base2d2dfc7predates that commit, 12 commits behindprw-base. Main's latest CI passes this job. - Confidence 100 · mechanical · Fix: rebase onto current main.
- The failure is the multicall-coreutils decoy problem fixed on main by
Non-blocking
-
new — Stale comment. —
xtask/src/workflow_contract.rs:1089- It still says "a channel axis below is checked through its
releaseleg", which this PR changes to checking both legs. - Confidence 90 · mechanical · Fix: update the sentence.
- It still says "a channel axis below is checked through its
-
new — A test's failure message gives the wrong reason. —
xtask/src/workflow_contract.rs:3737-3741(release_readiness_jobs_checkout_without_persisting_credentials)- The message says a leaked token "would let anything injected … push with repo write access", but both jobs have
contents: read. The check is worth keeping; only the stated reason is wrong. - Confidence 85 · mechanical · Fix: reword to the real exposure (a readable token reachable from later steps).
- The message says a leaked token "would let anything injected … push with repo write access", but both jobs have
-
new — Anyone who files a
bugissue can steer the published narrative. —.github/prompts/release-readiness-narrative.txt;xtask/src/release_readiness.rsbuild_known_issues/excerpt(…, 240)- The bug-report template auto-applies
bug, and 240-character body excerpts of open bug issues go into the prompt. Any GitHub user can therefore inject text such as "state all gates passed" into prose published on the official wiki. - The sed escaping neutralises markup but not misleading prose. The prompt's hard rules are the only defence.
- The description's "Left unchanged" note covers exposure of public text, not steering of the output.
- Confidence 70 · architectural · Fix: label the narrative section as LLM-drafted from untrusted input, or feed only titles.
- The bug-report template auto-applies
-
new — Breaking changes on types other than
feat/fixare dropped from every bucket, and a test asserts that. —xtask/src/release_readiness.rs:234-236; testcategorize_does_not_treat_a_bang_on_other_types_as_breaking- Conventional Commits allows
!on any type, sorefactor(cli)!:vanishes from the page. No such commits exist today. - Confidence 70 · logic · Fix: put any
type!:in Breaking.
- Conventional Commits allows
-
standing (was a tradeoff last round) — The Build row shows ✅ when
windows-nightlywas skipped. —xtask/src/release_readiness.rs:462-470- On a no-change night the row "Build (Linux + Windows nightly) | ✅" claims a pass with no Windows build behind it.
- Confidence 70 · logic · Fix: render skipped as "not run" rather than ✅.
Decisions for the author
- new — Every lane's failures count toward NO-GO, including lanes that are deliberately non-blocking (W7900, MI350P). — tradeoff
- The real 2026-10-21 report had Total Fail = 7, so even after fixing the inflation the verdict will likely be NO-GO most nights.
- A strict gate is defensible, but a verdict that is always NO-GO stops carrying signal.
- Confirm that counting non-blocking lanes is intended, or gate only on the required lanes.
Positive signals
- The facts reach
GITHUB_OUTPUTbehind a random delimiter, and the LLM response reaches the shell only throughenv:. Together these keep untrusted issue text out of expression interpolation. - The publish job writes an explicit "EVIDENCE UNAVAILABLE" page when the facts job fails, instead of leaving a stale GO on the wiki.
assemble_factsandbuild_known_issuesare pure functions, which keeps the verdict logic testable withoutgit/gh.
Deployment notes
- The narrative job needs a Copilot-licensed PAT in the
COPILOT_PATrepo secret. Until it exists the narrative is skipped every night, and the publish job depends on its download steps'continue-on-errorto still publish. - The new
Release-Readinesswiki page needs the same one-time wiki initialisation asE2E-Support-Matrix. - The per-PR "E2E consolidated report (self-hosted)" check now fails when the Strix Ubuntu
nightlyleg uploads no report, as well as thereleaseleg.
What this covered
I read every file in the change completely: the new xtask/src/release_readiness.rs, the main.rs wiring, the changed workflow_contract.rs tests, the new and changed jobs in nightly.yml and e2e-selfhosted.yml, the prompt and the docs hunk. I also read the e2e-report producer of support-matrix.md and the jobs these depend on or mirror. Reviewed range: 2d2dfc7…c333065a.
Two read-only workers ran in parallel: Rust logic, and workflows plus contract tests. I verified their headline findings myself:
- the 627 count, by re-running the parser logic on the downloaded artifact;
- the build-gate mutation: 46/46 tests pass;
- the workflow mutations: 75/75 tests pass.
The agent-instruction check (AGENTS.md: leak scan clean, action pins, docs sync), the history pass, CI attribution and the intent check (PR body, ROCMAI-568 by key) were run by me, the driver, not by an independent worker.
CI attribution for the failing checks:
- "E2E tests" (mock): the same two dash scenarios fail on main.
- "E2E tests (Strix Halo, Windows)": runner infrastructure (HTTP 403 on the TheRock tarball, GPU preflight).
- "E2E consolidated report (self-hosted)": fails correctly, downstream of the Windows lane.
Did not run:
- prior-changes: review comments on earlier PRs touching these files were not consulted, per this run's no-comments constraint.
routing/behavioralfor the LLM prompt: no API access.
Not reconciled against any discussion here. A worker left the scratch directory .rr-probe/ (the downloaded artifact) in the checkout.
Addressed in 78ae588 (see summary comment).
rominf
left a comment
There was a problem hiding this comment.
🔴 Automated review · pr-review-watcher · 78ae588
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.
Round 6. This is a full review of the whole change at 78ae588. It was not narrowed, because the commit-message objection cannot be scoped to a file. Each finding is marked new or standing. The author dismissed round 5's change request.
This change request rests on one item only: the standing commit-message objection, the last item under Blocking. The two new items under Blocking are real, but they do not gate:
- Red checks.
Test (affected crates)and hostedE2E testsare fixed onmain(#712, #713/#719), and this branch predates those fixes, so a rebase clears them.E2E tests (MI300X, current)also fails onmain's own latest completed runs (39948978595, 39947062610). The Strix Windows lane failed downloading the external tarball, which is an infrastructure fault, and the consolidated report fails downstream of that lane, as it should. - Untested delimiter newline. I checked its consequence. If the line is removed, only the narrative job fails, and that job is
continue-on-error, so the page publishes without a narrative. The code is correct today, so this missing guard is below the gating floor.
The round-5 objections were settled as follows. None was re-found by the blind review. Each was then put to the reviewer one at a time, so the evidence below is anchored, not blind:
-
E2E failure count inflated ~90×: resolved.
total_e2e_failuresnow scans only the rows between| Platformand| **Total**. The reviewer re-widened the slice to the end of the document.total_e2e_failures_ignores_the_expectation_grid_and_command_coverage_tables, which builds its input with the realconsolidated_summary_markdown, then failed withSome(2)instead ofSome(0). One gap remains in that test: it writes noplatform.jsonsidecar, so the expectation-grid table never renders and its rows are never parsed. The same slice excludes those rows, so there is no live defect. The 627-versus-7 figure was not re-derived. -
A cancelled or timed-out build reads as GO with all tests green: resolved. The reviewer changed both build gates to
== "failure", and 4 of 58 tests then failed (the cancelled and timed-out cases for each build). Making either collector return an empty list on failure is now caught too, bycollect_commit_subjects_fails_on_a_bogus_since_tagandcollect_known_issues_in_fails_outside_a_github_repo. Still open:run()and themain.rsargument mapping have no test. That is carried as the non-blocking wiring item below. -
Workflow contract wiring that survived mutation: resolved. The reviewer applied each round-5 mutation on its own. Each failed exactly one of the 79
workflow_contracttests:- dropping
continue-on-erroron either publish download; - hard-coding
--nightly-result; - dropping
release-readiness-narrativefromneeds; - dropping
fetch-depth: 0; - renaming the page to
E2E-Support-Matrix.md; - adding a step that inlines
steps.draft.outputs.responseintorun:.
Observation, not tested: most of these are
contains-style checks, so a matching line inside a YAML comment could satisfy some of them. - dropping
-
The description omitted the LANE_REPORTS change: resolved. It now has its own paragraph.
-
Commit messages that describe removed behaviour: still holds. I checked this myself; it is restated as the last item under Blocking.
Review — needs work
Full review of the whole change.
Jira ROCMAI-568 is named but could not be retrieved here (no tracker tool). The description matches what the diff does, except for the two non-blocking inaccuracies listed below.
Blocking
- new (does not gate; see banner) — The head commit has five red checks, all caused outside this diff, and all fixed on
mainor infrastructure-related —Test (affected crates),E2E tests,E2E tests (MI300X, current),E2E tests (Strix Halo, Windows, current),E2E consolidated report (self-hosted)Test (affected crates)fails onlyworkflow_contract::tests::preflight_runs_the_real_reclaim_assert_clear(coreutils: unknown program 'llama-server'). That is the multicall-coreutils decoy problem fixed onmainby 5dfa9a8 (#712).- Hosted
E2E testsfails on dash quit timeouts and short-name serve steps. #713 and #719 onmainaddress the quit timeouts, andmain's latestE2E tests(run 39950566642) is green. - MI300X fails on a vLLM
"auto" tool choice400. - Strix Windows fails because the external TheRock tarball download failed six times. The self-hosted consolidated check then correctly reports that lane's missing report.
- The branch is 15 commits behind
mainand merges cleanly (git merge-tree). On the merged tree,cargo test -p xtask --binsran 375 tests and all passed, including the reclaim test and all 58 release-readiness tests.cargo clippy -p xtask --all-targets -- -D warningsis clean. - Confidence 85 (the red checks come from the rollup; the attribution comes from the logs and
main's runs) · mechanical · Fix: rebase ontomainand re-run until green.
- new (does not gate; see banner) — The newline written before the
GITHUB_OUTPUTdelimiter is load-bearing, and no test exercises it —.github/workflows/nightly.yml:1799-1801,xtask/src/release_readiness.rs(run→write_parented)facts.jsonis written withserde_json::to_string_prettyand has no trailing newline. Without the bareecho, the delimiter lands on the}line and the step output is never terminated. That is exactly the bug commit 45d3cd5 fixed in this PR.- Verification: I deleted that
echoand re-rancargo test -p xtask --bins release_readiness. All 58 tests still passed, includingrelease_readiness_narrative_delimiter_is_random_not_fixed. - Confidence 100 · mechanical · Fix: extend the contract test to run the extracted step against a
facts.jsonwith no trailing newline and parse the output file. Alternatively, end the file with\ninrunand assert that.
- standing (round 3 onwards; this is what gates) — The commit messages that will form the squash body describe behaviour the code no longer has. — commits
40f5d624,fc76d6a8,ef489f5d,1cb356de- The history is unchanged at this head. The four commits still claim:
- a repeatable
--e2e-lane-resultflag (git grepat HEAD finds none); - that
release-readiness-nightlyneeds "the 6 GPU/WSL E2E lane jobs" (it needse2e-report-nightly); - that
extract_pr_numberis "kept test-only" (it no longer exists); - "Strip markdown links/images" (replaced by escaping);
- that the bare-
v3pin is "a frozen release, not a moving alias" (it was re-pinned to v2.1.1); - that
render_tabledrops the header (reversed infc76d6a8).
- a repeatable
- The description says these "will be reworded in the squash". That plan does not work here:
- This repository's squash setting is
squash_merge_commit_message: COMMIT_MESSAGES, so the squash body is built from these commit messages. - rocm-cli merges through the merge queue, which builds that commit itself and offers no step where the body can be edited.
- Unless the history is rewritten before the PR is queued, these claims land on
mainas the permanent record.
- This repository's squash setting is
- Confidence 85 · mechanical · Fix: before queueing, squash or reword the branch history (for example, one commit whose message describes the final design), then force-push.
- The history is unchanged at this head. The four commits still claim:
Non-blocking
- new — The "EVIDENCE UNAVAILABLE" notice can be false, and when it is, it overwrites a valid page —
.github/workflows/nightly.yml(publish-release-readiness-wiki, the! -f table.mdbranch)- The table download is
continue-on-error: true, so a transient download failure afterrelease-readiness-nightlysucceeded also takes this branch. - The page then says "release-readiness-nightly failed before it could compute a verdict", which is not what happened. That breaks AGENTS.md §3's rule that a printed message must match the behaviour.
- Confidence 70 · logic · Fix: pass
needs.release-readiness-nightly.resultthroughenv:and derive the wording from it.
- The table download is
- new — The test for
collect_known_issues_inpasses without the branch it targets —xtask/src/release_readiness.rs(testcollect_known_issues_in_fails_outside_a_github_repo)- Per the review worker's mutation run, the test still passes with the
!output.status.success()check removed, because the JSON-parse error text also containsgh issue list. The code still fails closed either way. - Confidence 100 (worker run, not repeated by me) · mechanical · Fix: assert on "
gh issue listfailed".
- Per the review worker's mutation run, the test still passes with the
- new — Nothing tests the
main.rs→release_readiness::runwiring, and its five result strings are untyped —xtask/src/main.rs:396-412- Swapping
windows_resultande2e_report_job_resultwould compile and keep every test green. - A typo such as
succeswould read as a failed gate rather than as an error. - Confidence 70 · architectural · Fix: use a clap
ValueEnumJobResult(or an inputs struct) and testassemble_factsthrough it.
- Swapping
- new —
skippedcounts as passing fore2e-report-nightly, which is never legitimately skipped —xtask/src/release_readiness.rs:147- That job is
if: always(). For the build gates, "skipped" has a real meaning (a no-op night); for this gate it has none, and a test pins the lenient behaviour. - Today the separate artifact download would fail first, which hides this.
- Confidence 70 · logic · Fix: accept only
successfor this gate.
- That job is
- new — The
""status arm intotal_e2e_failuresis unreachable —xtask/src/release_readiness.rs:437- The producer only emits PASS, FAIL or EMPTY for platform rows. Removing the arm leaves all tests green.
- Confidence 70 · mechanical · Fix: drop the arm, or add the fixture that needs it.
- new — A
**Total**line before the header panics instead of returningNone—xtask/src/release_readiness.rs:424- When
total_idxis less thanheader_idx,lines[header_idx + 1..total_idx]panics. That contradicts the function's contract that a broken report returnsNone. The outcome still fails closed. - Confidence 70 · mechanical · Fix: search for Total only after
header_idx.
- When
- new — Stale or incorrect comments —
xtask/src/release_readiness.rs:63-66,.github/workflows/nightly.yml(ai-inference pin comment),docs/ci-hardware-testing.md:204- The
failing_gatesdoc omits theE2E (lane report check: …)gate. - "per AGENTS.md's full-semver-tag policy" is wrong: AGENTS.md §6 requires a full commit SHA plus a
# vX.Y.Zcomment. The pin itself is correct. I checkeda7805884…againstv2.1.1;providerandsystem-prompt-fileexist there, and the default provider isgithub-models. - "Unlike the wiki-publish jobs below" is wrong:
publish-e2e-wikiis described above that sentence. - Confidence 85 · mechanical.
- The
- new — Cosmetic branches that no test exercises —
xtask/src/release_readiness.rs- The tie-break on issue number in
build_known_issues, the exact-240 boundary inexcerpt, and thenightly_result == "skipped"half ofbuild_skippedall survive single-branch mutation. - Confidence 85 · mechanical.
- The tie-break on issue number in
- new — Breaking-change commits of internal types reach the user-facing Breaking list —
xtask/src/release_readiness.rs:240-246- Only scopes are filtered, so
ci!:,build!:andchore!:land under "Breaking Changes". - Confidence 70 · logic · Fix: if that matches the intent, also drop internal types on the
!path.
- Only scopes are filtered, so
- new — The
sdk_versionaxis is now handled differently from thechannelaxis next to it —.github/workflows/e2e-selfhosted.yml(around line 1088),xtask/src/workflow_contract.rs(uploaded_report_artifact)- The new reasoning ("either leg can die independently, so check both") applies equally to the
-n-1/-n-2legs, which are still represented bycurrent. That is pre-existing, but the two are now stated side by side. - Confidence 70 · architectural · Follow-up.
- The new reasoning ("either leg can die independently, so check both") applies equally to the
- new — The description contradicts itself on escaping — PR body
- The second-round section says issue-title escaping "covers text fed into the LLM prompt". It does not:
escape_markdownruns only inrender_table, andfacts.jsoncarries the raw titles. - The commit messages "address second/third/fourth round of review feedback" convey nothing either. Squash-merge collapses them, and the description says the squash body will be reworded.
- The second-round section says issue-title escaping "covers text fed into the LLM prompt". It does not:
Decisions for the author
- The E2E gate fails on any failure in any lane and either channel — tradeoff
- The ROCm
nightly-channel legs test upstream SDK nightlies. With this PR'sLANE_REPORTSchange, a nightly leg that dies (for example, on a TheRock nightly download) now failse2e-report-nightlyand so yields NO-GO. - It also fails the per-PR
E2E consolidated report (self-hosted)check when the Strix Ubuntunightlyreport is missing. - For: stricter evidence, and no lane vanishes silently. Against: the verdict can sit at NO-GO for upstream or infrastructure reasons, which weakens its signal. The description already flags the non-blocking-lanes half of this.
- The ROCm
- The name collides with the existing release gate — non-blocking-improvement
scripts/release_readiness.pyis already "release readiness" (dist-asset and signature verification, cited in AGENTS.md §8 anddocs/testing.md).- A different name, such as
nightly-verdict, would keep "run release readiness" unambiguous.
COPILOT_PATversus the built-in token — tradeoff- A review worker cited a GitHub changelog (2026-07-02) saying that Copilot CLI accepts the built-in token with
copilot-requests: writewhen org policy allows it. That would remove a long-lived PAT from a step that reads untrusted text. - I did not verify this. If it is wrong, the PAT is required, as the comment and description state.
- A review worker cited a GitHub changelog (2026-07-02) saying that Copilot CLI accepts the built-in token with
- Narrative steering through
bug-labeled issue text — answered-by-intent- PR description: "Narrative-steering via
bug-labeled issue titles/excerpts … is a real exposure, but fixing it … is a product decision … left for a follow-up." - Otherwise this would have been non-blocking. ai-inference v2.1.1 passes no
--allow-tool, so the risk is steered prose rather than tool use. Backticks and bare URLs in the narrative are not escaped. - Confirm the follow-up is still planned before
COPILOT_PATis set.
- PR description: "Narrative-steering via
- A missing
support-matrix.mdaborts the job instead of producing a NO-GO table — answered-by-intent- PR description: "A genuinely missing E2E report file … still fails the
release-readiness-nightlyjob viaread_to_string(...)?… the wiki-publish job's 'evidence unavailable' fallback … already covers this case." - Otherwise this would have been non-blocking, because the
render_tablecomment implies that unreadable evidence still publishes a table.
- PR description: "A genuinely missing E2E report file … still fails the
Positive signals
- The gate reads
e2e-report-nightly's own result, not the lane results, whichcontinue-on-erroralways reports assuccess. Lanes added toLANE_REPORTSlater are covered automatically. - The verdict is computed only in the pure
assemble_facts. The LLM output reaches the shell only throughenv:, behind a random heredoc delimiter. The sed escaping doubles backslashes first, so a pre-escaped\[cannot slip a live link through. - The grid-slicing regression test builds its input with the real
e2e_report::consolidated_summary_markdownrather than a hand-written snippet, so it tracks format drift in the producer. - The publish job's
!= 'skipped'condition means an outright upstream failure replaces a stale GO on the wiki instead of leaving it in place.
Deployment notes
- The narrative step needs a Copilot-licensed PAT in the
COPILOT_PATrepo secret. Without it the job fails,continue-on-errorhides the failure, and the page publishes without a narrative. - This creates a new
Release-Readinesswiki page. It relies on the wiki already existing, which the E2E matrix page already requires. - Every PR's
E2E consolidated report (self-hosted)check now also requires the Strix Ubuntunightlyleg's report. The nightlye2e-report-nightlyjob requires both legs for all seven lanes.
What this covered
-
Read: every file in
main...HEAD(2d2dfc7…78ae5883), completely, split into two coupled groups, each read by an independent worker:- group 1:
release_readiness.rs,main.rsand the prompt; - group 2: both workflows,
workflow_contract.rsand the docs.
Also read: the
e2e-reportproducer (consolidated.rs), the source of the pinned ai-inference action, and the history ofLANE_REPORTS. - group 1:
-
Ran:
- 58 release-readiness tests plus 78
workflow_contracttests at HEAD, with single-branch mutations; - the full xtask suite (375 tests) and xtask clippy, on a local merge with
main; - a mutation check confirming the delimiter gap.
- 58 release-readiness tests plus 78
-
Did not run:
- the design-review step as independent workers; I ran it myself because of the session's two-worker cap;
- the prior-changes pass (by instruction, no earlier review comments were read);
cargo xtask coverageagainst the xtask floor;actionlint(not installed).
-
The test
release_readiness_wiki_publish_guard_…was judged by reading only, because it writes to/tmp. -
No prompt-injection content was found in the checkout.
-
This report was not reconciled against the PR's discussion.
|
Addressed in Blocking — fixed:
Non-blocking — fixed this round:
Left unchanged (judgment call, not an oversight):
Note on |
|
Addressed in Blocking (non-gating per the review's own banner):
Blocking (this is what gates) — left unchanged, judgment call not an oversight:
Non-blocking — fixed:
Non-blocking — left unchanged (deliberate deferrals, not oversights):
All of the above is also now in the PR description under "Changes since the fourth round of review." |
Addressed in 0c5f1fe (see PR description's "Changes since the fourth round of review" section and inline reply comment). The two remaining blocking items (commit-message history-rewrite objection; internal-type breaking-commit classification) are explained as deliberate judgment calls in that reply, not guessed-at code changes.
rominf
left a comment
There was a problem hiding this comment.
🔴 Automated review · pr-review-watcher · 0c5f1fe
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.
Round 7. This is a full review of the whole change at 0c5f1fe. It was not narrowed, because the commit-message objection cannot be scoped to a file. Each finding is marked new or standing. Round 6's change request had been dismissed; its objection is treated as standing here.
This change request rests on one item only: the standing commit-message objection, the first item under Blocking. The blind review re-found it on its own, and I checked it directly: 40f5d624, fc76d6a8, ef489f5d and 1cb356de are still on the branch unchanged, and git grep finds no e2e-lane-result at the head. Your reply agrees it still holds.
The other three items under Blocking are real, but they do not gate:
- Narrative can forge a GO heading. Round 6 recorded this exposure (bug-labelled issue text steering the narrative) as answered-by-intent, and the description still defers it to a follow-up. This round adds a demonstration of how far that steering reaches, but it is the same exposure, and it only goes live once
COPILOT_PATis set. It is not escalated to a gate. I strongly recommend closing it before that secret is configured. - Untested gate branches and artifact hand-offs. The code is correct today. Each item is a missing guard against a future regression, which is below the gating floor. Round 6 applied the same reasoning to the delimiter newline.
- Red checks.
E2E tests (MI300X, current)is red onmain's own recent runs. Strix Windows died on an HTTP 403 from the external tarball host, which is an infrastructure fault, and the consolidated report failed downstream of that, as it should. TheTest (affected crates)reclaim test passed in the same run'sCoveragejob and onmain, so treat it as a flake to re-run. As the item below explains, it is not a stale base.
Review — needs work
Full review of the whole change.
Implements ROCMAI-568 ("Nightly release-readiness summary (wiki page)") as the ticket describes it. One ticket claim is undercut, under Blocking: "the LLM ... never decides pass/fail".
Blocking
-
standing (round 3 onwards; this is what gates) — The squash body will put claims the code no longer makes onto main — commits
fc76d6a,40f5d62,ef489f5,1cb356d,e20b631
The repo builds the squash body from these commit messages. They describe things that are not in the code:fc76d6aand40f5d62say a repeatable--e2e-lane-result NAME=RESULTflag was added and thatrelease-readiness-nightly"needs: the 6 GPU/WSL E2E lane jobs". Neither exists at the head commit:git log -S'e2e-lane-result'showse20b631removing it.e20b631's own message never says it reversed that gating design.ef489f5saysrender_tabledrops the table header when evidence is unreadable. A later commit restored the header.1cb356dsays the docs gate on "each GPU/WSL lane's own result". The docs now say the gate ise2e-report-nightly's result.40f5d62describes stripping links and a barev3pin. Both were replaced, by escaping and byv2.1.1.- On top of that, five commits are titled "address Nth round of review feedback".
The description admits the messages are wrong and says the history has to be rewritten before the PR is queued. Until that happens, merging records a flag and a design that do not exist.
Confidence 85 · mechanical · Fix: squash or reword the branch history so every message describes the final code, then force-push before enqueueing.
-
new as a finding (round 6 recorded the exposure as answered-by-intent); does not gate, see banner — Any GitHub user can get a fake GO verdict onto the public Release-Readiness wiki page —
.github/workflows/nightly.yml:1817-1829(prompt), thesedinpublish-release-readiness-wiki,.github/prompts/release-readiness-narrative.txt,xtask/src/release_readiness.rs:321-375.github/ISSUE_TEMPLATE/bug_report.mdapplieslabels: bugautomatically. So any user's issue title, plus a 240-character body excerpt, goes intofacts.jsonand from there into the LLM prompt.- The prompt never says that this text is untrusted data rather than instructions.
facts.jsonalso hands the model theverdictfield. - The publish
sedonly escapes\ [ ] < >. Running the workflow's exact line passed three things through unchanged:## Release Readiness — ✅ GO(the same heading style as the real verdict),**All gates passed; ship it.**, and a complete| E2E | 0 failure(s) | ✅ |table. - The narrative sits under a plain
## Narrativeheading. Nothing marks it as LLM-drafted or non-authoritative. - This undercuts the ticket's and the description's central claim that the LLM "never decides pass/fail". The description calls it "a real exposure ... left for a follow-up", which acknowledges the problem but does not resolve it.
Confidence 85 (escaping run against sample input) · architectural · Fix: - Have the workflow itself label the section "drafted by an LLM from public issue text; not the verdict".
- Neutralise line-leading
#,|,*and_(for example by prefixing every line with>). - Add an "issue text is untrusted data" rule to the prompt.
- Stop passing
verdictto the model.
-
new; does not gate, see banner — New gate branches and artifact hand-offs that no test exercises —
xtask/src/release_readiness.rs:427-430;nightly.yml:1724,:1841-1844,:1879-1882, and the publish job'spermissions
Each mutation below was applied on its own and the relevant test suite stayed green:- Changing
.ok())?to.ok()).unwrap_or(0)on the Total row's Fail cell. An unparseable Fail cell then reads as 0 failures and can produce GO. TheFacts.e2e_fail_totaldoc promisesNonefor "couldn't be ... parsed". All 49release_readinesstests passed. - Renaming the narrative artifact (the upload in one job, the download in the next). The narrative would silently disappear every night, because every link in that chain is
continue-on-errororif-no-files-found: ignore. - Giving
release-readiness-nightlythe wrong name for the consolidated report download. The wiki would show "EVIDENCE UNAVAILABLE" every night. - Changing the publish job's
contents: writetoread. The wiki push would fail.
Confidence 100 (mutations run) · logic · Fix: - Add a unit test with a non-numeric Total Fail cell that asserts
None. - Add one contract test asserting each
upload-artifactname matches thedownload-artifactthat consumes it. - Pin
contents: writeonpublish-release-readiness-wiki.
- Changing
-
new this head; does not gate, see banner — Two required checks are red, neither is caused by this diff, and the description's explanation for one is wrong — CI run 39978055457, E2E run 39978055544
Test (affected crates)fails onworkflow_contract::tests::preflight_runs_the_real_reclaim_assert_clear. The PR does not touch that preflight code.- The description says the branch's merge-base predates the fix (#712) and that a rebase would settle it. That is wrong: CI ran on the merge ref
898e3c0, whose parent1c33248already contains #712 (5dfa9a8, Oct 21). - The same test passed in the same run's
Coveragejob, and on main's runs at1c33248andd510680. So this is a flake, not a stale base.
- The description says the branch's merge-base predates the fix (#712) and that a rebase would settle it. That is wrong: CI ran on the merge ref
E2E consolidated report (self-hosted)failed because the Strix Windows lane died during engine install withHTTP 403from the tarball host. It therefore uploaded noe2e-gpu-strix-windows-report, and the lane-report check flagged that correctly. The Strix Ubuntu two-leg check this PR adds passed.E2E tests (MI300X, current)is also red. It is red on main's latest push run too.
Confidence 100 (logs read) · mechanical · Fix: re-run both checks until green, and correct the description's root-cause claim for the preflight test.
Non-blocking
-
new —
publish-e2e-wikinow skips more often, and it skips silently —nightly.yml:1590-1597,:1640- The
LANE_REPORTScheck now also requires each lane'snightly-leg artifact. - So any of the 7 nightly legs dying before upload now fails
e2e-report-nightly.publish-e2e-wikirequires that job to succeed, so it skips, and the E2E matrix page goes stale with no notice. That is the same stale-page problem this PR fixes for the Release-Readiness page. docs/ci-hardware-testing.md:195lists that job's skip conditions and does not mention this one.- Before this change, a missing
nightly-leg artifact never failed that job.
Confidence 70 · architectural · Fix: gatepublish-e2e-wikion the table being present (as the new job does), or document the skip.
- The
-
new — A lane can drop out of the E2E total and still allow a GO —
xtask/src/e2e_report.rs:81-113discover()silently skips any artifact directory that has noreport.json.- The
LANE_REPORTScheck only tests that the artifact name exists. - So a lane that uploads other files but no
report.jsoncontributes no row, nothing fails, and the verdict can read GO. - The code is pre-existing, but this change makes it decide a release verdict.
Confidence 70 (argued by reading, not observed) · logic · Fix: emit an EMPTY row for an artifact directory that has noreport.json.
-
new — The contract helper hardcodes the two channel names —
xtask/src/workflow_contract.rs:1122-1133
uploaded_report_artifactsubstitutes the literalsreleaseandnightlyinstead of reading the lane'schannel:list. If a lane adds a third channel, that leg goes unchecked and the test stays green.
Confidence 70 · mechanical · Fix: derive the legs from the matrix. -
new — The narrative prompt's input grows without bound —
release_readiness.rs:329-345,nightly.yml:1817-1829- Known issues are fetched with
--limit 1000, and every one goes into both the public raw appendix and the prompt. - The pinned
ai-inferencepasses system prompt plus facts tocopilot -pas a single argv string. Linux caps a single argument at 128 KiB. - Today's input is about 25 issues and 109 commits, roughly 20–30 KB, and it grows as time passes since the last tag. Past the cap the narrative silently drops.
Confidence 70 · logic · Fix: cap the issues and commits handed to the prompt, and the appendix.
- Known issues are fetched with
-
new —
collect_known_issues_in_fails_outside_a_github_repodepends on the machine it runs on —release_readiness.rs:1272-1280- Without
ghinstalled, the error is the.context("failed to run \gh issue list` ...")text, not the asserted "gh issue list` failed", so the test fails. - With
GH_REPOset, it makes a live API call.
Confidence 70 · mechanical · Fix: gate the test onghbeing present, and clearGH_REPOfor the child process.
- Without
-
new — Two comments overstate what the lane check covers —
xtask/src/main.rs(--e2e-report-job-resultdoc: "already fails if any E2E lane that ran didn't upload"),release_readiness.rs:141-152("every lane ... for free")
The workflow's own comment on that check is the one that governs, because it describes what the step actually does: a re-run can pass on an earlier attempt's artifact, so "this can miss a lane".
Confidence 70 · mechanical · Fix: add the re-run caveat to both comments.
Decisions for the author
- new — The verdict will read NO-GO almost every night — tradeoff
- Each of the last 6 scheduled nightlies had a Total Fail of 6–12: MI300X/MI350P fail 1–2 every night, and Strix Windows 2–7.
e2e-report-nightlyfailed in 5 of the last 12 scheduled runs.- XPASS counts as Fail, and a dead
nightly-channel leg now also forces NO-GO. - Failing closed is defensible. But a verdict that is constant carries little signal unless persistent known failures are separated from new regressions. Confirm that this is the intended release bar.
- new — About 26% of real commit subjects never reach the buckets — non-blocking-improvement
- Over
v0.2.0..main, 28 of 109 squash subjects are ticket-prefixed (ROCMAI-NNN:,EAI-NNNN:) or otherwise non-conventional (vllm:).categorize()drops them all. - That loses at least 5 user-facing changes: #698, #648 (gfx90c detection), #424, #646, #535.
- Meanwhile
fix(release)(#550, release tooling) is shown as a user-facing fix. - Better: recognise
TICKET-N: type(scope):, addreleasetoINTERNAL_SCOPES, or say on the page that only conventional-commit subjects are counted.
- Over
- new — The two wiki-push jobs can race on the same wiki repo — answered-by-intent
Both doclone --depth 1, commit, and push, in separate concurrency groups and with no retry. The description says: "publish-e2e-wikiandpublish-release-readiness-wikiuse separate concurrency groups ... a retry/backoff story for wiki pushes would be a repo-wide improvement." This would otherwise have been a non-blocking finding (confidence 70). Confirm you accept that an overlap fails the later push.
Positive signals
- The LLM response reaches the shell only through
env: NARRATIVE. - The
$GITHUB_OUTPUTheredoc uses a random delimiter. - The
seddoubles backslashes before it escapes brackets. - The escaping contract tests run the workflow's real
sedline, not a copy of it. - The GO/NO-GO gating is well pinned: 24 of the 28 mutations of
assemble_facts,total_e2e_failures,categorizeandescape_markdownwere caught. - The new action pins are verified:
actions/ai-inferencev2.1.1→a7805884, andactions/setup-nodev7.1.0→949feb24. - Checking both
releaseandnightlylegs is load-bearing. The latest nightly (40279551041) has noe2e-gpu-mi350p-nightly-report, yet passed under the old release-leg-only check.
Deployment notes
- A new repo secret,
COPILOT_PAT(a Copilot-licensed PAT), is required. Without it the narrative fails quietly every night, and the table still publishes. - The per-PR check "E2E consolidated report (self-hosted)" now also fails when the Strix Ubuntu
nightlyleg uploads no report. release-readiness-nightlyandrelease-readiness-narrativerun on aworkflow_dispatchfrom any branch, so every dispatch spends Copilot quota. Publishing stays limited tomain.
What this covered
- What was read: every changed file, completely, plus the code around it:
nightly.yml'snightly,windows-nightly, all 7 nightly lanes,e2e-report-nightlyandpublish-e2e-wikijobs;- the lane matrices in
e2e-selfhosted.yml; xtask/src/e2e_report.rsandcrates/e2e-report's consolidated summary producer.
- Commits:
2d2dfc78…0c5f1fed. That is the merge-base withprw-base(1c33248c); the branch is 17 commits behind it, andgit merge-treeagainstprw-baseis clean. - What I ran:
cargo test -p xtask release_readiness: 61 passed, including all 49release_readiness::tests.cargo test -p xtask workflow_contract: 79 passed.cargo clippy -p xtask --all-targets -D warnings: clean.- 28 mutations of the Rust logic and 13 of the workflows.
- The leak scan: clean.
- CI logs, compared against main's runs of the same jobs.
- Fan-out: three independent workers — the Rust logic, the workflows with their contract tests, and the design questions. I ran the agent-instruction (AGENTS.md), history and CI passes myself.
- Did not run:
- The prior-changes pass, because reading earlier reviews is out of bounds for this run.
- Live checks of the
claude-sonnet-5.5model slug and Copilot CLI1.0.97behaviour.
- Disclosures:
- One contract test writes and then deletes a file under /tmp by design. One run of it happened before it was excluded from later runs.
- Scratch logs (
rr-*.txt,wc-log.txt) were left in the disposable checkout.
- Not reconciled against any PR discussion here.
|
Addressed in Blocking, gating: left unchanged, needs the author.
Blocking, non-gating: fixed (
Non-blocking, fixed.
Left unchanged on purpose.
|
e993de2 to
b204a68
Compare
|
The standing commit-message objection from review 5632364164 is addressed: the 19 branch commits are squashed into one, |
Addressed in b204a68 (history squashed into one commit with an accurate message; non-gating items fixed in the same stack)
rominf
left a comment
There was a problem hiding this comment.
🔴 Automated review · pr-review-watcher · b204a68
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.
Round 8, a full review of b204a686. The previous review point, 0c5f1fed, is no longer in the branch history, so the review could not be narrowed. Each finding below is marked new. Round 7's change request was dismissed after the squash.
The round 7 objection is settled. It said commits 40f5d624, fc76d6a8, ef489f5d, 1cb356de and e20b631 describe code that no longer exists. The blind review did not re-find it. I then put the objection to the reviewer directly; that check was handed the objection, so it is anchored rather than blind. Both point the same way:
- The branch is now one commit.
- None of the stale claims, such as
--e2e-lane-result, the per-laneneeds, link stripping or a barev3pin, appear in its message. - The message describes the final gating, escaping and pin correctly.
This change request rests on one item only: the first item under Blocking. It is a new false claim in the squashed commit message. Rewording one paragraph clears it.
The other three Blocking items do not gate:
- Each one is a test that stays green under a mutation. The workflow behaves correctly today, so each is a missing guard against a future regression. That is below the gating floor, and round 7 treated its untested hand-offs and its hard-coded-channels item the same way. Fixing them is still recommended.
- The red
Test (affected crates)check is not caused by this diff. CI checked outb204a686itself, which does not contain #712, and the test passes on main. A rebase onto current main should clear it. - The round 7 comment and the PR description call that check a flake. This run's checkout shows the stale base is the real cause.
Review — needs work
Full review of the whole change.
Implements ROCMAI-568 ("Nightly release-readiness summary (wiki page)"), and covers what that ticket describes. The PR names no GitHub issue to close.
Blocking
-
new — this is what gates — The commit message gets the effect of the LANE_REPORTS change wrong, and that text becomes the squash body on main — commit
b204a68message, paragraph starting "e2e-selfhosted.yml:"- What it says: the
e2e-selfhosted.ymlchange makes a missing leg "faile2e-report-nightly", and "as a result,publish-e2e-wikialso skips". - What the code does: neither of those jobs exists in
e2e-selfhosted.yml. Its check lives in the per-PRe2e-reportjob (e2e-selfhosted.yml:1632).e2e-report-nightlyandpublish-e2e-wikiare innightly.yml(lines 1514 and 1633). - Where the effect really comes from: the
nightly.ymlLANE_REPORTS rewrite, which now requires both legs for all 7 nightly lanes. The commit message never mentions that change; its "Workflow (.github/workflows/nightly.yml)" bullets cover only the three new jobs. - What it leaves out: the actual effect of the
e2e-selfhosted.ymlchange, which is that the per-PR self-hosted report check now also fails when the Strix Ubuntunightlyleg is missing. - The code is the side that governs here; the message describes jobs that do not exist in the file it names.
- Confidence 85 · mechanical · Fix: reword the commit message. Attribute the
e2e-report-nightly/publish-e2e-wikieffect to thenightly.ymlLANE_REPORTS change, and give thee2e-selfhosted.ymlchange its real per-PR effect.
- What it says: the
-
new; does not gate, see banner — The contract test hard-codes the
release/nightlylegs instead of reading them from each lane's matrix —xtask/src/workflow_contract.rs:1122-1133(uploaded_report_artifact), and the "Derived from the lanes" comment at:1146-1149- Run, Strix Ubuntu: I changed the lane's matrix in
e2e-selfhosted.yml:434tochannel: [release].cargo test -p xtask workflow_contractstill passed (83 of 83). In that state the real check would demand anightlyartifact that can never exist, and fail on every run. - Run by the worker, rad3: adding a third channel to the nightly rad3 lane also stayed green, so that leg would go unchecked.
- This is a check whose comment claims a derivation it cannot fail on.
- Confidence 100 · architectural · Fix: read the job's
channel:list and emit one artifact name per value.
- Run, Strix Ubuntu: I changed the lane's matrix in
-
new; does not gate, see banner — The behavioural test of the per-leg loop never tests the
releaseleg missing on its own, and its "skip a middle lane" case no longer removes the skipped lane's artifacts —xtask/src/workflow_contract.rs:1405-1433,:1372-1378- Run, release leg: I changed the loop to
for artifact in "${wanted[@]: -1}"in bothnightly.ymlande2e-selfhosted.yml, so it checks only the last (nightly) leg. All 83 tests stayed green. Thereleaseleg, which used to be the only leg checked, could therefore silently stop being checked. Cause:one_leg_missingonly ever removes the second leg fromsplit_once(','), and the victim lane has only one artifact. - Traced, not run, middle lane:
lanes[1]is now Strix Ubuntu, whose value is comma-joined. The filtera != middle_artifactcompares single names against that joined string, so it removes nothing. The case therefore no longer models a skipped lane that uploaded nothing. - Confidence 100 · mechanical · Fix:
- run the one-leg-missing case once for each leg;
- filter with
!middle_artifact.split(',').any(|m| m == a).
- Run, release leg: I changed the loop to
-
new; does not gate, see banner — Nothing checks that
release-readiness-nightlylists the build jobs it reads results from —.github/workflows/nightly.yml:1705-1709, and therelease_readiness_nightly_gates_on_the_e2e_report_job_not_individual_lanestest inxtask/src/workflow_contract.rs- Run: I deleted
- windows-nightlyfrom that job'sneeds. All 83 tests stayed green, because the test only matches the literal--windows-result "${{ needs.windows-nightly.result }}"text. - Effect: with the need gone, that expression evaluates to an empty string. Every night would then read NO-GO with "windows build ()". The job is also no longer ordered after the Windows build through its own
needs. - Confidence 100 · logic · Fix: assert that
needscontains bothnightlyandwindows-nightly.
- Run: I deleted
Non-blocking
-
new — The red
Test (affected crates)check was not caused by this diff.- What failed: only
workflow_contract::tests::preflight_runs_the_real_reclaim_assert_clear, with the errorcoreutils: unknown program 'llama-server'. This PR leaves that test untouched. - Main is green: main's CI run at the base tip
63ecab2dpassed the same test. - Where it was fixed:
5dfa9a8(#712, "keep the GPU reclaim decoys alive on multicall coreutils") is on main. This branch's merge-base2d2dfc7is 18 commits behind main and predates it. - Fix: rebase onto current main.
- Confidence 85 · mechanical.
- What failed: only
-
new — The trailing-newline comment in
run()gives a reason that cannot apply —xtask/src/release_readiness.rs:103-107- The comment says the newline keeps the
$GITHUB_OUTPUTheredoc delimiter on its own line "even if that step's ownechoguard is ever dropped". - That step never copies
facts.jsondirectly. It runsjq 'del(.verdict)' …, and jq always writes its own trailing newline, so the file's newline has no effect on the heredoc. - The PR description repeats the same claim.
- Confidence 85 · mechanical · Fix: drop or reword the comment. The newline itself does no harm.
- The comment says the newline keeps the
-
new — A contract-test name claims more than the narrative quoting delivers —
publish_release_readiness_wiki_quotes_the_narrative_so_it_cannot_forge_a_verdictinxtask/src/workflow_contract.rs; thesedcomment in thenightly.yml"Push page to the wiki" step- Prefixing each line with
>still lets headings, tables and bold text render, just inside a blockquote. So> ## Release Readiness — ✅ GOstill renders as a heading. - The YAML comment's word "top-level" is accurate, so the comment is the side to keep and the test name should be narrowed to match it.
- I reasoned this from GFM rules; nothing was rendered.
- Confidence 70 · mechanical · Fix: rename the test to say "top-level", or also escape a leading
#and every|so the stronger claim becomes true.
- Prefixing each line with
-
new — The narrative prompt reaches the Copilot CLI as a single command-line argument, so it has a size ceiling —
nightly.yml"Draft narrative" step (prompt: ${{ steps.facts.outputs.json }});collect_known_issues_in(--limit 1000)- ai-inference v2.1.1 passes the prompt with
-p <prompt>throughspawn. - Linux limits a single argument to 128 KiB. At about 400 bytes per issue, the facts JSON passes that at roughly 280 open bugs; there are about 25 today.
- Past that point the narrative fails every night without anyone noticing, because the job is
continue-on-error. - Confidence 70 · logic · Fix: cap the number of issues sent to the model, or pass them through a file.
- ai-inference v2.1.1 passes the prompt with
-
new —
run()and the dispatch inmain.rshave no test —xtask/src/main.rs:398-414,xtask/src/release_readiness.rs:79-112- The seven parameters are passed by position as plain
&str/&Path. - Swapping
windows_resultande2e_report_job_resultwould compile and leave all tests green. - Confidence 70 · logic · Fix: put the inputs in a struct with named fields (or a
ValueEnumfor job results), and add one test that drivesrunwith a fixture report.
- The seven parameters are passed by position as plain
Decisions for the author
-
new — The GO/NO-GO verdict ignores
publish-nightly, which runs the repo's real release gate — non-blocking-improvementpublish-nightlyrunsscripts/release_readiness.py --require-signatures --require-exact-assets.- A night where both builds succeed but that exact-asset or signature check fails still reads GO.
- Gating on that job's result would make the page match its name. Treating
skippedas acceptable only on a no-op night would follow the same rulewindows-nightlyalready uses.
-
new — Requiring both legs on every lane widens what can block the E2E wiki page and the verdict — tradeoff
- For: a missing
nightlyleg artifact on any one lane is now caught, so no leg silently drops out of the report. - Against: that one missing artifact now suppresses the whole E2E support-matrix page, including every other lane's results. It also forces NO-GO, even when the lane is one that is still being proven out (W7900, MI350P). This is documented; confirm it is intended.
- For: a missing
-
new — Two different tools share the name "release-readiness" — tradeoff
cargo xtask release-readinessand the existingscripts/release_readiness.pygate do different things.- ROCMAI-568 addresses this deliberately: "Not to be confused with ROCMAI-443 … a different tool with a similarly-named command, not a conflict or rename."
- Readers of AGENTS.md §8 and
docs/testing.mdmay still mix the two up.
Positive signals
- Untrusted text never reaches a
run:script through a${{ }}expression. The narrative goes throughenv:, the facts through a random heredoc delimiter, and the only interpolated values areneeds.*.resultstrings. - The new action pins check out:
ai-inference@a780588is the v2.1.1 commit andsetup-node@949feb2is the v7.1.0 commit. They carry# vX.Y.Zcomments, as AGENTS.md requires. total_e2e_failuresis tested against the real output ofe2e_report::consolidated_summary_markdown, not a hand-written snippet. It fails closed on a missing Total row, an unparseable Total row, and anEMPTYplatform.- Every artifact name listed in LANE_REPORTS matches what that lane's upload step produces in both workflows. No listed artifact is impossible to produce today.
Deployment notes
- The narrative needs a
COPILOT_PATrepo secret holding a Copilot-licensed PAT. Without it the narrative fails every night, quietly, because the job iscontinue-on-error. The table still publishes. - The repository wiki must already exist. This is the same one-time setup
publish-e2e-wikineeds. - The per-PR self-hosted "E2E consolidated report" check now also fails when the Strix Ubuntu
nightlyleg uploads no report.
What this covered
- What was read: every file in the change, completely (7 files,
prw-base...HEAD, commits63ecab2d…b204a686). That includes all ofxtask/src/release_readiness.rs, theworkflow_contract.rsdiff with the helpers it uses, every lane's matrix and upload steps in both workflows, and thee2e-reportproducer incrates/e2e-report/src/consolidated.rs. - How it was split: two independent workers.
- One covered the release-readiness module, its CLI wiring and the three new nightly jobs.
- The other covered the workflow contract tests, the LANE_REPORTS changes and the docs.
- The driver re-ran the four test-strength mutations itself (83 tests ran each time); all four stayed green.
- Other checks:
- Read the CI rollup for the head commit and compared it with main's runs.
- Sign-off check: exit 0, 1 commit checked, nothing missing.
- Ran the AGENTS.md §2 leak scan over the diff: no hits.
- Retrieved ROCMAI-568 by its identifier, without comments.
- Did not run: the prior-changes pass, because no earlier reviews of these files were consulted. The history pass was limited to
git logonnightly.yml. - Not reconciled against this PR's discussion.
b204a68 to
5c732f8
Compare
|
Addressed in Blocking, gating: fixed.
Blocking, non-gating: fixed.
Non-blocking, fixed.
Left unchanged on purpose.
fmt, |
nowycondro
left a comment
There was a problem hiding this comment.
Automated high-recall review pass (8 finder angles: line-by-line diff scan, removed-behavior audit, cross-file tracing, reuse, simplification, efficiency, altitude, CLAUDE.md conventions). The cross-file tracer, removed-behavior auditor, reuse, and conventions angles turned up nothing solid after verification — this PR's prior review rounds already closed most of the obvious cross-file/behavioral gaps. 8 findings survived verification, posted inline below, ranked roughly most-to-least severe.
| # The final expression quotes every line so line-leading `#`, | ||
| # `|`, `*` and `_` can't forge a top-level verdict heading, | ||
| # status table, or bold "all gates passed" line. | ||
| sed -e 's/\\/\\\\/g' -e 's/\[/\\[/g' -e 's/\]/\\]/g' -e 's/</\</g' -e 's/>/\>/g' -e 's/^/> /' release-readiness/narrative.md |
There was a problem hiding this comment.
Narrative sanitization is weaker than the mechanical table's escaping on the same page.
This sed chain neutralizes \, [, ], <, > and blockquotes every line, but never escapes #, *, `, or | — unlike escape_markdown() in release_readiness.rs, which is applied to issue titles/commit subjects on this same generated page and additionally escapes those characters.
The comment above claims quoting every line "can't forge a top-level verdict heading, status table, or bold 'all gates passed' line," but GitHub renders > # Heading as a blockquote containing heading-styled (large/bold) text — just indented, not neutralized. If the model ever reflects attacker-controlled text from an issue title/commit subject (despite the system prompt instructing it not to), a line like # Release Readiness — ✅ GO or **All gates passed** would still render with heading/bold styling inside the blockquote, which a skimming reader could mistake for corroborating (or contradicting) the real verdict above it.
Suggest reusing escape_markdown's character set (or exposing it as a small CLI/xtask subcommand this sed step could pipe through) so both trust boundaries on this page share one tested escaping implementation instead of two that can silently drift.
| "e2e-prewarm", | ||
| ]; | ||
|
|
||
| fn is_internal_only_scope(scope: &str) -> bool { |
There was a problem hiding this comment.
is_internal_only_scope is case-sensitive, so a differently-cased scope bypasses the internal-tooling filter.
INTERNAL_SCOPES (line 206) is all lowercase, and this function compares s.trim() against it via exact &str equality — no case-folding. A commit subject like fix(CI): retry flaky job or fix(Nightly): ... would not be recognized as internal-only ("CI" != "ci"), so it survives categorize()'s filter and leaks into the public "Bug Fixes" bucket (and the narrative LLM's input), even though it's exactly the kind of CI/nightly-internal commit this filter exists to drop.
I didn't find a commitlint/commit-msg hook in this repo enforcing lowercase Conventional-Commits scopes, so this is reachable from any contributor who happens to capitalize a scope. Consider scope.split(',').all(|s| INTERNAL_SCOPES.contains(&s.trim().to_ascii_lowercase().as_str())) (or normalizing once before the .all()).
| # handed one it could echo back as a forged heading. Issues are | ||
| # already reaction-ranked; the cap keeps the prompt, which reaches | ||
| # the CLI as one argument, far under Linux's 128 KiB limit. | ||
| jq 'del(.verdict) | .known_issues |= .[:100]' release-readiness/facts.json |
There was a problem hiding this comment.
.known_issues |= .[:100] slices the narrative's input after build_known_issues has already sorted it by 👍-reaction count (descending). Once the repo has more than 100 open bug-labeled issues, a brand-new, zero-reaction-but-genuinely-blocking issue (e.g. a crash filed yesterday) sorts below the cap and is cut before the LLM ever sees it — even though the system prompt explicitly tells the model to judge relevance from title/body "not its reaction count (reaction count is not a reliable signal of real-world impact)". The cap silently contradicts that instruction once the issue count grows past it. (The unfiltered table.md list is unaffected — only the curated narrative input.) A simple mitigation: dedupe the 128 KiB concern from relevance by sending titles-only for issues beyond some smaller N, instead of dropping them outright.
| /// Parsing the already-rendered rows, rather than re-deriving per-platform | ||
| /// pass/fail here, keeps this in lockstep with that crate's known-bugs/xfail | ||
| /// handling instead of duplicating it. | ||
| fn total_e2e_failures(markdown: &str) -> Option<u64> { |
There was a problem hiding this comment.
total_e2e_failures recovers structured per-platform pass/fail data by re-parsing e2e_report::consolidated_summary_markdown's already-rendered markdown — scanning for the literal substrings "| Platform"/"| **Total**" and then indexing columns positionally (.nth(5) for Fail, .get(8) for Status at line ~434). That coupling is purely string/offset-based: if e2e_report's table format is ever reordered or gains a column (e.g. an Xfail-reason column inserted before Status), nothing here would fail to compile — the offsets would just silently point at the wrong cells, and because this still returns Some(n) rather than None, it wouldn't even trip the "report unreadable" failing gate this function was designed to prefer over a wrong answer. Since e2e_report already has the typed pass/fail/skip/xfail counts moments before rendering them to markdown, exposing a small typed summary (or at least named column-index constants shared with the renderer) would remove this cross-crate drift risk.
| out: &Path, | ||
| table_out: &Path, | ||
| ) -> Result<()> { | ||
| let subjects = collect_commit_subjects(since_tag)?; |
There was a problem hiding this comment.
collect_commit_subjects (shells out to local git log) and collect_known_issues (a network round-trip via gh issue list --json ... --limit 1000) are independent of each other — only categorize(&subjects) depends on the git call — but they run strictly sequentially here. Every nightly run pays git_log_latency + gh_api_latency back-to-back instead of max(...). Since this runs in a billed CI job, spawning both subprocess calls concurrently (e.g. std::thread::spawn + join) and only serializing on categorize afterward would shave real wall-clock time off every nightly run.
| node-version: '22' | ||
|
|
||
| - name: Install GitHub Copilot CLI | ||
| run: npm install -g @github/copilot@1.0.97 |
There was a problem hiding this comment.
npm install -g @github/copilot@1.0.97 reinstalls the same pinned version from the registry on every nightly run, with no cache. Since the version is pinned and only changes when this line is edited, caching the global npm prefix (or ~/.npm) keyed on the pinned version string via actions/cache would let unchanged-version runs restore the installed CLI instead of re-downloading/reinstalling it every night.
| name: e2e-consolidated-report-nightly | ||
| path: consolidated | ||
|
|
||
| - name: Compute release-readiness facts + table |
There was a problem hiding this comment.
assemble_facts's three job-result gates (nightly_result, windows_result, e2e_report_job_result, all plain &str) are each matched against a different hand-picked accepted-value set ("success" | "skipped" for the two build gates, "success"-only for the E2E report job) rather than going through one shared typed gate helper (e.g. a JobResult enum with a fn passes(&self, allow_skipped: bool) -> bool). This is adjacent to the already-acknowledged "untyped result strings" follow-up in the PR description, but specifically: a future 4th gate added here has nothing stopping a copy-paste of the wrong accepted-value set (e.g. accepting "skipped" for a gate that, like e2e_report_job_result, has no legitimate no-op night) — the compiler can't catch it since the whole check is string literal equality.
|
|
||
| - uses: actions-rust-lang/setup-rust-toolchain@166cdcfd11aee3cb47222f9ddb555ce30ddb9659 # v1.17.0 | ||
|
|
||
| - name: Download consolidated nightly report |
There was a problem hiding this comment.
This downloads the entire e2e-consolidated-report-nightly artifact (both support-matrix.md and the rendered index.html) solely to read support-matrix.md a few lines below — index.html is never referenced by anything in this job. Since e2e-report-nightly already produces both files separately before bundling them into one artifact, uploading support-matrix.md as its own smaller artifact (or exposing it via a job output, if it stays under the size cap) would let this job skip pulling down the HTML report it never opens.
|
🤖 Automated review (Claude Code) · 5c732f8 This supplements the inline comments and review I just posted (#655 (review)) with a single consolidated summary in the required format. What I read: the full diff of PR #655 at commit 5c732f8 ( Findings (most severe first):
No findings here rise to blocking in my judgment — they're hardening/robustness/efficiency items rather than correctness bugs that break the feature as described. The merge decision remains with a human reviewer. 🤖 Generated with Claude Code |
|
🤖 Automated review (Claude Code) · 5c732f8 I read the current head of the release-readiness xtask ( Likely bugs (worth a look before merge):
Non-blocking:
Could not verify: no Rust toolchain or CI runner available in this sandbox, so none of this was compile-checked or executed — findings are from static reading of the diff/source only. 🤖 Generated with Claude Code |
rominf
left a comment
There was a problem hiding this comment.
🔴 Automated review · pr-review-watcher · 5c732f8
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.
Round 9, a full review of 5c732f80. The previous review point, b204a686, is no longer in the branch history after the rebase, so the review could not be narrowed. Every finding below is new this round.
Nothing in this round gates, so no change request is filed. Round 8's change request (5651935137) has already been dismissed.
The round 8 objection is settled. It said the squashed commit message credited the e2e-report-nightly / publish-e2e-wiki effect to e2e-selfhosted.yml and never mentioned the nightly.yml LANE_REPORTS rewrite. The blind review did not re-find it. I then put the objection to the reviewer directly; that check was handed the objection, so it is anchored rather than blind. I also read the current message and nightly.yml's LANE_REPORTS block myself. All three point the same way:
- The message now credits that effect, and the NO-GO verdict, to the
nightly.ymlLANE_REPORTS change. That block lists both legs for all 7 lanes. - The
e2e-selfhosted.ymlparagraph now gives only that file's real effect: the per-PR check fails when the Strix Ubuntunightlyleg uploads no report.
The two items under Blocking do not gate. Each is a test that stays green under a mutation, while the code it guards behaves correctly today. That is a missing guard against a future regression, which is below the gating floor. Round 8 treated its three test-strength items the same way. Fixing them is still recommended.
The red E2E tests (MI300X, current) check does not gate. The same job also fails on main's latest e2e-selfhosted runs. E2E tests (W7900, current) is still queued, so that lane's evidence is missing rather than failing.
Review — needs work
Full review of the whole change.
Implements ROCMAI-568 (the nightly release-readiness wiki page) in full. The PR also tightens the LANE_REPORTS check in both nightly.yml and e2e-selfhosted.yml. The ticket does not cover that, but the commit message and the description both disclose it.
Blocking
- new; does not gate, see banner — Two verdict-rendering branches in
release_readiness.rscan be broken without any test failing —xtask/src/release_readiness.rs:447,xtask/src/release_readiness.rs:497- The non-PASS check at
:447. Intotal_e2e_failures, the platform-row check!matches!(status, Some("PASS") | None) && fail == 0exists to count a FAIL row whose Fail cell reads 0 as a failure. The legacy known-bugs path incrates/e2e-report/src/consolidated.rscan produce that row: an XPASS gives statusFAILwhile rawstats.failedis 0. I mutated the check tomatches!(status, Some("EMPTY")) && fail == 0, which drops that case and turns it into GO. - The Build row at
:497. I mutatedg.contains("build")tog.contains("nightly build"). A failed Windows build then renders the Build row ✅ under a NO-GO headline. - With both mutations applied,
cargo test -p xtask release_readiness::testspassed 50 of 50. Only the EMPTY status and the Linux build failure are ever exercised. - Confidence 100 · logic · Fix: add a test with a
FAILrow whose Fail cell is 0 (assert NO-GO), and a render test where onlywindows_resultfails (assert the Build row shows ❌).
- The non-PASS check at
- new; does not gate, see banner — The workflow hand-offs for the new jobs are not pinned, and when one breaks the run stays green —
.github/workflows/nightly.yml:1737,:1833,:1842,:1763,:1910- A review worker ran these single mutations; each left all 87
workflow_contracttests passing:- the narrative upload
path:changed tonarrative/narrative.md; - "Save narrative" writing to
./narrative.mdinstead; --e2e-report consolidated/support-matrix.mdchanged toconsolidated/index.md;continue-on-error: trueremoved fromrelease-readiness-narrative;- the degenerate-table header check
grep -q '^| Area | Evidence | Status |'replaced with[ ! -s table.md ].
- the narrative upload
- Why this matters: the narrative job is
continue-on-error. A path drift would therefore leave the wiki permanently on the "Narrative generation did not complete" fallback, or permanently on EVIDENCE UNAVAILABLE, with nothing going red. release_readiness_artifact_hand_offs_use_matching_namescompares artifact names only. The guard test's only degenerate input is"", so it cannot tell "no header" apart from "empty".- The docs claim the narrative is "allowed to fail without blocking the table", and no test checks that.
- Confidence 100 · logic · Fix: assert that upload and download paths agree with the paths written and read. Assert the
--e2e-reportfile name against whate2e-report-nightlywrites. Pincontinue-on-erroron the narrative job. Add a header-less, non-empty table case to the guard test.
- A review worker ran these single mutations; each left all 87
Non-blocking
- new — A revert of a breaking commit is listed as a new breaking change, and reverting a feature or fix leaves it listed —
xtask/src/release_readiness.rs:240-260Revert "feat!: drop flag (#12)" (#13)has the headRevert "feat!. That ends in!, so it goes intobreaking. A worker's probe test confirmed it.Revert "feat: x"matches no bucket, so the reverted feature still shows under New Features.- Any non-conventional head ending in
!, such asWow!:, is also read as breaking. - Confidence 85 · logic · Fix: skip or specially handle
Revert "subjects, and only treat a head as breaking when it matches the conventionaltype(scope)!form.
- new —
skippedis accepted as passing for the Linuxnightlybuild, but that job cannot be skipped —xtask/src/release_readiness.rs:127- The
nightlyjob has noneeds:and noif:, so nothing legitimate producesskipped. - The comment's justification ("windows-nightly's own skip flag") only applies to the Windows gate.
- Accepting it for Linux is an unneeded fail-open path, and
assemble_facts_treats_a_skipped_nightly_build_as_passingpins it in place. - Confidence 70 · logic · Fix: accept only
successfornightly_result.
- The
- new — No test pins least-privilege permissions on the new jobs —
.github/workflows/nightly.yml:1700,:1760,:1851- The narrative job holds a PAT and reads untrusted issue text. It currently runs with
contents: read. - A worker broadened each job's permissions (
contents: write/issues: write/actions: write), and every contract test still passed. publish_release_readiness_wiki_can_push_to_the_wikionly checks thatcontents: writeis present. The commit message's "contract tests covering … permissions" overstates what is pinned.- Confidence 85 · logic · Fix: assert each job's exact
permissions:block.
- The narrative job holds a PAT and reads untrusted issue text. It currently runs with
- new — The block-quote test can pass when
sedproduces no output —xtask/src/workflow_contract.rs,publish_release_readiness_wiki_quotes_the_narrative_so_it_cannot_forge_a_top_level_verdict- The test never checks the exit status and never checks that any line came out.
- Its sibling escaping test does check both, so the pair is covered today.
- Confidence 70 · logic · Fix: assert success and a non-empty output.
- new — Two unrelated "release readiness" tools now share a name, and the docs do not tell them apart —
docs/ci-hardware-testing.md:202,.github/workflows/nightly.yml:13scripts/release_readiness.pyis the signature/packaging gate that AGENTS.md §8,docs/testing.mdanddocs/release-trust.mdrefer to.nightly.yml:13says "every readiness step below runs --require-signatures". That is not true of the new jobs.- The ticket's naming note acknowledges the two tools, but nothing in the repo does.
- Confidence 70 · mechanical · Fix: add a sentence to the new docs paragraph that distinguishes the two, and narrow the
nightly.yml:13comment.
- new — The new docs paragraph cites
nightly.ymlas a bare backtick path —docs/ci-hardware-testing.md:202- AGENTS.md §5 asks for new citations of repo files to be relative links. The older prose around it uses the same pattern.
- Confidence 70 · mechanical · Fix:
[nightly.yml](../.github/workflows/nightly.yml).
Decisions for the author
- new —
run()andmain.rsargument wiring is untested and untyped — answered-by-intent- A worker found that these mutations survive the full suite:
- swapping
nightly_resultandwindows_resultinmain.rsor inrun(); - replacing
total_e2e_failures(&e2e_text)withSome(0)inrun().
- swapping
- Would otherwise be blocking under "behaviour introduced is tested".
- The PR description says: "
main.rs→release_readiness::run's argument wiring and its 5 result strings remain untested and untyped … aValueEnumJobResultwould be the fix, left for a follow-up." - Confirm that the follow-up is still planned. Splitting a read-report-and-assemble function out of
run()would cover theSome(0)case without that refactor.
- A worker found that these mutations survive the full suite:
- new — One PR carries two logical changes — tradeoff
- The
LANE_REPORTSboth-legs change innightly.yml,e2e-selfhosted.ymland its contract test stands on its own. It changes when the per-PR consolidated-report check andpublish-e2e-wikifail. AGENTS.md §11 asks for one logical change per PR. - On the other side, the verdict's E2E gate depends on that check being complete, and the commit message documents both changes.
- The change itself is correct: all 7 nightly lanes upload both legs, and the per-PR
sdk_version_matrixalways includescurrent.
- The
- new — The two wiki publishers use separate concurrency groups — tradeoff
publish-e2e-wikiandpublish-release-readiness-wikipush to the same wiki repo with no retry. A single-job re-run can therefore fail on a non-fast-forward push.- Normal ordering makes overlap unlikely. A shared group would remove the possibility, at the cost of serialising the two pages.
Positive signals
- The narrative escaping holds against everything tried. A worker rendered adversarial input through GitHub's markdown API: inline, image, reference-style, nested and footnote links, raw
<img>, autolinks and pre-escaped brackets all render as text, and each line stays inside the block quote. - LLM output reaches the shell only through
env:, and the$GITHUB_OUTPUTheredoc delimiter is random.${{ }}inrun:only ever carriesneeds.*.resultenum values. - The verdict fails closed on
failure,cancelled,timed_out, empty, unknown and unreadable inputs. Gating one2e-report-nightly's own result, rather than on lane results masked bycontinue-on-error, is the right signal.
Deployment notes
- A
COPILOT_PATrepo secret (a Copilot-licensed PAT) is needed before the narrative will draft. Without it, the narrative step fails silently undercontinue-on-error, and the page publishes only the table plus the fallback line. - From the first nightly run after merge, any lane whose
nightlyleg dies before uploading turnse2e-report-nightlyred. That also stops theE2E-Support-Matrixwiki page from updating.
What this covered
- What was read: all 7 changed files, completely, between
prw-basef6c8a8f and HEAD 5c732f8. Also read:- the report producer in
crates/e2e-report/src/consolidated.rs(status_text/ok); - every nightly lane's matrix and upload steps;
- the existing
publish-e2e-wikijob.
- the report producer in
- How the passes ran: two independent read-only workers, one on the xtask module plus
main.rs, one on the workflows, contract tests, prompt and docs. The workers ran mutation passes over each new test, about 29 and 34 mutations respectively. The tworelease_readinesssurvivors were re-run by the reviewer. - Source-reading passes: agent-instruction adherence and history (
git logon the touched workflows) ran in the reviewing context. The prior-changes pass did not run, because earlier review comments are deliberately out of scope for the review. - CI: every check is green except
E2E tests (MI300X, current), which also fails on main's latest e2e-selfhosted runs and so is not attributed to this PR.E2E tests (W7900, current)is still queued. - Sign-off check:
check_signoff.pyreported 1 commit checked, none missing (exit 0). - Description and ticket: the description was read, and ROCMAI-568 was retrieved by its ID.
- What was not verified: the
claude-sonnet-5.5Copilot model slug, and whetherv*release tags exist upstream (the checkout has none). - Concurrent-edit caveat: both workers mutated the shared checkout at the same time. The affected runs were re-run with narrower filters, and the workflow files were confirmed to match HEAD afterwards.
- The review was not reconciled against any PR discussion; the standing-objection check in the banner happened after it.
nowycondro
left a comment
There was a problem hiding this comment.
🔴 Automated review · pr-review-watcher · 5c732f8
Review
Full review of the whole change.
Implements Jira ROCMAI-568 ("Nightly release-readiness summary (wiki page)"); ticket status is "Backlog" but the PR's own scope matches the ticket's summary exactly, including its disambiguation from the unrelated scripts/release_readiness.py --require-signatures script.
Blocking
E2E tests (MI300X, current)is red on the current head — PR-level status check, commit5c732f80.
gh pr view 655 --json statusCheckRollupshows this check asFAILUREwhile every other check (includingE2E tests (W7900, current),E2E consolidated report (self-hosted),Test (affected crates), etc.) is green. The PR description asserts this is "a pre-existing flake unrelated to this diff," but this review could not independently verify that: the environment's network egress proxy returns 403 onrepos/.../actions/jobs/*andactions/runsendpoints, so the job log and a same-job comparison againstmain's recent runs were not reachable from here. Per this review's standing rule, a failing check is reported as blocking until confirmed otherwise, not assumed benign from the description's say-so.
Confidence 85 (the check's red state is directly observed; only the "unrelated to this diff" characterization is unverified) · logic · Fix: before merging, confirm (e.g. by re-running the job, or pointing at a concurrent failure onmainfor the same lane) that this is genuinely pre-existing and not a regression from this change; if confirmed, this is a non-issue.
Non-blocking
None
Decisions for the author
-
Two jobs now push to the same wiki git remote with no shared lock — tradeoff/answered-by-intent,
.github/workflows/nightly.yml:1633-1692(publish-e2e-wiki) and:1850-1964(publish-release-readiness-wiki).
Each job independently cloneshttps://.../${GITHUB_REPOSITORY}.wiki.git(shallow, depth 1), writes a different page, commits, andgit push origin HEADunderset -euo pipefailwith no retry. They use separateconcurrency.groupvalues (publish-e2e-wikivspublish-release-readiness-wiki) and nothing else serializes them, so if both run close together in the same nightly run, the second push can hit a non-fast-forward rejection and fail outright. Before this PR there was only one job pushing to the wiki, so this is a new race, not a pre-existing one. The PR description already names this precisely: "publish-e2e-wikiandpublish-release-readiness-wikiuse separate concurrency groups (each only serializes against itself) and neither retries a transient wiki-clone failure; a retry/backoff story for wiki pushes would be a repo-wide improvement, not something specific to this PR's new job." That is a real decision to leave it, not an oversight, so it's recorded here rather than under Blocking — would otherwise have been a blocking architectural finding (confidence 75: mechanism verified by reading both job definitions, real-world trigger odds depend on scheduling timing this review can't observe). The failure mode is self-healing (the next night's run succeeds cleanly) but produces a spurious red run; confirm that's an acceptable ongoing cost. -
release-readiness-nightly/release-readiness-narrativehave no branch/repo guard; onlypublish-release-readiness-wikidoes — tradeoff,.github/workflows/nightly.yml:1699-1849.
Verified directly:release-readiness-nightly'sif:is only${{ !cancelled() }}andrelease-readiness-narrative's is only${{ !cancelled() && needs.release-readiness-nightly.result == 'success' }}— neither checksgithub.repository/github.ref. Onlypublish-release-readiness-wiki(the job that actually writes anywhere) carriesgithub.repository == 'ROCm/rocm-cli' && github.ref == 'refs/heads/main'. So a manualworkflow_dispatchofnightly.ymlfrom any branch computes facts and (ifCOPILOT_PATis available there) drafts a narrative, without ever being able to publish it — burning CI minutes and a Copilot-CLI call but never reaching the wiki. Reasonable as designed (nothing public leaks beyond normal Actions-run visibility), but confirm the minute/API cost on a stray dispatch is acceptable rather than tightening the first two jobs' guards to match the third. -
Internal-commit-type filtering only looks at
scope, nottype, so a breaking commit of an internal type is never dropped — tradeoff/answered-by-intent,xtask/src/release_readiness.rs:220-269(is_internal_only_scope,categorize).
Traced independently:INTERNAL_SCOPES(ci,nightly,xtask,build,e2e, etc.) is checked only against a commit's parenthesized(scope), never against its bare Conventional-Committypebefore the colon. A commit likeci!: rework the dispatch matrix(no scope, typeci, marked breaking) is unconditionally pushed intobuckets.breakingregardless of its internal type — confirmed bycategorize_treats_a_bang_on_any_type_as_breaking's own test, which exercises this withdocs!:and asserts it lands inbreaking. The PR description already flags this exact case: "Whether internal-type breaking commits (ci!:,chore!:,build!:) should be dropped from the Breaking bucket is left as the reviewer explicitly flagged it as intent-dependent; no such commits exist in this branch today, so it doesn't affect the current page's output either way." No live commit triggers it today, so this doesn't change this page's output — confirm the intended behavior for when one eventually does. -
Stringly-typed job-result arguments could be silently swapped by a future refactor — non-blocking-improvement,
xtask/src/main.rs:398-414→xtask/src/release_readiness.rs:79-170.
nightly_result,windows_result, ande2e_report_job_resultare three same-typedStringparameters threaded positionally throughrun→assemble_facts. Correctly wired and test-pinned today (verified end-to-end againstnightly.yml's--nightly-result/--windows-result/--e2e-report-job-resultflags), but nothing at the type level would catch a future reordering. A smallenum JobResult { Success, Skipped, Other(String) }would make a swap a compile error instead of a silent behavior change. The PR description already defers this as a largermain.rsargument-surface refactor than this PR's scope — reasonable to leave for a follow-up. -
Whether non-blocking hardware lanes (W7900, MI350P) should count toward the NO-GO verdict — tradeoff.
Not something this review can decide unilaterally; the gate currently keys only onnightly,windows-nightly, ande2e-report-nightly's own result (which in turn depends on every lane inLANE_REPORTShaving uploaded a report, blocking or not). Confirm this matches the intended release gate.
Positive signals
assemble_facts's gates fail closed on every axis checked: an unparseable/missing E2E total readsNone→ NO-GO rather than defaulting to zero failures, and anynightly_result/windows_resultvalue other than the literal"success"/"skipped"is treated as a failing gate rather than silently passing — confirmed directly inxtask/src/release_readiness.rs:124-159and its paired tests.- Defense-in-depth around the LLM narrative step: the mechanical verdict is stripped from the JSON the model sees (
nightly.yml'sjq 'del(.verdict)'), the model's output is then escaped (not just link-stripped) before reaching the wiki with a dedicated bypass-via-nesting test, and the rendered page explicitly labels the section as "drafted by an LLM from public issue and commit text; not the verdict" — three independent layers rather than relying on prompt wording alone. - The new
workflow_contract.rstests extract and execute the real shell snippets from the YAML underbash(e.g. the narrative-escapingsedcommand, the env-only-reaches-the-shell check) rather than asserting against a hand-copied string, so they catch a real drift between the workflow file and what the test believes it does.
Deployment notes
release-readiness-narrativeneeds aCOPILOT_PATsecret (a Copilot-licensed PAT) configured in the repo; without it the step fails but the pipeline tolerates this (the table still publishes, with "narrative generation did not complete this run").publish-release-readiness-wikirequires the repo's wiki to already have at least one page created via the web UI (GitHub does not create the wiki's git repo otherwise) — the job fails with an explicit message if not, it does not silently no-op.- First real nightly dispatch after merge is the only way to confirm
release-readiness-nightly/release-readiness-narrative/publish-release-readiness-wikibehave end-to-end in production; the PR description notes a local dry run and one successful manualworkflow_dispatch(run 38396499964) but flags a follow-up dispatch to confirm the fail-safe paths specifically as still outstanding.
What this covered
Every file in the diff, read completely: xtask/src/release_readiness.rs (new, full file), xtask/src/main.rs (changed regions plus dispatch context), .github/prompts/release-readiness-narrative.txt (full), .github/workflows/nightly.yml and .github/workflows/e2e-selfhosted.yml (full files), every new/changed test function and helper in xtask/src/workflow_contract.rs, and docs/ci-hardware-testing.md (changed regions), via two independent fan-out workers grouped by coupling (Rust core logic + prompt; CI workflows + their contract tests), plus direct spot-verification of assemble_facts, total_e2e_failures, categorize, and the wiki-publish job bodies against the real files, cross-checked against crates/e2e-report/src/consolidated.rs for the E2E status-value assumptions. Base/head reviewed: 63ecab2d26ffb569ed857a0e7b0ab26a12bd5853…5c732f80f217c5b36a2f2d8a6a4c358ed64829bb, diffed locally via git merge-base/git diff against a local clone (GitHub's PR-diff API and Actions-log/run-listing endpoints both returned 403 through this environment's network proxy and were not used).
Did not run: cargo test/cargo clippy (no cargo toolchain in the worker environment) — all Rust findings are from static tracing and cross-referencing tests to production code, not execution; a GitHub Actions job-log/history comparison for the failing MI300X check (blocked by the proxy, see Blocking above); a dedicated "prior changes" pass (this PR's own description documents many rounds of a different automated reviewer's feedback, which — per this method's blind-review rule — was deliberately not shown to either fan-out worker and was read by this top-level pass only at the point the rubric calls for reading the change's stated intent, after the code-level findings above were already settled).
Methodology note: this PR already carries a distinct prior automated review (nowycondro, a "high-recall" pass) and a long series of reviews from a different account (rominf, tagged pr-review-watcher) including one already at this exact head (5c732f80). This review is independent of both — it did not read their conclusions, only (per the dedup check this task required) their author/body-prefix metadata to confirm no nowycondro review already covered this head under this review's own tag.
Add `cargo xtask release-readiness`, which computes a mechanical GO/NO-GO verdict and status table from the nightly results and the commits since the last release tag, and wire it into nightly.yml so the result is published to the repository wiki each night. Verdict (xtask/src/release_readiness.rs): NO-GO unless the nightly Linux build and `windows-nightly` each succeeded or were skipped (cancelled and timed_out count as failures), the consolidated E2E report's Total row parses and shows zero failures, and `e2e-report-nightly` succeeded. A missing or unreadable report is NO-GO, never zero failures. Commits since the tag are bucketed into features, fixes and breaking changes (`type!:` is breaking), with CI and dev-tooling scopes excluded and trailing PR references split out. Open bug-labelled issues are listed as a raw, reaction-ranked appendix. Workflow (.github/workflows/nightly.yml): - `release-readiness-nightly` runs the xtask against the consolidated E2E report and uploads facts.json and table.md. It runs even if an upstream job failed, and checks out full history. - `release-readiness-narrative` drafts prose from facts.json (verdict removed) with actions/ai-inference v2.1.1 and the pinned Copilot CLI. The prompt treats issue and commit text as untrusted data and the LLM never decides pass/fail. The response reaches the shell only through `env:`, and the step-output heredoc delimiter is random. - `publish-release-readiness-wiki` runs on main only and pushes the Release-Readiness page. It escapes backslashes, brackets and angle brackets in the narrative, block-quotes it under a heading that says it is LLM-drafted and not the verdict, and falls back to an evidence-unavailable notice or leaves the page unchanged when the table is missing or degenerate. nightly.yml LANE_REPORTS: the e2e-report-nightly missing-report check now lists both the `release` and `nightly` leg artifacts for all 7 nightly lanes and checks each, so a leg that dies before uploading fails e2e-report-nightly. As a result, publish-e2e-wiki (which needs e2e-report-nightly) also skips when any leg is missing, and the release-readiness verdict reads NO-GO. Documented in docs/ci-hardware-testing.md. e2e-selfhosted.yml: the per-PR e2e-report check's LANE_REPORTS now lists both legs of the Strix Ubuntu lane too, so that check also fails when its `nightly` leg uploads no report. Tests: unit tests for the gating, parsing and categorization logic, and workflow contract tests (xtask/src/workflow_contract.rs) covering the escaping `sed` line, the gate wiring, artifact names, permissions and the lane-report check. Needs a `COPILOT_PAT` repo secret; without it the table still publishes and the narrative is skipped. Signed-off-by: Juho Vainio <juho.vainio@amd.com>
5c732f8 to
0343a9f
Compare
There was a problem hiding this comment.
🔴 Automated review · pr-review-watcher · 0343a9f
Review — needs work
Full review of the whole change (single commit 0343a9f on top of merge-base ea6dd11).
Implements Jira ROCMAI-568 ("Nightly release-readiness summary wiki page") in full; the PR description's scope matches the ticket's summary closely, including one disclosed, reasoned deviation from the ticket's "mirrors publish-e2e-wiki's guards" framing — publish-release-readiness-wiki also runs (with an explicit "EVIDENCE UNAVAILABLE" notice) when release-readiness-nightly fails outright, not only on success, so the wiki page never goes stale without saying so.
Blocking
-
prek (lint / hygiene)CI check is failing on this PR's current head commit —.github/workflows (CI), jobprek (lint / hygiene), run 40666987693
GitHub's status-check rollup for commit0343a9f5reports this job asFAILURE. I could not retrieve the job's logs or annotations in this review environment (the GitHub API returned403/proxy errors forcheck-runs/.../annotationsand job-log endpoints) to determine root cause. Manual spot-checks of every file this diff touches — trailing whitespace, final-newline, CRLF, YAML validity — found nothing that would trip the hygiene hooks this job runs (trailing-whitespace,end-of-file-fixer,check-yaml,mixed-line-ending,ruff-check/ruff-format,shellcheck,cargo-fmt --check, per.pre-commit-config.yaml), which makes an unrelated/pre-existing flake plausible — but that is not established from here, only suggested. The PR description's blanket claim ("CI check failures... are pre-existing flakes unrelated to this diff") is an assertion, not evidence, and does not name this specific job; the one CI failure the description does explain by name ("Test (affected crates)... multicall-coreutils decoy issue") is a different check that currently showsSKIPPED, not this one.
Confidence 100 (that the check is failing, per GitHub's own rollup) · mechanical/CI · Fix: open the actual job log and either fix the real hygiene violation, or confirm and document that this specific job's failure is an unrelated, pre-existing flake. -
Build-row priority behavior is introduced by this change and has no test for the one case where it actually matters —
xtask/src/release_readiness.rs:496-503(theif ... contains("build") { "❌" } else if build_skipped { "not run" } ...chain), tests atxtask/src/release_readiness.rs:1037-1053and:1056-1070
The newrender_tablelogic must decide, per build row, whether a failing build gate (❌) outranks a skipped sibling build (not run) when both are true at once (e.g.nightly_result="failure",windows_result="skipped"). The currentif/else iforder gets this right today, but no test sets both conditions simultaneously — the two existing tests each isolate one condition alone. Swapping the branch order (checkingbuild_skippedfirst) would still pass both existing tests. This is new behavior this PR introduces, cheaply testable, and the PR's own later rounds explicitly closed equivalent gaps elsewhere (e.g. "Added tests for...build_skipped's skipped half for both the nightly and Windows build gates") without covering this specific combination.
Confidence 85 (traced directly against the two cited tests and the branch order) · logic/test-coverage · Fix: add a test withnightly_result="failure", windows_result="skipped"(or the reverse) asserting the row still renders❌, notnot run.
Non-blocking
-
Three new Action pins in
nightly.ymlare older than every other call site of the same actions in this file —.github/workflows/nightly.yml:1725(actions-rust-lang/setup-rust-toolchain@166cdcf... # v1.17.0, vs.@ecabd13d... # v2.0.0used at 7 other sites in this file);:1728,1782,1871,1883(actions/download-artifact@3e5f45b2... # v8.0.1, vs.@9000827c... # v8.0.2used elsewhere);:1751,1845(actions/upload-artifact@043fb46d... # v7.0.1, vs.@cf430e03... # v7.0.2used at 8+ other sites)
All three are still full-SHA pins per this repo's own convention, just stale ones — likely authored against an earlier branch state before the repo-wide Dependabot action bump (#688) landed. No confirmed behavioral break: the workflow's own top-level comment notesRUSTFLAGS: -D warningsis set at the workflow level specifically so warnings-as-errors reaches jobs regardless ofsetup-rust-toolchainversion, so the version drift is a consistency/maintenance issue rather than a demonstrated regression.
Confidence 90 · mechanical · Fix: bump all three to the SHAs already used elsewhere in this file (Dependabot will otherwise pick this up on its own schedule). -
Status-table row glyphs are derived by substring-matching free-text gate messages, not a structured tag —
xtask/src/release_readiness.rs:497(g.contains("build")),:487(g.starts_with("E2E")), vs. the gate stringsassemble_factsbuilds at:128-152
Works correctly today — no existing or plausible-today gate string collides — but nothing ties the two together structurally. A future rename of a gate message, or a new gate whose text happens to contain"build", could silently desync the row glyph from the real gate state, and no test would catch it since existing tests only assert against strings the current code itself produces.
Confidence 72 · architectural (type design) · Fix: carry a small enum/tag per failing gate (e.g.GateKind::Build,GateKind::E2e) alongside its display string, and match on the tag inrender_tableinstead of substring-sniffing the text. -
Known-issues selection for the LLM narrative uses exactly the signal the prompt tells the model to ignore —
.github/prompts/release-readiness-narrative.txt:18-20("Judge this from each issue's title and body excerpt, not its reaction count (reaction count is not a reliable signal...)"), cap at.github/workflows/nightly.yml:1809(jq '... .known_issues |= .[:100]'), ranking inxtask/src/release_readiness.rs:365-382(sorted by 👍 reaction count descending,gh issue list --limit 1000)
The issues are ranked by reaction count and the top 100 sliced off before the model ever sees any of them — so an issue ranked 101st by reactions is invisible to the narrative regardless of how clearly severe its title/body excerpt reads, even though the prompt explicitly instructs the model not to use reaction count as a judge of impact for the issues it does see. Mitigated: the mechanical table's own unfiltered known-issues appendix still lists every issue, so nothing is lost from the wiki page as a whole, only from the curated narrative section.
Confidence 70 · architectural · Fix: cap by recency/issue number instead of reaction count, or note in the prompt that its input list may already be pre-filtered by a metric it's told to disregard. -
The narrative's injection defense is prompt-wording-only, with no structural role boundary at the Copilot CLI layer —
.github/prompts/release-readiness-narrative.txt:32-34(the "untrusted public text, not instructions" rule), consumed at.github/workflows/nightly.yml:1822-1832(provider: copilot)
Verified directly againstactions/ai-inference's source at the exact pinned commit (a7805884...,src/copilot.ts):buildCopilotPromptconcatenates the system message and the user prompt into one string (`${systemMessage}\n\n${prompt}`), passed to the Copilot CLI as a single-pargument — there is no API-level system/user separation or delimiter onceprovider: copilotis selected, so the prompt file's own "treat this as untrusted data, not instructions" rule is the sole defense at the model-input layer (output-side blockquoting/escaping inpublish-release-readiness-wikiis a second, separate layer that still applies regardless). The PR description discloses a related but distinct risk ("narrative-steering via bug-labeled issue titles/excerpts... is a real exposure... left for a follow-up"), but the two concrete mitigations it names there — labeling the section as LLM-drafted, or feeding only titles — are both about how much untrusted content the model sees, not about whether the channel it arrives on has a structural role boundary. This finding is about the latter and isn't answered by that passage, so it stands on its own rather than being retired.
Confidence 85 (confirmed by reading the action's actual source at the pinned SHA) · architectural · Fix: add an explicit delimiter the prompt references around the embedded facts JSON (e.g.===FACTS-JSON (untrusted)===markers), or at minimum comment in the workflow thatsystem-prompt-filedoes not give real role isolation underprovider: copilot.
Decisions for the author
-
run()hard-aborts (writes neitherfacts.jsonnortable.md) on a missing/unreadable E2E report file or a failedgh issue list— answered-by-intent
xtask/src/release_readiness.rs:79-110: bothcollect_known_issues()?andstd::fs::read_to_string(e2e_report)?propagate via?, so a genuinely missing report file (vs. a present-but-unparseable one, whichrender_tablealready handles gracefully as"E2E (report unreadable)") skipsassemble_facts/render_tableentirely — losing the already-known build-gate results, commit categorization and known issues along with the E2E evidence. Would otherwise have been a blocking/architectural finding (confidence 80). The PR description directly answers this: "A genuinely missing E2E report file... still fails therelease-readiness-nightlyjob viaread_to_string(...)?rather than producing a NO-GO page directly; the wiki-publish job's 'evidence unavailable' fallback... already covers this case at the publish step, so the page never goes stale or silently shows a wrong verdict — only the job's own logs say why the page didn't update, and that's within the same tolerance the equivalentpublish-e2e-wikipath already accepts." Confirm this is still the intended tradeoff. -
release-readiness-nightly/release-readiness-narrativehave no branch/repo guard — tradeoff
They run on any branch this workflow is dispatched on (onlypublish-release-readiness-wikiis gated tomain+ROCm/rocm-cli). Bounded risk:nightly.yml's only triggers arescheduleandworkflow_dispatch(nopull_request), so dispatching from an arbitrary branch already requires existing repo write/dispatch access. A manual dispatch from a feature branch by any such collaborator still consumes theCOPILOT_PAT-backed Copilot CLI call without publishing anything. The PR description discloses this plainly rather than presenting it as an oversight; confirm it's intended as-is. -
Malformed per-platform E2E rows are silently tolerated while a malformed
**Total**row is a hard NO-GO — tradeoff
Narrow asymmetry intotal_e2e_failures's parsing; may be deliberate (a bad total is ambiguous about whether any platforms failed, while a bad per-platform row still leaves the total trustworthy) but worth the author confirming that reading is intentional.
Positive signals
assemble_facts/render_tableare pure (no I/O), which is what makes the GO/NO-GO verdict and table-rendering logic fully unit-testable without shelling out togit/gh— a design choice that visibly paid for itself in the test suite's depth.- Deliberate mutation-resistant tests at exactly the high-risk edges: the
n > 0vsn > 1failure-count boundary, multibyte-safe slicing insplit_trailing_pr_reference,**Total**-row-before-header ordering, andtotal_e2e_failures's scoping — tested against the reale2e_report::consolidated_summary_markdownproducer rather than a hand-rolled fixture. workflow_contract.rs's tests extract and execute the real bash/sed snippets from the actual YAML underbash -c(confirmed by hand-tracing several, including the markdown-escaping pipeline and the three-way wiki-publish guard), not copies that could silently drift from production.- Layered, well-reasoned defense for the narrative's untrusted content: backslash-doubling runs before bracket-escaping (correctly defeats a self-escaping bypass attempt), and every line is blockquoted (
s/^/> /), which neutralizes forged headings/tables even from markup the escaping step doesn't target. - Single, clean, DCO-signed commit whose message accurately describes the final code with no stale claims — a commit-message-accuracy objection raised in an earlier round of this PR's own review history appears to have been properly resolved by this head commit.
Deployment notes
- Requires a
COPILOT_PATrepository secret (a Copilot-licensed PAT; a bareGITHUB_TOKENis not accepted by the Copilot CLIactions/ai-inferenceshells out to). Its absence degrades gracefully:release-readiness-narrativefails viacontinue-on-error: trueand the mechanical table still publishes without a narrative section. - First run needs the repo wiki to already have at least one page saved via the web UI (GitHub does not create the wiki git repo otherwise) — the same prerequisite
publish-e2e-wikialready has, now shared bypublish-release-readiness-wiki.
What this covered
Read the full diff (ea6dd112e435d34e432b36ae7c8b2b88d57594ae...0343a9f5a6a5337cc23da84377298c9677f5f97a, 7 files, +2379/-36) and every changed file completely, via four independent blind fan-out passes grouped by coupling (xtask/src/release_readiness.rs + xtask/src/main.rs; .github/workflows/nightly.yml + .github/workflows/e2e-selfhosted.yml; xtask/src/workflow_contract.rs; .github/prompts/release-readiness-narrative.txt + docs/ci-hardware-testing.md), plus my own direct verification of every candidate finding against the source (including cloning actions/ai-inference at its pinned commit to confirm the role-collapse claim, and grepping every Action-pin call site across the touched workflow files) and the existing publish-e2e-wiki job for pattern comparison.
Did not run: cargo test/clippy/fmt — no Rust toolchain is available in this review environment and crates.io/rustup are network-blocked here, so Rust-level findings are scored on traced logic and cross-referenced tests rather than an actual run. History/prior-changes passes were light: xtask/src/release_readiness.rs is a net-new file (no history to check); nightly.yml/e2e-selfhosted.yml's changes were compared against the existing publish-e2e-wiki job they mirror; a full trawl of past review-thread comments on these files was not performed. CI status was read from gh pr view --json statusCheckRollup (taken at face value); the one failing check's own logs/annotations could not be fetched in this sandbox (API access blocked) — noted as a Blocking finding rather than assumed.
Not reconciled against this PR's own review discussion/threads — this report was produced blind to them, per method, and reads the PR description only for Step 9 (intent-vs-code reconciliation), never as a source of findings.
Signed-off-by: Juho Vainio <juho.vainio@amd.com>
nowycondro
left a comment
There was a problem hiding this comment.
🔴 Automated review · pr-review-watcher · e639a98
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, via three independent blind fan-out passes grouped by coupling: (1) xtask/src/release_readiness.rs + xtask/src/main.rs; (2) xtask/src/workflow_contract.rs + .github/workflows/e2e-selfhosted.yml; (3) .github/workflows/nightly.yml + .github/prompts/release-readiness-narrative.txt + docs/ci-hardware-testing.md.
Implements Jira ROCMAI-568 ("Nightly release-readiness summary (wiki page)") in full; the ticket's summary matches the PR's scope exactly, including its disambiguation from the unrelated pre-existing scripts/release_readiness.py --require-signatures gate. Ticket status is "Backlog" but the PR description's own account of the shipped behavior matches the code.
Blocking
None.
Non-blocking
- Three new GitHub Actions pins in
nightly.ymlare older than every other call site of the same actions in this file —.github/workflows/nightly.yml:1725(actions-rust-lang/setup-rust-toolchain@166cdcf... # v1.17.0, vs.@ecabd13d... # v2.0.0used at 8 other sites in this file),:1728,1782,1871,1883(actions/download-artifact@3e5f45b2... # v8.0.1, vs.@9000827c... # v8.0.2used elsewhere),:1751,1845(actions/upload-artifact@043fb46d... # v7.0.1, vs.@cf430e03... # v7.0.2used at 8+ other sites).
Confirmed directly: grepped every call site of all three actions in the PR-head file, and confirmed the base commit uses only the newer SHAs throughout — this PR introduces the older ones fresh, only in its new jobs. All three are still full-commit-SHA pins with a trailing version comment, so this satisfies this repo's own pinning rule; it's an unexplained version regression, not a security gap.
Confidence 90 · mechanical · Fix: align the three new pins to the SHAs already used elsewhere in this file, or note why they intentionally differ.
Decisions for the author
- Two jobs now push to the same wiki git remote with no shared lock — answered-by-intent,
.github/workflows/nightly.yml:1639-1698(publish-e2e-wiki, pre-existing) and:1856-1970(publish-release-readiness-wiki, new).
Each job independently cloneshttps://.../${GITHUB_REPOSITORY}.wiki.git(shallow), writes a different page, commits, and does a plaingit push origin HEAD(lines 1698 / 1970) with no retry. They carry separateconcurrency.groupvalues (publish-e2e-wikiat line 1648 vs.publish-release-readiness-wikiat line 1867) and noneeds:ordering between them, so if both run close together in the same nightly run the second push gets a non-fast-forward rejection and the job fails outright. Before this PR there was exactly one job touching the wiki repo, so this specific cross-job race could not previously occur — it is introduced by this PR. Would otherwise be a blocking architectural finding (confidence 85: verified directly from both job bodies, not merely argued). The PR description already names the exact mechanism and accepts it: "publish-e2e-wikiandpublish-release-readiness-wikiuse separate concurrency groups (each only serializes against itself) and neither retries a transient wiki-clone failure; a retry/backoff story for wiki pushes would be a repo-wide improvement, not something specific to this PR's new job." The failure mode is self-healing (the next night's run succeeds cleanly) but produces a spurious red run each time it triggers — confirm that ongoing cost is still acceptable, since a sharedconcurrency.groupacross both jobs would remove it cheaply. release-readiness-nightly/release-readiness-narrativehave no branch/repo guard — tradeoff,.github/workflows/nightly.yml:1699-1849. Verified: theirif:conditions check only!cancelled()(and the narrative job additionally checks the facts job's result), while onlypublish-release-readiness-wikichecksgithub.repository == 'ROCm/rocm-cli' && github.ref == 'refs/heads/main'. Already disclosed indocs/ci-hardware-testing.mdand the PR description. Low risk (workflow_dispatchalready requires repo write access), but a manual dispatch from any branch computes facts and drafts a narrative (burningCOPILOT_PAT/CI minutes) without ever being able to publish. Confirm that cost is acceptable as-is.- E2E evidence is derived by re-parsing the rendered support-matrix table (splitting on
|, indexing columns 5/8) rather than consumingcrates/e2e-report's structured types directly — tradeoff,xtask/src/release_readiness.rs. Fragile to column reordering in that crate; the code's own comment explains the choice (stays in lockstep with that crate's known-bugs/xfail handling), and one regression test exercises the real producer output rather than a hand-written fixture. Worth confirming the coupling is an accepted ongoing cost. collect_known_issues_inhard-caps at--limit 1000openbug-labeled issues with no pagination — tradeoff, low likelihood today, confirm it's accepted rather than an oversight.
Positive signals
assemble_facts's gates fail closed on every axis: an unparseable/missing E2E total reads asNone→ NO-GO rather than defaulting to zero failures, and any job-result value other than the literal"success"/"skipped"is treated as failing rather than silently passing.- Defense-in-depth around the LLM narrative step: the mechanical verdict is stripped from the JSON before the model sees it (
jq 'del(.verdict)', covered by a dedicated test), the model's output is then escaped — not just link-stripped — before reaching the wiki (with a bypass-via-nesting regression test), secrets/outputs are passed viaenv:rather than interpolated intorun:shell strings, and the$GITHUB_OUTPUTheredoc uses a per-run random delimiter specifically because the JSON carries untrusted issue titles. workflow_contract.rs's new tests extract and execute the real shell snippets from the actual YAML (e.g. theLANE_REPORTSper-leg check, the narrative-escapingsedpipeline) rather than asserting against a hand-copied string, and the per-leg test actually withholds an artifact and asserts the real script fails naming only that leg — this is checking production behavior, not a parallel reimplementation that could pass even if the real logic broke.- Deliberate mutation-resistant tests at the high-risk edges: multibyte-safe slicing in
split_trailing_pr_reference, the exactn > 0vsn > 1failure-count boundary, andcategorize's internal-only-scope detection reading offhead_no_bangrather thanhead(confirmed by hand that reverting toheadwould leak an internal-only breaking commit into the Breaking bucket). - Pure logic (
assemble_facts,categorize,render_table,total_e2e_failures) is cleanly separated from I/O, making the GO/NO-GO verdict fully unit-testable without shelling out togit/gh.
Deployment notes
release-readiness-narrativerequires aCOPILOT_PATrepository secret (a Copilot-licensed PAT; a bareGITHUB_TOKENis not accepted by the Copilot CLIactions/ai-inferenceshells out to). Its absence degrades gracefully — the mechanical table still publishes without a narrative section.publish-release-readiness-wikirequires the repo wiki to already have at least one page saved via the web UI (GitHub does not create the wiki's git repo otherwise) — the same pre-existing prerequisitepublish-e2e-wikihas, now shared by a second page, and the job fails with an explicit message rather than a silent no-op if unmet.- The PR's own test plan already flags that a follow-up live nightly dispatch is still needed to confirm the new fail-safe/guard paths end-to-end in production (local/dry-run verification is in place).
What this covered
Every file in the diff ea6dd112e435d34e432b36ae7c8b2b88d57594ae...e639a98250f961201871eced46bd8137922574a0 (7 files, +2379/-36), read completely by three independent blind fan-out workers grouped by coupling, each also reading the surrounding context needed to judge its files (call sites, the real e2e-report producer, the pre-existing publish-e2e-wiki job for comparison). I independently re-verified the wiki-push-race and action-pin findings directly against the PR-head YAML before scoring them, and checked live CI (gh/GitHub check-runs API): all required checks are green on this exact commit; two individual Strix-Ubuntu E2E matrix legs show cancelled but the consolidated "E2E consolidated report (self-hosted)" gate that depends on them still reports success, consistent with this PR's own tested per-lane-artifact-presence logic rather than a regression.
Did not run: cargo test/clippy/fmt myself or via the fan-out workers (no Rust toolchain available in these environments) — Rust-level findings and verifications are from direct tracing against the source and existing tests, not execution. No dedicated git log/blame history pass was run (release_readiness.rs is a net-new file; the workflow/doc diffs were judged against their current form and the pre-existing publish-e2e-wiki pattern they mirror, not their own history). This PR carries an extensive prior review history on this same branch (including earlier rounds from this same automated account); per this method's blind-review rule, none of that was shown to the fan-out workers or used to reach any finding above — it was read only afterward, to check for an existing review at this exact head (none found) and to evaluate the stated-intent reconciliation in "Decisions for the author."
Summary
cargo xtask release-readiness, which mechanically computes a GO/NO-GO verdict, categorized commits (New Features / Bug Fixes / Breaking Changes), known issues, and an Area/Evidence/Status table from git history, job results, and openbug-labeled issues.release-readiness-nightly→release-readiness-narrative→publish-release-readiness-wiki) that drafts short prose from those facts viaactions/ai-inference(using theCOPILOT_PATsecret, a Copilot-licensed PAT — the Copilot CLI that action shells out to does not accept a bareGITHUB_TOKEN) and publishes aRelease-Readinesswiki page.publish-release-readiness-wikishares the E2E support-matrix wiki job's branch/repo gate, own concurrency group, and degenerate-output guard, but additionally still runs (with an explicit "evidence unavailable" notice) whenrelease-readiness-nightlyitself fails outright, instead of leaving the wiki showing a stale verdict with no sign it's stale.release-readiness-nightly/release-readiness-narrativethemselves have no branch/repo guard — they run on any branch this workflow is dispatched on.ci,nightly,github,hooks,xtask,build,deps,e2e*) are excluded from the Bug Fixes / New Features summary, so the page stays focused on end-user-facing functionality and a summarized hardware-validation (E2E) result rather than CI housekeeping.Jira: ROCMAI-568
Changes since the automated review
e2e-report-nightlyjob's own result (--e2e-report-job-result), not individual per-lane results, so a lane that dies before uploading a report still fails the gate instead of silently vanishing from the consolidated totals."E2E (report unreadable)") instead of silently reading as zero failures. The status table's header always renders regardless (confirmed by test); this case does not trippublish-release-readiness-wiki's degenerate-output guard, which only trips when the table has no header row at all (e.g. a crash before writing anything).nightly/windows-nightlyjob (a no-op night) no longer causes a false NO-GO.extract_pr_numberno longer matches a mid-sentence(#NNN)parenthetical.assemble_factsfunction so the GO/NO-GO logic is directly unit-tested (previously zero coverage on the orchestration/rendering path).mainand re-authored 3 commits that had picked up the wrong git identity.Changes since the second round of review
publish-release-readiness-wikinow runs (instead of being skipped entirely) whenrelease-readiness-nightlyfails outright, publishing an explicit "EVIDENCE UNAVAILABLE" notice to the wiki rather than silently leaving a stale verdict in place with no indication it's stale.render_table's status table now lists the actualfailing_gatesreason strings beneath the Area/Evidence/Status table (e.g.E2E (lane report check: cancelled)), since a cancelled lane otherwise renders as a contradictory "0 failure(s)" next to a red ❌.publish-release-readiness-wiki'sseddoubles\and escapes[,],<,>instead of trying to strip[text](url)patterns). The old stripping regex left<img>tags, reference-style[a][1]links, and nested[outer [inner](url)](url)links untouched or still live; escaping brackets/angle-brackets can't be bypassed by nesting, since an escaped bracket is never re-parsed as link/image syntax. This is distinct from the issue-title escaping above: that covers titles asrender_tablerenders them into the wiki's status table, this covers the LLM's own drafted output on its way to the same page. Neither coversfacts.jsonitself, which is what the LLM prompt is actually built from and still carries raw, unescaped titles.build_known_issuesmapping/sort function out ofcollect_known_issues, so its test exercises the real production filter/sort instead of a parallel reimplementation that would pass even if the production logic broke.skippedtreated as passing for the nightly build, the Windows build, and the E2E report job (each asserted separately), the exactn > 0vsn > 1failure-count boundary,escape_markdown's backslash-doubling/newline-collapsing/angle-bracket-escaping, and a multibyte-safe guard insplit_trailing_pr_reference(removing it could panic by slicing mid-character on a subject ending in a multibyte character after a mid-sentence(#N)reference).actions/ai-inferencepin comment: that action tags full semver releases (v1.0.0,v2.1.1, ...) alongside a floating major alias, not bare majors only — the SHA pin still freezes an exact commit either way.docs/ci-hardware-testing.md's release-readiness section: it previously (incorrectly) scoped the whole feature tomainand claimed the wiki-publish guards are identical topublish-e2e-wiki's.LANE_REPORTSnow requires both thereleaseandnightlyleg's uploaded report for every channel-matrixed lane, not just thereleaseleg, in bothnightly.ymlande2e-selfhosted.yml. Ine2e-selfhosted.ymlthis means the per-PR "E2E consolidated report (self-hosted)" check now also fails if the Strix Ubuntunightlyleg uploads no report, not just itsreleaseleg.Left unchanged (judgment calls, not oversights)
publish-e2e-wikijob and this repo's public issue tracker already operate under, so it isn't a new exposure introduced here.publish-e2e-wikiandpublish-release-readiness-wikiuse separate concurrency groups (each only serializes against itself) and neither retries a transient wiki-clone failure; a retry/backoff story for wiki pushes would be a repo-wide improvement, not something specific to this PR's new job.release-readiness-nightlyjob viaread_to_string(...)?rather than producing a NO-GO page directly; the wiki-publish job's "evidence unavailable" fallback (above) already covers this case at the publish step, so the page never goes stale or silently shows a wrong verdict — only the job's own logs say why the page didn't update, and that's within the same tolerance the equivalentpublish-e2e-wikipath already accepts.Changes since the third round of review
total_e2e_failuresnow scans only the summary table's row range (between the| Platformheader and the| **Total**row), instead of every|-delimited line in the document — it was also matching rows from the expectation-grid and command-coverage tablese2e-reportprints after the summary, which inflated the failure count roughly 90-fold against a real report and made the verdict unable to ever read GO. Added a regression test againste2e_report::consolidated_summary_markdown's real output, not a hand-written snippet.nightly,windows-nightly) now treatcancelled/timed_outas failures instead of reading as GO. Added failure-path tests forcollect_commit_subjectsandcollect_known_issues(behind acurrent_dirseam) so agit/ghfailure is no longer an untested branch.fetch-depth: 0onrelease-readiness-nightly's checkout, the publish job'sneedson both upstream jobs, the twocontinue-on-error: trueartifact downloads, the distinct wiki page names, and the dynamic--nightly-resultread; and made the env-only-reaches-the-shell test fail if any other line in the job also referencessteps.draft.outputs.response.categorizenow puts anytype!:(not justfeat!/fix!) in the Breaking bucket, matching Conventional Commits' actual grammar.windows-nightlywas skipped, instead of claiming a pass with no Windows build behind it.workflow_contract.rs(no behavior change).Left unchanged (judgment calls, not oversights)
--e2e-lane-resultflag, "6 GPU/WSL E2E lane jobs", link-stripping, etc.) are left as-is in this pass; correcting them means rewriting already-pushed history, which this PR doesn't force-push to do. They'll be reworded in the squash-merge body at merge time instead.Test (affected crates)failing onpreflight_runs_the_real_reclaim_assert_clearis the pre-existing multicall-coreutils decoy issue fixed onmainby fix(ci): keep the GPU reclaim decoys alive on multicall coreutils #712; this branch's merge-base predates that fix. A rebase (not a code change in this diff) settles it, and it was already rated non-gating.bug-labeled issue titles/excerpts (any GitHub user can author text an LLM then drafts into wiki prose) is a real exposure, but fixing it (labeling the section as LLM-drafted from untrusted input, or feeding only titles) is a product decision about how much narrative detail to keep, not a one-line fix; left for a follow-up.Changes since the fourth round of review
facts.jsonnow always ends with a trailing newline, so the$GITHUB_OUTPUTheredoc delimiter innightly.yml's "Read facts for prompt" step can't land on the same line as the JSON's closing brace even if that step's ownechoguard is ever dropped.e2e-report-nightly's result no longer acceptsskippedas a passing gate; that job isif: always()and has no legitimate no-op-night skip.total_e2e_failuresno longer panics when a**Total**row precedes the| Platformheader; it now searches for Total only after the header index.""status arm intotal_e2e_failures(the producer only ever emits PASS, FAIL, or EMPTY).collect_known_issues_in_fails_outside_a_github_reponow asserts the specificgh issue listfailure message instead of a substring both thebail!and JSON-parse-error paths happen to share.failing_gatesdoc omitted the lane-report-check gate;nightly.yml'sai-inferencepin comment named the wrong AGENTS.md policy;ci-hardware-testing.mdpointed "below" at a section that's actually above.excerpt's exact-character-limit boundary, andbuild_skipped's skipped half for both the nightly and Windows build gates.Left unchanged (judgment calls, not oversights)
COMMIT_MESSAGES) builds the squash body verbatim from these commit messages and the merge queue has no step to edit it, this needs to happen as an actual history rewrite before the PR is queued, not "at merge time" as earlier rounds assumed.ci!:,chore!:,build!:) should be dropped from the Breaking bucket is left as the reviewer explicitly flagged it as intent-dependent; no such commits exist in this branch today, so it doesn't affect the current page's output either way.publish-release-readiness-wiki's "EVIDENCE UNAVAILABLE" notice can still be wrong when it's a transient table-download failure rather than an actualrelease-readiness-nightlyfailure (both take the same! -f table.mdbranch); narrowing this needs threadingneeds.release-readiness-nightly.resultthroughenv:and is left for a follow-up.main.rs→release_readiness::run's argument wiring and its 5 result strings remain untested and untyped (a swappedwindows_result/e2e_report_job_resultor a typo likesucceswould compile and read as a failed gate rather than an error); aValueEnumJobResultwould be the fix, left for a follow-up since it's a larger refactor ofmain.rs's argument surface than this pass's scope.sdk_versionaxis being checked differently from thechannelaxis inworkflow_contract.rspredates this PR; this round only put the two side by side, so it's left as a follow-up rather than a fix bundled into an unrelated pass.Test plan
cargo test -p xtask(full suite, 371 tests, including unit tests for the fixes above and workflow-contract tests exercising the real publish guards — including the three-way missing/degenerate/well-formed table guard and the narrative-escaping sed onpublish-release-readiness-wiki— against actual extracted shell snippets, not pinned strings)cargo clippy -p xtask --all-targets --all-features -- -D warningscargo fmt -p xtask --checkcargo xtask release-readinessend-to-end locally against this branch's real commit history and a fake E2E report, inspectedtable.mdoutputnightly.ymlon this branch (run 38396499964) and confirmedrelease-readiness-nightlyandrelease-readiness-narrativecomplete successfully; downloaded and read thenarrative.mdartifact to confirm it's genuine grounded prose citing real PR/issue numbers, not a refusal or empty output