Converge /review tests with azdo-build-investigator via shared CI facts doc - #36067
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 36067Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 36067" |
Skill Validation Results
✅ Skill Validation Results —
|
…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>
8015c6c to
2d77349
Compare
kubaflo
left a comment
There was a problem hiding this comment.
🤖 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/High → NEEDS_CHANGES/Medium (flipped after investigating GPT's finding)
- GPT-5.5: NEEDS_CHANGES/Low → NEEDS_CHANGES/High (maintained, confidence raised)
- Gemini 3.1 Pro: LGTM/High → NEEDS_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:
- Timeline fetch failures (lines 643-645): Return without setting
$result.error - 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
alsoFailsOnBaselinestamp) - ❌ 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):
- Canceled builds: Treat 'canceled' as weak/inconclusive baseline evidence (compounds the silent-zero gap)
- Log-only extraction: Surface the MaxLogs=8 cap in summary (under-match shouldn't look like confident clean baseline)
- 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-prskip is by-design for.github/**changes (path exclusion ineng/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:
- It contradicts the PR's own documentation (
maui-ci-facts.md) - It can misclassify pre-existing failures as PR-caused
- 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
left a comment
There was a problem hiding this comment.
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:
- ✅ Build-metadata inaccessible → now sets
result.error = "Build <id> metadata was not accessible."(was a possibly-null passthrough) - ✅ Timeline fetch failure → now sets
result.error = "Timeline for build <id> was not accessible (logs may be expired)."(previously returned witherrorunset) - ✅ Per-log read exception → the silent
# Ignoreis replaced with a$logReadFailurescounter; after the loop,result.errorreports"<n> of <N> baseline build log(s) could not be read … baseline failure list is incomplete." - ✅ Caller builds an honest
note($noteParts): surfaceserror, flags a not-succeeded base build that yielded 0 extractable failures as "inconclusive, not clean", and reports truncation whentotalFailedRecords > inspectedLogCount.notestays$nullonly for a genuinely clean baseline. - ✅
SKILL.mdnow instructs the agent that a row whosenoteflags inconclusive/incomplete is not a clean zero-failure baseline even whenbaselineFailureCount=0— don't conclude PR-caused; fold intoInsufficient data.
Correctness probes (all passed)
- No green-baseline false positive: the
elseif ($baseFailureCount -eq 0)inconclusive note lives strictly inside theforeach ($base in $notSucceeded)loop (failed/partiallySucceeded/canceled). Asucceededmost-recent base build takes the earliercontinueand legitimately reportsbaselineFailureCount=0/note=$null. ✔ - No schema regression:
totalFailedRecords/inspectedLogCountare 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 ineng/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] |
There was a problem hiding this comment.
💡 💡 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.errorset, - per-log read exception →
result.errormentions the unreadable count, notSucceededbase + 0 extractable failures →noteflags "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
left a comment
There was a problem hiding this comment.
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
Xisfailedand its AzDO build is accessible → bucketing loop (L1255–1266) hitscontinue(L1258);Xis added to neitherinaccessibleFailingChecksnorunmappedFailingChecks. Xyields zero extracted test failures (a compile/build/infra failure has no parseable test rows), sodedupedFailuresis empty.- Ceiling chain: L1276 false → L1280 false → L1289 false (
failingChecks.Countis not 0) → L1292else→Ready 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 view→mergeable: CONFLICTING,mergeStateStatus: DIRTY.- The 4 required checks (
maui-pr,maui-pr-devicetests,maui-pr-reviewer,maui-pr-uitests) areaction_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-awfrontmatter_hash/body_hashcollisions — both this PR andmainrecompiled them). - Action: rebase/merge
main, then re-rungh aw compileand commit the regenerated.lock.ymlfiles 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
verdictCeilingthe LLM can't override upward) is the right design; theInsufficient data/Needs human investigationprecedence for inaccessible/pending/unmapped checks is sound. matchesKnownIssueandretriedStillFailingare well-reasoned evidence signals;retriedStillFailingcorrectly resists "looks flaky" hand-waving.Get-KnownBuildIssuesfails open (empty patterns +knownIssues.queried=false) whenghis unavailable — good, and the docs tell the model not to treat a no-match as proof.ErrorMessagematching has no escaping bug (literalIndexOf);ErrorPatternregex is validated at load with a substring fallback.
💡 Non-blocking
- Known-issue generic over-match (inline @ L690): a short/generic
ErrorMessageregistry 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 ≥ 1withdedupedFailures == 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 |
There was a problem hiding this comment.
💡 💡 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.
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
left a comment
There was a problem hiding this comment.
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 / MSBuilderrorshapes — the common compile-break case from R4 is now caught. - New
$unexplainedLegsbackstop: a failed Task leg with no test-shaped failure caps the ceiling atNeeds human investigationinstead of falling through to green. - New
legsRegressedVsBasegate: a leg red-on-PR / green-on-base forcesNot ready(forbidsReady 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):
- Job-level infra break (job timeout / agent-provisioning loss) where no child
Taskisfailed(the running task is markedcanceled) → skipped by thetype -eq 'Task'filter. (Routine on maui-pr's long device/UI legs.) - Log-read throw on a failed Task → the exception escapes before
.Addto$unexplainedLegs(L1155). - Failed Task beyond the
-First 12cap → 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-uitests→action_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-rungh aw compileand commit the regeneratedci-status-*.lock.ymlso the hashes match and themaui-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
legsRegressedVsBaseretry-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) { |
There was a problem hiding this comment.
💡 💡 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.
|
Note 🤖 AI-generated follow-up (multi-model review automation) — timing correction. Heads-up on a race: my Round 5 ✅ Blocker 1 (residual false-green) — RESOLVED by
|
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>
Adversarial review round — 5 false-green / precision holes closed (
|
| # | 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-base → indeterminate (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) ina1efd04952. - 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/shortErrorMessagevia unanchoredIndexOf) 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>
|
Adversarial review round — 7 residual gate holes closed ( 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
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 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, |
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>
Adversarial review — round 6Ran another loop of three independent reviewers against the latest HEAD. They surfaced four attribution holes in the gate. All four are now fixed (commit
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:
Docs synced (skill contract, shared CI facts, workflow body); lock recompiled (body-hash-only). Gatherer AST-clean; 122/122 deterministic harness assertions pass. |
kubaflo
left a comment
There was a problem hiding this comment.
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 against512a2acb: the fingerprint digit-collapse is unchanged (still L490 — and the new512a2acbmessage-fingerprint fallback reuses the same normalization, so it doesn't close the hole), and the device-testconfirmedZerologic 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/-uitestsallaction_required"Skipped due to merge conflicts" — they have not run.- Action: merge/rebase
main, thengh aw compileand commit the regeneratedci-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 confirmedZero on -not $anyHelixReadError, or treat a thrown aggregated fetch as an unexplained leg. (Details inline.)
💡 Non-blocking
Get-FailCountsFromObject5000-node guard — the recursion cap could skip a deeply-nestedFailed > 0, yielding a wrongconfirmedZero. Narrow; worth a depth/teardown note. (Opus suggestion.)- Pester harness still missing —
test.ps1was 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 (Issue1234vsIssue5678must not collide) would catch. Reinforces the open R3 thread — not reposted inline.
🏁 Recommendation
REQUEST_CHANGES, but the gate is very close:
- Resolve the merge conflict + recompile the gh-aw locks (the only actual merge blocker — 4 rounds running).
- Tighten the fingerprint regex to standalone integers so it stops laundering numerically-suffixed symbols.
- (Recommended) gate device-test
confirmedZeroon 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.
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>
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
Assessed, not fixed (G, 1/3): the The deterministic truth-table harness was extended to 153 assertions (all green), and the three docs ( 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>
Two production bugs found by running the gatherer against a live PRStress-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
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 Deterministic harness extended to 167 assertions (PROD-1/PROD-2 coverage), all green. |
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>
|
Note 🤖 AI-generated status note (multi-model review automation). Round 8 (
❌ Still the one blocker — merge conflict with
|
…-merge-readiness # Conflicts: # .github/workflows/ci-status-main.lock.yml # .github/workflows/ci-status-net11.lock.yml
There was a problem hiding this comment.
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/DIRTYwithmain(gh-awci-status-*locks), so themaui-pr*required checks were stuck ataction_required"Skipped due to merge conflicts". - Round 9 merges
origin/maincleanly: nowmergeable: MERGEABLE. No leftover conflict markers; bothci-status-*.lock.ymlfiles are valid.maui-prnow correctly showsskipping(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
$unaccountedFailingChecksguard - R7
Get-ErrorFingerprintdigit-collapse ✅ fixed ((?<![A-Za-z])\d+(?![A-Za-z])) - R7 device-test
confirmedZeroon partial Helix data ✅ fixed (-not $anyHelixReadError) - R6
source/sourcesslip ✅ 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
left a comment
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
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, |
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.
…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>
…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>
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 testsrunner) — no product source or public API changes.It converges the automated
/review testsworkflow (thereview-test-failuresskill) with the interactiveazdo-build-investigatorskill 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-analysisplugin that powersazdo-build-investigatoris 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.
.github/docs/maui-ci-facts.md— single source of truth for pipeline names/IDs (maui-pr302,maui-pr-devicetests314,maui-pr-uitests313), 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
review-test-failures/SKILL.mdOn base?column.azdo-build-investigator/SKILL.mdmaui-publiccorrection, escalation tohelix-investigation).Gather-TestFailureContext.ps1failures.baseline,baselineMatchCount,baselineSummary,alsoFailsOnBaseline).copilot-review-tests.md(+ lock)On base?column, and merge-readiness verdict/colors.Review-Tests.ps1failures.baselineMatchCount.ci-status-main.md/ci-status-net11.md(+ locks)Validation
gh aw compileof all three workflows: 0 errors, and a second recompile is idempotent (no further lock diff) — the compiled.lock.ymlfiles are in sync with their.mdbodies. (gh-aw{{#runtime-import}}s the body and pins abody_hash, so body edits surface as a hash-only lock diff.)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.mdandtrigger-azdo-pipeline-setup, and there are some deadpragent references incopilot-instructions.md. Those are the next concentric ring of cleanup and aren't required for this convergence.