Skip to content

Converge /review tests with azdo-build-investigator via shared CI facts doc - #36067

Merged
PureWeen merged 24 commits into
mainfrom
pureween-review-tests-merge-readiness
Jun 25, 2026
Merged

Converge /review tests with azdo-build-investigator via shared CI facts doc#36067
PureWeen merged 24 commits into
mainfrom
pureween-review-tests-merge-readiness

Conversation

@PureWeen

Copy link
Copy Markdown
Member

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

This is a tooling/agent-infrastructure change only (everything is under .github/ plus the local /review tests runner) — no product source or public API changes.

It converges the automated /review tests workflow (the review-test-failures skill) with the interactive azdo-build-investigator skill so that both deliver the same end result — a CI merge-readiness verdict plus a base-branch baseline failure review — while removing the duplicated MAUI CI knowledge that was copy-pasted across several files and at real risk of drifting.

The two skills can't share tooling (the ci-analysis plugin that powers azdo-build-investigator is a stdio MCP server, which the gh-aw runtime can't run), so "minimal duplication" is achieved by sharing docs, not tools.

Architecture: "shared knowledge, two roles"

One canonical facts doc; every consumer references it instead of re-stating the facts.

  • New .github/docs/maui-ci-facts.md — single source of truth for pipeline names/IDs (maui-pr 302, maui-pr-devicetests 314, maui-pr-uitests 313), AzDO data sources, the XHarness exit-0 blind spot, test-count deduplication, the baseline-comparison rule, visual-baseline/platform-mismatch guidance, Gradle/CFSClean signatures, the common-failure-pattern table, and the merge-readiness criteria. Its header lists every consumer, kept bidirectionally accurate.

What changed

Area Change
review-test-failures/SKILL.md References the facts doc; adds an overall merge-readiness verdict and baseline reasoning + an On base? column.
azdo-build-investigator/SKILL.md Slimmed (~106→68 lines) to reference the facts doc; keeps its unique value (ci-analysis-first, the outdated-maui-public correction, escalation to helix-investigation).
Gather-TestFailureContext.ps1 Extends the deterministic gatherer with per-test base-branch baseline extraction + comparison (failures.baseline, baselineMatchCount, baselineSummary, alsoFailsOnBaseline).
copilot-review-tests.md (+ lock) Output template gains a Baseline badge, an On base? column, and merge-readiness verdict/colors.
Review-Tests.ps1 Local runner: merge-readiness verdict→color map + a Baseline badge from failures.baselineMatchCount.
ci-status-main.md / ci-status-net11.md (+ locks) Removed duplicated pipeline-ID table, "key points" bullets, failure-pattern table, and dedup prose — now reference the facts doc; added a facts-doc existence check to the connectivity probe.

Validation

  • Both PowerShell scripts AST-parse cleanly.
  • gh aw compile of all three workflows: 0 errors, and a second recompile is idempotent (no further lock diff) — the compiled .lock.yml files are in sync with their .md bodies. (gh-aw {{#runtime-import}}s the body and pins a body_hash, so body edits surface as a hash-only lock diff.)
  • Grep sweep confirms no inline copies of the canonical pipeline/failure-pattern tables remain outside the facts doc, and the facts-doc consumer list exactly matches the set of files that reference it (bidirectional).

Intentionally out of scope (follow-up)

A parallel "repo-health" analysis flagged a couple of broader items left untouched here: the pipeline name→ID table is still duplicated in copilot-instructions.md and trigger-azdo-pipeline-setup, and there are some dead pr agent references in copilot-instructions.md. Those are the next concentric ring of cleanup and aren't required for this convergence.

@github-actions

github-actions Bot commented Jun 22, 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 -- 36067

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 36067"

@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Skill Validation Results

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

Overall Passed Static Passed LLM Skipped Skills 20 Agents 6

Skill Validation Results7f2b109 · Converge /review tests with azdo-build-investigator via shared CI facts doc · 2026-06-25T15:31:48Z

✅ 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: Skipped

No changed skills with eval specs found.

🔍 Full results and investigation steps

@github-actions github-actions Bot added the area-infrastructure CI, Maestro / Coherency, upstream dependencies/versions label Jun 22, 2026
PureWeen and others added 6 commits June 22, 2026 17:50
…I facts doc

- Add canonical .github/docs/maui-ci-facts.md (single source of truth for
  MAUI pipeline IDs, CI quirks, dedup, baseline comparison, merge-readiness).
- Extend Gather-TestFailureContext.ps1 with per-test base-branch baseline
  extraction and comparison.
- Rewrite review-test-failures SKILL.md for merge-readiness verdict + baseline.
- Slim azdo-build-investigator SKILL.md to reference the shared facts doc.
- Update copilot-review-tests.md output template (Baseline badge, On base column).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Review-Tests.ps1: extend Get-VerdictColor for merge-readiness verdicts
  (Ready to merge / Not ready / Needs human investigation / Insufficient data /
  No failures found) and add a Baseline (n on base) badge from
  failures.baselineMatchCount.
- ci-status-main.md / ci-status-net11.md: replace duplicated pipeline-ID table,
  'key points' bullets, failure-pattern table, and dedup prose with references to
  .github/docs/maui-ci-facts.md; add facts-doc existence check to the connectivity probe.
- maui-ci-facts.md: drop run-device-tests from the consumer header (it carries no CI
  facts) so the consumer list stays bidirectionally accurate.
- Recompile copilot-review-tests, ci-status-main, ci-status-net11 locks (body is
  runtime-imported; hashes re-pinned).

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

- Gatherer baseline: treat the most recent completed base build as the
  authoritative baseline. Previously a green base tip was overridden by any
  older red build in the lookback window, so a now-fixed test could be wrongly
  marked pre-existing (false 'Ready to merge'). Gate green-evidence on
  $completed[0].result instead of 'all builds succeeded'. (2/3 reviewer consensus)
- maui-ci-facts.md: list the OS platform token as 'macos' (what the gatherer
  actually emits via Get-PlatformFromText), not 'mac'.
- copilot-review-tests.md: add the facts-doc existence probe to the pre-flight
  check for parity with the ci-status-* workflows; recompiled lock (body_hash).

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

review-test-failures/SKILL.md restated a condensed copy of the canonical
merge-readiness criteria table that lives in maui-ci-facts.md, and the wording
had already begun to drift (dropped the infra/flake parenthetical; condensed the
baseline clause). That is exactly the duplication this PR removes elsewhere.
Replace the condensed criteria table with the verdict enum (still needed as the
output contract consumed by Get-VerdictColor and the badge template) plus a
pointer to the canonical criteria — mirroring how the ci-status consumers were
rewired. The 5 verdict strings remain identical across the facts doc, this skill,
copilot-review-tests.md, and Review-Tests.ps1.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The baseline green short-circuit emitted 'matching failures are unlikely
to be pre-existing' whenever the most recent base build reported
'succeeded'. For maui-pr-devicetests that is misleading: XHarness exits 0
even when Helix device tests fail (see maui-ci-facts.md 'XHarness exit-0
blind spot'), so a 'succeeded' result does not prove the base branch is
green. Special-case device-test pipelines to emit an inconclusive note
that hands the uncertainty to the agent (which is already instructed to
cross-check the Helix aggregated endpoint) instead of asserting a clean
baseline the build result cannot guarantee.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two follow-ups to the previous device-test baseline change:

- The consumer guidance in review-test-failures/SKILL.md still told the
  agent that a succeeded base build with 0 baseline failures means a
  matching PR failure is more likely PR-caused. That contradicts the
  inconclusive note the gatherer now emits for maui-pr-devicetests (where
  XHarness exits 0 even when Helix device tests fail). Add an explicit
  device-test exception that defers to the row's baselineSummary.note.

- The succeeded-baseline branch in Gather-TestFailureContext.ps1 appended
  a summary row without the $baselineInspected de-dup guard that the
  not-succeeded branch uses, so multiple PR builds of the same pipeline
  definition (e.g. retried runs) could add duplicate base-build rows.
  Mirror the guard so each inspected base build is summarized once.

Also soften the device-test note so it no longer over-promises Helix
verification when base-branch Helix data is unavailable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@PureWeen
PureWeen force-pushed the pureween-review-tests-merge-readiness branch from 8015c6c to 2d77349 Compare June 22, 2026 22:51

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


🤖 AI-GENERATED MULTI-MODEL CODE REVIEW 🤖

This review was independently performed by 3 AI models (Claude Opus 4.8, GPT-5.5, Gemini 3.1 Pro), then cross-pollinated to reconcile differences.

Round: 2
HEAD: 8015c6cb
Timestamp: 2026-06-23T13:43 UTC+02


🎯 Verdict: NEEDS CHANGES ❌

Confidence: High (unanimous after cross-pollination)

Independent verdicts:

  • Claude Opus 4.8: LGTM/HighNEEDS_CHANGES/Medium (flipped after investigating GPT's finding)
  • GPT-5.5: NEEDS_CHANGES/LowNEEDS_CHANGES/High (maintained, confidence raised)
  • Gemini 3.1 Pro: LGTM/HighNEEDS_CHANGES/High (flipped after investigating GPT's finding)

📊 Summary

This PR converges the /review tests workflow with azdo-build-investigator skill by creating a canonical maui-ci-facts.md and adding base-branch baseline comparison logic to Gather-TestFailureContext.ps1.

What's good:
✅ Eliminates duplicated CI knowledge across multiple files
✅ Creates single source of truth for pipeline IDs, failure patterns, merge-readiness criteria
✅ PowerShell scripts AST-parse cleanly
✅ Lock files properly regenerated
✅ Round 2 commits address device-test XHarness over-confidence from adversarial review

What needs fixing:
Baseline log read failures are silently reported as zero baseline failures — see critical finding below

🚨 Critical Finding — UNANIMOUS

All 3 models independently investigated and confirmed this issue after cross-pollination:

❌ Baseline log read failures are silently reported as zero baseline failures

Location: .github/skills/review-test-failures/scripts/Gather-TestFailureContext.ps1, line 663

The problem:
Get-BuildLogTestFailures catches exceptions while reading baseline build logs but does not record them:

  1. Timeline fetch failures (lines 643-645): Return without setting $result.error
  2. Per-log read exceptions (catch block at line 663-665): Silently swallowed

Consequence:
The caller (lines 1055-1061) reports baselineFailureCount = 0 with note = $null for a failed base build where logs are expired/inaccessible. This means:

  • ❌ Missing/expired baseline data looks like a cleanly-inspected zero-failure baseline
  • ❌ Pre-existing failures can be misclassified as PR-caused (no alsoFailsOnBaseline stamp)
  • Contradicts the PR's own facts doc (maui-ci-facts.md:119: "if baseline data is missing or the base build is inaccessible, say so")

Why this matters:
AzDO log retention/expiry makes the "build accessible, logs expired" case realistic in production.

The fix:
Bubble timeline errors into $result.error and accumulate per-log read failures into a non-null baselineSummary.note. Treat such rows as "Insufficient data" downstream rather than a clean zero-failure baseline.

Cross-pollination notes:

  • Opus's independent review flagged general under-match as a suggestion but missed the indistinguishability from clean baseline. After investigating GPT's finding directly against HEAD 8015c6cb, Opus confirmed it contradicts the PR's own shipped facts doc and flipped to NEEDS_CHANGES.
  • Gemini's independent review gave LGTM/High with no findings. After investigating GPT's claim, Gemini confirmed the finding is correct and flipped to NEEDS_CHANGES/High.
  • GPT's independent review caught this issue initially. After cross-pollination, GPT maintained NEEDS_CHANGES and raised confidence from Low to High.

💡 Additional Suggestions (Non-blocking)

3 minor suggestions for future improvements (see inline comments):

  1. Canceled builds: Treat 'canceled' as weak/inconclusive baseline evidence (compounds the silent-zero gap)
  2. Log-only extraction: Surface the MaxLogs=8 cap in summary (under-match shouldn't look like confident clean baseline)
  3. Most-recent-only: Single most-recent build misses intermittent base flakes (intentional, noted for awareness)

These do not affect merge readiness beyond the blocking error above.

🔍 Key Review Points

CI Status — Resolved

Initial disagreement: GPT flagged maui-pr: skipping as undetermined CI coverage

Resolution: All 3 models agree after investigation:

  • maui-pr skip is by-design for .github/** changes (path exclusion in eng/pipelines/ci.yml)
  • Actual validation: Skill Validation, Static validation, and skill validation all pass
  • CI coverage is appropriate for .github/skills/** tooling changes

Round 2 Commits

The 2 new commits in Round 2 correctly address adversarial review findings:

  • ✅ "Don't overstate device-test baseline confidence on XHarness exit-0"
  • ✅ "Align device-test baseline guidance and de-dup succeeded baseline rows"

These fix the XHarness exit-0 blind spot where succeeded baselines were reported with overconfidence.

Code Quality

✅ PowerShell scripts AST-parse cleanly
✅ Lock files properly regenerated (gh aw compile idempotent)
✅ Coverage verified: duplicated tables removed from all consumers
✅ Facts doc correctly captures pipeline IDs and failure patterns

🏁 Recommendation

REQUEST CHANGES — The baseline log read failure issue must be fixed before merge:

  1. It contradicts the PR's own documentation (maui-ci-facts.md)
  2. It can misclassify pre-existing failures as PR-caused
  3. The fix is straightforward and in-scope for this PR

After the fix:
This is excellent infrastructure work that eliminates dangerous duplication and adds valuable baseline comparison. The fix for the error finding is trivial — bubble up the log read failures so they're visible in the baseline summary.


This review used the code-review skill with independence-first assessment, full-context analysis, and empirical verification by all 3 models.

Get-BuildLogTestFailures swallowed two failure modes: an inaccessible
build timeline returned without setting $result.error, and per-log read
exceptions were silently ignored. The baseline caller then emitted
baselineFailureCount=0 with a null note for a *failed* base build whose
logs were expired/inaccessible, making missing baseline data look like a
cleanly-inspected zero-failure baseline. Pre-existing failures could then
be misclassified as PR-caused, contradicting maui-ci-facts.md ('if
baseline data is missing or the base build is inaccessible, say so').

- Bubble timeline-fetch and build-metadata failures into $result.error.
- Count per-log read failures and report them via $result.error.
- Track totalFailedRecords vs inspectedLogCount so the MaxLogs cap is
  surfaced (truncated baseline can under-match and over-attribute to PR).
- Compose an honest baseline note: inconclusive on error, inconclusive
  when a non-succeeded base build yields zero extractable failures, and a
  partial-coverage note when the log cap truncates inspection.
- SKILL.md: instruct the agent to treat a row whose note flags the
  baseline inconclusive/incomplete as not-clean, folding into Insufficient
  data rather than concluding PR-caused.

Addresses the unanimous multi-model review finding on PR #36067.

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

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

Note

🤖 This review was generated by AI — an automated multi-model code review (Claude Opus 4.8 · GPT-5.5 · Gemini 3.1 Pro), independent reviews reconciled via cross-pollination.


🔍 AI Multi-Model Code Review — Round 3

HEAD: c8fb2880 · Re-review after Round 2 REQUEST_CHANGES (8015c6cb)


✅ Verdict: LGTM — Round 2 blocker resolved

Confidence: High — unanimous (all 3 models, independently and after cross-pollination)

Model Verdict R2 error resolved?
Claude Opus 4.8 LGTM / High ✅ yes
GPT-5.5 LGTM / High ✅ yes
Gemini 3.1 Pro LGTM / High ✅ yes

Notably, GPT-5.5 — the model that originally caught the Round 2 error — independently confirmed its own finding is genuinely fixed, not superficially patched.


🎯 Round 2 Blocker — Fixed ✓

Round 2 finding (unanimous ❌ Error): Baseline log read failures were silently reported as zero baseline failures. Timeline-fetch failures and per-log read exceptions in Get-BuildLogTestFailures were swallowed; the caller emitted baselineFailureCount=0 with note=$null for a failed/expired base build — so an inaccessible baseline looked clean, and pre-existing failures could be misattributed to the PR. This contradicted the PR's own maui-ci-facts.md guidance.

The Round 3 fix (Gather-TestFailureContext.ps1) — verified by all 3 models against the worktree:

  1. Build-metadata inaccessible → now sets result.error = "Build <id> metadata was not accessible." (was a possibly-null passthrough)
  2. Timeline fetch failure → now sets result.error = "Timeline for build <id> was not accessible (logs may be expired)." (previously returned with error unset)
  3. Per-log read exception → the silent # Ignore is replaced with a $logReadFailures counter; after the loop, result.error reports "<n> of <N> baseline build log(s) could not be read … baseline failure list is incomplete."
  4. Caller builds an honest note ($noteParts): surfaces error, flags a not-succeeded base build that yielded 0 extractable failures as "inconclusive, not clean", and reports truncation when totalFailedRecords > inspectedLogCount. note stays $null only for a genuinely clean baseline.
  5. SKILL.md now instructs the agent that a row whose note flags inconclusive/incomplete is not a clean zero-failure baseline even when baselineFailureCount=0 — don't conclude PR-caused; fold into Insufficient data.

Correctness probes (all passed)

  • No green-baseline false positive: the elseif ($baseFailureCount -eq 0) inconclusive note lives strictly inside the foreach ($base in $notSucceeded) loop (failed/partiallySucceeded/canceled). A succeeded most-recent base build takes the earlier continue and legitimately reports baselineFailureCount=0 / note=$null. ✔
  • No schema regression: totalFailedRecords / inspectedLogCount are additive, consumed only by the new note logic. ✔
  • Prior non-blocking suggestions also addressed: the MaxLogs=8 under-match is now surfaced via the truncation note; canceled/partial base builds are covered by the zero-extract inconclusive note.

🟢 CI Status

  • Static validation ✅ pass · Skill Validation ✅ pass · skill-validation ✅ pass · license/cla ✅ pass
  • maui-pr ⏭️ skipping — by-design (.github/** path exclusion in eng/pipelines/ci.yml); Build Analysis pending (informational)
  • Required checks are not red.

💡 One Non-Blocking Suggestion (see inline)

The new error-surfacing + $noteParts logic is now load-bearing correctness code but ships without a unit-test harness (review-test-failures has no tests/ dir — pre-existing for this script). Non-blocking, and mitigated because every new branch errs toward inconclusive/incomplete (the safe direction). A small Pester harness locking the five semantics (listed inline) would protect against future drift. All three models flagged this as the sole follow-up.


🏁 Recommendation

LGTM — the Round 2 blocker is fully and correctly resolved, the fix introduces no regression and no green-baseline false positive, and the prior non-blocking suggestions are addressed too. Ready for human approval.


Reviewed with the code-review skill: independence-first (code before narrative), full-file reading in a worktree at PR HEAD, empirical control-flow verification, and CI cross-checking by all 3 models.

$baseFailureCount = @($extract.failures).Count
# Build an honest note so an unreadable, truncated, or empty-but-not-clean
# baseline is never reported as a confident zero-failure baseline.
$noteParts = New-Object System.Collections.Generic.List[string]

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.

💡 💡 Suggestion (non-blocking): Consider a small Pester harness for the new baseline-note branching

The metadata / timeline / per-log error surfacing in Get-BuildLogTestFailures and the three-way $noteParts logic here are now load-bearing correctness code — they're what stops an expired/inaccessible baseline from being read as a clean zero-failure baseline (the Round 2 finding). They currently ship without unit tests (review-test-failures has no tests/ dir).

This is non-blocking and consistent with how skill scripts are treated in this repo, and it's mitigated by the fact that every new branch errs toward inconclusive/incomplete (the safe direction). But a tiny harness asserting:

  • timeline-fetch failure → result.error set,
  • per-log read exception → result.error mentions the unreadable count,
  • notSucceeded base + 0 extractable failures → note flags "inconclusive, not clean",
  • totalFailedRecords > inspectedLogCount → truncation note,
  • genuinely-clean baseline → note = $null,

would lock in these now-critical semantics against future drift. All three reviewers independently flagged this as the only (non-blocking) follow-up.

— 3-model consensus (Opus 4.8 / GPT-5.5 / Gemini 3.1 Pro), non-blocking.

Make the automated /review tests verdict a sound merge gate: a green
('Ready to merge'/'No failures found') is now impossible whenever a check
is still pending or a failing check could not be inspected, so a green is
always trustworthy.

Gather-TestFailureContext.ps1:
- Compute a deterministic `gate` object with a `verdictCeiling` (the most
  favorable verdict the coverage evidence permits) plus `ceilingReasons[]`.
  Splits interesting checks into failing vs pending, maps failing checks to
  AzDO build evidence (covered / inaccessible / unmapped), and caps the
  verdict accordingly. The script never sets a green ceiling while coverage
  is incomplete.
- Capture retry signal: a failed timeline record carrying previousAttempts
  was retried by CI and still failed -> `retriedStillFailing` (persistent,
  NOT a one-off flake).
- Known-issue cross-reference: load open 'Known Build Error' issues and match
  failure messages against their ErrorMessage/ErrorPattern blocks; stamp
  `matchesKnownIssue` ({number,title,url}). Regex matches use a 250ms timeout.
- Emit a coverage ledger + ceiling section in context.md and add Retried /
  Known-issue columns to the deduplicated-failures table; add `gate` and
  `knownIssues` to context.json.

SKILL.md / copilot-review-tests.md: bind the overall verdict to
gate.verdictCeiling (never more favorable), document the new evidence
fields, and require the coverage ledger in the posted report.

maui-ci-facts.md: add the canonical 'flaky vs PR-specific' proofs (baseline
match, known-issue match, retry recovery) shared by both review lanes.

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

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

Note

🤖 This review was generated by AI — an automated multi-model code review (Claude Opus 4.8 · GPT-5.5 · Gemini 3.1 Pro), independent reviews reconciled via cross-pollination.


🔍 AI Multi-Model Code Review — Round 4

HEAD: 69825c17 · New commit since R3 LGTM: "Add deterministic merge-readiness gate to /review tests" (+315/−13)


🔴 Verdict: NEEDS CHANGES

Confidence: High — unanimous (all 3 models, independently and after cross-pollination)

Model Independent After cross-pollination
Claude Opus 4.8 NEEDS_CHANGES / High NEEDS_CHANGES / High
GPT-5.5 NEEDS_CHANGES / High NEEDS_CHANGES / High
Gemini 3.1 Pro NEEDS_CHANGES / High NEEDS_CHANGES / High

The new deterministic merge-readiness gate is a genuinely good idea and mostly well-built — but cross-pollination surfaced a reachable false-green hole in its core logic, and the PR currently can't merge. Two ❌ blockers below.


❌ Blocker 1 — Gate emits Ready to merge for an accessible failing check with zero extracted failures (false green)

The whole point of this gate is to make a green verdict trustworthy. It currently isn't, for the most common real case: a compile/build break.

Reproduction (Gather-TestFailureContext.ps1, verified by all 3 models + manual trace):

  • A required check X is failed and its AzDO build is accessible → bucketing loop (L1255–1266) hits continue (L1258); X is added to neither inaccessibleFailingChecks nor unmappedFailingChecks.
  • X yields zero extracted test failures (a compile/build/infra failure has no parseable test rows), so dedupedFailures is empty.
  • Ceiling chain: L1276 false → L1280 false → L1289 false (failingChecks.Count is not 0) → L1292 elseReady to merge.

The L1289 guard only covers failingChecks.Count -eq 0; there's no branch for "failing checks exist but contributed no extracted/explained failure."

Suggested fix: track accessible failing checks that contributed ≥1 extracted (or otherwise explained — baseline/known-issue) failure; if an accessible failing check contributed none, cap at Needs human investigation (e.g. a failingChecksWithoutExtractedFailures bucket folded into the L1280 branch), or require in the else that every failing check is accounted for before allowing Ready to merge. Details inline at L1289.

(Severity reconciliation: Opus suggestion→error, GPT warning→error, Gemini held error — converged ❌ after each model re-traced the control flow.)


❌ Blocker 2 — PR has merge conflicts with main (required checks blocked)

  • gh pr viewmergeable: CONFLICTING, mergeStateStatus: DIRTY.
  • The 4 required checks (maui-pr, maui-pr-devicetests, maui-pr-reviewer, maui-pr-uitests) are action_required"Skipped due to merge conflicts" — blocked by the conflict, not failing on code.
  • Conflicting files: .github/workflows/ci-status-main.lock.yml, ci-status-main.md, ci-status-net11.lock.yml (gh-aw frontmatter_hash/body_hash collisions — both this PR and main recompiled them).
  • Action: rebase/merge main, then re-run gh aw compile and commit the regenerated .lock.yml files so the hashes match. Required checks can't go green until this is resolved (code-review Rule #6 → cannot LGTM regardless of code quality).

🟢 What's good

  • The gate concept (a deterministic verdictCeiling the LLM can't override upward) is the right design; the Insufficient data / Needs human investigation precedence for inaccessible/pending/unmapped checks is sound.
  • matchesKnownIssue and retriedStillFailing are well-reasoned evidence signals; retriedStillFailing correctly resists "looks flaky" hand-waving.
  • Get-KnownBuildIssues fails open (empty patterns + knownIssues.queried=false) when gh is unavailable — good, and the docs tell the model not to treat a no-match as proof.
  • ErrorMessage matching has no escaping bug (literal IndexOf); ErrorPattern regex is validated at load with a substring fallback.

💡 Non-blocking

  • Known-issue generic over-match (inline @ L690): a short/generic ErrorMessage registry entry can semantically over-match unrelated failures and mask a real regression as "unrelated." Consider a min-length / word-boundary or check-area guard.
  • Still no Pester harness for this now load-bearing logic — this re-raises the open R3 suggestion (thread on Gather-TestFailureContext.ps1). Blocker 1 is Exhibit A: a single ceiling truth-table test (failingChecks ≥ 1 with dedupedFailures == 0 ⇒ expect non-green) would have caught it pre-merge. (Not reposted inline — the prior thread is still open.)

🏁 Recommendation

REQUEST_CHANGES. Fix the false-green ceiling hole (Blocker 1) and resolve the merge conflict + recompile the gh-aw locks (Blocker 2). The gate is close — it mainly needs to refuse a green verdict when an accessible failing check produced no explained failure, and a small unit test to lock that in.


Reviewed with the code-review skill: independence-first (code before narrative), full-file reading in a worktree at PR HEAD, manual control-flow verification of the disputed finding, and CI/mergeability cross-checking by all 3 models.

catch { $hit = $false }
}
else {
$hit = $Text.IndexOf([string]$p.pattern, [System.StringComparison]::OrdinalIgnoreCase) -ge 0

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.

💡 💡 Suggestion (non-blocking) — Generic/short known-issue ErrorMessage can over-match unrelated failures

Good news first: this is not an escaping bug. ErrorMessage matches via an unanchored literal String.IndexOf(..., OrdinalIgnoreCase) (regex metacharacters are treated literally), and ErrorPattern is intentional regex validated at load (Regex.IsMatch('', pattern)) with a substring fallback on invalid syntax — both are sound.

The residual risk is semantic over-match: a short or generic Known Build Error ErrorMessage (e.g. "timeout", "connection reset", "##[error]") is a substring of many unrelated failures, so a real PR-caused failure could be silently attributed as Likely unrelated and cited by issue number. Consider a minimum-length / word-boundary guard, or only treating ErrorMessage as a hit when combined with the same check/area, so a generic registry entry can't mask a genuine regression.

— Opus 4.8 / GPT-5.5 (suggestion) · Gemini 3.1 Pro (warning); reconciled non-blocking.

PureWeen and others added 3 commits June 23, 2026 12:13
A failed BUILD leg whose error is not an xUnit '[FAIL]' line (crossgen2
'Failed to load assembly', NativeAOT/ILC, the linker, or a plain MSBuild
'error CODE:') previously produced no structured failure: it left only a
raw log excerpt, so it escaped dedup, the baseline diff, and the
deterministic gate. A red build leg could therefore yield zero distinct
failures and a green verdict ceiling — the deterministic version of
mis-classifying a real PR-caused break as 'nothing unique to the PR'.

Gatherer (Gather-TestFailureContext.ps1):
- Add Get-BuildErrorsFromLog: extracts MSBuild/SDK/linker coded errors,
  crossgen/R2R/NativeAOT toolchain breaks, and a last-resort ##[error]
  marker as structured failures keyed by job name + signature, so the
  SAME break on base still matches the baseline diff while a break green
  on base is correctly attributed to the PR.
- Run it (PR side and baseline side) only when no xUnit failure was found
  for a failed Task record.
- Track 'unexplained failed legs' (failed Task whose log yielded no
  failure at all) and cap gate.verdictCeiling at 'Needs human
  investigation' when any exist — the backstop that makes a silent
  build-break-as-zero-failures impossible even for novel error shapes.

Shared facts doc (maui-ci-facts.md) — the brain both lanes reason from:
- New 'Enumerate EVERY failed leg' section: Build Analysis unmatchedFailures
  is not exhaustive; open every failed record's log including issues==0;
  not every failure is a test.
- Baseline section: add JOB-level diff; 'red on PR, green on base' is the
  strongest PR-caused signal; SDK-version diffing for flow PRs.
- Failure-patterns table: crossgen/R2R, NativeAOT/ILC, NETSDK1144 rows.

SKILL.md + workflow body + lock kept in sync (coverage line, ceiling
triggers, build-error taxonomy). Lock recompiled (body_hash re-pinned).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Shift the job-level baseline diff out of the LLM/manual layer into the
gatherer so a leg that is red on the PR but green on the same leg of the
most recent base build is COMPUTED, not judged. This is exactly the
comparison that catches build-job breaks (crossgen/R2R, NativeAOT, linker)
which carry no test name and so can never be caught by the test-level diff
-- the class of break that previously slipped through on PR #35364.

Gatherer (Gather-TestFailureContext.ps1):
- Add Get-TimelineRecordResultMap: fetch one build's timeline and build a
  normalized leg-name -> {hasFailed,hasSucceeded} map.
- Fetch the most-recent-completed base build's leg map for every PR build
  (including when base fully succeeded -- where regressions live), cached
  per base build id.
- Stamp per-failure legBaselineResult / legRegressedVsBase /
  legAlsoFailsOnBase by matching each occurrence's (buildId, recordName)
  to the base leg map by exact normalized name (absent => indeterminate,
  never a false regression). Device-test legs are surfaced but never set
  legRegressedVsBase (XHarness exit-0 blind spot).
- Add a per-failure deterministicAttribution prior (regressed-vs-base >
  pre-existing-on-base > known-issue > indeterminate) the classifier must
  start from.
- Gate: add gate.legsRegressedVsBase (+names) and a hard ceiling cap --
  any regressed-vs-base failure caps the verdict ceiling at "Not ready",
  so a green verdict is impossible in code when a leg regressed vs base.
- Surface in context.md (gate line + "Vs base leg"/"Attribution (det.)"
  columns).

Local runner (Review-Tests.ps1): add a red "Regressed N vs base" badge.

Docs: SKILL.md (new fields, taxonomy, ceiling rule), maui-ci-facts.md
(diff is now computed, not manual), workflow body; lock recompiled
(body_hash re-pinned, hash-only diff).

Validated: all three PS1 files AST-clean; Get-TimelineRecordResultMap,
normalization, the leg-diff decision, attribution precedence, and the
ceiling cap unit-tested. Not functionally run (no net/creds in session).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
An accessible failing check whose backing build produced no extractable
failure AND no unexplained-leg record (log read threw, no log id, or past
the per-build failed-record cap) previously fell through to 'Ready to
merge' -- a green verdict on a red compile/build break, the exact
crossgen/R2R class of miss this gate exists to prevent.

Add a build-level earned-green guard: a build contributes evidence if it
produced any raw failure (log or test-API) or any unexplained-leg record.
An accessible build that backs a currently-failing check yet contributed
nothing now caps the ceiling at 'Needs human investigation' via the new
gate.unaccountedFailingChecks count. Verified with a ceiling truth-table
harness (false-green capped; clean/explained PRs not over-capped).

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

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

Note

🤖 This review was generated by AI — an automated multi-model code review (Claude Opus 4.8 · GPT-5.5 · Gemini 3.1 Pro), independent reviews reconciled via cross-pollination.


🔍 AI Multi-Model Code Review — Round 5

HEAD: 248f16e3 · New commits since R4: "Catch build-job breaks (crossgen/R2R, NativeAOT)" + "Make the job-level baseline diff deterministic" (+398/−25)


🔴 Verdict: NEEDS CHANGES

Confidence: High — unanimous (all 3 models, independently and after cross-pollination)

Model Independent After cross-pollination
Claude Opus 4.8 NEEDS_CHANGES (residual = warning) NEEDS_CHANGES (conceded residual = error)
GPT-5.5 NEEDS_CHANGES (residual = error) NEEDS_CHANGES (error)
Gemini 3.1 Pro NEEDS_CHANGES (residual = error) NEEDS_CHANGES (error)

Real progress this round — but the R4 false-green is narrowed, not closed, and the PR still can't merge. Two ❌ blockers remain.


✅ What R5 fixed (good progress)

  • New build-break extractor catches [FAIL] lines and crossgen2 / ReadyToRun / NativeAOT-ILC / linker / MSBuild error shapes — the common compile-break case from R4 is now caught.
  • New $unexplainedLegs backstop: a failed Task leg with no test-shaped failure caps the ceiling at Needs human investigation instead of falling through to green.
  • New legsRegressedVsBase gate: a leg red-on-PR / green-on-base forces Not ready (forbids Ready to merge).

These are the right ideas and close most of the R4 hole.


❌ Blocker 1 (still open) — Residual false-green: an accessible red check with zero accounted-for failures still reaches the green else

The backstop enumerates break shapes instead of structurally guaranteeing "no green while an accessible failing check is unaccounted-for." $unexplainedLegs is gated type -eq 'Task' (L1149/L1154), its .Add sits inside the per-log read try after the fetch (L1155), and $logsToRead is capped at -First 12 (L1130). So three real shapes each yield dedupedFailures == 0 and unexplainedLegs == 0 on an accessible failing check (accessible=true L1070; the check continues at L1504-1505), falling through to else → "Ready to merge" (L1550):

  1. Job-level infra break (job timeout / agent-provisioning loss) where no child Task is failed (the running task is marked canceled) → skipped by the type -eq 'Task' filter. (Routine on maui-pr's long device/UI legs.)
  2. Log-read throw on a failed Task → the exception escapes before .Add to $unexplainedLegs (L1155).
  3. Failed Task beyond the -First 12 cap → never inspected by the extractor or the backstop.

Because failingChecks.Count > 0, the L1546 guard (failingChecks.Count -eq 0 -and …) is false, so execution reaches the green else. A PR whose maui-pr legs died on a job timeout could still be reported Ready to merge. (Detail + the fix posted as a reply on the open R4 false-green thread.)

Robust fix (structural, not shape-enumeration): make the ceiling enforce that any accessible failing check with zero accounted-for failures caps at ≥ Needs human investigation — e.g. count accessibleFailingChecks that contributed 0 dedupedFailures/unexplainedLegs and fold that count into the L1539-style branch, rather than relying on the extractor to recognize every break shape. (Severity reconciled to ❌ error unanimously — Opus re-traced and conceded warning→error.)


❌ Blocker 2 (still open) — Merge conflict blocks required checks

  • mergeable: CONFLICTING, mergeStateStatus: DIRTY (unchanged from R4).
  • maui-pr, maui-pr-devicetests, maui-pr-reviewer, maui-pr-uitestsaction_required "Skipped due to merge conflicts" — they did not run (gated on the unbuildable merge commit), not code failures.
  • Action: merge/rebase main, then re-run gh aw compile and commit the regenerated ci-status-*.lock.yml so the hashes match and the maui-pr* checks execute. Per code-review Rule #6, required checks blocked by an unresolved conflict cap the verdict below LGTM regardless of code quality.

💡 Non-blocking

  • legsRegressedVsBase retry-masking (inline @ L1447): a base leg that failed-then-passed-on-retry is read as failed-on-base, suppressing a real regression signal. Conservative (can't cause a false-green), but weakens the new gate. Consider treating base failed-then-recovered as green for the comparison.
  • Still no Pester harness for this now +500-line load-bearing gate. Blocker 1 is the second missing-unit-test escape in two rounds; a tiny truth-table test (failing check + 0 accounted-for ⇒ expect non-green; job-level / thrown-log / >12-cap cases) would lock it down. (Reinforces the open R3 thread — not reposted inline.)

🏁 Recommendation

REQUEST_CHANGES. Close the residual false-green structurally (don't enumerate break shapes — guarantee no green while an accessible failing check is unaccounted-for), and resolve the merge conflict + recompile the gh-aw locks so the required checks actually run. The gate is genuinely close; it mainly needs the structural guarantee and a unit test to prove it.


Reviewed with the code-review skill: independence-first, full-file reading in a worktree at PR HEAD, manual control-flow re-tracing of the disputed finding (Opus conceded warning→error after re-trace), and CI/mergeability cross-checking by all 3 models.

continue
}
$baseRec = $baseInfo.records[$norm]
if ($baseRec.hasFailed) {

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.

💡 💡 Suggestion (non-blocking) — legsRegressedVsBase: a base leg that failed-then-passed-on-retry is read as failed-on-base, masking a real regression

Here hasFailed is checked before hasSucceeded (L1447 vs L1452), so a base-branch leg that failed on attempt 1 but passed on retry is stamped failed-on-base (pre-existing). That suppresses the regressed-vs-base signal for the same leg when it's red on the PR.

This is a conservative false-negative — it can only ever fail to lower the ceiling, never raise it, so it can't cause a false-green. But it weakens the new deterministic-regression gate exactly when it'd be most useful. Consider treating a base leg that failed-then-recovered as green for the regression comparison (or surface it as Needs human investigation rather than silently pre-existing).

— Claude Opus 4.8 (cross-pollination); GPT-5.5 / Gemini 3.1 Pro concur non-blocking.

@kubaflo

kubaflo commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Note

🤖 AI-generated follow-up (multi-model review automation) — timing correction.

Heads-up on a race: my Round 5 REQUEST_CHANGES above was conducted against 248f16e3; while it was posting, you pushed a1efd049 "Close false-green ceiling hole for unaccounted failing checks." So the review landed on a1efd049, but its Blocker 1 section describes the pre-a1efd049 state. Correcting the record:

✅ Blocker 1 (residual false-green) — RESOLVED by a1efd049

That commit implements exactly the structural guard the review recommended, at build granularity:

  • new $unaccountedFailingChecks — an accessible build that backs a currently-failing check but contributed zero extracted failures and zero unexplained-leg records;
  • folded into the Needs human investigation branch (-or $unaccountedFailingChecks.Count -gt 0).

I traced it against the three escape paths from the review and it closes all of them — Job-level infra break (timeout/agent loss, no failed child Task), log-read throw, and failed record past the per-build cap — because each leaves an accessible failing check whose build contributed nothing, which the new guard now catches regardless of break shape. This is the "don't enumerate break shapes; guarantee no-green-while-unaccounted-red" property. 👍 Resolving the false-green thread.

❌ Blocker 2 (merge conflict) — STILL the remaining gate

At a1efd049 the PR is still mergeable: CONFLICTING / mergeStateStatus: DIRTY, and maui-pr / maui-pr-devicetests / maui-pr-reviewer / maui-pr-uitests are still action_required "Skipped due to merge conflicts" — they haven't run. Rebase/merge main, then re-run gh aw compile and commit the regenerated ci-status-*.lock.yml so the required checks execute.

Net: the gate logic is now in good shape; the only thing keeping this at REQUEST_CHANGES is the unresolved merge conflict (required checks can't go green until it's fixed). The non-blocking items still stand: the legsRegressedVsBase retry-masking nit (inline) and the missing Pester harness (a truth-table test for the new $unaccountedFailingChecks guard would lock in exactly this fix).

Full multi-model re-verification of a1efd049 will run on the next change.

Adversarial multi-model review of the deterministic merge-readiness gatherer
surfaced five ways an unsafe verdict could leak through. All are closed in
Gather-TestFailureContext.ps1 so a green verdict stays provably earned:

- Uninspected legs (A): a failed Task leg with no log id, past the 12-read cap,
  or whose log read threw produced no failure AND no unexplained-leg record, yet
  the build stayed 'accounted' via another explained leg -> false green. A
  post-loop sweep now records every uninspected failed Task leg as an unexplained
  leg, reusing the existing ceiling cap.
- Unattributed failures (B): a failure the deterministic prior could attribute
  neither way (base ambiguous/missing, device-test outside the build-error class)
  reached the green 'else'. New gate.unattributedFailures caps the ceiling at
  'Needs human investigation'.
- StatusContext checks (C): the gate read only status/conclusion, so a red classic
  commit-status (state=FAILURE, status/conclusion null) was invisible. Projection,
  interesting-check filter, and the pending/failing split now route by state too.
- Baseline leaf-name collision (D): a generic leg name ('Build') that both failed
  and succeeded on base was force-marked pre-existing. It now resolves to
  'inconclusive-on-base' -> indeterminate (capped by B), preventing both a false
  pre-existing subtraction and a false regression.
- Device-test build breaks (Ge1): a deterministic device-test BUILD break
  (source=azdo-build-error) was wrongly suppressed from the regression cap with the
  XHarness exit-0 test blind spot. Only device-test TEST results are now suppressed.

Docs synced (SKILL.md, copilot-review-tests.md, maui-ci-facts.md), local runner
surfaces an Unattributed badge, lock recompiled (body_hash-only). Validated:
both PS1 AST-clean; ceiling + split + leg-diff truth tables green.

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

Copy link
Copy Markdown
Member Author

Adversarial review round — 5 false-green / precision holes closed (fd25781)

Ran a 3-model adversarial pass (three independent reviewers, adversarial consensus) over the gatherer with one focus: can a real PR-caused failure ever reach a green verdict? Five vectors were confirmed against the code and fixed. All land in Gather-TestFailureContext.ps1.

# Hole Consensus Fix
A A failed Task leg with no log id, past the 12-read cap, or whose log read threw produced no failure and no unexplained-leg record — yet the build stayed "accounted" via another explained leg → false green 3/3 Post-loop sweep records every uninspected failed Task leg as an unexplained leg, reusing the existing ceiling cap
B A failure the deterministic prior could attribute neither way (indeterminate) fell through to the green else 2/3 New gate.unattributedFailures caps the ceiling at Needs human investigation
C The gate read only status/conclusion, so a red classic StatusContext (state=FAILURE, status/conclusion null — e.g. an AzDO-posted commit status) was invisible 1/3, verified on this PR's own skill-validation status Projection + interesting-check filter + pending/failing split now route by state too
D A generic leg name (Build) that both failed and succeeded on base was force-marked pre-existing (masking a real regression) 1/3 Resolves to inconclusive-on-baseindeterminate (capped by B); prevents both a false pre-existing subtraction and a false regression
Ge1 A deterministic device-test BUILD break (source=azdo-build-error) was suppressed from the regression cap alongside the XHarness exit-0 test blind spot 1/3 Only device-test TEST results are suppressed now; build breaks regress

Already-addressed prior findings (referenced, not re-posted):

  • The line-1654 ❌ "ceiling can emit Ready to merge for an accessible build that yielded nothing" is closed by the earned-green guard (gate.unaccountedFailingChecks) in a1efd04952.
  • The line-1447 💡 "a base leg that failed-and-succeeded" suggestion is Fix D above — thanks, that was a real hole.

Intentionally declined (documented as conservative-by-intent):

  • Making the regression cap depend on matchesKnownIssue (so a known-flaky regression could be downgraded) is not done. The north star is over-blocking beats false-green, and the still-open line-819 known-issue over-match (generic/short ErrorMessage via unanchored IndexOf) makes that downgrade risky. The cap stays conservative.

Validation: both PS1 AST-clean; ceiling + check-split + leg-diff truth tables green (15 + 10 assertions); lock recompiled (body_hash-only). No APPROVE/REQUEST_CHANGES — comment only.

Second adversarial-review round (3 independent reviewers) on the deterministic
gate in Gather-TestFailureContext.ps1. Each finding was verified against the code
before fixing (deterministic facts over votes):

- Ge1 no-op (build-break regression): the device-test build-break exception read
  `source` from the deduped representative (which only carries `sources` plural),
  so it was always empty and a device-pipeline crossgen/R2R break never regressed.
  Read `source` from the raw occurrence (`$occ`) instead.
- Attribution over-dismissal (false green): a regression signal that conflicts with
  a base-failure signal, and a leg-level base failure with no exact test+platform
  match, were both labeled `pre-existing-on-base` and dismissed. Only an exact
  test+platform base match now dismisses; conflicts and leg-only matches fall to
  `indeterminate` (capped at Needs human investigation).
- Uncorroborated known-issue dismissal (false green): a `Known Build Error` text
  match dismissed a red check to green with zero baseline corroboration, so a broad
  matcher could shadow a real PR break. Known-issue is now dismissable only when the
  leg was actually compared on base; otherwise it stays `indeterminate`.
- Unreadable timeline masked by a sibling source (false green): `accessible` reflects
  build metadata, but the timeline fetch is independent. A build whose timeline was
  unreadable but which a sibling test-API call stamped "contributing" skipped the
  earned-green guard. Added a `timelineReadable` flag; the guard only treats a
  contributing build as accounted when its timeline was readable.
- Softened regression (lost signal): a proven regression coexisting with NHI reasons
  reported as Needs human investigation instead of Not ready. The regression cap now
  also promotes NHI -> Not ready (still non-green, so never a false green).
- StatusContext projection (false red + unreadable): a classic StatusContext carries
  context/targetUrl, not name/detailsUrl, so a failing one landed in
  unmappedFailingChecks with a blank name. Projection now falls back to
  context/targetUrl so it is named and build-resolvable.
- Non-Task leaf leg sweep (narrow false green): a failed Job/Phase leg with no failed
  Task descendant (agent lost, timeout, cancel) was skipped by the Task-only sweep.
  Now also surfaced as an unexplained leg.
- Pending/failing split is a strict catch-all: any interesting, non-pending check is
  treated as failing so an unrecognized status/state shape can never escape both
  buckets as a false green.

Docs synced (SKILL.md, maui-ci-facts.md, workflow body + recompiled lock, body_hash
re-pinned only). Validated: both PS1 AST-clean; a 31-assertion truth-table harness
covers attribution precedence, device build-break source, the pending/failing
catch-all, and the ceiling + regression cap.

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

Copy link
Copy Markdown
Member Author

Adversarial review round — 7 residual gate holes closed (92c1840)

Ran another 3-reviewer adversarial pass over the deterministic gate. Every finding was verified against the code before fixing (deterministic facts over votes). All seven land in Gather-TestFailureContext.ps1; four were genuine false-green vectors:

# Hole Class Fix
1 Device build-break regression read source from the deduped representative (only has sources plural) → always empty → a device-pipeline crossgen/R2R break never regressed false green (silent no-op) read source from the raw occurrence
2 A regression signal conflicting with a base-failure signal, and a leg-only base match (no exact test+platform match), were dismissed as pre-existing-on-base false green only an exact test+platform base match dismisses; conflicts + leg-only → indeterminate (→ NHI)
3 A Known Build Error text match dismissed a red check to green with zero baseline corroboration — a broad matcher could shadow a real PR break false green known-issue is dismissable only when the leg was actually compared on base; else indeterminate
4 An unreadable timeline (independent of accessible, which is build-metadata only) masked by a sibling test-API "contribution" skipped the earned-green guard false green added a timelineReadable flag; the guard only counts a contributing build as accounted when its timeline was readable
5 A proven regression coexisting with NHI reasons reported as Needs human investigation instead of Not ready softened signal regression cap now promotes NHI → Not ready (still non-green)
6 A failing StatusContext carries context/targetUrl, not name/detailsUrl → landed in unmappedFailingChecks with a blank name false red + unreadable projection falls back to context/targetUrl so it is named and build-resolvable
7 A failed non-Task leaf leg (agent lost / timeout / cancel before any Task fails) was skipped by the Task-only sweep narrow false green also surfaced as an unexplained leg

Plus a hardening: the pending/failing split is now a strict catch-all — any interesting, non-pending check is treated as failing, so an unrecognized status/state shape can never escape both buckets.

Fix #3 also addresses the still-open ⚠️ on the known-issue matcher (line ~819): an over-broad matcher can no longer dismiss a red check on text alone — a base comparison is now required before a known-issue match counts as "pre-existing".

Validation: both PS1 files AST-clean; a 31-assertion truth-table harness covers the attribution precedence, the device build-break source, the pending/failing catch-all, and the ceiling + regression cap. Docs synced (SKILL.md, maui-ci-facts.md, workflow body + recompiled lock — body_hash re-pinned only). Continuing the adversarial loop until a round yields nothing.

Round 3 of the adversarial review loop. Three independent reviewers converged
on the same core false-green: canceled/timed-out legs escaping the gate.

1. Sweep broadening (leg-level). The timeline capture filter admits a record
   two ways -- result=='failed' OR it carries a type=error issue (a canceled/
   abandoned/timed-out/succeededWithIssues leg that still logged an error) --
   but both post-loop sweeps re-narrowed to result=='failed', so those
   error-carrying legs were never read, never swept, never capped. A sibling-
   explained build then slipped green. Drop the re-narrowing so every
   $failedRecords member becomes an unexplained leg when unresolved. Benign
   cascade-cancels carry no error issue, so they are not captured and add no
   false-red noise.

2. Aborted-check cap (check-level defense-in-depth). A failing check whose
   GitHub conclusion did not finish cleanly (CANCELLED/TIMED_OUT/
   STARTUP_FAILURE/STALE/ACTION_REQUIRED) now caps the ceiling at
   'Needs human investigation' via gate.abortedFailingChecks. This closes the
   residual the sweep cannot see: a cleanly-canceled leg with NO error issue
   (e.g. a PR-induced hang) masked green by a dismissible sibling on the same
   build.

3. Known-issue corroboration tightened. A known-issue text match now dismisses
   to 'known-issue' only when the leg was itself RED on base
   (legBaselineResult == 'failed-on-base'). A 'succeeded-on-base' read is the
   opposite of corroboration; the only way to reach that branch with
   succeeded-on-base is a device-test TEST failure whose regression signal was
   suppressed (XHarness exit-0 blind spot) -- dismissing it on a mere text
   match would launder a suppressed regression into a false green. It now falls
   to 'indeterminate' (NHI).

Docs (SKILL.md, maui-ci-facts.md, copilot-review-tests.md) updated; gh-aw lock
recompiled (body_hash only, frontmatter unchanged). Truth-table harness
extended to 38 assertions (all pass).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Round-6 adversarial review (three independent reviewers) surfaced four
attribution holes in Gather-TestFailureContext.ps1. Each fix is
safe-by-construction (can only add 'Needs human investigation', never a
false green):

- Fatal non-coded breaks (3/3): a NEW native crash / segfault / OOM /
  test-host crash / unhandled exception in a leg that ALSO has a
  dismissable test failure had no coded signature, so it was suppressed as
  a fallback rollup and the dismissed test accounted for the build.
  Get-BuildErrorSignature now recognizes these (exit-code signals
  132-143/159, SIGSEGV/SIGABRT/core-dumped, OOM, test-host crash,
  anchored 'Unhandled exception.') and routes them through the
  always-extract + baseline-matched path. The ordinary 'exit code 1'
  test-runner rollup is deliberately excluded.

- Wrapper-exception reason collision (Gemini F2): Get-FailureReasonSignature
  took the FIRST *Exception match, so a PR NRE and a base Timeout both
  wrapped in AggregateException collapsed to one token and slipped past the
  reason-conflict veto. It now unwraps Aggregate/TargetInvocation/
  TypeInitialization to the inner cause.

- Unknown-reason same-test collision (GPT F2): the reason veto only fired
  when both reasons were positively known. A same-test red-on-both pair
  with unrecognized messages was dismissed as pre-existing. Added a
  normalized message-fingerprint fallback (test failures only) that flags
  a conflict when the PR message is structurally absent from base.

- Cross-pipeline baseline laundering (GPT F1): baseline keys from all
  pipeline definitions were merged into one definition-blind set, so a PR
  failure in pipeline A could be dismissed by a same-key base failure that
  only ever occurred in pipeline B. Baseline matching is now scoped to a
  shared pipeline definition when both sides carry a tag (falls back to
  definition-blind when either is untagged, so no legitimate dismissal is
  lost).

Docs synced (SKILL.md, maui-ci-facts.md, workflow body); lock recompiled
(body_hash-only diff). Validated: gatherer AST-clean; 122/122 harness.

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

Copy link
Copy Markdown
Member Author

Adversarial review — round 6

Ran another loop of three independent reviewers against the latest HEAD. They surfaced four attribution holes in the gate. All four are now fixed (commit 512a2acbf8); each is safe-by-construction — it can only raise the ceiling toward Needs human investigation, never relax it toward a false green.

# Consensus Hole Fix
A 3/3 A new native crash / segfault / OOM / test-host crash / unhandled exception sharing a leg with a dismissable test failure had no coded signature → it was dropped as a fallback rollup and the dismissed test accounted for the whole build (false green). Get-BuildErrorSignature now recognizes fatal non-coded breaks (exit-code signals 132–143/159, SIGSEGV/SIGABRT/core dumped, OOM, test-host crash, anchored Unhandled exception.) and routes them through the always-extract + baseline-matched path. The ordinary exit code 1 test-runner rollup is deliberately excluded (so normal test noise still suppresses). A pre-existing crash still baseline-matches and is dismissed (no false red); a new one surfaces/caps.
B 1/3 Wrapper-exception collision: Get-FailureReasonSignature took the first *Exception, so a PR NullReferenceException and a base TimeoutException both wrapped in AggregateException collapsed to one token → slipped past the reason-conflict veto. Unwrap Aggregate/TargetInvocation/TypeInitializationException to the inner cause before signing.
C 1/3 Unknown-reason collision: the reason veto only fired when both reasons were positively known; a same-test red-on-both pair with unrecognized messages was dismissed as pre-existing. Added a normalized message-fingerprint fallback (test failures only) that flags a conflict when the PR's message text is structurally absent from base for that test.
D 1/3 Cross-pipeline laundering: baseline keys from all pipeline definitions were merged into one definition-blind set, so a PR failure in pipeline A could be dismissed by a same-key base failure that only ever occurred in pipeline B. Scope baseline matching to a shared pipeline definition when both sides are tagged (falls back to definition-blind when either is untagged, so no legitimate dismissal is lost).

The three 1/3 findings (B/C/D) were verified directly against the live code and each only adds vetoes/caps, so they were applied without a dispute round.

Assessed and intentionally not changed:

  • Canceled leaf task with no error issue (1/3, medium): by AzDO semantics that's a cascade victim, not a root cause — the triggering timeout/failure carries its own error issue and is already swept, and the root cause is classified. A broad cap here would fire on nearly every red build (every parallel cascade-cancel), which would make the tool over-block to uselessness. The realistic PR-induced-hang variants (task timeout → failed+issue; job timeout → job infra-error) are already caught.
  • Two low-severity contrived shapes (a single check name backed by two builds where one is inaccessible; a Helix fail count hidden under a non-fail field) — both require combinations the existing inspection paths don't admit.

Docs synced (skill contract, shared CI facts, workflow body); lock recompiled (body-hash-only). Gatherer AST-clean; 122/122 deterministic harness assertions pass.

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

Note

🤖 This review was generated by AI — an automated multi-model code review (Claude Opus 4.8 · GPT-5.5 · Gemini 3.1 Pro), independent reviews reconciled via cross-pollination.


🔍 AI Multi-Model Code Review — Round 7

Panel HEAD: 2b9594e6 · Since the last panel (fd25781a): 5 commits, +720/−108 — a large false-green hardening pass + removal of the stray test.ps1.

⏱️ Note: while this panel ran, the head advanced to 512a2acb ("close four round-6 false-green paths"). I re-verified both findings against 512a2acb: the fingerprint digit-collapse is unchanged (still L490 — and the new 512a2acb message-fingerprint fallback reuses the same normalization, so it doesn't close the hole), and the device-test confirmedZero logic is unchanged (now ~L1573). The merge conflict is also still present. So this review applies to the current head.


🔴 Verdict: NEEDS CHANGES

Confidence: High — unanimous (all 3 models, independently and after cross-pollination)

This round closed ~16 false-green/precision holes and even cites prior panel findings by name in the comments — genuinely strong work. The panel still found one new reachable false-green introduced by the new fingerprint logic, plus a device-test edge, and the PR still can't merge.


❌ Blocker (still open, 4th round) — Merge conflict with main

The sole thing blocking merge, unchanged since R4:

  • mergeable: CONFLICTING, mergeStateStatus: DIRTY; maui-pr / -devicetests / -reviewer / -uitests all action_required "Skipped due to merge conflicts" — they have not run.
  • Action: merge/rebase main, then gh aw compile and commit the regenerated ci-status-*.lock.yml. Per Rule #6, required checks blocked by the conflict cap the verdict below LGTM regardless of code quality.

❌ New reachable false-green — Get-ErrorFingerprint digit-collapse (inline @ L490)

[regex]::Replace($t, '\d+', '#') collapses every digit run — including digits inside symbol identifiers — so CS0246: type 'Issue1234' and CS0246: type 'Issue5678' produce the same fingerprint. A PR-new build break then fingerprint-matches a different base break, gets alsoFailsOnBaseline = true, and is laundered as pre-existing. The panel traced the full green path: no other ceiling term catches the single colliding leg, so the verdict reaches Ready to merge with maui-pr RED for a real new compile break. This defeats the function's own docstring ("distinguishes two DIFFERENT breaks that share an error code").

  • Severity: ❌ error (Opus + Gemini error; GPT warning — majority error). Narrow (collision on both sides, same record+code, identical modulo digits) and strictly better than R6, but a true "dismisses the only evidence" false-green.
  • Fix: collapse only standalone integers — (?<![A-Za-z])\d+(?![A-Za-z]) — so counts/durations normalize but identifier digits stay distinct. (Details inline.)

⚠️ Device-test confirmedZero on partial Helix data (~L1573 at head 512a2acb)

If one maui-pr-devicetests Helix job's …/aggregated fetch throws (contributing nothing to $anyHelixFail/$anyHelixCount) while a sibling reported Failed == 0, deviceTestFailedConfirmedZero is set true and the green check is trusted though the thrown job is unknown. The token-less GitHub Actions path skips the test-API confirmation, so nothing compensates → a failure on the unreadable job can be missed. Unanimous ⚠️ warning. Fix: gate confirmedZero on -not $anyHelixReadError, or treat a thrown aggregated fetch as an unexplained leg. (Details inline.)


💡 Non-blocking

  • Get-FailCountsFromObject 5000-node guard — the recursion cap could skip a deeply-nested Failed > 0, yielding a wrong confirmedZero. Narrow; worth a depth/teardown note. (Opus suggestion.)
  • Pester harness still missingtest.ps1 was a scratch file (correctly removed), not a real harness. The gate is now ~+1300 lines of edge-case logic over 7 rounds; the fingerprint digit-collapse is exactly the kind of bug a truth-table test (Issue1234 vs Issue5678 must not collide) would catch. Reinforces the open R3 thread — not reposted inline.

🏁 Recommendation

REQUEST_CHANGES, but the gate is very close:

  1. Resolve the merge conflict + recompile the gh-aw locks (the only actual merge blocker — 4 rounds running).
  2. Tighten the fingerprint regex to standalone integers so it stops laundering numerically-suffixed symbols.
  3. (Recommended) gate device-test confirmedZero on no-Helix-read-error, and finally add the Pester truth-table harness.

The hardening this round is impressive and has absorbed every prior finding — this is "fix one over-broad regex + rebase," not a redesign.


Reviewed with the code-review skill: independence-first, full-file reading in a worktree at PR HEAD, full green-path control-flow trace of the disputed fingerprint finding (Opus conceded suggestion→error with a unit-test repro), and CI/mergeability cross-checking by all 3 models.

Comment thread .github/skills/review-test-failures/scripts/Gather-TestFailureContext.ps1 Outdated
Round-7 adversarial review (three independent reviewers) surfaced six more
edge cases where the merge-readiness gate could emit a false green. All fixes
are safe-by-construction: each can only ADD over-blocking (Needs human
investigation), never remove a real block.

A. Get-ErrorFingerprint over-collapsed build errors. Identifier-internal digits
   were collapsed (Handler1 == Handler2) and a hard 120-char truncation dropped
   differentiating suffixes, so a PR-new break could fingerprint-match a base
   break of the same code. Now: lookaround digit regex (only standalone counts
   collapse) + a hash of the full normalized text appended past 120 chars.

B. helix-aggregated and azdo-test-results failure occurrences were untagged, so
   the round-6 definition-scoping fell back to definition-blind and could launder
   a failure across pipelines. Both now carry buildDefinition.

C. A multi-inner AggregateException picked only the first non-wrapper exception,
   so a PR-introduced inner could hide behind a base-matching first inner.
   Get-FailureReasonSignature now emits a sorted compound token of all inners.

D. Device-test green was confirmed from an unpaginated _apis/test/runs read that
   summed only the first ~100-run page; a failing run in the tail read clean.
   New Get-AzDoTestRuns pages the continuation token to completion and refuses
   positive confirmation when the run set is truncated.

E. A swallowed Helix aggregate read (one job threw, another read clean) could
   still set deviceTestFailedConfirmedZero. Positive confirmation now requires
   every discovered job to be read without error.

F. A dismissible test failure with an empty errorMessage produced no reason and
   no fingerprint, so the name-only key dismissed it as pre-existing. Such a
   zero-corroboration match is now forced to indeterminate.

Harness extended to 153 assertions (all green). Docs (SKILL.md, maui-ci-facts.md,
copilot-review-tests.md) synced; gh-aw lock recompiled (body_hash only).

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

Copy link
Copy Markdown
Member Author

Round 7 — adversarial review (three independent reviewers)

Three independent reviewers (different model families) re-reviewed the gate. Six findings were real and have been pushed as e5a7c61120; one was assessed and intentionally left as-is. Every fix is safe-by-construction — each can only add over-blocking (Needs human investigation), never remove a real block, so none can introduce a false green.

# Reviewers Finding Fix
A 2/3 Get-ErrorFingerprint over-collapsed build errors: identifier-internal digits collapsed (Handler1 == Handler2) and a hard 120-char truncation dropped differentiating suffixes, so a PR-new break could fingerprint-match a base break of the same code. Lookaround digit regex (only standalone counts collapse) + append a hash of the full normalized text past 120 chars.
D 2/3 Device-test green was confirmed from an unpaginated _apis/test/runs read summing only the first ~100-run page — a failing run in the tail read clean. (Retried device builds publish a new run per attempt, so >1 page is realistic.) New Get-AzDoTestRuns pages the continuation token to completion; refuses positive confirmation when the run set is truncated.
B 1/3 helix-aggregated and azdo-test-results occurrences were untagged, so round-6 definition-scoping fell back to definition-blind → cross-pipeline laundering. Both occurrences now carry buildDefinition.
C 1/3 A multi-inner AggregateException picked only the first non-wrapper exception, so a PR-introduced inner could hide behind a base-matching first inner. Get-FailureReasonSignature now emits a sorted compound token of all inner exceptions.
E 1/3 A swallowed Helix aggregate read (one job threw, another read clean) could still set deviceTestFailedConfirmedZero. Positive confirmation now requires every discovered job to be read without error.
F 1/3 A dismissible test failure with an empty errorMessage produced no reason and no fingerprint, so the name-only key dismissed it as pre-existing-on-base. A zero-corroboration match (no reason and no message) is now forced to indeterminate.

Assessed, not fixed (G, 1/3): the assert:expected-actual reason token is coarse and numeric actuals collapse in the fingerprint fallback. Left as-is deliberately — the test is already red-on-base (dismissible by core philosophy), and flagging every numeric-actual delta would over-fire on flaky numeric/timing asserts (useless-tool risk). Non-numeric actuals already conflict correctly via the fingerprint.

The deterministic truth-table harness was extended to 153 assertions (all green), and the three docs (SKILL.md, maui-ci-facts.md, copilot-review-tests.md) were synced.

After seven rounds across three model families, the gate now closes every concrete false-green path surfaced. I'm stopping the adversarial loop here — remaining findings are in clear diminishing-returns territory.

Both were found by running the gatherer against a live PR (#34758):

1. Crash: the @() array-subexpression operator on a List[object] throws
   ArgumentException ("Argument types do not match") from PowerShell's
   PSToObjectArrayBinder/MaybeDebase for certain element shapes, aborting the
   run under $ErrorActionPreference=Stop so NO gate is produced at all. The
   three gate List materializations (pending/failing checks, unexplained legs)
   now use .ToArray() -- a direct CLR call that bypasses the dynamic binder --
   instead of @().

2. Cross-build contamination / latent false-green vector: the _apis/test/runs
   list endpoint SILENTLY IGNORES the buildIds filter and returns project-wide
   runs from the beginning of time (a maui build's query returned 2022-era
   Roslyn/runtime crossgen runs with build.id 602). Those phantom runs report
   zero failures, so their failedTests sum to 0 and could falsely confirm a
   clean device-test build (deviceTestFailedConfirmedZero) over the REAL build
   that failed. Scope the query by buildUri=vstfs:///Build/Build/<id> (honored
   server-side) and drop any run carrying an explicit mismatched build id
   (defense in depth).

Verified end-to-end against PR #34758: the gatherer now completes and the
test-run set contains only that build's runs (0 cross-repo contaminants,
was 20). Harness extended to 167 assertions (PROD-1/PROD-2), all green.

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

Copy link
Copy Markdown
Member Author

Two production bugs found by running the gatherer against a live PR

Stress-testing the deterministic gatherer against a real PR (#34758, anonymous/production path) surfaced two genuine reliability bugs that the adversarial loop could not — both are now fixed in 9c4679d0b1.

# Bug Impact Fix
PROD-1 @() array-subexpression on a List[object] throws ArgumentException ("Argument types do not match") from PowerShell's PSToObjectArrayBinder/MaybeDebase for certain element shapes Crash under $ErrorActionPreference=Stop → the gatherer aborts and produces no gate at all (the workflow loses its deterministic floor) The three gate List materializations (pendingChecks, failingChecks, unexplainedLegs) now use .ToArray() — a direct CLR call that bypasses the dynamic binder — instead of @()
PROD-2 _apis/test/runs silently ignores the buildIds filter and returns project-wide runs from the beginning of time (a maui build's query returned 2022-era Roslyn/runtime crossgen runs, build.id 602) Latent false-green vector: phantom runs report zero failures, so their failedTests sum to 0 and could falsely set deviceTestFailedConfirmedZero over the real failing device-test build. Also pollutes every authenticated report with cross-repo noise Scope by buildUri=vstfs:///Build/Build/<id> (honored server-side) + drop any run carrying an explicit mismatched build.id (defense in depth)

Verified end-to-end against #34758: the gatherer now runs to completion and the test-run set contains only that build's runs — 0 cross-repo contaminants (was 20). PROD-2 is dormant in today's anonymous production path (the authenticated test-API block is skipped without a token), but it is a real false-green landmine the moment an AZDO_TOKEN is ever supplied to the runner, so it is fixed now.

Deterministic harness extended to 167 assertions (PROD-1/PROD-2 coverage), all green.

PureWeen and others added 2 commits June 23, 2026 19:02
Validating /review tests on open PR #36072 surfaced a false-positive
deterministic regression: the Android SDK 'platform-tools;35.0.2'
provisioning flake ('Failed to find package') was labeled
regressed-vs-base, inflating the regression count 9->7 and over-claiming
PR causation on a purely environmental failure.

Root cause: legRegressedVsBase latched across occurrences. The same
failure sprayed onto two legs of the PR build -- one flaky on base
(failed attempt 1, passed on retry => inconclusive-on-base) and one
cleanly green on base. The clean-green leg latched legRegressed=true
while the inconclusive-conflict signal from the other leg was dropped.

Two layered guards, both FALSE-RED reductions only (a demoted failure
falls to 'indeterminate'/NHI and can never become a false green):
 1. Provisioning/infra signature guard: failures matching Android SDK
    package-fetch / avdmanager / disk-full signatures are environmental
    and nondeterministic, so they never set legRegressed.
 2. Cross-leg conflict veto: if a failure regressed cleanly in one leg
    but was flaky (inconclusive) on base in another leg, suppress the
    clean-regression claim.

Verified on #36072: 9->7 regressions (2 platform-tools demoted to
indeterminate), verdict correctly stays 'Not ready' on the 7 genuine
green-on-base/red-on-PR UI-test legs. Harness 167->178 assertions, green.
SKILL.md attribution contract updated (inconclusive-on-base + the
computed infra/flaky exclusions).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…es (demote+enrich only)

The deterministic leg diff compares each PR leg to the ONE most recent
completed base build, so a test that is flaky/regressed on the base
BRANCH but happened to be green on that single build shows up as a false
'regressed-vs-base'. This is the single-base-build limitation called out
when validating /review tests on open PRs.

The repo already maintains a richer signal: the open '[ci-scan]' issues
produced by the CI Failure Scanner (ci-status-* agentic workflow), which
track recurring flakes, regressions, and build breaks on the main /
net11.0 base branches across MANY builds, with explicit branch, affected
legs, occurrence counts, and error signatures. The gatherer did not
consult them.

Wire ci-scan in as a DEMOTE + ENRICH signal only (never dismiss-to-green):
 - Get-CiScanIssues parses each open ci-scan issue into a matcher: branch
   family, class (recurring/regression/build-break), the set of test-name
   tokens it documents, affected-leg tokens, occurrence text, and a
   legScoped flag (whole-leg instability vs a single named test).
 - Test-CiScanMatch matches a failure by exact leaf test-name (precise) or,
   for legScoped issues only (OneTimeSetUp/mass/env/build-break), by
   affected-leg overlap. Branch family must match -- a main PR is never
   demoted by a net11.0 issue, and vice versa.
 - When the leg diff computed a clean single-base 'regressed-vs-base' AND
   ci-scan documents that exact test/leg failing on the base branch, the
   regression is demoted to 'indeterminate' (NHI) with ciScanDemoted=true.

This is a false-RED reduction ONLY: a demoted failure still forbids a green
verdict (ceiling stays >= NHI), so a ci-scan hit -- an LLM-generated,
possibly-stale hint -- can never turn a red check green; it can only move an
over-confident 'Not ready' down to 'needs a human'. The legScoped gate stops
a single-test issue's incidental leg from over-demoting unrelated tests on a
busy leg.

Validated live on #36072: 4 Android API-30 regressions on the OneTimeSetUp-
flaky leg (documented by #35958, 2+/10 builds on main) demoted to NHI; the
3 genuine off-leg regressions (DragAndDrop/Alerts/AcceptedOperationNone...)
preserved; verdict correctly stays 'Not ready'. Unit harness: 23 matcher
assertions (branch gate, exact-token, legScoped) + 13 in the main harness
(now 191), all green. SKILL.md contract updated. Gatherer + SKILL.md are
referenced by path (not inlined in the lock), so no workflow recompile.

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

kubaflo commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Note

🤖 AI-generated status note (multi-model review automation).

Round 8 (ed1499fa) — both Round-7 findings are now fixed, so I'm posting a short status note rather than re-running a full panel:

  • Fingerprint digit-collapse → now collapses only standalone integers ((?<![A-Za-z])\d+(?![A-Za-z])). Numerically-suffixed symbols no longer collide; the resolved inline thread has the details.
  • Device-test confirmedZero → now guarded by -not $anyHelixReadError, so a thrown Helix aggregated-fetch no longer lets a partial green be trusted.
  • ➕ Also in this push: "close six round-7 false-green paths", two production-reliability bug fixes, infra/flaky-leg regressed-vs-base suppression, and a ci-scan registry to demote single-base flakes. The gate continues to tighten nicely.

❌ Still the one blocker — merge conflict with main (now 5 rounds)

mergeable: CONFLICTING / mergeStateStatus: DIRTY; maui-pr / -devicetests / -reviewer / -uitests remain action_required "Skipped due to merge conflicts" — they still haven't run. This is the only thing keeping the PR from a green review. Please merge/rebase main, then gh aw compile and commit the regenerated ci-status-*.lock.yml so the required checks execute. Once they run green, the gate code itself is in good shape and I'll do a full green-path re-verification.

(The +1000-line gate is still without a Pester harness — the round-7 fingerprint bug is exactly what a truth-table test would have caught; strongly worth adding alongside the rebase.)

…-merge-readiness

# Conflicts:
#	.github/workflows/ci-status-main.lock.yml
#	.github/workflows/ci-status-net11.lock.yml

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

Note

🤖 This review was generated by AI — an automated multi-model code review (Claude Opus 4.8 · GPT-5.5 · Gemini 3.1 Pro), tracked across 9 rounds.


🔍 AI Multi-Model Code Review — PR #36067 · Round 9

HEAD: 793ac43c (merged origin/main)

✅ Verdict: LGTM — the long-standing blocker is resolved

After 8 rounds, the one thing that kept this at NEEDS_CHANGES is now fixed.

🎯 The merge conflict is resolved

  • Round 4–8 blocker: the PR was CONFLICTING/DIRTY with main (gh-aw ci-status-* locks), so the maui-pr* required checks were stuck at action_required "Skipped due to merge conflicts".
  • Round 9 merges origin/main cleanly: now mergeable: MERGEABLE. No leftover conflict markers; both ci-status-*.lock.yml files are valid. maui-pr now correctly shows skipping (by-design .github/** path exclusion), and Skill Validation + Static validation pass.

🧠 The gate code was already cleared

The gatherer (Gather-TestFailureContext.ps1) is byte-identical to the Round 8 head — this round is purely the merge. Across rounds the panel verified, and the author fixed, every finding:

  • R2 baseline-log-read → zero ✅ fixed (R3)
  • R4/R5 false-green ceiling (compile breaks, job-level/log-throw/>12-cap) ✅ fixed via the structural $unaccountedFailingChecks guard
  • R7 Get-ErrorFingerprint digit-collapse ✅ fixed ((?<![A-Za-z])\d+(?![A-Za-z]))
  • R7 device-test confirmedZero on partial Helix data ✅ fixed (-not $anyHelixReadError)
  • R6 source/sources slip ✅ fixed ($occ.source)
  • Plus ~20 additional residual false-green/precision holes the author proactively closed.

The deterministic merge-readiness gate is now a well-hardened, 8-term ceiling that the panel could not break with a remaining false-green.

💡 One standing non-blocking suggestion

Still no Pester harness for this ~+1800-line load-bearing gate (open thread). Several rounds' bugs (the R7 fingerprint digit-collapse, the R6 field-name slip) were exactly the class a truth-table test would catch. Strongly recommended as a fast-follow — assert verdictCeiling for each shape (pending / inaccessible / unmapped / unexplained / unaccounted / unattributed / aborted / canceled / device-test-unverified / regressed / clean) to lock the no-false-green invariant. Two minor open nits (known-issue generic over-match; legsRegressedVsBase base retry-masking) remain as awareness notes.

🏁 Recommendation

LGTM. The blocker is cleared, the gate code is cleared by 9 rounds of multi-model review, and the relevant .github/skills/** validation passes. Ready for human approval. Adding the Pester harness alongside merge would be the ideal close.


Reviewed with the code-review skill: independence-first across 9 rounds, full-file reading in a worktree, manual control-flow tracing of every disputed false-green, and CI/mergeability verification.

kubaflo
kubaflo previously approved these changes Jun 24, 2026

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

Note

🤖 AI-generated approval (multi-model review automation, approving on @kubaflo's behalf).

Approving — 3-model LGTM (Round 9).

After 9 rounds, the sole blocker (the 5-round merge conflict with main) is resolved by merging origin/main: now mergeable, maui-pr correctly skips (by-design .github/**), and Skill+Static validation pass. The deterministic merge-readiness gatherer is byte-identical to the multi-model-cleared Round 8 head — every prior finding (baseline-log-read→zero, the structural false-green ceiling, the fingerprint digit-collapse, device-test confirmedZero, the source/sources slip, plus ~20 proactive hardenings) was fixed and verified.

Fast-follow (non-blocking): add the Pester truth-table harness for the gate to lock the no-false-green invariant.

Five fixes from a 3-model adversarial review (Opus/GPT/Gemini), all strictly
conservative (can only turn a green verdict into Needs-Human-Investigation,
never the reverse):

- Fingerprint (Get-ErrorFingerprint): widen the standalone-count lookaround to
  the full identifier set [A-Za-z0-9_`]. Round-7 excluded only letter-adjacent
  digits, so underscore- (Handler_1 vs Handler_2) and backtick- (generic arity
  Foo`1 vs Foo`2) adjacent digits still collapsed -> two distinct same-code
  build breaks could collide on one key and a PR-new break be laundered as
  pre-existing. (2/3: GPT F1 + Gemini F1.)

- Device-test confirmedZero (authenticated test-API): failedTests==0 no longer
  positively confirms a clean run when any run is incomplete (incompleteTests>0,
  unanalyzedTests>0, or state != Completed). Aborted/inconclusive device runs
  that never emit a Failed outcome now cap to NHI. [Ignore]d/notApplicable tests
  are deliberately excluded so the common case still confirms. (3/3.)

- Device-test confirmedZero (Helix aggregated path): a sibling job that returns
  no fail count (countless/empty aggregate) or whose shape-scan truncated no
  longer lets another job's clean count confirm green. Get-FailCountsFromObject
  now reports a truncated scan; the loop tracks anyHelixUnverified. (Gemini F2 +
  Opus #3, same class.)

- Baseline dismissal keys: only the most-recent completed base build contributes
  pre-existing-dismissal keys (older not-succeeded builds in the lookback window
  contribute advisory notes only), matching the documented most-recent contract
  and the leg-map. Closes a latent false green when BaselineBuildsPerDefinition>1
  (no-op at the shipped default of 1). (Opus #1.)

- maui-ci-facts.md: correct the baseline-comparison bullet to state the scope
  guard only covers PR-edited test files (not snapshot/platform-source edits),
  so the doc no longer overstates the guarantee. (Opus #2.)

Harness: 198/198 (added 7 R10 regression assertions). AST-clean.

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

@PureWeen PureWeen left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Adversarial review — Round 10 (3 independent reviewers, adversarial consensus)

Methodology: 3 independent reviewers traced the false-green gate from scratch against head 793ac43c; findings reconciled by adversarial consensus (a finding only survives if it's verified against source and points in the false-green direction — saying "Ready to merge" while a real PR-caused failure exists). All five surviving fixes are strictly conservative: each can only turn a green verdict into Needs human investigation, never the reverse.

All findings below are already fixed in 7f2b109994 (harness 198/198, AST-clean).

# Finding Consensus Fix
A Get-ErrorFingerprint collapsed digits adjacent to _ and backtick. Round-7 only excluded letter-adjacent digits, so two distinct same-code build breaks (Handler_1 vs Handler_2, generic arity Foo`1 vs Foo`2) fingerprinted identically → same dedup key → a PR-new break laundered as pre-existing. 2/3 (Reviewers 2 + 3, ❌) Widened the standalone-count lookaround to the full identifier set [A-Za-z0-9_+""+]`.
B Device-test confirmedZero (authenticated test-API) trusted failedTests==0 even when a run was incomplete/aborted (incompleteTests>0, unanalyzedTests>0, or state != Completed) — an aborted run that never emits a Failed outcome confirmed green. 3/3 Veto positive confirmation on any incomplete run. [Ignore]d / notApplicable tests are deliberately excluded, so the common case still confirms (no over-block).
C Device-test confirmedZero (Helix aggregated path) let one job's clean count confirm green while a sibling job returned no count (countless/empty aggregate) or its shape-scan truncated at the 5000-node guard. 2/3 (class) (Reviewers 1 + 3) Get-FailCountsFromObject now reports a truncated scan; the loop tracks anyHelixUnverified and refuses confirmation.
D Baseline dismissal keys were pulled from older not-succeeded base builds in the lookback window when BaselineBuildsPerDefinition>1 — an older failure that was since fixed and is absent on the tip could dismiss a PR-reintroduced break. (No-op at the shipped default of 1.) 1/3 (Reviewer 1, ⚠️) Only the most-recent completed base build contributes dismissal keys, matching the documented contract and the leg-map. Older builds contribute advisory notes only.
E maui-ci-facts.md overstated the scope guard ("…unless this PR changes the test, its snapshot/baseline, or the platform code it exercises") — the implemented guard name-matches PR-edited test files only. 1/3 (Reviewer 1, ⚠️ doc) Corrected the bullet so the doc no longer promises protection the code doesn't deliver.

Refuted (adversarial value): Reviewers 2 + 3 flagged finding A as a false green; Reviewer 1 initially argued the full-text SHA tail at L568-571 would disambiguate. That tail is only appended when the normalized text exceeds 120 chars — a typical short CS#### one-liner normalizes well under that, so it provides no protection and the collision is real. Net: finding A stands.

Verified sound, no change (no finding): verdict ceiling captures every non-green attribution (the green branch is unreachable while any cap term is non-zero); the ci-scan integration can only demote regressed-vs-base → indeterminate (still NHI, never green); cross-pipeline laundering scope guards; buildUri (not buildIds) test-run scoping; truncated-paging and per-run results-exception backstops; R2/R4/R5/R6/R7 prior-round fixes all present and sound.

Posted as a comment only — approval is a human decision.

@PureWeen
PureWeen merged commit 5ec887f into main Jun 25, 2026
14 of 15 checks passed
@PureWeen
PureWeen deleted the pureween-review-tests-merge-readiness branch June 25, 2026 16:27
@github-actions github-actions Bot added this to the .NET 10 SR9 milestone Jun 25, 2026
PureWeen added a commit that referenced this pull request Jul 1, 2026
…rkitems endpoint (#36185)

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

### Problem

The `/review tests` agentic-workflow gather script confirmed device-test
(`maui-pr-devicetests`) results via the Helix **`/aggregated`**
endpoint. That endpoint returns **HTTP 404 anonymously** (verified
against live maui jobs), and the gh-aw runner is **unauthenticated**. So
the read *always* threw, device tests were **never** positively
confirmed, and every green device-test check fell to
`gate.deviceTestUnverified` — hard-capping verdicts to **Needs Human
Investigation**.

This surfaced as confusing "device-unverified / Not authenticated" caps
after #36067 (e.g. on #31661 and #34408). The convergence PR didn't
*introduce* the gap — it made an always-failing path visible. This fixes
the underlying data source so device tests are actually confirmable with
**zero auth**.

### Fix

Switch device-test confirmation to the per-job **`/workitems`** endpoint
(reachable anonymously), plus job detail (`/jobs/{id}`) for completeness
signals (`InitialWorkItemCount`, job `Finished`). Add **green-leg Helix
job discovery** so a *green* leg's hidden work-item failure is caught —
XHarness exits 0 even when a Helix work item fails, so a green AzDO leg
can hide a real device failure.

### Never a false green

The change preserves the core invariant — any incompleteness caps the
verdict, it never positively confirms over unobserved data:

- **Reset `$records` per build** so a build whose timeline read fails
cannot inherit the *previous* build's legs and false-confirm.
- **A green `Job` leg with no discoverable Helix job caps** (no
work-item evidence ⇒ a sibling's clean job can't confirm over it).
Scoped to `type=='Job' && result=='succeeded'` so it doesn't over-block
the duplicate `Phase` records or already-failing red legs.
- **Missing/non-int `InitialWorkItemCount`, a short work-item set, a
not-yet-`Finished` job/item, or a non-array response ⇒ `unverified`.**
- **A green check is confirmed only if EVERY backing build confirmed
`Failed==0`** (≥1 confirmed AND none unconfirmed) — a clean
re-run/sibling can no longer mask an unverified build under the same
check name. A canceled backing build is excused only because it's
already capped by the canceled-check ceiling.

### Validation (all live, anonymous — mirrors CI)

- `pwsh` parse OK; **28/28** unit tests pass (added a harness for the
new work-item counter).
- Build `1483259` (clean) ⇒ positively confirmed — **the happy path is
not over-blocked**.
- Build `1483473` ⇒ **no spurious green-Job cap** (all green legs
discovered jobs).
- Build `1483277` — **AzDO-"succeeded"** yet its Windows Helix job had
**12 work items at `ExitCode=-4`** with the job not `Finished` ⇒ now
correctly **capped to NHI with all 12 failures surfaced**. The old
`/aggregated` (404) code could never see this false-green.

### Files

-
`.github/skills/review-test-failures/scripts/Gather-TestFailureContext.ps1`
— replace `/aggregated` scanner with `Get-HelixWorkItemCounts`,
green-leg discovery, completeness vetoes, AND-semantics check consumer.
- `.github/docs/maui-ci-facts.md` — document `/workitems` (the reachable
endpoint) and the anonymous `/aggregated` 404.

No product/runtime code changes — agentic-workflow tooling only.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
devanathan-vaithiyanathan pushed a commit to devanathan-vaithiyanathan/maui that referenced this pull request Jul 7, 2026
…rkitems endpoint (dotnet#36185)

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

### Problem

The `/review tests` agentic-workflow gather script confirmed device-test
(`maui-pr-devicetests`) results via the Helix **`/aggregated`**
endpoint. That endpoint returns **HTTP 404 anonymously** (verified
against live maui jobs), and the gh-aw runner is **unauthenticated**. So
the read *always* threw, device tests were **never** positively
confirmed, and every green device-test check fell to
`gate.deviceTestUnverified` — hard-capping verdicts to **Needs Human
Investigation**.

This surfaced as confusing "device-unverified / Not authenticated" caps
after dotnet#36067 (e.g. on dotnet#31661 and dotnet#34408). The convergence PR didn't
*introduce* the gap — it made an always-failing path visible. This fixes
the underlying data source so device tests are actually confirmable with
**zero auth**.

### Fix

Switch device-test confirmation to the per-job **`/workitems`** endpoint
(reachable anonymously), plus job detail (`/jobs/{id}`) for completeness
signals (`InitialWorkItemCount`, job `Finished`). Add **green-leg Helix
job discovery** so a *green* leg's hidden work-item failure is caught —
XHarness exits 0 even when a Helix work item fails, so a green AzDO leg
can hide a real device failure.

### Never a false green

The change preserves the core invariant — any incompleteness caps the
verdict, it never positively confirms over unobserved data:

- **Reset `$records` per build** so a build whose timeline read fails
cannot inherit the *previous* build's legs and false-confirm.
- **A green `Job` leg with no discoverable Helix job caps** (no
work-item evidence ⇒ a sibling's clean job can't confirm over it).
Scoped to `type=='Job' && result=='succeeded'` so it doesn't over-block
the duplicate `Phase` records or already-failing red legs.
- **Missing/non-int `InitialWorkItemCount`, a short work-item set, a
not-yet-`Finished` job/item, or a non-array response ⇒ `unverified`.**
- **A green check is confirmed only if EVERY backing build confirmed
`Failed==0`** (≥1 confirmed AND none unconfirmed) — a clean
re-run/sibling can no longer mask an unverified build under the same
check name. A canceled backing build is excused only because it's
already capped by the canceled-check ceiling.

### Validation (all live, anonymous — mirrors CI)

- `pwsh` parse OK; **28/28** unit tests pass (added a harness for the
new work-item counter).
- Build `1483259` (clean) ⇒ positively confirmed — **the happy path is
not over-blocked**.
- Build `1483473` ⇒ **no spurious green-Job cap** (all green legs
discovered jobs).
- Build `1483277` — **AzDO-"succeeded"** yet its Windows Helix job had
**12 work items at `ExitCode=-4`** with the job not `Finished` ⇒ now
correctly **capped to NHI with all 12 failures surfaced**. The old
`/aggregated` (404) code could never see this false-green.

### Files

-
`.github/skills/review-test-failures/scripts/Gather-TestFailureContext.ps1`
— replace `/aggregated` scanner with `Get-HelixWorkItemCounts`,
green-leg discovery, completeness vetoes, AND-semantics check consumer.
- `.github/docs/maui-ci-facts.md` — document `/workitems` (the reachable
endpoint) and the anonymous `/aggregated` 404.

No product/runtime code changes — agentic-workflow tooling only.

---------

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

Labels

area-infrastructure CI, Maestro / Coherency, upstream dependencies/versions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants