Skip to content

feat: nightly release-readiness summary (wiki page) - #655

Open
juhovainio wants to merge 17 commits into
mainfrom
feat/nightly-release-readiness
Open

juhovainio wants to merge 17 commits into
mainfrom
feat/nightly-release-readiness

Conversation

@juhovainio

@juhovainio juhovainio commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Adds 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 open bug-labeled issues.
  • Adds a nightly pipeline (release-readiness-nightly → release-readiness-narrative → publish-release-readiness-wiki) that drafts short prose from those facts via actions/ai-inference (using the COPILOT_PAT secret, a Copilot-licensed PAT — the Copilot CLI that action shells out to does not accept a bare GITHUB_TOKEN) and publishes a Release-Readiness wiki page. publish-release-readiness-wiki shares 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) when release-readiness-nightly itself fails outright, instead of leaving the wiki showing a stale verdict with no sign it's stale. release-readiness-nightly/release-readiness-narrative themselves have no branch/repo guard — they run on any branch this workflow is dispatched on.
  • The LLM only ever drafts prose from already-computed facts; it never decides pass/fail, counts, or PR/issue numbers.
  • Commits scoped entirely to internal CI/dev tooling (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

  • The release-readiness verdict's E2E gate now reads the consolidated e2e-report-nightly job'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 evidence that's missing or unparseable (e.g. a truncated/broken report) now fails the gate explicitly ("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 trip publish-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).
  • A legitimately skipped nightly/windows-nightly job (a no-op night) no longer causes a false NO-GO.
  • Untrusted GitHub issue titles are escaped before reaching the generated wiki page.
  • extract_pr_number no longer matches a mid-sentence (#NNN) parenthetical.
  • Extracted a pure assemble_facts function so the GO/NO-GO logic is directly unit-tested (previously zero coverage on the orchestration/rendering path).
  • Rebased onto current main and re-authored 3 commits that had picked up the wrong git identity.
  • The Strix Halo Windows E2E pool-label fix that had been bundled in here is split out into fix(nightly): run Strix Halo Windows E2E on the devlab-dispatch pool #682.

Changes since the second round of review

  • publish-release-readiness-wiki now runs (instead of being skipped entirely) when release-readiness-nightly fails 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 actual failing_gates reason 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 ❌.
  • The narrative's embedded markdown is now escaped, not link-stripped, before it's pushed to the wiki (publish-release-readiness-wiki's sed doubles \ 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 as render_table renders them into the wiki's status table, this covers the LLM's own drafted output on its way to the same page. Neither covers facts.json itself, which is what the LLM prompt is actually built from and still carries raw, unescaped titles.
  • Extracted a pure build_known_issues mapping/sort function out of collect_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.
  • Added test coverage for branches that were previously exercised by no test: the GO/NO-GO verdict headline, PR-number-suffix rendering, skipped treated as passing for the nightly build, the Windows build, and the E2E report job (each asserted separately), the exact n > 0 vs n > 1 failure-count boundary, escape_markdown's backslash-doubling/newline-collapsing/angle-bracket-escaping, and a multibyte-safe guard in split_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).
  • Fixed the actions/ai-inference pin 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.
  • Corrected docs/ci-hardware-testing.md's release-readiness section: it previously (incorrectly) scoped the whole feature to main and claimed the wiki-publish guards are identical to publish-e2e-wiki's.
  • LANE_REPORTS now requires both the release and nightly leg's uploaded report for every channel-matrixed lane, not just the release leg, in both nightly.yml and e2e-selfhosted.yml. In e2e-selfhosted.yml this means the per-PR "E2E consolidated report (self-hosted)" check now also fails if the Strix Ubuntu nightly leg uploads no report, not just its release leg.

Left unchanged (judgment calls, not oversights)

  • The narrative job drafts prose from already-public GitHub issue titles/commit subjects and publishes it to the (public) wiki; this is the same trust boundary the existing publish-e2e-wiki job and this repo's public issue tracker already operate under, so it isn't a new exposure introduced here.
  • publish-e2e-wiki and publish-release-readiness-wiki use 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.
  • A genuinely missing E2E report file (as opposed to a present-but-unparseable one) still fails the release-readiness-nightly job via read_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 equivalent publish-e2e-wiki path already accepts.
  • Bare URLs in issue titles or commit subjects can still be autolinked by the wiki renderer even though bracket/angle-bracket markup is escaped; this is a GitHub-renderer behavior outside what escaping on our side can prevent, not a gap in the escaping itself.
  • The CI check failures showing on this PR are pre-existing flakes unrelated to this diff (see individual check runs); not a regression from these changes.

Changes since the third round of review

  • total_e2e_failures now scans only the summary table's row range (between the | Platform header and the | **Total** row), instead of every |-delimited line in the document — it was also matching rows from the expectation-grid and command-coverage tables e2e-report prints 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 against e2e_report::consolidated_summary_markdown's real output, not a hand-written snippet.
  • Both build gates (nightly, windows-nightly) now treat cancelled/timed_out as failures instead of reading as GO. Added failure-path tests for collect_commit_subjects and collect_known_issues (behind a current_dir seam) so a git/gh failure is no longer an untested branch.
  • Strengthened the workflow-contract tests a prior mutation-testing pass found gaps in: pinned fetch-depth: 0 on release-readiness-nightly's checkout, the publish job's needs on both upstream jobs, the two continue-on-error: true artifact downloads, the distinct wiki page names, and the dynamic --nightly-result read; and made the env-only-reaches-the-shell test fail if any other line in the job also references steps.draft.outputs.response.
  • categorize now puts any type!: (not just feat!/fix!) in the Breaking bucket, matching Conventional Commits' actual grammar.
  • The Build row now renders "not run" instead of ✅ when windows-nightly was skipped, instead of claiming a pass with no Windows build behind it.
  • Fixed a stale comment and a misleading assertion message in workflow_contract.rs (no behavior change).

Left unchanged (judgment calls, not oversights)

  • Commit messages describing behavior the code no longer has (a --e2e-lane-result flag, "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 on preflight_runs_the_real_reclaim_assert_clear is the pre-existing multicall-coreutils decoy issue fixed on main by 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.
  • Narrative-steering via 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.
  • Whether non-blocking lanes (W7900, MI350P) should count toward NO-GO is a tradeoff for the author/team to confirm, not something to decide unilaterally in this pass.

Changes since the fourth round of review

  • facts.json now always ends with a trailing newline, so the $GITHUB_OUTPUT heredoc delimiter in nightly.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 own echo guard is ever dropped.
  • e2e-report-nightly's result no longer accepts skipped as a passing gate; that job is if: always() and has no legitimate no-op-night skip.
  • total_e2e_failures no longer panics when a **Total** row precedes the | Platform header; it now searches for Total only after the header index.
  • Dropped the unreachable "" status arm in total_e2e_failures (the producer only ever emits PASS, FAIL, or EMPTY).
  • collect_known_issues_in_fails_outside_a_github_repo now asserts the specific gh issue list failure message instead of a substring both the bail! and JSON-parse-error paths happen to share.
  • Fixed 3 stale comments: the failing_gates doc omitted the lane-report-check gate; nightly.yml's ai-inference pin comment named the wrong AGENTS.md policy; ci-hardware-testing.md pointed "below" at a section that's actually above.
  • Added tests for 3 previously-untested branches: the known-issue reaction tie-break, excerpt's exact-character-limit boundary, and build_skipped's skipped half for both the nightly and Windows build gates.

Left unchanged (judgment calls, not oversights)

  • Commit-message history-rewrite objection still holds: correcting the 4 commits whose messages describe removed behavior means rewriting already-pushed branch history and force-pushing, which this pass doesn't do. Since this repo's squash setting (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.
  • 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.
  • publish-release-readiness-wiki's "EVIDENCE UNAVAILABLE" notice can still be wrong when it's a transient table-download failure rather than an actual release-readiness-nightly failure (both take the same ! -f table.md branch); narrowing this needs threading needs.release-readiness-nightly.result through env: 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 swapped windows_result/e2e_report_job_result or a typo like succes would compile and read as a failed gate rather than an error); a ValueEnum JobResult would be the fix, left for a follow-up since it's a larger refactor of main.rs's argument surface than this pass's scope.
  • The sdk_version axis being checked differently from the channel axis in workflow_contract.rs predates 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 on publish-release-readiness-wiki — against actual extracted shell snippets, not pinned strings)
  • cargo clippy -p xtask --all-targets --all-features -- -D warnings
  • cargo fmt -p xtask --check
  • Ran cargo xtask release-readiness end-to-end locally against this branch's real commit history and a fake E2E report, inspected table.md output
  • Dispatched nightly.yml on this branch (run 38396499964) and confirmed release-readiness-nightly and release-readiness-narrative complete successfully; downloaded and read the narrative.md artifact to confirm it's genuine grounded prose citing real PR/issue numbers, not a refusal or empty output
  • A follow-up dispatch to confirm the CI-scope exclusion and the new fail-safe/guard behavior end-to-end in the real pipeline (local verification already confirms mechanical correctness deterministically)

@juhovainio
juhovainio requested a review from a team as a code owner October 13, 2026 13:53
@juhovainio
juhovainio requested a review from siloteemu October 13, 2026 13:53
rominf
rominf previously requested changes Oct 13, 2026

@rominf rominf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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_failures returns .unwrap_or(0) when there is no | **Total** row. That is what consolidated_summary_markdown (crates/e2e-report/src/consolidated.rs:966-970) writes when no lane uploaded a report. It is also what an empty or partial support-matrix.md yields when the e2e-report-nightly step fails, because the artifact upload there runs if: always(). A probe run returned 0 failures for an empty file, for the real "no report.json files" output, and for an EMPTY-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 all continue-on-error. The verdict never receives needs.e2e-report-nightly.result or any per-lane result. The test total_e2e_failures_defaults_to_zero_without_a_totals_row pins 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
    When nightly sets skip=true, the nightly job succeeds, windows-nightly is skipped, and windows_result != "success" adds the failing gate "windows build (skipped)". The verdict is then NO-GO, and publish-release-readiness-wiki publishes it because release-readiness-nightly succeeded.
    Confidence 85 · logic · Fix: treat skipped as "no build this night" (or skip the whole release-readiness chain when needs.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:11 auto-applies labels: bug, so any GitHub user controls entries in known_issues.
    • The raw titles go into table.md as unescaped markdown. A probe rendered injected image and link markdown verbatim. That file is written to $GITHUB_STEP_SUMMARY and to the public Release-Readiness wiki 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 -p string, 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-tools is unset.

    Confidence 85 · architectural · Fix: escape titles or render issues as #NNN links 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→find in extract_pr_number
    • the excerpt boundary <=→<

    No test reaches run, render_table or collect_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_reference misses rfind→find.
    • (2) excerpt_… misses the boundary mutation.
    • (2) total_e2e_failures_reads_the_totals_row catches 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 greps table.md for | Area | Evidence | Status |, but render_table writes that header unconditionally (release_readiness.rs:335). The publish job also only runs when release-readiness-nightly succeeded, so the file always exists. The comment "same degenerate-output guard" as publish-e2e-wiki is false: that guard catches an empty consolidation and is covered by wiki_publish_guard_stops_on_the_generators_empty_output in xtask/src/workflow_contract.rs. The new job has no contract test, unlike publish-e2e-wiki's nightly_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-windows from native to devlab-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.md still says both nightly Strix lanes "still pin the native label" 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.yml says "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 pins native.

    Confidence 85 · architectural · Fix: move this to its own PR, with the doc, comment and contract-test updates.

  • Three commits use a placeholder identity and fail the required signature gate — commits 49070b1, e654b4c, 7235dd9
    These three are authored, committed and signed off as test <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 bogus Signed-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 at cargo nextest run --locked -p rocm-core --lib --features e2e-test-hooks with "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 new secrets.COPILOT_PAT (a Copilot-licensed PAT, per its own comment) and declares only contents: read. Nothing in the diff has models: 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_PAT dependency;
    • 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/copilot runs with COPILOT_PAT in 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 :1805 keeps its credentials, which this job does not need.
    Confidence 85 · architectural · Fix: pin @github/copilot@x.y.z, and set persist-credentials: false on that checkout.

  • actions/ai-inference pin comment is # v3, not # vX.Y.Z — .github/workflows/nightly.yml:1836
    The SHA matches upstream's v3 tag, which is the release name. No v3.x.y tag exists, so §6's comment form cannot be met literally. Note that v3 is 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 spaced feat (cli):;
    • Revert "…" subjects, so the reverted feat still lists as new.

    extract_pr_number also takes the last (# anywhere in the subject, not only a trailing one, despite its doc. Probe: fix: handle (#12) refs renders … (#12) twice. The table never reports how many commits went unclassified, and --limit 100 caps 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.

  • The two wiki publish jobs can race — .github/workflows/nightly.yml:1880-1882
    publish-e2e-wiki and publish-release-readiness-wiki push 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, or pull --rebase and 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 restating failing_gates verbatim 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.py is already the release gate under that name: AGENTS.md §8, docs/release-trust.md, and nightly.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-report already 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 -rc and -preview tags, so "since last release" can mean "since the last RC". It resolves to v0.2.0 today.

Positive signals

  • The verdict, counts and PR numbers are computed outside the LLM step, and the publish job tolerates a missing narrative.
  • The GITHUB_OUTPUT heredoc uses a random delimiter for content holding untrusted data, and the LLM response reaches the shell through env:, 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-Readiness wiki 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.5 slug 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.

@juhovainio

Copy link
Copy Markdown
Collaborator Author

Thanks for the thorough review — addressed everything below. Branch was rebased onto current main and force-pushed; the 3 misauthored commits and the squash-noise pairs no longer exist in history.

Blocking, addressed in bab831ad

  • Fail-open GO verdict — total_e2e_failures now returns Option<u64>; a missing/unparseable **Total** row is None and is surfaced as the failing gate "E2E (report unreadable)", not silently read as zero failures.
  • False NO-GO on skipped nights — "skipped" now passes the gate the same as "success" for both nightly_result and windows_result.
  • Unescaped issue titles reaching the wiki — added escape_markdown(), applied to every known-issue title before it reaches render_table.
  • Untested verdict/gate/table logic — extracted a pure assemble_facts(...) (no git/gh I/O) out of run, with direct unit tests for all-success, unreadable-E2E, skipped-windows, and failed-windows cases. Added a render_table test asserting the header-omission and escaped-title behavior.
  • Dead wiki publish guard — render_table now omits the | Area | Evidence | Status | header when E2E evidence is unreadable, so publish-release-readiness-wiki's existing grep guard is a real signal. Added release_readiness_wiki_publish_guard_stops_on_unreadable_e2e_evidence in xtask/src/workflow_contract.rs, which runs the actual guard snippet from nightly.yml against both degenerate and well-formed render_table output.
  • Bundled Strix Windows runner-pool move — split into its own PR: fix(nightly): run Strix Halo Windows E2E on the devlab-dispatch pool #682 (includes the docs/ci-hardware-testing.md sync).
  • 3 commits with a placeholder identity — re-authored as Juho Vainio <juho.vainio@amd.com> with proper DCO sign-off during the rebase.
  • Stale merge-base / failing required check — rebased onto current main.
  • Inaccurate secrets claim in the description — corrected: the narrative job uses the COPILOT_PAT secret, not permissions: models: read.
  • Description omitted scope — rewritten to reflect the final diff and list the fixes made in response to this review.

Non-blocking, addressed in 3eb77057 / 367acd35

  • Pinned npm install -g @github/copilot to 1.0.97.
  • Added persist-credentials: false to the release-readiness-nightly / release-readiness-narrative checkout steps.
  • always() → !cancelled() on both jobs.
  • Prompt self-contradiction (section 3 vs. the hard-rule forbidding pass/fail judgments) — reworded to ask for evidence, not a verdict.
  • Noisy commit history (834ae24/e654b4c, 300abb0/cc75a79) — dropped the net-zero pair, squashed the model-slug fix into one commit.

Not addressed in this PR (left as follow-ups)

  • Commit-classification completeness (ticket-prefixed subjects, Revert "…" handling, case-insensitive type matching, reporting an unclassified count) — real gaps, but a separate change from this review's blocking findings; happy to file a follow-up if useful.
  • The two wiki-publish jobs' race potential — pre-existing pattern shared with publish-e2e-wiki, out of scope here.
  • actions/ai-inference's # v3 pin comment not matching the vX.Y.Z convention — no patched tag exists upstream to pin to.

Naming overlap with scripts/release_readiness.py

Filed ROCMAI-568 for this feature and cross-referenced the existing ROCMAI-443 (the unrelated Python signing gate) in its description, so the naming overlap is documented rather than silently ambiguous. Elected to keep the cargo xtask release-readiness name rather than rename the shipped command.

@juhovainio
juhovainio requested a review from rominf October 14, 2026 14:25
@juhovainio
juhovainio dismissed rominf’s stale review October 14, 2026 14:25

feedback addressed

rominf
rominf previously requested changes Oct 15, 2026

@rominf rominf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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_facts now passes skipped, 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 dependency detect-libc ^2.1.2 still floats. It is loaded inside the step that holds COPILOT_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-nightly needs:)
    • total_e2e_failures reads only the Fail cell 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 uploading report.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 Status being 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-wiki guard and its result == 'success' condition), xtask/src/workflow_contract.rs (release_readiness_wiki_publish_guard_stops_on_unreadable_e2e_evidence)
    • When E2E evidence is unreadable, render_table leaves 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-nightly succeeded. Examples: the artifact download fails, git describe fails, or gh fails.
    • An empty report is a realistic case: e2e-report-nightly redirects into support-matrix.md before 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_table always emits.
  • 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_readiness tests still passed:
      • the Some(n) if n > 0 E2E check made to never fire
      • the nightly_result check removed
      • the Build cell forced to ✅
      • the E2E cell forced to ✅
    • 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.
  • 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-inference pin is commented # v3, not # vX.Y.Z — .github/workflows/nightly.yml (Draft narrative step)
    • AGENTS.md §6 requires a full SHA "with a trailing # vX.Y.Z comment, never a moving tag".
    • Checked via gh api: 2c43c91… is the lightweight major tag v3. No v3.x.y exists 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.
  • 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 ("added escape_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_markdown does not escape \. A title written as \[click\](https://evil.example) comes out with doubled backslashes, and under CommonMark that renders as a live link in table.md. The reviewer ran escape_markdown on 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.md is still cat'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 bug label, since a licence comment comes before it. If it does not, a triager applying bug is the gate. That narrows who can do this but does not remove it.
    • Fix: escape \ first, or render issues as #NNN links only. Either keep the narrative as a run artifact or step summary, or feed the model only maintainer-authored inputs, never issue bodies.

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 bug issues come from outside contributors (the bug template asks for labels: 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.md is cat'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.
  • 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 through escape_markdown. Confidence 70 · logic · Fix: escape e.subject too.
  • new — "👍 N" counts every reaction type — xtask/src/release_readiness.rs:321, :446. It sums users.totalCount over all reactionGroups, including 👎 and confused. Confidence 85 · logic · Fix: filter to THUMBS_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. Unscoped chore!:, docs!:, ci!: and test!: 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. For fix: x(#12), pr = 12 is extracted, but only " (#12)" is stripped, so it renders as x(#12) (#12). Confidence 70 · mechanical · Fix: strip the exact span that extract_pr_number matched.
  • 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 has v0.2.0-rc.1, v0.1.0-preview.1 and 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 (the publish-e2e-wiki paragraph). The doc covers the E2E wiki page and its one-time setup. Nothing under docs/ mentions the Release-Readiness page, the COPILOT_PAT prerequisite, or cargo 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 in docs/release-trust.md and docs/testing.md, and referenced in nightly.yml's env comments) is the signing/trust release gate.
    • cargo xtask release-readiness is 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, nightly succeeds without building and windows-nightly is skipped.
    • The row "Build (Linux + Windows nightly) ✅" reads as "built and passed". "Not built (no new commits)" would be more accurate.
    • Accepting skipped for nightly_result does nothing, because that job has no if:.

Positive signals

  • The verdict, counts and PR numbers are computed without the LLM, and the narrative job is continue-on-error with an optional artifact download. A broken LLM step cannot change the table.
  • The new contract test runs the real bash guard from nightly.yml against real render_table output instead of a pinned string. Removing exit 0 from the guard made it fail.
  • The LLM response reaches the shell only through env: and printf '%s\n', never inline ${{ }}. GITHUB_OUTPUT uses a random heredoc delimiter.
  • Each job has narrow permissions (contents: read / issues: read), and both checkouts use persist-credentials: false. Every other new action pin was verified via gh api to match its # vX.Y.Z comment.
  • A failing git log or gh issue list aborts the run instead of producing an empty list.

Deployment notes

  • Requires a COPILOT_PAT repo 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 describe fails 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.yml and .github/prompts/release-readiness-narrative.txt. Also read the jobs these depend on in nightly.yml, the consolidated-report generator in crates/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.5 slug.
  • Not reconciled against the PR's discussion or earlier reviews. Standing objections were settled separately, as noted above.

@juhovainio

Copy link
Copy Markdown
Collaborator Author

Addressed the blocking findings from @rominf's review (5522757577):

  1. E2E lane results not gating the verdict — added a repeatable --e2e-lane-result NAME=RESULT flag threaded from nightly.yml (one per GPU/WSL lane job's own needs.<job>.result) through to assemble_facts. A lane that dies before uploading its report.json (preflight failure, timeout, runner loss) now fails the verdict instead of silently reading as zero E2E failures.
  2. Table header hidden on unreadable E2E evidence — render_table no longer suppresses the header in that case; unreadable evidence is itself a NO-GO signal and should be visible, not hidden. Updated workflow_contract.rs's wiki-publish-guard test to match (only a truly missing/empty table.md stops the publish now).
  3. Markdown injection via commit subjects — commit subjects are now escaped before being interpolated into the rendered table.
  4. actions/ai-inference@...# v3 looking like a moving major-tag pin — verified via the GitHub API that this SHA is exactly what the v3 tag (a genuine, non-prerelease release) points to, and that main has since moved past it. actions/ai-inference tags its releases as bare majors rather than vX.Y.Z; left the SHA pin as-is and added a comment explaining why it's a frozen release, not an AGENTS.md §6 violation.
  5. Only THUMBS_UP reactions should count toward issue ranking — fixed, plus bumped the gh issue list limit from 100 to 1000 so older issues aren't silently dropped from consideration.

Also fixed the two non-blocking findings: escape_markdown now doubles a literal backslash, and categorize() no longer treats a trailing ! as breaking for commit kinds other than feat/fix, nor double-appends the PR suffix.

On the two "decisions for the author" items, left as-is deliberately:

  • The naming overlap with scripts/release_readiness.py's own git describe calls is a pre-existing convention in this codebase, not something introduced here — didn't want to rename either in this PR's scope.
  • Skipping a dedicated nightly-Build row distinct from the Linux/Windows build gates is intentional: the verdict already treats either build failing as NO-GO via nightly_result/windows_result, so a separate row would be redundant with the existing gates.

cargo fmt, cargo clippy --workspace --all-targets -- -D warnings, and cargo test --workspace are all clean (one tar-launch test is flaky under full-parallel runs, confirmed passing in isolation — unrelated to this change).

Commits: 68d7cee, f389e2f, 7cfce6a.

@juhovainio
juhovainio dismissed rominf’s stale review October 15, 2026 15:27

Addressed all blocking and non-blocking findings in 68d7cee, f389e2f, 7cfce6a (see PR comment for details). Re-requesting review.

@juhovainio
juhovainio requested a review from rominf October 15, 2026 15:28
Mirrors the existing E2E support-matrix automation: a new `cargo xtask
release-readiness` subcommand computes a GO/NO-GO verdict, status table,
and categorized commit/known-issue facts purely mechanically from nightly
build results, the E2E report, and `git`/`gh` data. A separate narrative
job hands those facts to the GitHub Copilot CLI (via actions/ai-inference)
to draft prose summaries and curate known issues by end-user relevance,
and the verdict/table/counts are never left to that step. Publishes to
the Release-Readiness wiki page nightly, same guard pattern as the
existing E2E wiki job.

Signed-off-by: Juho Vainio <juho.vainio@amd.com>
…susing an ai-inference output as an input

Two bugs surfaced by the first live run with COPILOT_PAT: the job had no
actions/checkout step, so system-prompt-file (read from the repo) always
failed with "file not found"; and response-file was passed as an input,
but actions/ai-inference only exposes it as an *output* (the path it
already wrote the response to) — the action logged an "unexpected input"
warning and never produced release-readiness/narrative.md.

Add the checkout step, and write steps.draft.outputs.response to
narrative.md ourselves instead of guessing a response-file path.

Signed-off-by: Juho Vainio <juho.vainio@amd.com>
claude-sonnet-4.5 is disabled under this org's Copilot model policy
("Model ... is not available"). Tried gpt-4.1 (the action's own default,
enabled for every Copilot license) first, but Copilot CLI's --model slugs
use dots, not the Anthropic API's hyphenated IDs, and claude-sonnet-4.5 was
retired when Claude Sonnet 5.5 went GA — claude-sonnet-5.5 is the current
slug and is enabled for this org.

Signed-off-by: Juho Vainio <juho.vainio@amd.com>
ai-inference's {{var}}/file_input templating only applies to a
structured .prompt.yml loaded via prompt-file. This job uses an
inline prompt string, so file_input was silently ignored and the
model literally received the text "{{facts}}" -- it correctly
refused to draft a narrative rather than invent facts, so the job
"succeeded" with an empty/refusal narrative.md every run.

Read facts.json into a step output and interpolate it directly.

Signed-off-by: Juho Vainio <juho.vainio@amd.com>
facts.json has no trailing newline, so the closing delimiter line got
appended to its last line instead of starting its own line, which the
$GITHUB_OUTPUT file-command parser requires. Add a bare `echo` between
the cat and the delimiter to guarantee one regardless of whether the
source file ends with a newline.

Signed-off-by: Juho Vainio <juho.vainio@amd.com>
categorize() bucketed commits purely by Conventional Commit type
(feat/fix), ignoring scope entirely. This let CI/workflow-internal
commits like fix(nightly) and fix(ci) leak into the nightly release-
readiness summary's Bug Fixes section, which should only describe
end-user-facing functionality and fixes.

Add an INTERNAL_SCOPES denylist and drop commits scoped entirely to
those (ci, nightly, github, hooks, xtask, build, deps, e2e variants).
A multi-scope commit is kept if any of its scopes falls outside the
denylist, so a fix touching both CI and real CLI behavior is still
surfaced.

Signed-off-by: Juho Vainio <juho.vainio@amd.com>
release_readiness.rs previously had several correctness bugs:

- total_e2e_failures() defaulted to 0 failures when the E2E report's
  **Total** row was missing or unparseable, so a broken/truncated report
  silently produced a GO verdict instead of failing the gate. It now
  returns Option<u64>, and a None reads as a failing gate
  ("E2E (report unreadable)").
- A legitimately skipped nightly/windows job (a no-op night) was treated
  as a gate failure. "skipped" now passes the gate the same as "success".
- Untrusted GitHub issue titles were interpolated into the generated wiki
  page unescaped. escape_markdown() now neutralizes markdown/table-
  breaking characters before known-issue titles reach render_table.
- extract_pr_number() matched "(#NNN)" anywhere in a commit subject,
  including mid-sentence parentheticals; it now only matches a trailing
  "(#NNN)".

Also extracted assemble_facts() as a pure function (no git/gh I/O) out of
run(), so the GO/NO-GO logic is directly unit-testable.

In workflow_contract.rs, render_table() now drops the
"| Area | Evidence | Status |" header when E2E evidence is unreadable,
which makes nightly.yml's existing publish guard
(grep -q '^| Area | Evidence | Status |') an actual signal instead of
structurally dead code. Added a test that runs that guard snippet against
both degenerate and well-formed table output.

Signed-off-by: Juho Vainio <juho.vainio@amd.com>
Hard Rules already forbade "any pass/fail/status judgment" from the
narrative step, but section 3's instruction told it to highlight "failing
gates" anyway. Reworded it to ask for the evidence behind each gate
instead of a judgment on whether it clears the bar.

Signed-off-by: Juho Vainio <juho.vainio@amd.com>
…eds)

- Pin "npm install -g @github/copilot" to 1.0.97 instead of floating
  latest.
- Add persist-credentials: false to the release-readiness-nightly and
  release-readiness-narrative checkout steps; neither job pushes.
- Change if: always() to if: !cancelled() on both jobs, matching
  publish-e2e-wiki's existing reasoning: still run on a failure, not on a
  cancelled run.

Signed-off-by: Juho Vainio <juho.vainio@amd.com>
- Thread per-lane E2E results (--e2e-lane-result NAME=RESULT, repeatable)
  through to the verdict so a lane that dies before uploading its
  report.json (preflight failure, timeout, runner loss) still fails the
  release-readiness verdict instead of silently reading as zero failures.
- render_table no longer omits the table header when E2E evidence is
  unreadable; unreadable evidence is itself a NO-GO signal and should be
  shown, not hidden.
- Escape markdown in commit subjects before they're interpolated into the
  rendered table, preventing a crafted subject line from breaking table
  formatting.
- Only count THUMBS_UP reactions (not all reaction kinds) when ranking
  known issues, and bump the gh issue list limit from 100 to 1000 so
  older issues aren't silently excluded.
- Replace extract_pr_number with split_trailing_pr_reference for
  production code; the former is kept test-only so its existing unit
  tests keep compiling unchanged.
- Fix categorize() to only treat a trailing '!' as breaking for feat/fix
  commit kinds, and to stop double-appending the PR suffix.

Signed-off-by: Juho Vainio <juho.vainio@amd.com>
…wiki path

- release-readiness-nightly now needs: the 6 GPU/WSL E2E lane jobs (not
  just the consolidated e2e-report-nightly), and passes each lane's own
  needs.<job>.result to cargo xtask release-readiness via a repeated
  --e2e-lane-result flag, so a lane that dies before producing a report
  still fails the verdict.
- since_tag resolution now excludes pre-release tags (v*-*) so the
  commit-bucketing window starts at the last real release, not a
  prerelease/RC tag.
- Strip markdown links/images out of the LLM-drafted narrative before
  publishing it to the public wiki, since the narrative text is
  untrusted-derived and could otherwise smuggle a redirect.
- Document why actions/ai-inference is pinned to a bare major tag (v3):
  that project releases under bare-major tags rather than vX.Y.Z, and
  this SHA is exactly what the genuine, non-prerelease v3 release
  points to, predating the repo's current main — a frozen release, not
  a moving alias.

Signed-off-by: Juho Vainio <juho.vainio@amd.com>
Explain what release-readiness-narrative and publish-release-readiness-wiki
do, the cargo xtask release-readiness gating inputs (nightly Linux/Windows
build results, E2E failure total, each GPU/WSL lane's own result, commit
bucketing since the last release tag), the COPILOT_PAT prerequisite, and
that the Release-Readiness wiki page shares publish-e2e-wiki's one-time
setup/skip guards.

Signed-off-by: Juho Vainio <juho.vainio@amd.com>
@juhovainio
juhovainio force-pushed the feat/nightly-release-readiness branch from 7cfce6a to 1cb356d Compare October 16, 2026 14:36
rominf
rominf previously requested changes Oct 22, 2026

@rominf rominf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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-result gate 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-nightly still 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_markdown now 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.
  • # v3 pin comment (not gating): still holds. It was re-found as non-blocking below.
  • Copilot CLI's floating detect-libc ^2.1.2 dependency in the COPILOT_PAT step (not gating): not re-checked this round.

CI at this head (from the driver, compared against main's own runs):

  • E2E tests fails 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-result help), docs/ci-hardware-testing.md:199-203

    • Why it never fires: every lane job sets continue-on-error: true, so needs.<lane>.result reads success even 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" in NEEDS even though three of them failed.
    • 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-result help 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-w7900 is in e2e-report-nightly's needs and LANE_REPORTS, but not in the new job's needs or 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 in needs but 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-result plumbing or correct what it claims. Add a contract test that ties any lane list to LANE_REPORTS.
  • standing (narrative half) — The link-stripping sed does 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.md sets labels: 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_reactions never 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 → KnownIssue mapping and sort into a pure function, and test that function.
  • 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 — ✅ GO vs ❌ NO-GO is never asserted; swapping them passes. (release_readiness.rs:406-413)
    • PR-number rendering. Dropping the (#N) suffix from rendered entries passes. (:452-459)
    • Treating skipped as passing for the nightly job and for lanes. Only the Windows twin is tested. (:123, :141)
    • The n > 0 boundary. Changing it to n > 1 passes. (:131)
    • escape_markdown's newline collapsing and its </> escaping. Removing either passes. (:394, :396)
    • The ends_with(')') guard in split_trailing_pr_reference. Removing it passes, and it is not equivalent: the len()-1 slice can then panic on a subject ending in a multibyte character. (:269)
    • Confidence 100 · mechanical · Fix: add an assertion for each.
  • 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_table always 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-result gate 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-wiki also skips when e2e-report-nightly fails; this job does not.
    • Confidence 90 · Fix: update the description to match the code. Narrow it; don't add work to fit it.
  • 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-serving regressed (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, and E2E 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-nightly fails, nothing is published, so the wiki keeps the previous verdict — .github/workflows/nightly.yml (publish-release-readiness-wiki's needs.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 describe fails;
      • gh issue list fails (:332-337), which runs before the E2E report is read, so a flaky issue listing alone suppresses the page;
      • the xtask build fails.
    • 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", but release-readiness-nightly and release-readiness-narrative have no ref guard. They run on any branch dispatch and use the COPILOT_PAT secret there.
    • It says "same … guards as publish-e2e-wiki", which is not true (see the description finding).
    • Confidence 85.
  • 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.
  • new — The status table hides which gate failed — xtask/src/release_readiness.rs:421-441
    • failing_gates is never rendered in table.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-inference pin comment is # v3, not the # vX.Y.Z form AGENTS.md §6 requires — .github/workflows/nightly.yml (Draft narrative step)
    • The SHA does match the lightweight v3 tag, and no v3.x.y tag 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.
  • new — extract_pr_number is a #[cfg(test)] wrapper that only forwards to split_trailing_pr_reference — xtask/src/release_readiness.rs:281-284. The tests could call the real function directly. Confidence 80.
  • new — The --since-tag help 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-wiki removes 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, against render_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_PAT repo 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.97 install and the claude-sonnet-5.5 model slug are pinned in run: and with:, so Dependabot will not bump them.
  • A non-prerelease v* tag must be reachable from main, or the job fails before publishing. v0.2.0 satisfies 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) and cargo clippy -p xtask --all-targets -D warnings pass, 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.

- publish-release-readiness-wiki now runs on release-readiness-nightly
  failure too, publishing an explicit EVIDENCE UNAVAILABLE notice
  instead of silently leaving a stale verdict on the wiki with no sign
  it's stale.
- render_table lists the actual failing_gates reasons beneath the
  status table, since a cancelled E2E lane otherwise renders as a
  contradictory "0 failure(s)" next to a red status glyph.
- Fix the actions/ai-inference pin comment: that action tags full
  semver releases, not bare majors only.
- Correct docs/ci-hardware-testing.md's release-readiness section:
  release-readiness-nightly/-narrative have no main/repo guard, and
  publish-release-readiness-wiki's guards are no longer identical to
  publish-e2e-wiki's.
- Update the PR description to match current behavior and record
  deliberately-unchanged judgment calls from review.

Signed-off-by: Juho Vainio <juho.vainio@amd.com>
…iling_pr_reference

Round-3 review flagged that removing the ends_with(')') guard still
passed every existing test, because none of them exercised a subject
ending in a multibyte character after a mid-sentence (#N) reference.

Signed-off-by: Juho Vainio <juho.vainio@amd.com>
@juhovainio

Copy link
Copy Markdown
Collaborator Author

Addressed the latest review (id 5600395338) at 2ada5764. This review had no inline comments, so replying here at the top level.

Blocking

  • Per-lane E2E gate never fires; W7900 missing — fixed. release-readiness-nightly now gates on needs.e2e-report-nightly.result (--e2e-report-job-result) instead of per-lane needs.<lane>.result, since every lane is continue-on-error: true and its own result can never read anything but success. e2e-report-nightly already fails when any lane in LANE_REPORTS (which includes W7900) ran but didn't upload a report, so gating on that job's result covers every lane, including future ones, for free. Dropped the --e2e-lane-result plumbing entirely rather than leaving dead flags around. Pinned by a new contract test (release_readiness_nightly_gates_on_the_e2e_report_job_not_individual_lanes) that asserts needs contains e2e-report-nightly and none of the 7 individual lane jobs.
  • Link-stripping sed doesn't stop what its comment claims, and is attacker-controllable — fixed by switching from stripping to escaping: sed -e 's/\[/\\[/g' -e 's/\]/\\]/g' -e 's/</\&lt;/g' -e 's/>/\&gt;/g'. Ran this against the exact three bypass examples from the review (<img src=...>, reference-style [a][1], nested [outer [inner](url)](url)) — all render as inert escaped text now. Pinned by publish_release_readiness_wiki_escapes_markdown_link_and_html_syntax, which extracts the real sed line from the workflow and runs it via bash -c against those same three inputs. One accepted residual gap, called out in the PR description: GitHub's bare-URL autolinking isn't something sed-level escaping can prevent.
  • collect_known_issues_counts_only_thumbs_up_reactions never calls production logic — fixed. Extracted the RawIssue → KnownIssue mapping and sort into a pure build_known_issues function; the test now calls it directly instead of reimplementing the filter/sort inline.
  • Untested branches, including a real panic risk — fixed. Added assertions for: the GO/NO-GO headline, PR-number-suffix rendering, skipped-as-passing tested separately for the nightly build, the Windows build, and the E2E report job, the n > 0 vs n > 1 boundary, and escape_markdown's newline-collapsing/angle-bracket-escaping. Also added the one you specifically flagged as a real panic risk: split_trailing_pr_reference_does_not_panic_on_multibyte_text_trailing_a_mid_sentence_reference, confirming the ends_with(')') guard is load-bearing against a len()-1 mid-character slice panic.
  • PR description misstates the change — rewritten. The header-omission claim was inverted (it's now described correctly: the header always renders, and the guard only trips on a table missing the header entirely). The per-lane gate is no longer mentioned since it no longer exists. The "mirrors publish-e2e-wiki's guards" line now explicitly says what's shared (branch/repo gate, concurrency group, degenerate-output guard) versus what's deliberately different (this job still runs and publishes an "evidence unavailable" notice when release-readiness-nightly fails outright, instead of skipping like publish-e2e-wiki does).
  • Required CI checks failing at 1cb356d — unrelated to this diff; same dashboard-quit/download-infra flakes reproduce on main's own runs. Left unchanged, called out explicitly in the PR description.
  • release-readiness-nightly failing outright publishes nothing, wiki keeps a stale (possibly GO) verdict — fixed. publish-release-readiness-wiki's guard loosened from == 'success' to != 'skipped', so it still runs when release-readiness-nightly fails. The artifact-download step is continue-on-error: true (the failed job may not have uploaded anything), and the push script now branches three ways: table missing → publish an explicit "EVIDENCE UNAVAILABLE" notice with run/commit links; table present but missing its header → warn and leave the wiki page unchanged; well-formed table → publish normally. Pinned by release_readiness_wiki_publish_guard_handles_missing_degenerate_and_well_formed_tables, which runs the real extracted guard against all three constructed inputs.

Non-blocking

  • Docs paragraph inaccurate beyond the lane sentence — fixed, docs/ci-hardware-testing.md no longer claims main-only scope or guard-parity with publish-e2e-wiki.
  • Missing (not just unparseable) E2E report publishes nothing — left unchanged, judgment call: a missing report file still fails the job via read_to_string(...)?, but the "evidence unavailable" fallback above already covers this at the publish step, so the wiki itself never goes stale or shows a wrong verdict silently — only the job's own logs say why the page didn't update.
  • Status table hides which gate failed — fixed. render_table now lists the failing_gates reason strings beneath the table (e.g. E2E (lane report check: cancelled)), so a cancelled lane no longer renders as a contradictory "0 failure(s)" next to a red ❌.
  • # v3 pin comment — fixed, corrected to note the action tags full semver releases alongside the floating major, not bare majors only.
  • extract_pr_number test-only wrapper — left as-is; purely a test-ergonomics nit, not gating behavior.
  • --since-tag help text out of step with --exclude 'v*-*' — fixed, help text now mentions both flags.

All fixes verified via cargo test -p xtask (349 tests), cargo clippy -p xtask --all-targets --all-features -- -D warnings, and cargo fmt -p xtask --check, all passing at 2ada5764.

@juhovainio
juhovainio dismissed rominf’s stale review October 22, 2026 10:23

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.

@juhovainio
juhovainio requested a review from rominf October 22, 2026 10:23
rominf
rominf previously requested changes Oct 22, 2026

@rominf rominf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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-result is gone. The job now gates on e2e-report-nightly, whose LANE_REPORTS includes W7900. The blind review raised a separate new gap (only the release leg of each lane is checked, fourth item under Blocking).
    • Commit messages: still holds. It is restated as the last item under Blocking.
  • Link-stripping sed lets links through; narrative built from outsiders' issue text: still holds. It was re-found blind in a new form: the escaping sed that 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-nightly publishes 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-test fails on this PR's own new test, and passes on main. This gates (first item under Blocking).
  • Test (affected crates) fails in preflight_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 tests fails with the same dash-gen-tps-expiry-boundary / dash-esc-menu-quit-row-confirms-when-serving regressions as main's own run. Does not gate.
  • The actions/ai-inference pin 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 sibling release_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), bash resolves 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" and 340 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.
  • 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 (the sed … release-readiness/narrative.md line in publish-release-readiness-wiki)

    • The actual command is sed -e 's/\[/\\[/g' -e 's/\]/\\]/g' -e 's/</\&lt;/g' -e 's/>/\&gt;/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.
  • 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 Fail cell of the **Total** row and never reads any platform's Status.
    • When a report has no platform.json, crates/e2e-report uses its legacy counts. For a report.json of [] or not 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 gave Some(0) and Verdict::Go.
    • Also, total_e2e_failures_reads_the_totals_row uses a hand-copied table, although the doc comment says parsing the rendered row "keeps this in lockstep" with the renderer. A column reorder in consolidated.rs:1083 would 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: have e2e-report emit a machine-readable summary and gate on that, and test it against real renderer output.
  • new — The lane-report gate covers only the release leg 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-result help), docs/ci-hardware-testing.md:199-204, nightly.yml:1588-1596

    • Every E2E lane runs channel: [release, nightly]. LANE_REPORTS lists 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, and e2e-report-nightly still 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.
  • 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-inference pin has a # v3 comment, not the required # vX.Y.Z, and the comment explaining it is wrong for v3 — nightly.yml (the Draft narrative step)

    • AGENTS.md §6: "pin … to a full commit SHA with a trailing # vX.Y.Z comment, never a moving tag".
    • gh api repos/actions/ai-inference/git/matching-refs/tags/v lists v2.1.1 and earlier, plus only the floating v3 → 2c43c91a…. There is no v3.x.y tag.
    • 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.
  • 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_contract green (70 passed):
      • removing if: ${{ !cancelled() }} from release-readiness-nightly. A failed e2e-report-nightly then 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_lanes still 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/main guard
      • feeding --windows-result from needs.nightly.result
      • inlining the narrative as ${{ }} in run: (expression injection)
      • replacing the random GITHUB_OUTPUT delimiter with a fixed EOF
      • dropping persist-credentials: false
    • In release_readiness.rs, these survived cargo test -p xtask release_readiness (36 passed):
      • taking the scope from head instead of head_no_bang (:237-239), so fix(ci)!: leaks into Breaking Changes
      • dropping .trim() in is_internal_only_scope (:215), so fix(core, ci) is misclassified
      • removing *, _ and the backtick, or '\r', from escape_markdown (:395-397)
    • 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).
  • 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:
      • fc76d6a and 40f5d62 describe the per-lane --e2e-lane-result gate, including "a lane that dies before producing a report still fails the verdict". 1cb356d says the gate uses "each GPU/WSL lane's own result". e20b631 removed that gate, but its message does not say so, and no message says the gate moved to e2e-report-nightly.
      • 40f5d62 says links and images are stripped from the narrative. The code now escapes brackets and angle brackets instead, and no message says so.
      • fc76d6a says extract_pr_number "is kept test-only". The function no longer exists.
      • On the actions/ai-inference tags, 40f5d62 says "bare-major tags" and e20b631 says "full semver releases". Neither holds for the pinned v3 line.
    • I checked the e20b631 message 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.

Non-blocking

  • new — The LLM narrative can render headings and tables, so it can print a fake verdict — nightly.yml (the ## Narrative section)
    • Only [ ] < > are escaped. A prompt-injected narrative could render ## Release Readiness — ✅ GO or 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.
  • standing (was a tradeoff last round) — The two wiki publish jobs can race on the same wiki repo — publish-e2e-wiki and publish-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.
  • new — A broken consolidated-report upload shows "EVIDENCE UNAVAILABLE" rather than NO-GO — the release-readiness-nightly "Download consolidated nightly report" step has no continue-on-error. If e2e-report-nightly dies before writing consolidated/, 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-readiness is already in use — non-blocking-improvement. scripts/release_readiness.py is the release trust gate (AGENTS.md §8, docs/release-trust.md, the nightly.yml env comments). The new cargo xtask release-readiness / release_readiness.rs is 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. skipped is accepted for nightly, windows-nightly and e2e-report-nightly. That is safe today, because skip=true only 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_facts is 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/gh non-zero exits are bail!ed rather than swallowed.
  • The narrative reaches the shell through env: NARRATIVE rather than ${{ }} in run:. The facts are inlined with a random 16-hex GITHUB_OUTPUT delimiter.
  • At this SHA, ai-inference passes 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_table output 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_PAT repository secret. Without it, the narrative job fails (it is allowed to) and the page publishes without prose.
  • Publishes a new Release-Readiness wiki page on main, and needs the wiki already initialised (it is a prerequisite shared with publish-e2e-wiki).

What this covered

  • Read: every changed file in full, 32834b29…2ada5764 (prw-base...HEAD). That includes xtask/src/release_readiness.rs, the xtask/src/main.rs command, the three new nightly.yml jobs, the new workflow_contract.rs tests, the prompt file and the docs paragraph. Also read: the e2e-report-nightly, publish-e2e-wiki, lane and trigger sections of nightly.yml, and the renderer in crates/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-test fails on this PR's own test (Blocking item 1).
    • Test (affected crates) fails in preflight_runs_the_real_reclaim_assert_clear (coreutils: unknown program 'llama-server'). The branch does not include 5dfa9a8 (#712, which addresses multicall coreutils and is on main).
    • E2E tests fails on two dash-* scenarios and on missing resolved model lines.
  • Not done: ROCMAI-568 was not retrievable here. Not reconciled against this PR's review discussion.

- Check both the release and nightly artifact legs of a channel-matrixed
  lane independently in the report-check shell script, instead of one leg
  standing in for the whole lane; add a test covering a single missing leg.
- Add mutation-coverage tests for nightly.yml's release-readiness gating:
  the release-readiness-nightly !cancelled() guard, --windows-result
  sourced from the Windows job (not the Linux one), the exact
  publish-release-readiness-wiki guard string, persist-credentials: false
  on both checkout steps, the random GITHUB_OUTPUT delimiter, and the
  narrative reaching the shell only via env rather than an inlined
  expression.
- Add mutation-coverage tests for release_readiness.rs's categorize (scope
  read from head_no_bang, not head; whitespace trimmed around scope names)
  and escape_markdown (emphasis/code markers and bare carriage returns).
- Re-pin actions/ai-inference to its real v2.1.1 semver release (v3 is a
  floating major alias with no semver tag yet) and set provider: copilot
  explicitly, since v2.1.1's default provider differs from v3's.
- Fix a clippy unnested-or-patterns lint in total_e2e_failures.

Signed-off-by: Juho Vainio <juho.vainio@amd.com>
@juhovainio

Copy link
Copy Markdown
Collaborator Author

Addressed in c333065 (round 3 of feedback):

Blocking — fixed:

  • LANE_REPORTS in e2e-selfhosted.yml's consolidated-report-check step: e2e-gpu-strix-ubuntu now checks both its release and nightly artifact legs (comma-separated), so a run that uploads only one of the two no longer silently passes the "all expected artifacts present" gate.
  • Added 10 new contract tests closing the "standing (untested branches)" mutation-coverage gap:
    • xtask/src/release_readiness.rs: categorize_drops_a_breaking_commit_scoped_entirely_to_internal_tooling, categorize_trims_whitespace_around_scope_names, escape_markdown_escapes_emphasis_and_code_markers, escape_markdown_collapses_a_bare_carriage_return.
    • xtask/src/workflow_contract.rs: assertions on --windows-result sourcing from needs.windows-nightly.result (not the Linux build's own result), plus new tests release_readiness_nightly_runs_even_if_an_upstream_job_failed, publish_release_readiness_wiki_guard_is_exact, release_readiness_jobs_checkout_without_persisting_credentials, release_readiness_narrative_delimiter_is_random_not_fixed, release_readiness_narrative_reaches_the_shell_only_through_env.

Non-blocking — fixed this round (not just acknowledged):

  • actions/ai-inference was pinned to 2c43c91 tagged # v3, but v3 is a floating major alias with no semver release yet (confirmed via the GitHub API — only v1.x/v2.x have real tags). Re-pinned to v2.1.1's real commit SHA (a780588) with an exact # v2.1.1 comment per AGENTS.md's full-semver-pin policy. v2.1.1's default provider differs from v3's (github-models vs copilot), so added an explicit provider: copilot to keep behavior unchanged.
  • Fixed a clippy::unnested_or_patterns lint in total_e2e_failures surfaced by --all-targets, unrelated to functional behavior.

Judgment call — deliberately left as-is:

  • The commit-message/content mismatch you flagged (confidence 100) can't be fixed without rewriting already-pushed commit history, which I'm avoiding mid-review. It's best corrected in the squash-merge commit message or PR description at merge time rather than as a history rewrite now.

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 — scripts/release_readiness.py name collision, skipped-build-counts-as-pass, dropped deps scope) are left unchanged as previously discussed/acknowledged tradeoffs.

Build/test/lint: cargo fmt --check, cargo clippy --workspace --all-targets -- -D warnings, and cargo test --workspace all pass.

rominf
rominf previously requested changes Oct 23, 2026

@rominf rominf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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)], and windows-build-and-test passes 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 with gh 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 EMPTY platform row now counts as a failure. The other half still holds: the parser is tested only against hand-written tables, never against e2e-report's real output. That is folded into the first item under Blocking, whose fix already asks for exactly that test.
  • Only the release leg of each lane is checked: resolved. All 7 channel-matrixed lanes now list both legs in LANE_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."
  • # v3 pin: resolved. The driver checked that it is now actions/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 in preflight_runs_the_real_reclaim_assert_clear. This PR's code does not cause it: the branch predates 5dfa9a8 (#712), which fixes it, and main passes 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 tests fails on the same dash-* quit scenarios as main'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 at xtask/src/release_readiness.rs:658-682

    • The "silently empty platform" pass checks every | line in the document, not only the summary table.
    • consolidated_summary_markdown writes 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.md from 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 | Platform header and the | **Total** row. Add a test that runs e2e-report's real consolidated_summary_markdown (with sidecars, so the grid renders) and parses its output.
  • 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 46 release_readiness tests still pass, so cancelled or timed_out on nightly/windows-nightly would yield GO.
    • A worker's mutation run also found that making git log / gh issue list failures return empty lists survives. run, collect_commit_subjects and collect_known_issues have no tests.
    • Confidence 100 · logic · Fix: add cancelled and timed_out cases for both build gates, and failure-path tests for the two subprocess collectors (behind a seam if needed).
  • 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-error from both publish-job downloads and also hard-coded --nightly-result "success". All 75 workflow_contract tests still pass.
    • Without continue-on-error, download-artifact fails whenever there is no narrative, which is every night until COPILOT_PAT exists. That fails the publish job and makes the "evidence unavailable" path unreachable.
    • The worker also found that these single mutations survive:
      • dropping release-readiness-narrative from the publish job's needs (the publish job then races the narrative);
      • dropping fetch-depth: 0 (git describe then fails every night);
      • renaming the page so it overwrites E2E-Support-Matrix.md.
    • release_readiness_narrative_reaches_the_shell_only_through_env only checks that the safe lines are present (job.contains(...)). A second step that inlines ${{ steps.draft.outputs.response }} into run: 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.response appears nowhere in the job except the NARRATIVE: env line (and the if:).
  • 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 nightly leg's artifact as well as the release leg's for every channel-matrixed lane. In e2e-selfhosted.yml that 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.
  • 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-result flag (git grep finds none at HEAD);
      • release-readiness-nightly needing "the 6 GPU/WSL E2E lane jobs" (it needs e2e-report-nightly);
      • extract_pr_number "kept test-only" (it is gone);
      • "Strip markdown links/images" (replaced by escaping);
      • a bare-v3 pin as "a frozen release, not a moving alias" (re-pinned to v2.1.1, and the later message calls v3 floating);
      • in ef489f5, that render_table drops the header (reversed in fc76d6a).
    • 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.
  • 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-base 2d2dfc7 predates that commit, 12 commits behind prw-base. Main's latest CI passes this job.
    • Confidence 100 · mechanical · Fix: rebase onto current main.

Non-blocking

  • new — Stale comment. — xtask/src/workflow_contract.rs:1089

    • It still says "a channel axis below is checked through its release leg", which this PR changes to checking both legs.
    • Confidence 90 · mechanical · Fix: update the sentence.
  • 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).
  • new — Anyone who files a bug issue can steer the published narrative. — .github/prompts/release-readiness-narrative.txt; xtask/src/release_readiness.rs build_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.
  • new — Breaking changes on types other than feat/fix are dropped from every bucket, and a test asserts that. — xtask/src/release_readiness.rs:234-236; test categorize_does_not_treat_a_bang_on_other_types_as_breaking

    • Conventional Commits allows ! on any type, so refactor(cli)!: vanishes from the page. No such commits exist today.
    • Confidence 70 · logic · Fix: put any type!: in Breaking.
  • standing (was a tradeoff last round) — The Build row shows ✅ when windows-nightly was 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_OUTPUT behind a random delimiter, and the LLM response reaches the shell only through env:. 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_facts and build_known_issues are pure functions, which keeps the verdict logic testable without git/gh.

Deployment notes

  • The narrative job needs a Copilot-licensed PAT in the COPILOT_PAT repo secret. Until it exists the narrative is skipped every night, and the publish job depends on its download steps' continue-on-error to still publish.
  • The new Release-Readiness wiki page needs the same one-time wiki initialisation as E2E-Support-Matrix.
  • The per-PR "E2E consolidated report (self-hosted)" check now fails when the Strix Ubuntu nightly leg uploads no report, as well as the release leg.

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/behavioral for 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.

- categorize(): treat `!` on any Conventional Commits type as breaking,
  not just feat!/fix!.
- render_table(): render "not run" instead of a false (check mark)
  when the Windows nightly build was skipped.
- Propagate the new Facts::build_skipped field to the two direct
  Facts literals in workflow_contract.rs tests.

Signed-off-by: Juho Vainio <juho.vainio@amd.com>
@juhovainio
juhovainio dismissed rominf’s stale review October 23, 2026 11:03

Addressed in 78ae588 (see summary comment).

@juhovainio
juhovainio requested a review from rominf October 23, 2026 11:03
rominf
rominf previously requested changes Oct 23, 2026

@rominf rominf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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 hosted E2E tests are fixed on main (#712, #713/#719), and this branch predates those fixes, so a rebase clears them. E2E tests (MI300X, current) also fails on main'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_failures now scans only the rows between | Platform and | **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 real consolidated_summary_markdown, then failed with Some(2) instead of Some(0). One gap remains in that test: it writes no platform.json sidecar, 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, by collect_commit_subjects_fails_on_a_bogus_since_tag and collect_known_issues_in_fails_outside_a_github_repo. Still open: run() and the main.rs argument 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_contract tests:

    • dropping continue-on-error on either publish download;
    • hard-coding --nightly-result;
    • dropping release-readiness-narrative from needs;
    • dropping fetch-depth: 0;
    • renaming the page to E2E-Support-Matrix.md;
    • adding a step that inlines steps.draft.outputs.response into run:.

    Observation, not tested: most of these are contains-style checks, so a matching line inside a YAML comment could satisfy some of them.

  • 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 main or 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 only workflow_contract::tests::preflight_runs_the_real_reclaim_assert_clear (coreutils: unknown program 'llama-server'). That is the multicall-coreutils decoy problem fixed on main by 5dfa9a8 (#712).
    • Hosted E2E tests fails on dash quit timeouts and short-name serve steps. #713 and #719 on main address the quit timeouts, and main's latest E2E tests (run 39950566642) is green.
    • MI300X fails on a vLLM "auto" tool choice 400.
    • 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 main and merges cleanly (git merge-tree). On the merged tree, cargo test -p xtask --bins ran 375 tests and all passed, including the reclaim test and all 58 release-readiness tests. cargo clippy -p xtask --all-targets -- -D warnings is clean.
    • Confidence 85 (the red checks come from the rollup; the attribution comes from the logs and main's runs) · mechanical · Fix: rebase onto main and re-run until green.
  • new (does not gate; see banner) — The newline written before the GITHUB_OUTPUT delimiter is load-bearing, and no test exercises it — .github/workflows/nightly.yml:1799-1801, xtask/src/release_readiness.rs (run → write_parented)
    • facts.json is written with serde_json::to_string_pretty and has no trailing newline. Without the bare echo, 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 echo and re-ran cargo test -p xtask --bins release_readiness. All 58 tests still passed, including release_readiness_narrative_delimiter_is_random_not_fixed.
    • Confidence 100 · mechanical · Fix: extend the contract test to run the extracted step against a facts.json with no trailing newline and parse the output file. Alternatively, end the file with \n in run and 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-result flag (git grep at HEAD finds none);
      • that release-readiness-nightly needs "the 6 GPU/WSL E2E lane jobs" (it needs e2e-report-nightly);
      • that extract_pr_number is "kept test-only" (it no longer exists);
      • "Strip markdown links/images" (replaced by escaping);
      • that the bare-v3 pin is "a frozen release, not a moving alias" (it was re-pinned to v2.1.1);
      • that render_table drops the header (reversed in fc76d6a8).
    • 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 main as the permanent record.
    • 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.

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.md branch)
    • The table download is continue-on-error: true, so a transient download failure after release-readiness-nightly succeeded 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.result through env: and derive the wording from it.
  • new — The test for collect_known_issues_in passes without the branch it targets — xtask/src/release_readiness.rs (test collect_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 contains gh issue list. The code still fails closed either way.
    • Confidence 100 (worker run, not repeated by me) · mechanical · Fix: assert on "gh issue list failed".
  • new — Nothing tests the main.rs → release_readiness::run wiring, and its five result strings are untyped — xtask/src/main.rs:396-412
    • Swapping windows_result and e2e_report_job_result would compile and keep every test green.
    • A typo such as succes would read as a failed gate rather than as an error.
    • Confidence 70 · architectural · Fix: use a clap ValueEnum JobResult (or an inputs struct) and test assemble_facts through it.
  • new — skipped counts as passing for e2e-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 success for this gate.
  • new — The "" status arm in total_e2e_failures is 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 returning None — xtask/src/release_readiness.rs:424
    • When total_idx is less than header_idx, lines[header_idx + 1..total_idx] panics. That contradicts the function's contract that a broken report returns None. The outcome still fails closed.
    • Confidence 70 · mechanical · Fix: search for Total only after header_idx.
  • 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_gates doc omits the E2E (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.Z comment. The pin itself is correct. I checked a7805884… against v2.1.1; provider and system-prompt-file exist there, and the default provider is github-models.
    • "Unlike the wiki-publish jobs below" is wrong: publish-e2e-wiki is described above that sentence.
    • Confidence 85 · mechanical.
  • 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 in excerpt, and the nightly_result == "skipped" half of build_skipped all survive single-branch mutation.
    • Confidence 85 · mechanical.
  • 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!: and chore!: land under "Breaking Changes".
    • Confidence 70 · logic · Fix: if that matches the intent, also drop internal types on the ! path.
  • new — The sdk_version axis is now handled differently from the channel axis 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-2 legs, which are still represented by current. That is pre-existing, but the two are now stated side by side.
    • Confidence 70 · architectural · Follow-up.
  • 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_markdown runs only in render_table, and facts.json carries 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.

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's LANE_REPORTS change, a nightly leg that dies (for example, on a TheRock nightly download) now fails e2e-report-nightly and so yields NO-GO.
    • It also fails the per-PR E2E consolidated report (self-hosted) check when the Strix Ubuntu nightly report 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 name collides with the existing release gate — non-blocking-improvement
    • scripts/release_readiness.py is already "release readiness" (dist-asset and signature verification, cited in AGENTS.md §8 and docs/testing.md).
    • A different name, such as nightly-verdict, would keep "run release readiness" unambiguous.
  • COPILOT_PAT versus 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: write when 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.
  • 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_PAT is set.
  • A missing support-matrix.md aborts 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-nightly job via read_to_string(...)? … the wiki-publish job's 'evidence unavailable' fallback … already covers this case."
    • Otherwise this would have been non-blocking, because the render_table comment implies that unreadable evidence still publishes a table.

Positive signals

  • The gate reads e2e-report-nightly's own result, not the lane results, which continue-on-error always reports as success. Lanes added to LANE_REPORTS later are covered automatically.
  • The verdict is computed only in the pure assemble_facts. The LLM output reaches the shell only through env:, 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_markdown rather 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_PAT repo secret. Without it the job fails, continue-on-error hides the failure, and the page publishes without a narrative.
  • This creates a new Release-Readiness wiki 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 Ubuntu nightly leg's report. The nightly e2e-report-nightly job 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.rs and the prompt;
    • group 2: both workflows, workflow_contract.rs and the docs.

    Also read: the e2e-report producer (consolidated.rs), the source of the pinned ai-inference action, and the history of LANE_REPORTS.

  • Ran:

    • 58 release-readiness tests plus 78 workflow_contract tests 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.
  • 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 coverage against 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.

- facts.json now always ends with a trailing newline, so the
  $GITHUB_OUTPUT heredoc in nightly.yml's "Read facts for prompt"
  step can't land its closing delimiter on the same line as the
  JSON's closing brace even if that step's own echo guard is ever
  dropped.
- e2e-report-nightly's result no longer accepts "skipped" as a
  passing gate; that job is if: always() and has no legitimate
  no-op-night skip.
- total_e2e_failures no longer panics when a **Total** row precedes
  the | Platform header; it now searches for Total only after the
  header index.
- Dropped the unreachable "" status arm in total_e2e_failures (the
  producer only ever emits PASS, FAIL or EMPTY).
- collect_known_issues_in_fails_outside_a_github_repo now asserts the
  specific `gh issue list` failed message instead of a substring both
  the bail! and JSON-parse-error paths happen to share.
- Fixed stale comments: the failing_gates doc omitted the lane report
  check gate, nightly.yml's ai-inference pin comment named the wrong
  AGENTS.md policy, and ci-hardware-testing.md pointed "below" at a
  section that's actually above.
- Added tests for three previously-untested branches: the
  known-issue reaction tie-break, excerpt's exact-character-limit
  boundary, and build_skipped's skipped half for both the nightly and
  windows build gates.

Left for a reply comment rather than guessed at: the commit-message
history-rewrite objection (conflicts with this pass not force-pushing)
and whether internal-type breaking commits (ci!:, chore!:) should be
dropped from the Breaking bucket (reviewer explicitly flagged this as
intent-dependent).

Signed-off-by: Juho Vainio <juho.vainio@amd.com>
@juhovainio

Copy link
Copy Markdown
Collaborator Author

Addressed in 78ae5883 (round 4 of feedback, review 5625115103 at c333065a). Detail in the PR description's "Changes since the third round of review" section:

Blocking — fixed:

  • E2E failure count inflated ~90x — total_e2e_failures now scans only the rows between the | Platform header and the | **Total** row, instead of every |-line in the document (it was also counting rows from the expectation-grid and command-coverage tables e2e-report emits after the summary). Added a regression test built against the real e2e_report::consolidated_summary_markdown output, not a hand-written snippet.
  • Cancelled/timed-out builds read as GO — both build gates (nightly, windows-nightly) now treat cancelled/timed_out as failures. Added failure-path tests for collect_commit_subjects and collect_known_issues (behind a current_dir seam), closing the untested-subprocess-collector gap.
  • Untested workflow-contract wiring — pinned every mutation the review's worker found survived: fetch-depth: 0 on the nightly checkout, the publish job's needs on both upstream jobs, both continue-on-error: true artifact downloads, the distinct wiki page names, and the dynamic --nightly-result read. The narrative env-only test now fails if any other line in the job references steps.draft.outputs.response.
  • Description omitted the LANE_REPORTS change — now has its own paragraph (last bullet of the first "Changes since..." section).

Non-blocking — fixed this round:

  • categorize now puts any type!: (not just feat!/fix!) in the Breaking bucket, matching Conventional Commits' actual grammar.
  • Build row renders "not run" instead of ✅ when windows-nightly was skipped.
  • Fixed the stale comment and misleading assertion message in workflow_contract.rs.

Left unchanged (judgment call, not an oversight):

  • Commit messages describing removed behavior (--e2e-lane-result, "6 GPU/WSL E2E lane jobs", link-stripping, etc.) are left as-is; fixing them means rewriting already-pushed history, which this pass doesn't force-push to do.
  • Test (affected crates) failing on the reclaim test is the pre-existing multicall-coreutils decoy issue fixed on main by fix(ci): keep the GPU reclaim decoys alive on multicall coreutils #712; a rebase (not a code change here) settles it, and this was already rated non-gating.

Note on 78ae5883's own commit message: it undersells what the commit actually contains (it names only 3 small items). The content above reflects what the diff actually contains, cross-checked against this review, which evaluated this exact commit.

@juhovainio

Copy link
Copy Markdown
Collaborator Author

Addressed in 0c5f1fed (round 5 of feedback, review 5628333214 at 78ae5883). Detail in the PR description's "Changes since the fourth round of review" section:

Blocking (non-gating per the review's own banner):

Blocking (this is what gates) — left unchanged, judgment call not an oversight:

  • Commit-message history-rewrite objection — still holds. The 4 commits (40f5d624, fc76d6a8, ef489f5d, 1cb356de) still describe removed behavior. Fixing this means squashing/rewording already-pushed branch history and force-pushing, which this pass doesn't do. Given this repo's COMMIT_MESSAGES squash setting and the merge queue's lack of an edit step, this needs to be an actual history rewrite before the PR is queued, not something deferred to "squash time" as earlier rounds assumed — flagging that explicitly rather than silently leaving it.
  • Internal-type breaking commits (ci!:, chore!:, build!:) reaching the Breaking list — left as the reviewer explicitly flagged this as intent-dependent ("if that matches the intent, also drop internal types on the ! path"). No such commits exist in this branch today, so it doesn't affect the current page's output either way; a design call on the final policy, not guessed at here.

Non-blocking — fixed:

  • e2e-report-nightly's result no longer accepts skipped as a passing gate (that job is if: always() with no legitimate no-op-night skip).
  • total_e2e_failures no longer panics on a **Total** row preceding the | Platform header.
  • Dropped the unreachable "" status arm (the producer only ever emits PASS, FAIL, or EMPTY).
  • collect_known_issues_in_fails_outside_a_github_repo now asserts the specific gh issue list failure message instead of a substring both failure paths happen to share.
  • Fixed 3 stale comments (failing_gates doc, ai-inference pin comment, ci-hardware-testing.md's "below" reference).
  • Added tests for the 3 previously-untested branches this review's worker found survive mutation: the known-issue reaction tie-break, excerpt's exact-character-limit boundary, and build_skipped's skipped half for both build gates.
  • The description's self-contradiction on escaping scope (claiming issue-title escaping covers the LLM prompt, when it only covers the rendered wiki table) is corrected in the current description text.

Non-blocking — left unchanged (deliberate deferrals, not oversights):

  • EVIDENCE UNAVAILABLE false positive on a transient table-download failure — real; narrowing it needs threading needs.release-readiness-nightly.result through env:, left for a follow-up.
  • main.rs → run wiring untested, result strings untyped — real; a ValueEnum JobResult would be the fix, left for a follow-up as a larger refactor than this pass's scope.
  • sdk_version/channel axis asymmetry — pre-existing, just now stated side by side; left as the review's own fix label says ("Follow-up").

All of the above is also now in the PR description under "Changes since the fourth round of review."

@juhovainio
juhovainio dismissed rominf’s stale review October 23, 2026 15:34

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.

@juhovainio
juhovainio requested a review from rominf October 23, 2026 15:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants