Skip to content

Enhance MauiBot AI summary review output + support net11-targeting PRs - #35677

Merged
PureWeen merged 55 commits into
mainfrom
feature/enhanced-reviewer
Jun 29, 2026
Merged

Enhance MauiBot AI summary review output + support net11-targeting PRs#35677
PureWeen merged 55 commits into
mainfrom
feature/enhanced-reviewer

Conversation

@kubaflo

@kubaflo kubaflo commented May 31, 2026

Copy link
Copy Markdown
Collaborator

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!

Summary

  • Posts MauiBot AI Summary output as a pull request review with parsed APPROVE, REQUEST_CHANGES, or safe COMMENT fallback.
  • Uses the new AI Review Summary layout with segmented status chips, collapsed review sessions, and merged Future Action content.
  • Keeps PR finalization out of the automated review process; AI Summary updates no longer preserve or merge SECTION:PR-FINALIZE blocks.
  • Adds visible AI Summary guidance telling users to comment /review rerun after new comments or commits when they want a fresh review. The command implementation is intentionally split into a follow-up PR.
  • Hides stale MauiBot AI Summary / try-fix artifacts with GitHub minimization instead of deleting them, while preserving same-run try-fix and AI Summary reviews.
  • Updates the Copilot pipeline to pass review IDs and patch review bodies after deep UI tests.
  • Hardens gate setup/retry handling by committing squashed PR changes before verification, resetting the review branch before gate retries, and detecting BlazorWebView unit-test project paths.

Validation

  • Parsed changed PowerShell scripts with System.Management.Automation.Language.Parser.
  • Parsed .github/workflows/review-trigger.yml as YAML.
  • Invoke-Pester .github/scripts/Post-AISummaryComment.Tests.ps1,.github/scripts/Remove-StaleMauiBotComments.Tests.ps1 -CI
  • Dry-run AI Summary generation verified the rerun note, segmented chips, and collapsed review session layout.
  • Verified Detect-TestsInDiff.ps1 maps src/BlazorWebView/tests/MauiBlazorWebView.UnitTests/UriExtensions_Tests.cs to src/BlazorWebView/tests/MauiBlazorWebView.UnitTests/MauiBlazorWebView.UnitTests.csproj.

net11.0-targeting PR support

This branch also makes the Copilot review pipeline handle net11.0-targeting PRs as well as main/net10, from a single pipeline branch (consolidates and supersedes #35994):

  • Runtime base-branch auto-detection (eng/pipelines/ci-copilot.yml): the CopilotReview and DeepUITests stages read the PR's baseRefName via gh, allowlist-validate it (^(main|net[0-9]+\.0)$), and check out that base before workload install and the squash-merge — so workloads (net11 11.0.100 vs net10 10.0.100) and the merge base follow the PR. Trusted scripts are captured from the pipeline ref first (security rule 3); Task 1 Setup runs from $TRUSTED.
  • Branch-aware test TargetFramework: BuildAndRunHostApp.ps1 and run-device-tests/Run-DeviceTests.ps1 now derive the TFM from Directory.Build.props (Get-MauiTfmVersion in shared-utils.ps1) instead of hardcoding net10.0, so deep-UI/device tests build net11.0-android on net11. The DeepUITests stage restores the reviewed pipeline-branch scripts over the worktree before the per-category loop.

main/net10 behavior is unchanged by design (base-detection checks out main = the original flow; global.json stays 10.0.108). review-trigger.yml and Review-PR.ps1 are intentionally untouched.

Validation: 8 live net11 runs with 100% correct base detection (e.g. #35891 gate ran net11 Core tests 44/45 → 45/45, conflict-free squash-merge onto net11.0); reproduced and root-caused a separate net11.0-baseline MAUIX2017 Xaml.UnitTests build break (unrelated to this change). Confirmation runs from this branch cover one net11 PR and one main PR.

@github-actions

github-actions Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35677

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35677"
@github-actions github-actions Bot added the area-infrastructure CI, Maestro / Coherency, upstream dependencies/versions label May 31, 2026
@github-actions

github-actions Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

Skill Validation Results

@kubaflo — new skill validation results are available based on this last commit: 34738c8.
To request a fresh validation after new comments or commits, comment /evaluate-skills.

Overall Failed Static Passed LLM Failed Skills 20 Agents 6

Skill Validation Results34738c8 · Enhance MauiBot AI summary review output + support net11-targeting PRs · 2026-06-29T17:03:43Z

✅ Static Checks Passed

Skills: 20 | Eval specs linted: 7

Full lint output
── .github/skills/agentic-labeler/tests/eval.vally.yaml
npm warn deprecated prebuild-install@7.1.3: No longer maintained. Please contact the author of the relevant native addon; alternatives are available.
✔ .github/skills/agentic-labeler/tests/eval.vally.yaml is valid
── .github/skills/code-review/tests/eval.capability.vally.yaml
✔ .github/skills/code-review/tests/eval.capability.vally.yaml is valid
── .github/skills/code-review/tests/eval.vally.yaml
✔ .github/skills/code-review/tests/eval.vally.yaml is valid
── .github/skills/code-review/tests/hermeticity.vally.yaml
✔ .github/skills/code-review/tests/hermeticity.vally.yaml is valid
── .github/skills/evaluate-pr-tests/tests/eval.vally.yaml
✔ .github/skills/evaluate-pr-tests/tests/eval.vally.yaml is valid
── .github/skills/try-fix/tests/eval.vally.yaml
✔ .github/skills/try-fix/tests/eval.vally.yaml is valid
── .github/skills/verify-tests-fail-without-fix/tests/eval.vally.yaml
✔ .github/skills/verify-tests-fail-without-fix/tests/eval.vally.yaml is valid

❌ LLM Evaluation Failed

🔍 Full results and investigation steps

@kubaflo kubaflo added the area-ai-agents Copilot CLI agents, agent skills, AI-assisted development label May 31, 2026
@kubaflo kubaflo changed the title Enhance MauiBot review posting May 31, 2026
@kubaflo kubaflo changed the title Enhance MauiBot review posting and rerun gating Jun 1, 2026
@kubaflo

kubaflo commented Jun 7, 2026

Copy link
Copy Markdown
Collaborator Author

/review -b feature/enhanced-reviewer -p android

@MauiBot MauiBot 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.

Expert Review — 1 findings

See inline comments for details.

Comment thread .github/scripts/post-ai-summary-comment.ps1 Outdated
@MauiBot MauiBot added s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Jun 7, 2026
MauiBot

This comment was marked as outdated.

T-Gro pushed a commit to T-Gro/maui that referenced this pull request Jun 8, 2026
Runs the Pester suite on every PR that touches .github/scripts/**,
.github/skills/**, eng/scripts/**, or eng/pipelines/ci-copilot.yml. Make
this a REQUIRED check via repo settings → branches → branch protection.

Defense-in-depth gates:

* Explicit required-test-files list (refuses to run if any required
  .Tests.ps1 has been deleted/renamed — prevents silent green from
  "nothing ran"). Includes the four tests added by dotnet#35677
  (Post-AISummaryComment, Review-PR, Aggregate-UITestArtifacts,
  Run-DeviceTests, Remove-StaleMauiBotComments) plus this PR's new
  Write-SafeSubprocessOutput and Review-PR.Phase-Audit suites.
* Minimum-passed-count floor (catches BeforeAll discovery errors
  that flip green tests into Skipped state — Pester reports
  "Passed: 0, Failed: 0" as success otherwise).
* Zero-skipped tolerance (catches leftover -Skip / Inconclusive /
  NotRun states).
* Pinned Pester 5.3+ with -SkipPublisherCheck for the runner image's
  pre-installed copy.

The workflow also runs Verify-AuditNegativeControls.ps1 — an
end-to-end harness that injects ~245 bypass patterns into a
real Gate-reachable script and asserts the audit catches each.
This catches regressions where the extractor works but scope
detection silently drops lines.

Without this workflow, the .Tests.ps1 files already in the repo
(plus the ones added by dotnet#35677) are unrun and provide no
verification value.
kubaflo pushed a commit that referenced this pull request Jun 8, 2026
… fail-closed Gate state, HMAC verdict, Phase-Audit (#35804)

> [!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!

## Stacked on #35677

This PR layers a security-hardening stack on top of @kubaflo's #35677
(`feature/enhanced-reviewer`). When merged here, the combined work flows
to `main` via #35677.

## Scope

Re-applies and updates the security audit work originally landed in
#35324 against the latest #35677 base, plus three new defenses caught by
adversarial review rounds.

| # | Change | Why |
|---|---|---|
| F1 | Cross-phase state hardening — dedicated `phase-state/` subdir;
CopilotReview re-derives `` instead of restoring a persisted path;
fail-closed on missing/garbage Gate verdict | Original review said
writable `Split-Path -Parent` of `\` allowed PR test code to forge
`gate-result.txt` and to redirect `& pwsh -File` execution under
`COPILOT_GITHUB_TOKEN` |
| F1.A | HMAC-sign `gate-result.txt` via per-job `openssl rand -hex 32`
key passed to CopilotReview/Post through an AzDO secret variable |
Adversarial reviewer demonstrated empirically that `chmod -R a-w` is
bypassable same-user (`mv state state.bak && mkdir state && echo PASSED
> ...`). Key generated AFTER pwsh exits → PR-spawned daemons cannot read
it |
| F2 | `Out-SafePRSubprocessLine` helper + inline `-replace
'(?i)##(vso\[\|\[)', '##~SANITIZED~\'` at every PR-controlled
`Write-Host` site + `& pwsh -NoProfile -File` subprocess wraps at every
shared runner site | Prevents PR test code from forging
`##vso[task.setvariable / task.complete / task.uploadfile]` AzDO logging
commands through stdout |
| F3 | Marker-comment hiding now requires `Test-IsMauiBotCommentAuthor`
| A maintainer who quotes `<!-- AI Summary -->` in their own comment was
being silently hidden |
| D1 / D1.A | Whitelist `^(NONE\|ALL\|[A-Za-z0-9_,]+)\$` before
re-emitting `detectedCategories`; `^[A-Za-z0-9_\-]+\$` for
`aiSummaryReviewNodeId` | Closes a future-proofing class where
PR-controlled text could inject a second `##vso` directive via
setvariable echo |
| M1 / M2 | Setup invokes `\/scripts/Review-PR.ps1` (not the worktree);
`exit \` on failure; Tasks 2-4 gated on `ne(variables['SetupFailed'],
'true')` so a half-set-up worktree can't keep running | Original review
said the worktree-vs-trusted asymmetry was inconsistent and the lack of
short-circuit let Gate run on stale state |
| Phase-Audit | AST-based static analyzer of the `` call-graph closure:
default-deny `gh`-write-verb classifier + YAML token-binding audit, run
as a Pester suite | Locks the contract that Gate's GH_TOKEN is
read-only; any future write-verb regression fails CI |
| Pester CI | `security-scripts-pester.yml` workflow runs all 9
`.Tests.ps1` files on every PR touching `.github/scripts`,
`.github/skills`, `eng/scripts`, or `eng/pipelines/ci-copilot.yml`.
Required-files allowlist + minPassed floor + zero-skipped tolerance →
silent test deletion / Pester skip cannot land green | Pre-existing PS
tests had no CI runner; this is the first Pester gate in the repo |

The original sanitizer / phase-state work passed an adversarial review
with three independent reviewers (opus-4.8 + opus-4.7-xhigh + gpt-5.5);
the rebase onto #35677's new Copilot-token-usage commits was
re-validated by the same set. `gpt-5.5` caught one pre-existing F2 gap
(UI/Device runner `\` re-emit at L1526/1539 was raw `Write-Host`) which
is fixed in this branch.

## Validation

- **Pester: 233/233 green** (50s runtime)
- **Phase-Audit Axis A: 21/21** — no `gh` write-verb regressions on real
Gate-reachable scripts
- **Phase-Audit Axis B: 3/3** — every YAML task's `GH_TOKEN` binding is
the documented PAT alias; `CopilotReview` and the new
`AnalyzeCopilotTokenUsage` stage have no GH_TOKEN
- **openssl ↔ .NET HMAC cross-check** verified locally (`-mac HMAC
-macopt hexkey:` round-trips with `Convert.FromHexString`)
- **YAML lint**: `yaml.safe_load` clean

## Note for #35677 owner

The PR title and description should be reviewed against the actual diff
before merge; this stack only adds defenses, no functional changes to
the review pipeline beyond the trust-boundary tightening.

cc @kubaflo

---------

Co-authored-by: Copilot <Copilot@users.noreply.github.com>
kubaflo pushed a commit that referenced this pull request Jun 8, 2026
… stdout, fail-closed Gate state, HMAC verdict, Phase-Audit (#35804)"

This reverts commit 9f78d76.
PureWeen added a commit that referenced this pull request Jun 8, 2026
<!-- 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!

## Summary

- Moves the actual `/review rerun` command implementation out of #35677
and into this follow-up PR.
- Adds deterministic `/review rerun` eligibility checks for new comments
or commits and applies `s/agent-ready-for-rerun` when another review is
justified.
- Adds deterministic rerun context for pre-flight so the next review can
focus on activity since the prior AI Summary / rerun checkpoint.
- Adds an hourly gh-aw workflow (`rerun-review-scanner.md` + compiled
`.lock.yml`) that scans queued PRs, asks AI for a `trigger` / `skip`
decision, and uses a custom safe-output job for reactions, queue-label
cleanup, and AzDO triggering.
- Adds helper scripts for querying queued PRs and processing scanner
decisions.

## Validation

- `gh aw compile .github/workflows/rerun-review-scanner.md`
- Parsed changed PowerShell scripts with
`System.Management.Automation.Language.Parser`.
- Parsed `.github/workflows/rerun-review-scanner.lock.yml` and
`.github/workflows/review-trigger.yml` as YAML.
- `Invoke-Pester
.github/scripts/Resolve-RerunEligibility.Tests.ps1,.github/scripts/Post-AISummaryComment.Tests.ps1,.github/scripts/Remove-StaleMauiBotComments.Tests.ps1
-CI`
- Read-only query helper run: `Query-RerunReadyPRs.ps1 -MaxPRs 1
-OutputPath /tmp/rerun-candidates.json`

## Depends on

- #35677 for the AI Summary layout and visible `/review rerun`
instruction text.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Tomas Grosup <tomasgrosup@microsoft.com>
Co-authored-by: Shane Neuville <shane94@hotmail.com>
kubaflo and others added 2 commits June 25, 2026 21:34
Per user feedback, the AI Summary 'Gate: Inconclusive' (8957e5) and 'Platform'
(8250df) badges, plus the review-tests 'Failures' badge (8250df), used purple —
which is too close to GitHub's merged-PR purple and reads as confusing. Recolor:
 - Gate Inconclusive -> teal (0e7490)
 - Platform          -> blue (1f6feb)
 - Failures          -> amber (bf8700)
No other semantics change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…cted categories'

When the deep UI test stage produces no results (most often because the PR build
failed or the stage was skipped), the UI Tests section was posted as just
'**Detected UI test categories:** X' with nothing else — confusing readers into
thinking tests silently didn't run (e.g. #33007, which has committed
merge-conflict markers in PublicAPI.Unshipped.txt that block the build).

Add Add-MissingUITestResultsNote: when the uitests content is only the detected-
categories placeholder (no DEEP_UITESTS block / 'Deep UI tests' / 'N passed'
results), append a [!WARNING] note explaining the run produced no results — likely
a failed build (see Gate) or a skipped stage — and to fix it and '/review rerun'.
No-op for already-populated results or the no-categories/full-matrix placeholders.

Tests: 5 new cases in Post-AISummaryComment.Tests.ps1; full .github/scripts suite 336/336.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jun 25, 2026
@MauiBot

MauiBot commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

⚠️ Merge Conflict Detected — This PR has merge conflicts with its target branch. Please rebase onto the target branch and resolve the conflicts.

@MauiBot MauiBot removed the s/agent-review-in-progress AI review is currently running for this PR label Jun 25, 2026
… N real failures)

When the Android HostApp crashes mid-run (e.g. during a heavy category like
Shell), the test that was running fails in UITestBaseTearDown with 'The app was
expected to be running still, investigate as possible crash', and every
SUBSEQUENT fixture's OneTimeSetUp then times out waiting for 'Go To Test button'
(the app is dead / 'keeps stopping'). The result was a whole category rendered
as e.g. 'Shell 0/312 (312 ❌)' — looking like 312 real test failures when it was
one crash cascading.

The setup-failure classifier required 100% of a category's failures to match the
OneTimeSetUp/Go-To-Test signature; the 1-2 crash-teardown failures didn't match,
so the category fell back to 'N regular failures'. Now:
 - The matcher also recognizes the app-crash teardown signature
   ('investigate as possible crash' / UITestBase.UITestBaseTearDown), so the
   whole cascade is recognized as a single setup/infra event.
 - A new SetupFailureIsAppCrash flag distinguishes a crash from a pure
   navigation/fixture setup flake, and the crash entry is preferred as the
   representative sample so the rendered message shows the crash.
 - The renderer surfaces crashes CAUTIOUSLY and honestly: '⚠️ the HostApp
   crashed mid-run … an app crash can be an infrastructure flake OR a regression
   introduced by this PR — review the screenshots + logcat', instead of either
   'N ❌' (misleading) or 'infrastructure, not a PR failure' (which could hide a
   real regression). Genuine passes in other categories (e.g. Navigation 88/88)
   are unaffected and still shown.

Validated against the real PR #36133 deep artifact: Navigation stays 88/88
(SetupFailure=False); Shell -> SetupFailure=True, IsAppCrash=True; ViewBaseTests
-> SetupFailure=True, IsAppCrash=False (secondary cascade). Pester: 10/10.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… category

When the Android HostApp crashes mid-run, the test that was running fails in
UITestBaseTearDown ('investigate as possible crash'), and then EVERY following
fixture's OneTimeSetUp times out on 'Go To Test button' — because a crash can
leave the Android 'App keeps stopping'/ANR system dialog up, which blocks the
relaunched activity. The existing Reset()/relaunch recovery doesn't dismiss that
system dialog or clear corrupt app state, so one crash cascades into the entire
category showing 0 passed (e.g. ScrollView 0/198, Shell 0/312).

This makes the gallery navigation self-heal so a crash costs ~one failure
instead of the whole category (matching normal UI-test behaviour):

- New Appium lifecycle command 'recoverFromCrash' (Android): dismiss the system
  crash/ANR dialog (BACK+HOME), 'am force-stop' the app, optionally 'pm clear'
  it on a hard recovery to break a corrupt-state startup crash loop, then
  relaunch (ActivateApp, with an adb monkey fallback if the driver is wedged).
  Non-Android falls back to force-close + relaunch.
- New shared helper App.WaitForGoToTestButtonWithRecovery(): waits for the
  gallery as before; on timeout (Android only) it runs a tiered recovery —
  soft, then hard (pm clear) — with a short 45s retry per tier, then surfaces
  the failure if the app still can't come back (deterministic crash / broken
  emulator).
- Both gallery entry points use it: _GalleryUITest -> NavigateToGallery and
  _IssuesUITest.NavigateToIssue.

The healthy path is unchanged (first 2-minute wait succeeds and returns); the
recovery only runs after a crash has already broken the run, so it can't make
passing tests fail. Builds clean (Controls.TestCases.Mac.Tests, 0 warnings).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 26, 2026 12:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR enhances the MauiBot/Copilot PR review pipeline output and robustness, and adds support for PRs targeting net11.0 (branch-aware base checkout + TFM selection) while improving deep UI test resilience and diagnostics.

Changes:

  • Add base-branch auto-detection/validation in ci-copilot.yml so workloads + merge base follow the PR’s actual target branch (e.g., net11.0).
  • Improve deep UI test reliability and reporting (timeouts around adb, crash/fixture-setup classification, richer artifacts, time-budget guardrails).
  • Add telemetry aggregation/publishing for Copilot token usage plus supporting tests and label/outcome parsing hardening.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs Adds an Appium command to attempt crash recovery (Android-focused) via adb + relaunch.
src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs Uses crash-recovery-aware wait for “Go To Test” to reduce cascading OneTimeSetUp failures.
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs Switches issue navigation to the new recovery-aware “Go To Test” wait.
eng/scripts/detect-ui-test-categories.ps1 Skips redundant fork-head checkout when already on prepared review worktree; adds dependency-bump smoke category fallback.
eng/pipelines/ci-copilot.yml Adds base-branch validation/checkout, more robust adb handling, trusted gate verdict propagation, deep UI test budget/diagnostics, and token-usage stage.
.github/workflows/copilot-review-tests.md Tweaks review-trigger text formatting for rerun instructions.
.github/workflows/copilot-review-tests.lock.yml Regenerates gh-aw lock metadata for the workflow doc change.
.github/skills/verify-tests-fail-without-fix/scripts/Verify-TestsFail.Tests.ps1 New Pester tests for build-error classification (ensures “build broke” => INCONCLUSIVE).
.github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 Improves env/build error detection, retry recovery, and supports PR-deleted fix files.
.github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 Makes device-test TFMs branch-aware (net10 vs net11).
.github/skills/review-test-failures/SKILL.md Updates the skill template wording/formatting.
.github/scripts/shared/Update-AgentLabels.Tests.ps1 New tests for label derivation from authoritative artifacts (winner.json, gate-result.txt).
.github/scripts/shared/Update-AgentLabels.ps1 Uses gate-result.txt + winner.json as primary sources instead of prose parsing.
.github/scripts/shared/shared-utils.ps1 Adds Get-MauiTfmVersion to derive MAUI TFM version from repo metadata.
.github/scripts/shared/Get-AggregatedTrxFromDirectory.ps1 Adds app-crash cascade detection/flagging for deep UI TRX aggregation.
.github/scripts/shared/Aggregate-UITestArtifacts.Tests.ps1 Adds coverage for new “app crash cascade” classification behavior.
.github/scripts/shared/Aggregate-UITestArtifacts.ps1 Mirrors app-crash cascade detection/flagging in the shared aggregator.
.github/scripts/shared/Aggregate-CopilotTokenUsage.ps1 New script to aggregate Copilot telemetry records into publishable artifacts.
.github/scripts/Review-Tests.ps1 Adjusts badge coloring and consolidates rerun guidance into one line.
.github/scripts/Review-PR.Tests.ps1 Adds tests for Copilot token usage helpers and AzDO log-command defanging.
.github/scripts/Post-AISummaryComment.Tests.ps1 Expands tests for gate-status mapping, approve veto behavior, and UI-results annotations.
.github/scripts/post-ai-summary-comment.ps1 Updates AI summary rendering, gate/status chip behavior, approve veto logic, and UI-results annotation.
.github/scripts/DetectUITestCategories.Tests.ps1 New tests for prepared-review-worktree detection helper.
.github/scripts/BuildAndRunHostApp.ps1 Makes HostApp TFMs branch-aware; adds Android warm-up and Windows app-path resolution.
.github/scripts/Aggregate-CopilotTokenUsage.Tests.ps1 New Pester tests validating token-usage aggregation outputs.
Comment thread src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs
Comment on lines +1358 to +1362
if [ -d "$TRUSTED/scripts" ]; then
rm -rf .github/scripts .github/skills eng/scripts
cp -r "$TRUSTED/scripts" .github/scripts
cp -r "$TRUSTED/skills" .github/skills
cp -r "$TRUSTED/eng-scripts" eng/scripts

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

🔍 AI-generated reply on @kubaflo's behalf.

Valid refinement — flagging as a follow-up rather than changing it blind, since it touches the deep-UI security orchestration on a branch with concurrent work. Context: the copy at :1360-1362 is deliberately security-positive today (it replaces the PR's .github/scripts with the trusted main versions before the per-category loop, per the comment citing rule 3), so PR scripts never execute. Your point stands that the trusted copy then lives in a PR-writable worktree (chmod u+w) and could in principle be mutated by a later untrusted dotnet test within the job. The clean fix is to invoke the deep-UI scripts directly from $TRUSTED (read-only) and drop the cp-back entirely — but that requires rewiring every .github/scripts/… call in the stage (retry wrapper, aggregators, label/token scripts) to $TRUSTED/… while keeping CWD on the worktree src/. I'd rather land that as its own validated change. Leaving open.

Comment on lines 1427 to 1431
# Dot-source the shared retry wrapper so Stage 2 gets the same
# env-error detection, device recovery, and retry logic as Stage 1.
$retryScript = ".github/scripts/shared/Invoke-UITestWithRetry.ps1"
$hasRetryWrapper = Test-Path $retryScript

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

🔍 AI-generated reply on @kubaflo's behalf.

Same root cause as the trusted-copy thread above — the per-category retry wrapper resolves from .github/scripts/shared/… (worktree) rather than $TRUSTED. Mitigated today by the trusted-over-worktree restore at :1360, but the proper fix is to resolve it (and the other stage scripts) from $TRUSTED and fail closed if absent. Tracking with the companion thread; leaving open for a dedicated, pipeline-validated change rather than a partial edit on a contested branch.

Comment thread .github/scripts/BuildAndRunHostApp.ps1 Outdated

@PureWeen PureWeen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Multi-model adversarial review ⚠️

Methodology: 3 independent reviewers (different frontier models) analyzed this round in parallel, with adversarial cross-validation — single-reviewer findings were sent to the other two models for adjudication before inclusion. This is review round 12 (HEAD 8eca146f).

The security architecture held up under scrutiny again. The findings below are all bounded by the same fact: MauiBot's posted review event (APPROVE/COMMENT/REQUEST_CHANGES) is advisory — human approval is still required and the real merge gate is the maui-pr build. So none of these are merge-blocking risks; they're edge cases where the advisory APPROVE could be slightly too generous.


✅ Verified safe (trust chain held)

Spot-checked and confirmed correct by multiple reviewers:

  • Gate trust chainGet-AIReviewEventForRun fails closed on an empty TrustedGateResult (throws), keys the APPROVE veto off the trusted pipeline verdict (not the agent-writable gate-result.txt), and is covered by a test asserting a forged gate-result.txt cannot flip a trusted PASSED. Solid.
  • Trusted-script overlay / DeepUITests / fork-head handling — the fork head is never checked into the trusted worktree; detect-ui-test-categories.ps1 skips fork checkout (trusted-gated).
  • Aggregate-CopilotTokenUsage.ps1 — null-safe sums, and the path filter requires /copilot-token-usage/raw/ while excluding the PR/agent-writable CustomAgentLogsTmp + agent-pr-session dirs.
  • AppiumLifecycleActions.RunAdb / WaitForGoToTestButtonWithRecoveryRunAdb uses ArgumentList (no shell injection) with a 15s timeout+Kill; the recovery helper rethrows on genuine failure (does not swallow).
  • The round-11 💡 (the tools.dotnet-is-a-proxy note) was addressed — thanks.

⚠️ A non-compiling fix is reported as INCONCLUSIVE instead of FAILED — 2/3 reviewers

verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 (status map L1238, exit routing L1850 / L1893).

$gateInfraError (L1850) lumps both the without-fix (baseline) build error and the with-fix build error into one non-blocking bucket → exit 3 (INCONCLUSIVE). But the script already distinguishes them in prose just above: L1278 $wBuildError renders "🩺 Fix does not compile" vs L1282 $woBuildError renders "Base branch does not compile".

A baseline build failure is legitimately inconclusive (you can't establish the bug exists). But a with-fix-only build failure means the PR's own fix is broken — that's a definitive failure, not infra noise, yet it currently exits 3 (non-blocking) on every branch.

Scenario: PR adds a fix that doesn't compile; baseline builds fine. The verify gate exits 3 (INCONCLUSIVE) rather than 1 (FAILED), so the advisory signal under-reports a genuinely broken fix.

Suggested fix: at L1850, only treat baseline build errors as infra-INCONCLUSIVE. If $wBuildError.Count -gt 0 -and $woBuildError.Count -eq 0 (only the fix fails to build), keep FAILED/exit 1. The two sets are already computed at L1257-1258.

Mitigation: the maui-pr build blocks a non-compiling fix regardless, so this only weakens the advisory verify signal — it doesn't let broken code merge.


⚠️ An all-crash deep-UI run leaves APPROVE intact — 2/3 reviewers (design question)

post-ai-summary-comment.ps1 Test-RunValidationFailed (veto regex L477) vs the deep-UI render in ci-copilot.yml (L1887-1906).

When every deep-UI category crashes the HostApp (regularFailed==0, appCrashCategories>0), the render uses ⚠️ and "the HostApp crashed mid-run, so N tests could not complete" (L1890/L1906). The veto regex only matches ❌ **…tests** or a non-zero N failed count — so the all-crash header matches neither, the veto doesn't fire, and an APPROVE recommendation survives a run where zero deep-UI tests actually completed.

This is partly deliberate: an app crash is genuinely ambiguous (emulator/infra flake vs. PR regression), and you intentionally render it ⚠️ rather than so a good PR isn't shown as "all failed". The narrow question is whether a positive APPROVE is right when no deep-UI test produced a passing signal — arguably it should land on COMMENT (neutral) rather than APPROVE. Mixed runs (regularFailed>0) already render and do veto, and the body prominently warns the human either way.

Option: when appCrashCategories>0 -and regularFailed==0, downgrade APPROVE→COMMENT (not REQUEST_CHANGES — the crash may be a flake).


💡 A strictly-better alternative fix doesn't downgrade an explicit APPROVE — 2/3 flagged, but tested as intentional

post-ai-summary-comment.ps1 Get-AIReviewEventForRun L510: the non-PR-winner check (Test-HasNonPRWinner) only upgrades COMMENT→REQUEST_CHANGES, leaving an explicit APPROVE intact.

Two reviewers flagged this as a veto gap. On adjudication it turns out to be deliberate and explicitly unit-testedPost-AISummaryComment.Tests.ps1 has a test named "does not override an exact approve recommendation" (isPRFix=$false + Final Recommendation: APPROVE → APPROVE). The design treats a non-PR winner as an advisory "we found a stronger alternative" preference, not a validation failure. Flagging only to confirm the intent is what you want; no change needed unless you'd prefer a strictly-better alternative to soften APPROVE→COMMENT.


Investigated and discarded (single-reviewer, refuted on follow-up)

  • Labels from agent-writable artifacts (raised as Security): discarded. The s/agent-* labels are triage/metrics signal only — the real merge-readiness check uses GitHub-native reviewDecision, which derives from the gate-protected posted review event. Not a trust boundary; at most a cosmetic dashboard inconsistency.
  • NaN/Infinity in token aggregation: discarded as unreachable. [double]::TryParse("NaN"/"Infinity", Float) does succeed and would throw on the later [long] round — but the aggregator only reads the pipeline-written raw/ token logs (typed integer counts), which are not PR-influenceable and never emit non-finite values. Purely defensive; left as a 💡 for optional hardening (reject non-finite in Get-NumericOrNull).

Non-blocking observation

This branch is currently CONFLICTING / DIRTY against main — there's a real merge conflict to resolve before it can merge (a fresh conflict from the new commits, not a code-quality issue).


Test coverage: strong. The veto/gate logic is well covered by Post-AISummaryComment.Tests.ps1 (forged-gate, INCONCLUSIVE-doesn't-block, deep-UI real-render veto, TRX-wording false-trigger). Gaps map exactly to the findings above: no test for a with-fix-only build error (Finding A) or an all-crash deep-UI run (Finding B).

Prior reviews: round-11 verdict was clean; the round-11 suggestion was addressed this round. No unresolved blocking threads from prior rounds.

3 independent reviewers · adversarial consensus · code-only review (CI status intentionally out of scope).

…adb robustness

PureWeen / Copilot round-12 findings:

- [PureWeen A] verify-tests-fail.ps1: a with-fix-ONLY build error (baseline compiles,
  the PR's fix does not) is now FAILED (exit 1), not INCONCLUSIVE (exit 3). Only a
  baseline build error or an env error stays infra-INCONCLUSIVE. Adds a clear console
  note for the non-compiling-fix case.
- [PureWeen B] post-ai-summary-comment.ps1: add Test-DeepUITestsHadNoSignal and soften
  APPROVE->COMMENT (not REQUEST_CHANGES) when every deep-UI category hit a setup/crash
  failure and nothing passed — an APPROVE over a run where zero deep-UI tests completed
  was too generous. Mixed/regular failures still veto to REQUEST_CHANGES.
- [Copilot] AppiumLifecycleActions.RunAdb: drain stdout/stderr asynchronously so a chatty
  command (adb shell monkey) can't fill the pipe buffer and deadlock WaitForExit.
- [Copilot] BuildAndRunHostApp.ps1: replace the non-portable external 'timeout 90 adb
  wait-for-device' with a .NET process timeout (the timeout binary differs/absent on Windows).

Tests: +6 Post-AISummaryComment cases (Finding A/B), 36/36 + verify-tests-fail 5/5;
UITest.Appium builds.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@kubaflo

kubaflo commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator Author

🔍 This is an AI-generated comment posted on @kubaflo's behalf.

@PureWeen — addressed the round-12 findings in 2317c9555e (forward commit on the current head 8eca146f):

Finding Status
⚠️ A — with-fix-only build error reported INCONCLUSIVE Fixed — only a baseline build error or an env error stays infra-INCONCLUSIVE; a with-fix-only build error (baseline compiles, the fix doesn't) is now FAILED/exit 1.
⚠️ B — all-crash deep-UI leaves APPROVE intact Fixed — new Test-DeepUITestsHadNoSignal softens APPROVE→COMMENT (neutral, not REQUEST_CHANGES) when every category setup-failed and nothing passed.
💡 C — non-PR-winner doesn't downgrade explicit APPROVE Confirmed intentional — kept as-is (the "does not override an exact approve" test encodes the design: a non-PR winner is an advisory preference, not a validation failure). Thanks for verifying.
Copilot: RunAdb pipe-drain deadlock Fixed — async drain before WaitForExit.
Copilot: non-portable timeout adb wait-for-device Fixed — .NET process timeout.
Copilot: deep-UI scripts run from worktree (×2, security) Left open — valid refinements; the clean fix rewires every deep-UI .github/scripts/… call to $TRUSTED. Replied inline; landing it as a dedicated, pipeline-validated change rather than a partial edit.

Tests: +6 Post-AISummaryComment cases (A/B), 36/36 + verify-tests-fail 5/5; UITest.Appium builds.

On the merge conflict: still outstanding — I deliberately did not fold a main merge into this push because the branch has concurrent commits landing (the round-12 RunAdb/deep-UI/token-usage work arrived while I was preparing this), and a divergent merge could collide. The conflict (a Review-Tests.ps1 badge block + the gh-aw lock) is small and should be resolved in a focused merge once the in-flight commits settle.

@PureWeen PureWeen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Adversarial re-review — round 13 (commit 2317c95)

Thanks for turning the round-12 notes around so quickly, @kubaflo. This commit ("Address round-12 review: build-error classification, all-crash veto, adb robustness") is a direct response to the previous review, so I focused on verifying each of the three fixes is correct and complete — i.e. that it closes the exact scenario originally flagged, not just an adjacent one.

I read the full source (not just the diff), traced the trusted-gate trust chain end-to-end, and empirically tested both regex/branch changes against the real pipeline render strings. Three reviewers reached the same conclusion independently: the adb fix is fully correct; the other two fixes handle a sibling case but each misses the originally-flagged one.


✅ adb robustness — correct and complete (verified)

This one is done right:

  • RunAdb (AppiumLifecycleActions.cs:374-408) — both RedirectStandardOutput and RedirectStandardError are set (L381-382) with UseShellExecute=false, so BeginOutputReadLine()/BeginErrorReadLine() (L395-396) won't throw, and the no-op handlers drain both pipes before WaitForExit(15000) — the deadlock is eliminated. Kill() is try/catch-guarded. ✅
  • BuildAndRunHostApp.ps1 wait-for-device (~L283-287) — portable Start-Process -PassThru -NoNewWindow + WaitForExit(90000) + guarded Kill() correctly replaces the non-portable timeout 90 adb. ✅

No regression here.


⚠️ Finding B (all-crash deep-UI APPROVE veto) — still incomplete: it catches the setup-failure sibling but misses the originally-flagged app-crash render

(2 of 3 reviewers rated this must-fix; all 3 agree the originally-flagged escape is still open.)

The new Test-DeepUITestsHadNoSignal (post-ai-summary-comment.ps1:485-503) keys its positive match on (L501):

'(?im)could not run:\s*OneTimeSetUp/fixture setup failure'

But ci-copilot.yml emits two mutually-exclusive regularFailed==0 "no-signal" headers, and appCrashCategories>0 takes priority (L1900):

  • App-crash render — ci-copilot.yml:1906 (the originally-flagged case): … the HostApp crashed mid-run, so N tests could not complete …
  • Pure setup-failure render — ci-copilot.yml:1911 (the sibling): … could not run: OneTimeSetUp/fixture setup failure …

The regex wording appears only in the L1911 sibling. The L1906 app-crash header (and the per-category app-crash body at L1800, app crashed mid-run; … could not complete) never contain could not run: OneTimeSetUp/fixture setup failure.

Concrete trigger: every deep-UI category crashes the HostApp (appCrashCategories>0, regularFailed==0, totalPassed==0) and the run recommends APPROVE → Test-DeepUITestsHadNoSignal returns $false → the APPROVE→COMMENT soften at L534 does not fire → a positive APPROVE survives a run where zero deep-UI tests completed — the exact escape Finding B set out to close. The doc-comment (L487) claims HostApp-crash coverage, and the new tests in Post-AISummaryComment.Tests.ps1 only exercise the could not run: OneTimeSetUp/fixture setup failure wording, so the gap isn't caught.

Suggested fix — broaden the positive match to the app-crash render, keeping the completed-test guards:

$noSignalHeader = ($uiContent -match '(?im)could not run:\s*OneTimeSetUp/fixture setup failure') -or
                  ($uiContent -match '(?im)the HostApp crashed mid-run, so .*could not complete')
return ($noSignalHeader -and
        $uiContent -notmatch '(?im)\b\d+\s+passed\b' -and
        $uiContent -notmatch '(?im)\b[1-9]\d*\s+failed\b')

The \b\d+\s+passed\b guard still correctly excludes the app-crash-with-passes header (L1908, which emits passNote). Please add a test using the literal L1906 wording so the app-crash path is covered.

(Scope note: this is bounded by APPROVE being advisory — a human still merges — so it's not a hard-gate bypass. But it leaves the originally-identified misleading-APPROVE escape open, so it's worth closing in this same PR.)


⚠️ Finding A (build-error classification) — verdict/veto fix is correct ✅, but the report headline + Gate chip still render INCONCLUSIVE for a non-compiling fix

The important half is done right and I verified it: the exit-code split (verify-tests-fail.ps1:1851-1854) means a with-fix-only build error (baseline compiles) now hits the else branch → exit 1Review-PR.ps1 maps to FAILEDci-copilot.yml:827 makes the process exit code authoritative → GATE_VERDICT=FAILED → the APPROVE veto fires → REQUEST_CHANGES. Definitive build failures now block and veto APPROVE correctly.

What's still inconsistent is the display — and it's the exact contradiction round-12 pointed at. Write-MarkdownReport (L1236-1238) still computes $hasBuildError from both without-fix and with-fix results:

$hasBuildError = ($WithoutFixResultsList | ? { $_.BuildError }) -or ($WithFixResultsList | ? { $_.BuildError })
$status = if ($VerificationPassed) { "PASSED" } elseif ($hasEnvError -or $hasBuildError) { "INCONCLUSIVE" } else { "FAILED" }

So a non-compiling fix renders ⚠️ INCONCLUSIVE in the report headline while the gate exits FAILED and the body says "Fix does NOT compile … definitive failure". That report is copied verbatim into gate/content.md, and Get-GateStatus (post-ai-summary-comment.ps1:235) parses the INCONCLUSIVE header → the human-facing Gate status chip (L744) shows Inconclusive. Net: the posted comment frames a definitive, blocking build failure as a non-blocking infra flake — the inconsistency round-12 asked to remove.

Suggested fix — mirror the L1851-1854 split inside Write-MarkdownReport so only a baseline build/env error maps to INCONCLUSIVE:

$baselineBuildError = ($WithoutFixResultsList | Where-Object { $_.BuildError }).Count -gt 0
$status = if ($VerificationPassed) { "PASSED" }
          elseif ($hasEnvError -or $baselineBuildError) { "INCONCLUSIVE" }
          else { "FAILED" }

Summary

Round-12 fix Correct? Complete?
adb robustness ✅ Complete
A — build-error classification ✅ verdict/veto/blocking now correct (exit-code authoritative) ⚠️ headline (verify-tests-fail.ps1:1238) + Gate chip still render INCONCLUSIVE for a with-fix-only build error
B — all-crash deep-UI veto ⚠️ works for the setup-failure sibling ❌ regex misses the app-crash render (ci-copilot.yml:1906) — the originally-flagged all-crash APPROVE escape is still open

New regressions introduced by this commit: none — the trusted-gate trust chain (FAILED veto + exit-code authority) is intact. Both open items are incomplete fixes of the originally-flagged issues, not new breakage.

One non-blocking housekeeping note: the PR is currently CONFLICTING / DIRTY against main — there's a merge conflict to resolve before this can merge.


🔍 Methodology: three independent reviewers cross-checked each round-12 fix against full source + the real pipeline render strings; empirical regex/branch testing confirmed the app-crash gap. Posted as a non-blocking comment (not an approval/changes-requested) — sign-off is a human call.

PureWeen round-13 verified the round-12 fixes were correct but incomplete:

- [A] verify-tests-fail.ps1: the exit-code/veto split was right, but Write-MarkdownReport
  still computed the report headline from BOTH baseline and with-fix build errors, so a
  non-compiling fix rendered '⚠️ INCONCLUSIVE' in the headline + Gate chip while the gate
  exited FAILED. Mirror the split: only a BASELINE build/env error → INCONCLUSIVE.
- [B] post-ai-summary-comment.ps1: Test-DeepUITestsHadNoSignal matched only the
  OneTimeSetUp/fixture setup-failure render, missing the originally-flagged app-crash
  render (ci-copilot.yml ~1906 'the HostApp crashed mid-run, so N … could not complete'),
  which takes priority when appCrashCategories>0. Broaden the match to both no-signal
  headers, keeping the 'N passed'/'N failed' guards (so the with-passes crash header is
  still excluded). Added a test using the literal app-crash wording.

Tests: 43/43 (Post-AISummaryComment + Verify-TestsFail).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 29, 2026 14:49
@kubaflo

kubaflo commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator Author

🔍 This is an AI-generated comment posted on @kubaflo's behalf.

@PureWeen — thanks for the precise round-13 verification; you were right that A and B each closed a sibling case but not the originally-flagged one. Both completed in 5369e1dd49:

  • Finding A (display): mirrored the exit-code split inside Write-MarkdownReport — only a baseline build/env error now maps to INCONCLUSIVE; a with-fix-only build error renders FAILED in the headline + Gate chip, consistent with the gate exit and the "Fix does NOT compile" body. No more "definitive failure framed as infra flake".
  • Finding B (app-crash): broadened Test-DeepUITestsHadNoSignal to match both no-signal headers — the OneTimeSetUp/fixture setup failure render and the app-crash render (the HostApp crashed mid-run, so N … could not complete, ci-copilot.yml ~1906) which takes priority when appCrashCategories>0. The N passed/N failed guards still exclude the with-passes crash header. Added a test using the literal app-crash wording.

Tests: 43/43 (Post-AISummaryComment + Verify-TestsFail), including the new app-crash case. adb robustness already confirmed ✅.

Merge conflict still pending — noted; will land as a focused merge once the in-flight branch commits settle.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated 3 comments.

Comment on lines +205 to +211
else
REPO=$(git config --get remote.origin.url | sed -E 's#.*github\.com[:/]+([^/]+/[^/.]+)(\.git)?$#\1#')
[ -z "$REPO" ] && REPO="dotnet/maui"
BASE_REF=$(curl -fsSL -H "Authorization: Bearer ${GH_TOKEN}" -H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${REPO}/pulls/${PARAM_PR_NUMBER}" \
| python3 -c "import sys,json;print(json.load(sys.stdin)['base']['ref'])")
fi
Comment on lines +1064 to +1073
# Prefer gh; fall back to REST (gh isn't pre-installed on macOS agents this early).
if command -v gh >/dev/null 2>&1; then
BASE_REF=$(gh pr view "${PARAM_PR_NUMBER}" --json baseRefName -q .baseRefName)
else
REPO=$(git config --get remote.origin.url | sed -E 's#.*github\.com[:/]+([^/]+/[^/.]+)(\.git)?$#\1#')
[ -z "$REPO" ] && REPO="dotnet/maui"
BASE_REF=$(curl -fsSL -H "Authorization: Bearer ${GH_TOKEN}" -H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${REPO}/pulls/${PARAM_PR_NUMBER}" \
| python3 -c "import sys,json;print(json.load(sys.stdin)['base']['ref'])")
fi
Comment on lines +666 to +673
# Match coded build errors generally — `error <ABBR><NNNN>` — so the MAUI XAML
# compiler (MAUIX####, e.g. MAUIX2017 "set multiple times"), MSBuild (MSB####),
# C# (CS####), .NET SDK (NETSDK####), NuGet (NU####) and Android (XA####) diagnostics
# are all caught. This matters on branches where an unrelated test fixture fails to
# compile (e.g. the net11 Controls.Xaml.UnitTests MAUIX2017 baseline break): the whole
# test assembly won't build, so the PR's own test can't run — that is INCONCLUSIVE, not
# "the fix does not pass". The negative lookahead on `0 error(s)` avoids false positives
# on MSBuild summary lines like "Build succeeded. 0 Error(s)".

@PureWeen PureWeen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Adversarial re-review — round 14 (commit 5369e1d)

Both round-13 items are now fully closed @kubaflo — thanks for the quick turnaround. This commit ("round-13: complete Findings A & B (display headline + app-crash veto)") directly completes the two incomplete-fix findings from the last review, so I focused on verifying each now closes the exact originally-flagged scenario (not just an adjacent one) and introduces no new regression. Three reviewers plus an independent empirical pass all reached the same conclusion: complete, correct, no regressions.


✅ Finding A (build-error headline / Gate chip) — RESOLVED

The headline now keys off a baseline-only build error, mirroring the exit-code split exactly:

$baselineBuildError = @($WithoutFixResultsList | Where-Object { $_.BuildError }).Count -gt 0
$status = if ($VerificationPassed) { "PASSED" } elseif ($hasEnvError -or $baselineBuildError) { "INCONCLUSIVE" } else { "FAILED" }

Verified consistent across every layer for a with-fix-only build error (baseline compiles, the PR's fix doesn't):

  • headline $status❌ FAILED (was ⚠️ INCONCLUSIVE) — verify-tests-fail.ps1:1241-1243
  • matches the exit-code predicate $gateInfraError = $anyEnvError -or $baselineBuildError → exit 1 (same .Count -gt 0 shape, L1856/L1859)
  • matches the body classification 🩺 Fix does not compile (L1283) and the console box (L1916)
  • the human-facing Gate chip now renders Failed: Get-GateStatus's structured marker Gate Result:\s*(?:\S+\s*)?(FAILED|…) (L235) consumes the emoji and returns Failed before the \binconclusive\b word-fallback (L244) is ever reached.

The old $hasBuildError variable is fully removed (zero remaining references). The contradiction round-13 flagged is gone.


✅ Finding B (all-crash deep-UI APPROVE veto) — RESOLVED

Test-DeepUITestsHadNoSignal now matches both no-signal renders:

$noSignalHeader = ($uiContent -match '(?im)could not run:\s*OneTimeSetUp/fixture setup failure') -or
                  ($uiContent -match '(?im)the HostApp crashed mid-run, so .*could not complete')

I empirically tested the function against the exact ci-copilot.yml render strings for every variant:

Deep-UI render Softened? Correct?
L1906 app-crash, no passes (the originally-flagged escape) yes → COMMENT
L1906 app-crash with passes no (kept APPROVE)
L1908 app-crash + regular failures no
L1911 setup-failure, no passes (sibling, already handled) yes
L1913 setup-failure with passes/fails no
normal pass/fail run no
L1800/L1803 per-category <details> body no false-positive

The originally-flagged all-crash escape is closed: because appCrashCategories>0 forces the crash header (L1900→L1906), matching that header explicitly is exactly right. No false-positive on the <details> body — it says "app crashed mid-run;" / "crashed during this category", neither of which contains the required literal "the HostApp crashed mid-run, so". The (?im) (no s flag) is intentionally safe — . can't cross newlines, so .* stays on the single header line. And the two new tests (Post-AISummaryComment.Tests.ps1:326-336) exercise the app-crash path directly — both the no-passes (TRUE) and with-passes (FALSE) cases.


✅ No new regressions

  • @(...).Count -gt 0 is a strict hardening over the old truthy-object -or (null-safe; now matches the exit-code block's idiom). Logically equivalent for the true/false cases.
  • Broadening $noSignalHeader does not risk over-softening — it's only one of three conjuncts; any N passed or non-zero N failed anywhere in the report still forces $false and preserves APPROVE.
  • No third no-signal render path remains; the all-empty "0 passed, 0 failed" no-tests case is a distinct scenario, correctly out of scope.

Summary

Round-13 finding Status
A — headline/Gate chip for a non-compiling fix ✅ Complete & correct
B — app-crash APPROVE veto ✅ Complete & correct
New regressions None

From a code standpoint this is clean — I have no remaining findings on the review-infrastructure changes. The one non-code item left before merge: the PR is still CONFLICTING / DIRTY against main, so there's a merge conflict to resolve.


🔍 Methodology: three independent reviewers cross-checked each round-13 fix against full source + the real pipeline render strings, with empirical regex/branch testing of all deep-UI render variants. Posted as a non-blocking comment (not an approval/changes-requested) — sign-off is a human call.

# Conflicts:
#	.github/scripts/Review-Tests.ps1
#	.github/workflows/copilot-review-tests.lock.yml
@PureWeen
PureWeen merged commit 6e10735 into main Jun 29, 2026
10 of 23 checks passed
@PureWeen
PureWeen deleted the feature/enhanced-reviewer branch June 29, 2026 17:21
@github-actions github-actions Bot added this to the .NET 10 SR9 milestone Jun 29, 2026
devanathan-vaithiyanathan pushed a commit to devanathan-vaithiyanathan/maui that referenced this pull request Jul 7, 2026
dotnet#35677)

<!-- 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!

## Summary

- Posts MauiBot AI Summary output as a pull request review with parsed
`APPROVE`, `REQUEST_CHANGES`, or safe `COMMENT` fallback.
- Uses the new AI Review Summary layout with segmented status chips,
collapsed review sessions, and merged Future Action content.
- Keeps PR finalization out of the automated review process; AI Summary
updates no longer preserve or merge `SECTION:PR-FINALIZE` blocks.
- Adds visible AI Summary guidance telling users to comment `/review
rerun` after new comments or commits when they want a fresh review. The
command implementation is intentionally split into a follow-up PR.
- Hides stale MauiBot AI Summary / try-fix artifacts with GitHub
minimization instead of deleting them, while preserving same-run try-fix
and AI Summary reviews.
- Updates the Copilot pipeline to pass review IDs and patch review
bodies after deep UI tests.
- Hardens gate setup/retry handling by committing squashed PR changes
before verification, resetting the review branch before gate retries,
and detecting BlazorWebView unit-test project paths.

## Validation

- Parsed changed PowerShell scripts with
`System.Management.Automation.Language.Parser`.
- Parsed `.github/workflows/review-trigger.yml` as YAML.
- `Invoke-Pester
.github/scripts/Post-AISummaryComment.Tests.ps1,.github/scripts/Remove-StaleMauiBotComments.Tests.ps1
-CI`
- Dry-run AI Summary generation verified the rerun note, segmented
chips, and collapsed review session layout.
- Verified `Detect-TestsInDiff.ps1` maps
`src/BlazorWebView/tests/MauiBlazorWebView.UnitTests/UriExtensions_Tests.cs`
to
`src/BlazorWebView/tests/MauiBlazorWebView.UnitTests/MauiBlazorWebView.UnitTests.csproj`.


## net11.0-targeting PR support

This branch also makes the Copilot review pipeline handle
**`net11.0`-targeting PRs** as well as `main`/net10, from a single
pipeline branch (consolidates and supersedes dotnet#35994):

- **Runtime base-branch auto-detection**
(`eng/pipelines/ci-copilot.yml`): the `CopilotReview` and `DeepUITests`
stages read the PR's `baseRefName` via `gh`, allowlist-validate it
(`^(main|net[0-9]+\.0)$`), and check out that base **before** workload
install and the squash-merge — so workloads (net11 `11.0.100` vs net10
`10.0.100`) and the merge base follow the PR. Trusted scripts are
captured from the pipeline ref first (security rule 3); Task 1 Setup
runs from `$TRUSTED`.
- **Branch-aware test TargetFramework**: `BuildAndRunHostApp.ps1` and
`run-device-tests/Run-DeviceTests.ps1` now derive the TFM from
`Directory.Build.props` (`Get-MauiTfmVersion` in `shared-utils.ps1`)
instead of hardcoding `net10.0`, so deep-UI/device tests build
`net11.0-android` on net11. The `DeepUITests` stage restores the
reviewed pipeline-branch scripts over the worktree before the
per-category loop.

`main`/net10 behavior is unchanged by design (base-detection checks out
`main` = the original flow; `global.json` stays `10.0.108`).
`review-trigger.yml` and `Review-PR.ps1` are intentionally untouched.

**Validation:** 8 live net11 runs with 100% correct base detection (e.g.
dotnet#35891 gate ran net11 Core tests 44/45 → 45/45, conflict-free
squash-merge onto net11.0); reproduced and root-caused a separate
net11.0-baseline `MAUIX2017` Xaml.UnitTests build break (unrelated to
this change). Confirmation runs from this branch cover one net11 PR and
one main PR.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Tomas Grosup <tomasgrosup@microsoft.com>
Co-authored-by: Copilot <Copilot@users.noreply.github.com>
Co-authored-by: Copilot CI <copilot-ci@microsoft.com>
Co-authored-by: kubaflo <kubaflo@users.noreply.github.com>
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 30, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-ai-agents Copilot CLI agents, agent skills, AI-assisted development area-infrastructure CI, Maestro / Coherency, upstream dependencies/versions s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

6 participants