Fix /review tests trigger and output - #35820
Conversation
Require exact /review tests command matching, suppress gh-aw status comments, and submit the final test-failure report as a collapsed PR review. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35820Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35820" |
🔍 Skill Validation Results✅ Static Checks PassedSkills checked: 19 | Agents checked: 4 Full validator output⏭️ LLM Evaluation: SkippedNo changed skills with eval tests found. |
PureWeen
left a comment
There was a problem hiding this comment.
Adversarial Multi-Model PR Review
Methodology: 3 independent reviewers analyzed this PR in parallel with adversarial consensus. The full gh-aw workflow, the compiled lock.yml, the local PowerShell helper, and the skill instruction file were reviewed against the gh-aw safe-outputs reference.
Verdict: Mergeable with one ⚠️ worth addressing
The core change (tightening the trigger to exact /review tests and switching from add-comment to submit-pull-request-review with allowed-events: [COMMENT]) is coherent, security-preserving, and correctly compiled. Permission reductions are correct least-privilege. The new exact-match regex ^[[:space:]]*/review[[:space:]]+tests[[:space:]]*$ properly rejects /review rerun, /review tests --foo, and trailing-text variants. The double-write to $GITHUB_OUTPUT (should_run=false then conditional should_run=true) is fine — Actions takes the last value.
Findings
-
⚠️ Config Impact / Performance —pre_activation.if:gate dropped (2/3 reviewers)
Addingjobs.pre-activation:with custom outputs caused the compiler to drop the previous author-association gate in the lock.yml. Downstream security is intact (activation.ifstill requiresactivated == 'true'), but every PR/issue comment now spins up apre_activationrunner — and becausecancel-in-progress: falseshares thereview-tests-<pr#>concurrency group, those comment-triggered runners queue serially behind any active/review testsrun. See inline comment with the suggested fix. -
💡 Regression / UX — Reviews accumulate on repeated
/review tests(2/3 reviewers)
The oldadd-commentconfig usedhide-older-comments: trueand the local script edited a single comment in place.submit-pull-request-reviewhas no equivalent for COMMENT-event reviews (supersede-older-reviews:only dismisses REQUEST_CHANGES). Each new/review testsinvocation now appends a fresh entry to the PR's Reviews tab. By-design tradeoff for the new non-blocking model — worth a doc note so users know iterative re-runs will stack. -
💡 Error Handling —
gh api ... --jq .html_url 2>&1URL parsing (2/3 reviewers)
2>&1merges anyghwarnings (auth refresh, deprecation notices) into$postOutput.Select-Object -Last 1could then return a warning line instead of the URL. The review is already posted by that point, so this is cosmetic (wrong URL in theWrite-Hostline). See inline comment.
Disputed findings (discarded after validation)
- One reviewer flagged
pull_request_numberas missing from the safe-output validation schema and predictedworkflow_dispatchruns would fail. Refuted: the upstream gh-aw safe-outputs-pull-requests reference confirms that withtarget: "*"the agent must supplypull_request_numberin eachsubmit_pull_request_reviewcall — exactly what the .md and SKILL.md already instruct. The validation JSON in the lock.yml only enumerates fields needing extra constraints (bodyfor sanitization,eventfor enum);pull_request_numberis implicit. The skill instruction is correct as written.
Test coverage
This is a workflow/script change; the validation listed by the author (gh aw compile --validate, PowerShell parse check, git diff --check) is appropriate. No additional test scaffolding required.
Restore early comment-author skipping, document stacked PR review reruns, and avoid mixing gh stderr into the posted review URL output. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use add-comment with hidden older comments for /review tests output while preserving exact command matching and disabled gh-aw status comments. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PureWeen
left a comment
There was a problem hiding this comment.
Adversarial Multi-Model PR Review — Round 2
Methodology: 3 independent reviewers re-analyzed the PR after round-1 changes were applied, with adversarial consensus and multi-round self-correction (findings against round-2-introduced changes warrant closer scrutiny).
Round-1 follow-up
| Round 1 finding | Status |
|---|---|
pre_activation |
✅ Fully resolved. Restored via gh-aw built-in skip-author-associations: — cleaner than the manual if: I suggested. Lock.yml lines 1431-1432 compile the correct allowlist. |
| 💡 Reviews accumulate in PR Reviews tab on re-runs | ✅ Resolved differently than expected — the PR reversed course and switched back to add-comment with hide-older-comments: true. Cleaner end-state than submit-pull-request-review for this use case. |
💡 gh api ... 2>&1 mixed stderr into URL |
✅ Fully resolved. New Invoke-GhApiWithJsonPayload helper writes stderr to a separate temp file and filters stdout to ^https?://. |
Round-2 findings
-
⚠️ Regression / Config Impact —on.steps: exit 1pattern fails thepre_activationjob for every non-matching collaborator comment, producing red workflow runs across the repo. This is a regression fromb6a264655d'sshould_run=true/falseoutput pattern. The gh-aw guide explicitly warns against this. 3/3 reviewers (mixed severity). -
⚠️ Logic / UX — Local PS1 script and the gh-aw bot share the same<!-- Test Failure Review -->marker but use incompatible dedup strategies (PATCH vs. POST+hide), leading to invisible edits or orphaned comments. 2/3 reviewers. -
💡 Defense-in-depth —
$verdictis interpolated unescaped into the comment HTML<summary>while$prTitleis correctly escaped — inconsistency, defense-in-depth gap. 3/3 reviewers.
Discarded / out-of-scope
- ❌
target: "*"allows the agent to post on any PR/issue — 1/3, pre-existing inorigin/main. Not introduced by this PR; raise separately if you want to harden it. - 💡 Strict regex rejects
/review tests <trailing text>— 1/3, intentional (the step is literally called "Confirm exact /review tests command"). - 💡 No failure feedback path (
reaction: none+status-comment: false) — 1/3, deliberate quiet-mode design choice. - 💡 Tempfile cleanup not in
try/finally— 1/3, minor cleanup gap. - One reviewer initially claimed HEAD still uses
submit-pull-request-review— discarded as a stale-context misread (grepconfirmedadd-commentin current HEAD).
Verdict
Mergeable. The round-2 regression (/review tests will produce a failed workflow run, drowning out genuine failures in the Actions UI.
| exit 0 | ||
| fi | ||
| if [ "$EVENT_NAME" != "issue_comment" ] || [ -z "${ISSUE_PULL_REQUEST_URL:-}" ]; then | ||
| exit 1 |
There was a problem hiding this comment.
exit 1 on the non-match path fails the pre_activation job for every OWNER/MEMBER/COLLABORATOR comment that isn't exactly /review tests — including comments on plain issues (no ISSUE_PULL_REQUEST_URL → line 24 exit 1) and any other slash command (line 29 exit 1).
The compiled lock.yml (pre_activation, lines 1429–1495) has no continue-on-error: true, so the failed step fails the job, which marks the whole run red in the Actions tab. The activation job is then skipped via the needs: failure path rather than the intended if: exact_command_result == 'success' gate — which works for skipping, but produces noisy failed runs on essentially every collaborator comment repo-wide.
Flagged by: 3/3 reviewers (one called this ❌, two called it
Why this matters specifically here: the gh-aw guide explicitly warns against exit 1 in on.steps: blocks: "Use step outputs rather than exit 1 — failing the job normalizes failures and masks real errors." The previous revision (b6a264655d) used the correct pattern (echo "should_run=…"; exit 0), so this is a round-2 regression.
Suggested fix — revert to the explicit-output variant:
steps:
- name: Confirm exact /review tests command
id: exact_command
env:
EVENT_NAME: ${{ github.event_name }}
COMMENT_BODY: ${{ github.event.comment.body }}
ISSUE_PULL_REQUEST_URL: ${{ github.event.issue.pull_request.url }}
run: |
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
echo "should_run=true" >> "$GITHUB_OUTPUT"; exit 0
fi
if [ "$EVENT_NAME" != "issue_comment" ] || [ -z "${ISSUE_PULL_REQUEST_URL:-}" ]; then
echo "should_run=false" >> "$GITHUB_OUTPUT"; exit 0
fi
if [[ "$COMMENT_BODY" =~ ^[[:space:]]*/review[[:space:]]+tests[[:space:]]*$ ]]; then
echo "should_run=true" >> "$GITHUB_OUTPUT"
else
echo "should_run=false" >> "$GITHUB_OUTPUT"
fiThen surface should_run as an output and gate on … == 'true'. All paths exit 0 → clean skipped runs.
| throw "Failed to update PR comment: $patchOutput" | ||
| } | ||
| return $existing.html_url | ||
| $existing = @($comments | Where-Object { $_.body -and $_.body.Contains($marker) }) | Select-Object -Last 1 |
There was a problem hiding this comment.
<!-- Test Failure Review -->, but they use different deduplication strategies that don't compose:
- The workflow uses
add-comment+hide-older-comments: true→ POSTs a new comment and hides the older one via GraphQLminimizeComment. - The local script (here) scans for
body.Contains("<!-- Test Failure Review -->"), picks the last match, and PATCHes it in place (lines 335 → 339-343).
Concrete scenario:
- Workflow runs → bot posts Comment A (marker, by
github-actions[bot]). - Workflow runs again → posts Comment B, hides Comment A (still has marker).
- A developer runs
Review-Tests.ps1 -PostComment. - The script's REST scan returns both A and B (hidden comments are still returned by the REST API).
Select-Object -Last 1picks B and PATCHes it cross-author. Or worse: in a race, it edits Comment A which is already hidden → developer's update is invisible on the PR. - Next workflow run won't recognize the developer-edited comment as its own lineage → posts a fresh comment and hides the edited one → orphaning the local update.
Flagged by: 2/3 reviewers
Suggested fix — give the local path a distinct marker so it never collides with bot-authored comments:
$marker = "<!-- Test Failure Review (local) -->"Apply the same marker change in New-TestFailureReviewBody (line 221). Alternatively, change the local path to always POST-new + minimize older so both paths use the same strategy.
|
|
||
| ## Test Failure Review | ||
| <details> | ||
| <summary>$verdictIcon <strong>Test Failure Review:</strong> $verdict — <a href="$commitUrl"><code>$commitSha7</code></a> · <strong>$prTitle</strong></summary> |
There was a problem hiding this comment.
💡 Defense-in-depth / Consistency — $verdict is interpolated unescaped into the HTML <summary> tag here and into the author-ping line above (line 283 in the heredoc). $prTitle is correctly run through Escape-Html on line 236, so this is an inconsistency.
$verdict comes from Get-ReportVerdict (line 154-160), which regex-extracts the text after **Overall verdict:** from agent-generated content. The agent is constrained to four taxonomy values, but the report content originates from untrusted PR diffs, test logs, and CI evidence — so injection isn't impossible, just unlikely in practice.
Flagged by: 3/3 reviewers
Suggested fix:
$verdict = Escape-Html (Get-ReportVerdict -Content $ReportContent)or at the interpolation site. The badge URL path already URL-encodes the value via New-Badge; only the <summary> and author-ping interpolations are raw.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Applied the review suggestions from #35820 (review) and merged the latest Summary:
|
…ix blast-radius rubric ground truth Two changes addressing kubaflo's review request that the skill's new safety-critical behaviors (prior-review reconciliation and the CI hard gate) have no eval coverage, plus a one-line factual correction to the blast-radius rubric. 1. New scenario: "Prior review reconciliation - skill surfaces prior findings before verdict" - Anchor: PR #35685 (gh-aw rerun review scanner, merged) - Has substantive review history across all three surfaces: 4 human reviewers (PureWeen, JanKrivanek, T-Gro, kubaflo), top-level reviews with ❌ findings, inline review comments with ❌ markers at specific files, AND MauiBot AI Summary in issue comments with 17 ❌ markers - Assertions verify the section heading exists, that at least one real reviewer is named (defeats "no prior findings" boilerplate), and that a verdict is delivered - Rubric verifies all three review surfaces are queried, prior ❌ findings are reconciled against current code state, and no finding is silently dropped 2. New scenario: "CI hard gate - skill refuses LGTM when required checks are skipping" - Anchor: PR #35820 (Fix /review tests trigger, merged) - Has license/cla=pass and maui-pr=skipping — `gh pr checks --required` exits 0 with "All checks were successful" plus "1 skipped". This is precisely the exit-0-with-skipping trap the skill's Step 5 calls out as NOT a clean pass. - Assertions verify the CI Status section exists, evidence of required-check inspection (named check or skipping/undetermined classification), and that the verdict is NEEDS_DISCUSSION rather than LGTM (per Rule #6: no LGTM on pending/skipping/undetermined CI). - Rubric verifies the agent ran gh pr checks --required, classified per the skill's exit-code semantics, and did not interpret the "All checks were successful" gh summary line as license to LGTM when a required check actually skipped. 3. Rubric fix: blast-radius scenario line 121 - The previous rubric stated "the PR makes the registration conditional, so the post-PR claim is false". This is factually incorrect about MAUI PR #35223: AddCallback registration remains unconditional (Activity.cs:3372); only the callback's Enabled state is conditional (lines 3405/3408). - The judge had been penalizing the more-precise nuanced answer ("registration unconditional, Enabled conditional") at 3.3/5 because it didn't match the rubric's binary "refute". - New rubric awards credit for the correct technical distinction and removes the false expectation that the hypothesis must be binary-refuted. The eval scenarios anchor on PRs not used elsewhere in eval.yaml, addressing PureWeen's anchor-reuse concern in a different direction. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
<!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ### Description of Change Refines the `/review tests` gh-aw workflow so it only activates for the exact `/review tests` command, not other `/review` subcommands such as `/review rerun`. Also removes the noisy gh-aw status/reaction comments and changes the final test-failure report to post as a non-blocking PR review with a single collapsed `<details>` block. ### Validation - `gh aw compile copilot-review-tests --validate` - PowerShell parse check for `.github/scripts/Review-Tests.ps1` - `git diff --check` --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Description of Change
Refines the
/review testsgh-aw workflow so it only activates for the exact/review testscommand, not other/reviewsubcommands such as/review rerun.Also removes the noisy gh-aw status/reaction comments and changes the final test-failure report to post as a non-blocking PR review with a single collapsed
<details>block.Validation
gh aw compile copilot-review-tests --validate.github/scripts/Review-Tests.ps1git diff --check