Skip to content

release-readiness: preview readiness — blessed-build source + subscription-wiring & feed-drift checks (evolve #36268) - #36213

Merged
PureWeen merged 9 commits into
mainfrom
release-readiness-skill
Jul 9, 2026
Merged

release-readiness: preview readiness — blessed-build source + subscription-wiring & feed-drift checks (evolve #36268)#36213
PureWeen merged 9 commits into
mainfrom
release-readiness-skill

Conversation

@PureWeen

@PureWeen PureWeen commented Jun 29, 2026

Copy link
Copy Markdown
Member

<!-- 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 from this PR and let us know in a comment if this change resolves your issue. Thank you!

What

Makes the local ask "run release readiness skill to see if net11 preview6 is ready" consult the authoritative official-preview build and verify the preview branch is actually ship-wired — not just public CI/regression health. Three files:

1. release-readiness/SKILL.md — net-new bridge (the star of this PR).
A new Preview: authoritative blessed-build source subsection so the release-readiness skill, after its public survey, runs the access gate and — when the caller has access and the plugin is enabled — invokes the private dotnet-release-tracker plugin for the blessed build / BAR id + stage, then combines that with the CI/regression verdict. It branches on the gate token (AVAILABLE_ENABLED → use the plugin; AVAILABLE_NOT_ENABLED → offer the opt-in; ACCESS_ON_INACTIVE_ACCOUNT → advise an account switch; NO_ACCESSpublic-feed fallback: report the latest build on the public Preview N channel as a labeled candidate that may not be the official/blessed build, without naming the private tool) and adds a "Blessed ≠ green" caveat so the blessed build never masks open regressed-in-* blockers. DRY — it cross-references dependency-flow's tier table / opt-in / guardrails rather than duplicating them.

2. dependency-flow/ gate — evolves the version already merged in #36268.
The deterministic classifier scripts/Get-PreviewReleaseReadiness.ps1 emits:

RELEASE_TRACKER_STATUS = NO_ACCESS | ACCESS_ON_INACTIVE_ACCOUNT | AVAILABLE_NOT_ENABLED | AVAILABLE_ENABLED

It checks (1) GitHub read access to the private marketplace repo that hosts the internal .NET Release Tracker plugin and (2) whether that plugin is enabled locally. It fetches no release data and always exits 0. This PR carries improvements over the version on main:

  • JSONC-tolerant enabled-plugin parser — the strict ConvertFrom-Json on main chokes on the //-commented opt-in snippet the skill itself documents;
  • $USERPROFILE support (Windows user scope) alongside $HOME;
  • multi-account advisory — when the active gh identity can't read the repo but a logged-in inactive account can, the gate emits ACCESS_ON_INACTIVE_ACCOUNT and advises gh auth switch --user <account> instead of a false-positive AVAILABLE (the plugin loads under the active identity). Only fires when access is confirmed on some account, so a true no-access caller still gets a silent NO_ACCESS;
  • -ReleaseRepo / -PluginId / -Json parameters and a public-safe .NOTES contract;
  • try/catch hardening so an unexpected terminating error still emits the safe NO_ACCESS default;
  • the "why a special source" / Preview 6 ([net11.0] Update dependencies from dotnet/android, dotnet/dotnet, dotnet/macios #35364) trap prose in SKILL.md.

> Relationship to #36268. #36268 ("public-safe preview release readiness gate") already merged the first version of this gate. This PR is a forward-evolution of those two files, plus the net-new release-readiness bridge — it does not revert any shipped behavior. In particular the plugin opt-in stays user-scope only (~/.copilot/settings.json); there is no committable project-scope enablement, so forks and no-access users are never silently opted in. The "comment-only on GitHub" guardrail is retained.

3. Preview wiring checks — subscriptions + feed drift + component pins (net-new).
A preview can pass CI and even have a blessed build yet still not be ship-wired. Three public (BAR/Maestro + git) checks close that gap:

  • Check A — subscriptions wired? Confirm release/11.0.1xx-previewN has its default-channel mapping and the baseline three subs (android + macios + dotnet on .NET 11.0.1xx SDK Preview N). Branch cut + default-channel present but zero subs = a start-of-preview flow gap → surfaced as an FYI note (not a ship blocker); the skill still knows how to remediate via the existing combined-PR pattern (DRY, honoring its confirm/draft-PR gate).
  • Check B — feed matches the branch? Compare the latest build promoted to the .NET 11.0.1xx SDK Preview N channel (maestro_latest_build) against origin/release/11.0.1xx-previewN HEAD. Branch ahead of the promoted build = stale feed → flag.
  • Check C — component pins coherent? Report which dotnet/android, dotnet/macios, and dotnet/dotnet (VMR) builds MAUI bundles (version + SHA from eng/Version.Details.xml) and confirm they match the inflight netN.0 branch the preview was cut from. Match = clean cut ✅; divergence or an off-band pin (macios/dotnet missing the -net11-pN/preview.N stamp) → flag. The .NET Release Tracker exposes only SDK/runtime-level data, so there is no "blessed" per-component android/macios build to look up — this is git+BAR only. "Behind the latest component build" is expected for a cut branch (don't flag it); android's -ci.main.NN scheme is normal for net11 and validated against inflight rather than alarmed on.

Mechanics (exact MCP/darc/git commands, interpretation tables, remediation, and live net11 Preview 6 worked examples) live in a new "Wiring checks: is Preview N actually plumbed?" subsection (Checks A/B/C) in dependency-flow/SKILL.md (its Maestro/subscription domain); release-readiness/SKILL.md gets a short orchestration hook that cross-references it and folds the results into the preview report.

4. Preview generator — scope Maestro PRs to the target branch (net-new bug fix).
scripts/Get-PreviewReadiness.ps1 (the deterministic generator the GitHub Action runs to author the [Release Readiness] preview tracker, e.g. #35866) was listing Maestro / dependency-flow PRs that target netN.0 (the inflight branch) inside the preview tracker's own "Maestro / dependency-flow PRs" section. Once a preview is branched, those netN.0 bumps belong to the inflight branch's own readiness, not the preview tracker. Get-CategorizedPullRequests now computes the Maestro bucket from $TargetPRs (the survey ref) only instead of target + inflight, so netN.0 (inflight) Maestro PRs land in no rendered bucket and are intentionally dropped from a branched preview tracker. Non-Maestro inflight PRs still surface unchanged in the Inflight-human bucket. In candidate mode the survey ref is netN.0 and the inflight list is empty, so target-only is correct there too. Unit tests (tests/Test-ReleaseReadiness.ps1, precedence + AutomationNull null-safety) updated to assert target-only scoping, including a new assertion that an inflight Maestro PR appears in no bucket.

5. Surface human-authored dependency-bump PRs in High-priority items + drift-proof SR E2E tests (net-new).
Two follow-ups in scripts/Get-PreviewReadiness.ps1:

  • Dependency-bump detection was author-only. The component-bump PR that is the release (e.g. [release/11.0.1xx-preview6] Bump dotnet/dotnet (BAR 321614), dotnet/android (BAR 321622) and dotnet/macios (BAR 321780) #36433rmarinho, "Bump dotnet/dotnet (BAR 321614), dotnet/android (BAR 321622) and dotnet/macios (BAR 321780)", head update-321614, no labels) was authored by a human, so the old -match "dotnet-maestro" author filters missed it and it fell into the generic release-branch bucket instead of High-priority items. A new Test-IsDependencyFlowPr helper now flags a PR as dependency-flow if it matches any of: dotnet-maestro author OR a Bump dotnet/(dotnet|android|macios|runtime|sdk|…) … (BAR NNN) title OR an update-<id> head ref. The three maestro bucket filters were rewired to use it, and the high-priority row kind was renamed 📦 Maestro PR📦 Dependency-flow PR. Merge-up PRs ([automated] Merge branch …, head merge/…) are intentionally not matched.
  • Human-notes block repositioned. The blessed-build / wiring / component-pin notes now render directly under High-priority items (previously below the Target section), so the authoritative-build context sits next to the items it qualifies.
  • Drift-proof SR E2E tests. The end-to-end tests in tests/Test-ReleaseReadiness.ps1 run the real detector against the live repo and had pinned a specific SR as the not-yet-cut candidate; when that SR shipped/cut the fixtures rotted (7 stale failures). They now derive the in-flight/candidate split structurally from the detector output (≥1 SR, exactly one candidate numbered one past the highest in-flight SR, clean partition, regression-label formula mirrored from New-RegressionLabelList), so they survive every future SR cut/ship without a per-cut edit.

6. Action-owned best-effort component-build section (net-new).
The authoritative blessed build lives in the private .NET Release Tracker (dotnet/release), which the GitHub Action's repo-scoped GITHUB_TOKEN cannot reach — so on an automated run that row can only come from a maintainer's local notes (spliced into the preserved human-notes block). To give the Action something to say on its own, scripts/Get-PreviewReadiness.ps1 now emits a best-effort component-build section sourced from the branch's own eng/Version.Details.xml (a public git source always readable in CI). A new Get-BranchComponentPins reads the dotnet/dotnet (VMR/SDK), dotnet/android and dotnet/macios anchor pins (version + commit SHA) and renders a 🏷️ Preview N component build — branch pins (best-effort) table. It is explicitly labeled NOT a confirmed blessed build — it reports what's currently bundled on the branch and points maintainers to the Release Captain Notes block (filled locally with tracker access) for the authoritative designation. The section renders outside the human-notes markers so it self-refreshes on every automated re-run (verified: after #36433 merged, the pins advanced to the post-bump build automatically), and it surfaces an open component-bump PR (e.g. #36433) as a pending advance when one is still open. Get-BranchComponentPins handles the [xml] attribute-vs-child gotcha (Name/Version are attributes; Uri/Sha are child elements), prefers the most representative dependency name per repo with a Uri-based fallback, and returns $null (no throw) when the file can't be read/parsed. Adds 9 unit assertions. Full suite: 794 passed / 0 failed.

Why

During the Preview 6 cycle (see #35364), two same-band VMR builds (e.g. …26325.125 vs …26326.122) looked interchangeable. Public BAR/Maestro data can enumerate candidate builds but cannot, on its own, identify which staged build releases.dot.net has blessed as the official preview. That authoritative signal lives in the internal release tracker. This change lets developers with access get the authoritative answer automatically from the release-readiness skill, while developers without access fall back to the public preview-feed candidate — the latest build promoted to the public Preview N channel, explicitly labeled as possibly-not-the-official build — with the private tool never named or implied. The wiring checks add the complementary "is the branch even receiving flow, and is its feed current?" signal — validated live: net11 Preview 6 is branched with a promoted build but has no subscriptions authored yet (Preview 5's set was never rolled forward), exactly the gap Check A surfaces (as an FYI).

Privacy / safety

The plugin is double-gated (GitHub read access to load it + an authorized Azure AD identity to pull data), so referencing it from this public repo is safe. The change deliberately:

  • contains no Azure AD resource ids / api://… audiences, backend hostnames, or internal endpoint paths — only the sanctioned marketplace pointer (repo name + plugin name);
  • performs no fetch-and-exec of remote code;
  • defaults to NO_ACCESS for any unconfirmed-access case, so the agent never reveals the private plugin tool to users who can't use it — the NO_ACCESS path now emits an honest, public-source-labeled preview-feed candidate (public data), which reveals nothing about the gated tooling — the multi-account advisory only fires when access is confirmed on some logged-in account, and never prints a token;
  • keeps the opt-in user-scope (personal ~/.copilot/settings.json), so forks and no-access users are unaffected;
  • the wiring checks read only public BAR/Maestro + git data and never mutate config — remediation is opt-in and routes through the documented confirm/draft-PR gate.

Testing

scripts/Get-PreviewReleaseReadiness.ps1 was exercised across all states locally with pwsh (token and -Json forms):

Scenario Result
Real access probe (dotnet/release), plugin enabled in ~/.copilot/settings.json (JSONC w/ comments) AVAILABLE_ENABLED, exit 0
Real access probe, plugin not enabled AVAILABLE_NOT_ENABLED, exit 0
Active identity lacks access, but a logged-in inactive account has it ACCESS_ON_INACTIVE_ACCOUNT + gh auth switch --user &lt;account&gt; advice, exit 0 (and GH_TOKEN restored after probing)
Same user settings with : false AVAILABLE_NOT_ENABLED (correctly not matched), exit 0
Nonexistent repo (no account can read) NO_ACCESS, exit 0

The wiring checks (items 3A/3B) were validated against live Maestro/BAR + git for net11 Preview 6: maestro_subscriptions(targetBranch="release/11.0.1xx-preview6")0 rows (FYI note correctly surfaced); maestro_latest_build(".NET 11.0.1xx SDK Preview 6") → build #321033 @ 6e35dc58d0 == branch HEAD (feed current); Check C (component pins) → dotnet/dotnet 11.0.0-preview.6.26325.125, dotnet/macios 26.5.11717-net11-p6, dotnet/android 37.0.0-ci.main.51 all byte-identical to net11.0 HEAD = clean cut.

The Action-owned best-effort component section (item 6) is unit-tested (9 assertions over Get-BranchComponentPins: parse correctness, name preference, [xml] attribute/child handling, unreadable-file → $null) and validated end-to-end by dispatching the real GitHub Action against this PR branch — the section rendered on #35866 sourced purely from eng/Version.Details.xml and self-refreshed to the post-#36433 pins with no local tracker access. Full suite: 794 passed / 0 failed. Docs/skill-only change — no product code or public API is affected.

Copilot AI review requested due to automatic review settings June 29, 2026 17:16
@github-actions

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

Or

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

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Skill Validation Results

@PureWeen — new skill validation results are available based on this last commit: f7d3e54.
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 Resultsf7d3e54 · release-readiness: preview readiness — blessed-build source + subscription-wiring & feed-drift checks (evolve #36268) · 2026-07-08T21:09: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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a deterministic “access gate” for preview release-readiness so the dependency-flow skill can choose between an authoritative internal source (when available) vs public BAR/Maestro-only guidance (when not), without leaking private-source existence on the no-access path.

Changes:

  • Document a new “Preview release readiness” workflow in dependency-flow with access-tiered behavior and privacy guardrails.
  • Add Get-PreviewReleaseReadiness.ps1 to classify environment access (NO_ACCESS / AVAILABLE_NOT_ENABLED / AVAILABLE_ENABLED) without fetching release data.
Show a summary per file
File Description
.github/skills/dependency-flow/SKILL.md Adds preview release-readiness guidance with a deterministic gate + tiered behavior and guardrails.
.github/skills/dependency-flow/scripts/Get-PreviewReleaseReadiness.ps1 New PowerShell classifier script that probes repo access + local plugin enablement and emits a status token.

Copilot's findings

  • Files reviewed: 2/2 changed files
  • Comments generated: 1

Comment on lines +96 to +98
# Project-scope settings, if invoked from within a repo checkout.
$candidates += (Join-Path (Get-Location) '.github/copilot/settings.json')

@github-actions github-actions Bot added the area-infrastructure CI, Maestro / Coherency, upstream dependencies/versions label Jun 29, 2026
kubaflo
kubaflo previously approved these changes Jun 30, 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.

🤖 AI-generated review — multi-model panel (Claude Opus 4.8 · GPT-5.5 · Gemini 3.1 Pro), independent reviews + cross-pollination. Security/privacy findings empirically verified (leak-grepped + ran the gate under every uncertain path).

✅ LGTM (high confidence) — with non-blocking hardening

A clean, well-designed access gate that meets its security/privacy goals. I independently confirmed:

  • Leak audit CLEAN — no real Azure AD GUIDs, api:// audiences, or internal hostnames/endpoints in either file; the only api://…/Azure-AD mentions are the guardrail text itself (placeholders telling agents NOT to copy such things), and the only coordinates are the sanctioned dotnet/release + dotnet-release-tracker marketplace pointer. (The pre-existing dev.azure.com/dnceng URLs are not added by this PR — 0 added lines reference them.)
  • Fail-closed — structurally (AVAILABLE_* is reachable only inside the else of if (-not $access.Access)) and empirically: gh missing, 404/no-access, garbage input, and JSON mode all resolve to NO_ACCESS, exit 0, no data fetched. A false-AVAILABLE requires a genuine 200 from the private dotnet/release repo.
  • Probe safe — GitHub API path only; body (--silent) + stderr (2>$null) suppressed; no token, no internal URL.
Model Verdict Confidence
Claude Opus 4.8 LGTM high
Gemini 3.1 Pro LGTM high
GPT-5.5 NEEDS_CHANGES high (driven by a false-positive — see note)

Note on the split: GPT-5.5 returned NEEDS_CHANGES primarily on "internal coordinates in SKILL.md" — but those dnceng/maestro-configuration URLs are pre-existing (0 lines added by this PR) and are AzDO repo paths, not secrets. Its other two concerns are real and non-blocking (below).

💡 Non-blocking hardening suggestions

  1. Wrap the gh probe in try/catch (L79). Today the native non-zero exit doesn't throw (so 404/auth-fail → NO_ACCESS correctly), but if an invoking session sets $PSNativeCommandUseErrorActionPreference = $true, the probe would throw and the script would exit non-zero emitting no token — still privacy-safe (never AVAILABLE_*), but it violates the documented "always exits 0 / single-token" contract the SKILL.md branch table relies on. (GPT-5.5 + Opus 4.8)
  2. Project-scope settings scan vs user-scope-only opt-in (L97). Test-PluginEnabled reads the committable .github/copilot/settings.json, while the Tier-B opt-in correctly targets the personal ~/.copilot/settings.json marked "do NOT commit". Because the gate reads the project path, a contributor could paste+commit the snippet and auto-enable the plugin for all checkouts/forks — undercutting the "forks and no-access users are unaffected" guarantee. (No data leak — public pointer only — but a behavior inconsistency.) Drop the project-scope candidate, or add an explicit "never commit this to .github/copilot/settings.json" line. (GPT-5.5 + Opus 4.8)
  3. Document the private-repo assumption — the first gate equates "can read dotnet/release" with authorization; a one-line NOTE that this rests on the repo staying private would guard the assumption. (Opus 4.8)
  4. Silence-guardrail consistency — on NO_ACCESS the gate prints a reason naming the repo; a note that the gate's stdout is for internal branching and shouldn't be surfaced verbatim keeps behavior consistent with the "stay silent about the internal source" guardrail. (Opus 4.8)

Reviewed at head 013445dc. CI: license/cla ✅ pass; maui-pr skipping (by-design .github/** path-exclusion). Verified by reading both full files + executing the gate under failure paths.


# --silent suppresses the repo JSON body; we only care about the exit code.
# A 404 (no access) or auth failure both yield a non-zero exit -> no access.
& gh api "repos/$Repo" --silent 2>$null | Out-Null

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 (multi-model panel) · 💡 non-blocking

The native gh non-zero exit doesn't throw today (so 404/auth-fail → NO_ACCESS correctly), but if an invoking session has $PSNativeCommandUseErrorActionPreference = $true (with $ErrorActionPreference='Stop' set above), this probe would throw and the script would exit non-zero emitting no token — still privacy-safe (never AVAILABLE_*), but it breaks the documented "always exits 0 / single status token" contract the SKILL.md branch table depends on. Wrap the probe (or the whole body) in try/catch that resolves to NO_ACCESS. (GPT-5.5 + Opus 4.8)

if ($env:HOME) { $candidates += (Join-Path $env:HOME '.copilot/settings.json') }
if ($env:USERPROFILE) { $candidates += (Join-Path $env:USERPROFILE '.copilot/settings.json') }
# Project-scope settings, if invoked from within a repo checkout.
$candidates += (Join-Path (Get-Location) '.github/copilot/settings.json')

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 (multi-model panel) · 💡 non-blocking

Test-PluginEnabled scans the committable project-scope .github/copilot/settings.json here, but the Tier-B opt-in in SKILL.md correctly targets the personal ~/.copilot/settings.json and says "do NOT commit this". Because the gate reads the project path, a contributor could paste + commit the snippet and auto-enable the plugin for all checkouts/forks — undercutting the "forks and no-access users are unaffected" guarantee. No data leak (the snippet is only the public pointer), but it's a behavior inconsistency. Either drop this project-scope candidate, or add an explicit "never commit this" guard. (GPT-5.5 + Opus 4.8)

@kubaflo

kubaflo commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Note

🤖 AI-generated note (multi-model review housekeeping).

This PR appears superseded by #36268. All three reviewers (Opus 4.8, GPT-5.5, Gemini 3.1 Pro) independently flagged that #36268 — the public-safe rework — modifies the identical two files (dependency-flow/SKILL.md + Get-PreviewReleaseReadiness.ps1), same author/base, and is a strict hardening superset of this PR: it wraps the gh probe in try/catch + pins $PSNativeCommandUseErrorActionPreference=$false, drops the committable project-scope settings scan (personal ~/.copilot/settings.json only), and emits only a single status token. Those were exactly the non-blocking hardening suggestions from my review here.

Leaving both open risks a double-merge / conflict. Recommend closing this one in favor of #36268 (which I've approved). Flagging for a maintainer to decide — I won't close it automatically.

@kubaflo

kubaflo commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Note

🔍 AI-generated review note (automated multi-model orchestrator, on behalf of @kubaflo) — non-blocking, flagging an overlap for maintainer decision.

⚠️ Overlaps already-merged #36268 — needs reconciliation (not approving)

This PR touches exactly the two files that #36268 ("Add public-safe preview release readiness gate to dependency-flow skill") already merged to main on 2026-07-05:

  • .github/skills/dependency-flow/SKILL.md
  • .github/skills/dependency-flow/scripts/Get-PreviewReleaseReadiness.ps1

I diffed this PR's head against current main, and the two versions have diverged in both directions:

  • main (via Add public-safe preview release readiness gate to dependency-flow skill #36268) has a ## Quick reference: lifecycle commands by phase table that this PR does not — landing this PR as-is would delete that table.
  • This PR adds explanatory prose (the "Why a special source" paragraph, the #35364 / Preview 6 two-same-band-builds trap) that main does not have.
  • Get-PreviewReleaseReadiness.ps1 also differs (e.g. #Requires casing, synopsis wording, the access-tier design).

Because #36268 is already merged, this is a maintainer reconciliation call, not a clean approve or request-changes:

Holding for @kubaflo to decide. No code issues found in the change itself — the only concern is the collision with merged work.

Copilot AI review requested due to automatic review settings July 7, 2026 18:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot's findings

  • Files reviewed: 3/3 changed files
  • Comments generated: 3

Comment on lines +104 to +110
# Match an enabled entry, tolerant of a marketplace suffix being present or
# absent. Anchored to the start of a line (after optional whitespace) so a
# commented-out entry such as `// "dotnet-release-tracker@x": true` is ignored.
# Examples that match: "dotnet-release-tracker": true
# "dotnet-release-tracker@dotnet-release": true
$pattern = '(?m)^\s*"' + [regex]::Escape($Plugin) + '(@[^"]+)?"\s*:\s*true'
foreach ($path in ($candidates | Select-Object -Unique)) {
Comment on lines +134 to +136
if ($enabled) {
$status = 'AVAILABLE_ENABLED'
$reason = "enabled-via=$($pluginState.Source)"
Comment on lines +428 to +435
{
"extraKnownMarketplaces": {
"dotnet-release": { "source": { "source": "github", "repo": "dotnet/release" } }
},
"enabledPlugins": {
"dotnet-release-tracker@dotnet-release": true
}
}
PureWeen pushed a commit that referenced this pull request Jul 7, 2026
…p reverting shipped hardening)

#36213 rewrote the two dependency-flow files that #36268 already merged to main.
The rewrite is a net improvement (JSONC-tolerant enabled-plugin parser, $USERPROFILE
Windows support, $ReleaseRepo/$PluginId/$Json params, try/catch hardening, the
"why a special source" / Preview 6 trap prose), but it also silently reverted two
things #36268 shipped. Remove those reverts so the PR is additive, not competing:

- Drop the committable project-scope scan (.github/copilot/settings.json) from
  Test-PluginEnabled. The private-plugin opt-in must stay user-scope only so forks
  and no-access users are never silently opted in — this restores #36268's hardening.
  The documented opt-in target is ~/.copilot/settings.json (user scope), so the flow
  is unchanged: the gate still resolves AVAILABLE_ENABLED via the user settings file
  (verified, both token and -Json forms).
- Restore the "Comment-only on GitHub" guardrail bullet that the rewrite dropped.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@PureWeen PureWeen changed the title dependency-flow: add preview release-readiness access gate release-readiness: consult the .NET Release Tracker for the blessed preview build (evolve the #36268 gate) Jul 7, 2026
Copilot AI review requested due to automatic review settings July 7, 2026 20:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot's findings

  • Files reviewed: 5/5 changed files
  • Comments generated: 4

Comment on lines 786 to 788
} else {
Write-Host " ❌ SR9 tracker missing" -ForegroundColor Red; $script:failed++
Write-Host " ❌ no in-flight SR to anchor candidate numbering" -ForegroundColor Red; $script:failed++
}
Comment on lines +236 to +237
Write-Output "RELEASE_TRACKER_STATUS=$status"
Write-Output "# access=$($access.Access.ToString().ToLower()) enabled=$($enabled.ToString().ToLower()) reason=$reason"
Comment on lines +240 to +245
catch {
# Unreachable in normal operation, but the always-exit-0 / always-emit-a-line
# contract must hold even if an unexpected terminating error occurs. Fall back
# to the safe privacy default and never leak the error detail.
Write-Output 'RELEASE_TRACKER_STATUS=NO_ACCESS'
}
Comment on lines +546 to +548
# dotnet/dotnet (VMR runtime/SDK) -> Microsoft.NETCore.App.Ref (11.0.0-preview.N.<date>.<rev>)
# dotnet/android -> Microsoft.Android.Sdk.Windows (android's own scheme, e.g. 37.0.0-ci.main.NN on net11)
# dotnet/macios -> Microsoft.iOS.Sdk.net11.0_26.5 (+ MacCatalyst/macOS/tvOS; 26.5.<build>-net11-pN)
Adversarial-review round fixes on the preview readiness engine:

- Wrap Get-MergedPullRequests (flow-signal fetch) in try/catch so a
  non-retryable gh failure (403 secondary-rate-limit, 500) degrades the
  Flow-signal column to open-PR-only inference instead of aborting the
  whole readiness report. Every other data section already degrades; this
  best-effort section was the lone abort path.
- Get-UpstreamDriftSignal: symmetric guard so a compare payload with
  ahead_by but missing behind_by degrades to 'unknown' rather than throwing
  a StrictMode PropertyNotFoundException on the unguarded behind_by read.
- Tighten the VMR/android/macios pin-selection URI patterns with a
  (?![\w-]) negative-lookahead so 'github.com/dotnet/dotnet' can't collide
  with 'dotnet-optimization' etc. (matches the sibling flow-signal guard).
- Test-PluginEnabled (dependency-flow access gate): strip /* ... */ block
  comments before matching so a block-commented plugin entry isn't misread
  as enabled — the code comment already claimed this tolerance.
- Access-gate final catch now honors -Json and emits the diagnostic line,
  keeping the documented output contract on the unexpected-error path.
- dependency-flow SKILL.md Check C: note the engine's automated pins table
  anchors the VMR on Microsoft.NET.Sdk (SDK band) vs this manual check's
  Microsoft.NETCore.App.Ref (runtime band) — same repo/SHA, compare by SHA.
- Add drift unit tests: pure-behind (ahead 0, behind >0 -> diverged, locks
  check order) and missing-behind_by -> unknown (guards the deref).

Suite: 921 passed / 0 failed. Local preview6 render verified (5-col pins
table intact; VMR 4-ahead 'VMR churn', android/macios current).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 8, 2026 20:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot's findings

  • Files reviewed: 5/5 changed files
  • Comments generated: 2

Comment on lines +1247 to +1248
$refs = & $Fetcher "repos/$Repo/git/matching-refs/heads/$BranchName"
$branchFound = @($refs | Where-Object { $_.ref -eq "refs/heads/$BranchName" }).Count -gt 0
Comment on lines +1261 to +1263
try {
$cmp = & $Fetcher "repos/$Repo/compare/$Sha...$BranchName"
} catch {
… signals

Fixes found by the round-2 adversarial pass (2/3 + sharp 1/3 findings):

- Test-PluginEnabled (dependency-flow gate): the round-2 block-comment
  strip was NOT string-aware and could DELETE a genuinely-enabled plugin
  entry when stray /* */ // sequences appear inside neighbouring JSON string
  VALUES (path globs, URLs) — a false-negative regression. Replace it with
  a new pure, string-aware Remove-JsoncComments helper that alternates a
  full quoted string against the two comment forms and keeps only strings.
  (2/3 reviewers, self-introduced regression.)
- Add a dot-source guard to Get-PreviewReleaseReadiness.ps1 (mirrors the
  engine's guard) so its helpers are unit-testable without running the gate
  or hitting the trailing . Adds 6 Remove-JsoncComments tests.
- Get-PreviewReadiness.ps1 flow section: a merged-PR-history fetch failure
  previously still rendered '❌ none seen — sub may be missing', falsely
  asserting a subscription is absent when we simply couldn't check. Thread
  a  flag and render an honest 'history unavailable'
  cell instead. Extract the render into a testable Format-FlowSignalCell
  helper (+7 tests). (1/3, valid — completes the round-2 try/catch.)
- Get-UpstreamDriftSignal: extend the count guard to also reject present-
  but-null ahead_by/behind_by so a null-count payload degrades to 'unknown'
  rather than [int]$null → 0 → falsely 'current'. (+1 test.)

Suite: 935 passed / 0 failed. Local preview6 render verified (flow cells +
5-col pins table intact via the extracted helper).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 8, 2026 20:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot's findings

  • Files reviewed: 5/5 changed files
  • Comments generated: 3

Comment on lines +4507 to +4509
} finally {
Remove-Item function:global:Get-ContentFromRepo -ErrorAction SilentlyContinue
}
Comment on lines +267 to 270
} else {
Write-Output "RELEASE_TRACKER_STATUS=$status"
Write-Output "# access=$($access.Access.ToString().ToLower()) enabled=$($enabled.ToString().ToLower()) reason=$reason"
}
Comment on lines +287 to 290
} else {
Write-Output 'RELEASE_TRACKER_STATUS=NO_ACCESS'
Write-Output '# access=false enabled=false reason=unexpected-error'
}
Round-3 adversarial review (gpt-5.5) found the dot-source guard's
`$MyInvocation.Line -match '^\.\s'` fallback can false-skip a real `&`/
`-File` invocation that follows a dot-source on the SAME command line,
because $MyInvocation.Line carries the whole command-line text (which
starts with the earlier dot-source). Empirically, `InvocationName -eq '.'`
alone already detects every dot-source form (literal, \$var, parenthesized,
absolute path) and stays false for every call form, so the fallback added
zero coverage while being the sole cause of the false-skip.

Simplified all three mirrored guards to `InvocationName -eq '.'` only
(Get-PreviewReleaseReadiness.ps1, Get-PreviewReadiness.ps1,
Find-ReleaseReadinessTrackers.ps1) and added a child-pwsh regression test
that locks both halves (dot-source skips the gate; a real call after a
dot-source still runs it). Suite: 937/0.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 8, 2026 20:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot's findings

  • Files reviewed: 6/6 changed files
  • Comments generated: 2

Comment on lines +1157 to +1158
$ageDays = if ($mergedUtc) { [Math]::Round(($Now - $mergedUtc).TotalDays) } else { $null }
$status = if ($null -ne $ageDays -and $ageDays -gt $StaleDays) { 'stale' } else { 'fresh' }
$PR.title
} else { $null }

return [bool]($title -and $title -match '(?i)\bBump\b.*dotnet/(dotnet|sdk)\b')
kubaflo
kubaflo previously approved these changes Jul 8, 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.

🔍 AI-generated review (multi-model: Opus 4.8 · GPT-5.5 · Gemini 3.1 Pro), on behalf of @kubaflo.

✅ LGTM — approving (prior hold resolved)

I previously held this at 937ccd7c because it collided with the already-merged #36268 (would have deleted #36268's "Quick reference: lifecycle commands by phase" table). That's now reconciled: I diffed the head against main and confirmed the lifecycle table (heading + all 7 rows) is fully preserved at its new location, and the preview-readiness content is layered on top rather than replacing it.

What I verified this round:

  • No regression of merged work — the #36268 lifecycle table survives intact.
  • All 4 PowerShell scripts parse clean (Get-PreviewReleaseReadiness.ps1, Find-ReleaseReadinessTrackers.ps1, Get-PreviewReadiness.ps1, Test-ReleaseReadiness.ps1) via the PS parser.
  • Privacy/access-tier guardrails intact — the RELEASE_TRACKER_STATUS state machine keeps the private dotnet-release-tracker plugin name out of the NO_ACCESS path (falls back to public preview-feed data, explicitly labelled "may not be the final blessed build"), and distinguishes AVAILABLE_NOT_ENABLED / ACCESS_ON_INACTIVE_ACCOUNT cleanly.
  • Wiring/feed-drift checks (Check A subscriptions, Check B feed-vs-branch) are well-scoped with FYI-vs-blocker calibration.

CI: 10 pass, 7 skipping (.github-only), Build Analysis pending — no red.

Scope note: this is a large evolution of an existing skill (+1593/−285); I reviewed structure, content-preservation, parse-validity, and the privacy/security surface rather than line-by-line runtime behavior of the full readiness engine — the extensive Test-ReleaseReadiness.ps1 harness covers that.

Round-4 adversarial review (gpt-5.5) flagged that the new guard
regression test invoked the real access gate via `& $gateScript`, which
calls Test-MarketplaceAccess -> `gh api`, so the fast (-SkipE2E) tier made
a live GitHub call (the fast tier is otherwise network-free by
convention, e.g. detect-script tests pass -NoFetch).

Replaced it with a hermetic fixture: read the REAL guard line out of the
gate script (so the test cannot drift from the production guard) plus a
sentinel, then exercise both halves via child pwsh with the fixture path
passed through an env var (also fixes a theoretical path-quoting edge from
the same review). Verified empirically: (a) pure dot-source skips the body
(sentinel absent); (b) a real `&` call after a dot-source on the same
command line runs the body (sentinel present); and restoring the old
`-or ... -match '^\.\s'` fallback flips (b) to absent, proving it is a real
regression guard. No network. Suite: 937/0 (845/0 -SkipE2E).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 8, 2026 21:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot's findings

  • Files reviewed: 6/6 changed files
  • Comments generated: 1

Comment on lines +197 to +201
# A string-aware JSONC comment scrub below also removes block-commented and
# inline-commented entries before matching, so neither is read as enabled.
# Examples that match: "dotnet-release-tracker": true
# "dotnet-release-tracker@dotnet-release": true
$pattern = '(?m)^\s*"' + [regex]::Escape($Plugin) + '(@[^"]+)?"\s*:\s*true'

@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 review (multi-model: Opus 4.8 · GPT-5.5 · Gemini 3.1 Pro), on behalf of @kubaflo.

✅ LGTM — re-affirming (6e3ccdb1f7d3e541)

The single new commit ("Make dot-source guard regression test hermetic") touches only Test-ReleaseReadiness.ps1 (+37/−15) — a test-harness hardening that makes the dot-source guard regression test self-contained. Re-parsed the file: clean. No change to the shipped skill scripts or the #36268 reconciliation I verified last round. Approval stands.

@PureWeen
PureWeen merged commit 3c1b74f into main Jul 9, 2026
25 of 26 checks passed
@PureWeen
PureWeen deleted the release-readiness-skill branch July 9, 2026 20:24
@github-actions github-actions Bot added this to the .NET 10 SR9 milestone Jul 9, 2026
PureWeen added a commit that referenced this pull request Jul 16, 2026
…Copilot follow-ups from #36213) (#36483)

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

### What

Two small follow-up fixes to the release-readiness reporting skill,
closing two **low-severity** edge cases that the GitHub Copilot reviewer
flagged on #36213 and that shipped into `main`. Both are docs/skill-only
(PowerShell + tests) — no product code, no public API.

**1. `Test-PluginEnabled` — minified `settings.json` false negative**
`.github/skills/dependency-flow/scripts/Get-PreviewReleaseReadiness.ps1`

The enabled-plugin matcher was anchored to the start of a physical line
(`(?m)^\s*`). A **minified / single-line** `settings.json` (e.g.
`{"enabledPlugins":{"dotnet-release-tracker@dotnet-release":true}}`)
therefore failed to match, so an *enabled* plugin was reported as
**not** enabled (a false-negative that wrongly degrades to
`AVAILABLE_NOT_ENABLED`). It fails safe — it never produces a false
*enabled* — but it's still wrong for anyone whose settings file isn't
pretty-printed.

Fix: anchor the key to a JSON boundary (`{`, `,`, or whitespace) via a
look-behind `(?<=[{,\s])` instead of a line start. Comment-avoidance is
already handled by the string-aware `Remove-JsoncComments` scrub applied
just below, so the line anchor was redundant.

**2. `Test-IsSdkBumpPr` — `dotnet-optimization` collision**
`.github/skills/release-readiness/scripts/Get-PreviewReadiness.ps1`

`'(?i)\bBump\b.*dotnet/(dotnet|sdk)\b'` — the trailing `\b` sits between
`t` and `-`, so `Bump dotnet/dotnet-optimization …` was misclassified as
an SDK/VMR bump (which would attach a spurious "verify blessed build
locally" emphasis). Fix: use the `(?![\w-])` boundary that its sibling
matchers already use (`selectPin`, `Get-ComponentFlowSignal`).
Practically dormant on maui today (real dep-flow PRs are titled
`[netN.0] Update dependencies from…`), but now correct.

### Tests

Added hermetic regression guards in `Test-ReleaseReadiness.ps1`:
- `Test-PluginEnabled`: minified, pretty, suffix-only-key (no false
positive), and absent-entry cases (writes fixtures into a throwaway
`HOME`/`USERPROFILE`, restored in `finally`; no `gh`/network).
- `Test-IsSdkBumpPr`: `dotnet/dotnet-optimization` does **not** collide
→ `false`; a real `dotnet/sdk` later in the same title still → `true`.
This mirrors the `Get-ComponentFlowSignal` collision guard that already
existed — the sibling matcher just never got the parallel assertion (the
exact gap this closes).

Suite: **853 passed / 0 failed** (`-SkipE2E`).

### Why low-risk

Skill/tooling only. Fix 1 only ever *widens* a previously-too-narrow
match and still can't produce a false enable; Fix 2 only *narrows* an
over-broad match to exclude a hyphenated sibling. Both are covered by
new tests that fail against the old patterns.

Co-authored-by: PureWeen <223556219+Copilot@users.noreply.github.com>
SyedAbdulAzeemSF4852 pushed a commit to SyedAbdulAzeemSF4852/maui that referenced this pull request Jul 27, 2026
…ption-wiring & feed-drift checks (evolve dotnet#36268) (dotnet#36213)

&lt;!-- Please let the below note in for people that find this PR --&gt;
&gt; [!NOTE]
&gt; Are you waiting for the changes in this PR to be merged?
&gt; 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!

### What

Makes the local ask **"run release readiness skill to see if net11
preview6 is ready"** consult the *authoritative* official-preview build
**and** verify the preview branch is actually ship-wired — not just
public CI/regression health. Three files:

**1. `release-readiness/SKILL.md` — net-new bridge (the star of this
PR).**
A new **Preview: authoritative blessed-build source** subsection so the
`release-readiness` skill, after its public survey, runs the access gate
and — when the caller has access **and** the plugin is enabled — invokes
the private **`dotnet-release-tracker`** plugin for the blessed build /
BAR id + stage, then combines that with the CI/regression verdict. It
branches on the gate token (`AVAILABLE_ENABLED` → use the plugin;
`AVAILABLE_NOT_ENABLED` → offer the opt-in; `ACCESS_ON_INACTIVE_ACCOUNT`
→ advise an account switch; `NO_ACCESS` → **public-feed fallback**:
report the latest build on the public Preview N channel as a *labeled
candidate* that may not be the official/blessed build, without naming
the private tool) and adds a **"Blessed ≠ green"** caveat so the blessed
build never masks open `regressed-in-*` blockers. DRY — it
cross-references dependency-flow's tier table / opt-in / guardrails
rather than duplicating them.

**2. `dependency-flow/` gate — evolves the version already merged in
dotnet#36268.**
The deterministic classifier `scripts/Get-PreviewReleaseReadiness.ps1`
emits:

```
RELEASE_TRACKER_STATUS = NO_ACCESS | ACCESS_ON_INACTIVE_ACCOUNT | AVAILABLE_NOT_ENABLED | AVAILABLE_ENABLED
```

It checks (1) GitHub read access to the private marketplace repo that
hosts the internal **.NET Release Tracker** plugin and (2) whether that
plugin is enabled locally. It fetches **no** release data and always
exits `0`. This PR carries improvements over the version on `main`:

- **JSONC-tolerant** enabled-plugin parser — the strict
`ConvertFrom-Json` on `main` chokes on the `//`-commented opt-in snippet
the skill itself documents;
- `$USERPROFILE` support (Windows user scope) alongside `$HOME`;
- **multi-account advisory** — when the active `gh` identity can't read
the repo but a logged-in *inactive* account can, the gate emits
`ACCESS_ON_INACTIVE_ACCOUNT` and advises `gh auth switch --user
<account>` instead of a false-positive `AVAILABLE` (the plugin loads
under the *active* identity). Only fires when access is confirmed on
some account, so a true no-access caller still gets a silent
`NO_ACCESS`;
- `-ReleaseRepo` / `-PluginId` / `-Json` parameters and a public-safe
`.NOTES` contract;
- try/catch hardening so an unexpected terminating error still emits the
safe `NO_ACCESS` default;
- the *"why a special source"* / Preview 6 (dotnet#35364) trap prose in
`SKILL.md`.

&gt; **Relationship to dotnet#36268.** dotnet#36268 ("public-safe preview release
readiness gate") already merged the first version of this gate. This PR
is a **forward-evolution** of those two files, **plus** the net-new
release-readiness bridge — it does **not** revert any shipped behavior.
In particular the plugin opt-in stays **user-scope only**
(`~/.copilot/settings.json`); there is no committable project-scope
enablement, so forks and no-access users are never silently opted in.
The "comment-only on GitHub" guardrail is retained.

**3. Preview wiring checks — subscriptions + feed drift + component pins
(net-new).**
A preview can pass CI and even have a blessed build yet still not be
*ship-wired*. Three **public** (BAR/Maestro + git) checks close that
gap:

- **Check A — subscriptions wired?** Confirm `release/11.0.1xx-previewN`
has its default-channel mapping **and** the baseline three subs (android
+ macios + dotnet on `.NET 11.0.1xx SDK Preview N`). Branch cut +
default-channel present but **zero subs** = a start-of-preview flow gap
→ surfaced as an **FYI note** (not a ship blocker); the skill still
knows how to remediate via the existing **combined-PR pattern** (DRY,
honoring its confirm/draft-PR gate).
- **Check B — feed matches the branch?** Compare the latest build
promoted to the `.NET 11.0.1xx SDK Preview N` channel
(`maestro_latest_build`) against `origin/release/11.0.1xx-previewN`
HEAD. Branch ahead of the promoted build = stale feed → flag.
- **Check C — component pins coherent?** Report which `dotnet/android`,
`dotnet/macios`, and `dotnet/dotnet` (VMR) builds MAUI bundles (version
+ SHA from `eng/Version.Details.xml`) and confirm they **match the
inflight `netN.0` branch the preview was cut from**. Match = clean cut
✅; divergence or an off-band pin (macios/dotnet missing the
`-net11-pN`/`preview.N` stamp) → flag. The `.NET Release Tracker`
exposes **only** SDK/runtime-level data, so there is **no** "blessed"
per-component android/macios build to look up — this is git+BAR only.
"Behind the latest component build" is *expected* for a cut branch
(don't flag it); android's `-ci.main.NN` scheme is normal for net11 and
validated against inflight rather than alarmed on.

Mechanics (exact MCP/`darc`/git commands, interpretation tables,
remediation, and **live net11 Preview 6 worked examples**) live in a new
**"Wiring checks: is Preview N actually plumbed?"** subsection (Checks
A/B/C) in `dependency-flow/SKILL.md` (its Maestro/subscription domain);
`release-readiness/SKILL.md` gets a short orchestration hook that
cross-references it and folds the results into the preview report.

**4. Preview generator — scope Maestro PRs to the target branch (net-new
bug fix).**
`scripts/Get-PreviewReadiness.ps1` (the deterministic generator the
GitHub Action runs to author the `[Release Readiness]` preview tracker,
e.g. dotnet#35866) was listing Maestro / dependency-flow PRs that target
`netN.0` (the **inflight** branch) inside the preview tracker's own
"Maestro / dependency-flow PRs" section. Once a preview is **branched**,
those `netN.0` bumps belong to the inflight branch's own readiness, not
the preview tracker. `Get-CategorizedPullRequests` now computes the
Maestro bucket from `$TargetPRs` (the survey ref) **only** instead of
target + inflight, so `netN.0` (inflight) Maestro PRs land in no
rendered bucket and are intentionally dropped from a branched preview
tracker. Non-Maestro inflight PRs still surface unchanged in the
Inflight-human bucket. In **candidate** mode the survey ref *is*
`netN.0` and the inflight list is empty, so target-only is correct there
too. Unit tests (`tests/Test-ReleaseReadiness.ps1`, precedence +
AutomationNull null-safety) updated to assert target-only scoping,
including a new assertion that an inflight Maestro PR appears in no
bucket.

**5. Surface human-authored dependency-bump PRs in High-priority items +
drift-proof SR E2E tests (net-new).**
Two follow-ups in `scripts/Get-PreviewReadiness.ps1`:

- **Dependency-bump detection was author-only.** The component-bump PR
that *is* the release (e.g. dotnet#36433 — `rmarinho`, "Bump dotnet/dotnet
(BAR 321614), dotnet/android (BAR 321622) and dotnet/macios (BAR
321780)", head `update-321614`, no labels) was authored by a human, so
the old `-match "dotnet-maestro"` author filters missed it and it fell
into the generic release-branch bucket instead of **High-priority
items**. A new `Test-IsDependencyFlowPr` helper now flags a PR as
dependency-flow if it matches *any* of: `dotnet-maestro` author **OR** a
`Bump dotnet/(dotnet|android|macios|runtime|sdk|…) … (BAR NNN)` title
**OR** an `update-<id>` head ref. The three maestro bucket filters were
rewired to use it, and the high-priority row kind was renamed `📦 Maestro
PR` → `📦 Dependency-flow PR`. Merge-up PRs (`[automated] Merge branch
…`, head `merge/…`) are intentionally *not* matched.
- **Human-notes block repositioned.** The blessed-build / wiring /
component-pin notes now render directly under **High-priority items**
(previously below the Target section), so the authoritative-build
context sits next to the items it qualifies.
- **Drift-proof SR E2E tests.** The end-to-end tests in
`tests/Test-ReleaseReadiness.ps1` run the *real* detector against the
live repo and had pinned a specific SR as the not-yet-cut candidate;
when that SR shipped/cut the fixtures rotted (7 stale failures). They
now **derive** the in-flight/candidate split structurally from the
detector output (≥1 SR, exactly one candidate numbered one past the
highest in-flight SR, clean partition, regression-label formula mirrored
from `New-RegressionLabelList`), so they survive every future SR
cut/ship without a per-cut edit.

**6. Action-owned best-effort component-build section (net-new).**
The authoritative *blessed* build lives in the private **.NET Release
Tracker** (`dotnet/release`), which the GitHub Action's repo-scoped
`GITHUB_TOKEN` **cannot** reach — so on an automated run that row can
only come from a maintainer's local notes (spliced into the preserved
human-notes block). To give the Action *something* to say on its own,
`scripts/Get-PreviewReadiness.ps1` now emits a **best-effort**
component-build section sourced from the branch's own
`eng/Version.Details.xml` (a **public** git source always readable in
CI). A new `Get-BranchComponentPins` reads the `dotnet/dotnet`
(VMR/SDK), `dotnet/android` and `dotnet/macios` anchor pins (version +
commit SHA) and renders a `🏷️ Preview N component build — branch pins
(best-effort)` table. It is **explicitly labeled NOT a confirmed blessed
build** — it reports what's *currently bundled on the branch* and points
maintainers to the **Release Captain Notes** block (filled locally with
tracker access) for the authoritative designation. The section renders
**outside** the human-notes markers so it **self-refreshes** on every
automated re-run (verified: after dotnet#36433 merged, the pins advanced to
the post-bump build automatically), and it surfaces an open
component-bump PR (e.g. dotnet#36433) as a *pending advance* when one is still
open. `Get-BranchComponentPins` handles the `[xml]` attribute-vs-child
gotcha (`Name`/`Version` are attributes; `Uri`/`Sha` are child
elements), prefers the most representative dependency name per repo with
a `Uri`-based fallback, and returns `$null` (no throw) when the file
can't be read/parsed. Adds 9 unit assertions. Full suite: **794 passed /
0 failed**.

### Why

During the Preview 6 cycle (see dotnet#35364), two same-band VMR builds (e.g.
`…26325.125` vs `…26326.122`) looked interchangeable. Public BAR/Maestro
data can enumerate candidate builds but **cannot, on its own, identify
which staged build releases.dot.net has *blessed* as the official
preview**. That authoritative signal lives in the internal release
tracker. This change lets developers *with access* get the authoritative
answer automatically from the `release-readiness` skill, while
developers *without access* fall back to the **public preview-feed
candidate** — the latest build promoted to the public Preview N channel,
explicitly labeled as possibly-not-the-official build — with the private
*tool* never named or implied. The wiring checks add the complementary
*"is the branch even receiving flow, and is its feed current?"* signal —
validated live: net11 Preview 6 is branched with a promoted build **but
has no subscriptions authored yet** (Preview 5's set was never rolled
forward), exactly the gap Check A surfaces (as an FYI).

### Privacy / safety

The plugin is **double-gated** (GitHub read access to load it + an
authorized Azure AD identity to pull data), so referencing it from this
public repo is safe. The change deliberately:

- contains **no** Azure AD resource ids / `api://…` audiences, backend
hostnames, or internal endpoint paths — only the sanctioned *marketplace
pointer* (repo name + plugin name);
- performs **no** fetch-and-exec of remote code;
- defaults to `NO_ACCESS` for any unconfirmed-access case, so the agent
**never** reveals the private plugin *tool* to users who can't use it —
the NO_ACCESS path now emits an honest, public-source-labeled
preview-feed candidate (public data), which reveals nothing about the
gated tooling — the multi-account advisory only fires when access is
*confirmed* on some logged-in account, and never prints a token;
- keeps the opt-in **user-scope** (personal `~/.copilot/settings.json`),
so forks and no-access users are unaffected;
- the wiring checks read only **public** BAR/Maestro + git data and
never mutate config — remediation is opt-in and routes through the
documented confirm/draft-PR gate.

### Testing

`scripts/Get-PreviewReleaseReadiness.ps1` was exercised across all
states locally with `pwsh` (token **and** `-Json` forms):

| Scenario | Result |
|----------|--------|
| Real access probe (`dotnet/release`), plugin enabled in
`~/.copilot/settings.json` (JSONC w/ comments) | `AVAILABLE_ENABLED`,
exit 0 |
| Real access probe, plugin not enabled | `AVAILABLE_NOT_ENABLED`, exit
0 |
| Active identity lacks access, but a logged-in inactive account has it
| `ACCESS_ON_INACTIVE_ACCOUNT` + `gh auth switch --user &lt;account&gt;`
advice, exit 0 (and `GH_TOKEN` restored after probing) |
| Same user settings with `: false` | `AVAILABLE_NOT_ENABLED` (correctly
not matched), exit 0 |
| Nonexistent repo (no account can read) | `NO_ACCESS`, exit 0 |

The wiring checks (items **3A/3B**) were validated against **live
Maestro/BAR + git** for net11 Preview 6:
`maestro_subscriptions(targetBranch="release/11.0.1xx-preview6")` → **0
rows** (FYI note correctly surfaced); `maestro_latest_build(".NET
11.0.1xx SDK Preview 6")` → build #321033 @ `6e35dc58d0` == branch HEAD
(feed current); Check C (component pins) → dotnet/dotnet
`11.0.0-preview.6.26325.125`, dotnet/macios `26.5.11717-net11-p6`,
dotnet/android `37.0.0-ci.main.51` all **byte-identical to `net11.0`
HEAD** = clean cut.

The **Action-owned best-effort component section** (item **6**) is
unit-tested (9 assertions over `Get-BranchComponentPins`: parse
correctness, name preference, `[xml]` attribute/child handling,
unreadable-file → `$null`) and validated end-to-end by dispatching the
real GitHub Action against this PR branch — the section rendered on
dotnet#35866 sourced purely from `eng/Version.Details.xml` and self-refreshed
to the post-dotnet#36433 pins with no local tracker access. Full suite: **794
passed / 0 failed**. Docs/skill-only change — no product code or public
API is affected.

---------

Co-authored-by: PureWeen <223556219+Copilot@users.noreply.github.com>
SyedAbdulAzeemSF4852 pushed a commit to SyedAbdulAzeemSF4852/maui that referenced this pull request Jul 27, 2026
…Copilot follow-ups from dotnet#36213) (dotnet#36483)

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

### What

Two small follow-up fixes to the release-readiness reporting skill,
closing two **low-severity** edge cases that the GitHub Copilot reviewer
flagged on dotnet#36213 and that shipped into `main`. Both are docs/skill-only
(PowerShell + tests) — no product code, no public API.

**1. `Test-PluginEnabled` — minified `settings.json` false negative**
`.github/skills/dependency-flow/scripts/Get-PreviewReleaseReadiness.ps1`

The enabled-plugin matcher was anchored to the start of a physical line
(`(?m)^\s*`). A **minified / single-line** `settings.json` (e.g.
`{"enabledPlugins":{"dotnet-release-tracker@dotnet-release":true}}`)
therefore failed to match, so an *enabled* plugin was reported as
**not** enabled (a false-negative that wrongly degrades to
`AVAILABLE_NOT_ENABLED`). It fails safe — it never produces a false
*enabled* — but it's still wrong for anyone whose settings file isn't
pretty-printed.

Fix: anchor the key to a JSON boundary (`{`, `,`, or whitespace) via a
look-behind `(?<=[{,\s])` instead of a line start. Comment-avoidance is
already handled by the string-aware `Remove-JsoncComments` scrub applied
just below, so the line anchor was redundant.

**2. `Test-IsSdkBumpPr` — `dotnet-optimization` collision**
`.github/skills/release-readiness/scripts/Get-PreviewReadiness.ps1`

`'(?i)\bBump\b.*dotnet/(dotnet|sdk)\b'` — the trailing `\b` sits between
`t` and `-`, so `Bump dotnet/dotnet-optimization …` was misclassified as
an SDK/VMR bump (which would attach a spurious "verify blessed build
locally" emphasis). Fix: use the `(?![\w-])` boundary that its sibling
matchers already use (`selectPin`, `Get-ComponentFlowSignal`).
Practically dormant on maui today (real dep-flow PRs are titled
`[netN.0] Update dependencies from…`), but now correct.

### Tests

Added hermetic regression guards in `Test-ReleaseReadiness.ps1`:
- `Test-PluginEnabled`: minified, pretty, suffix-only-key (no false
positive), and absent-entry cases (writes fixtures into a throwaway
`HOME`/`USERPROFILE`, restored in `finally`; no `gh`/network).
- `Test-IsSdkBumpPr`: `dotnet/dotnet-optimization` does **not** collide
→ `false`; a real `dotnet/sdk` later in the same title still → `true`.
This mirrors the `Get-ComponentFlowSignal` collision guard that already
existed — the sibling matcher just never got the parallel assertion (the
exact gap this closes).

Suite: **853 passed / 0 failed** (`-SkipE2E`).

### Why low-risk

Skill/tooling only. Fix 1 only ever *widens* a previously-too-narrow
match and still can't produce a false enable; Fix 2 only *narrows* an
over-broad match to exclude a hyphenated sibling. Both are covered by
new tests that fail against the old patterns.

Co-authored-by: PureWeen <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 9, 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.

4 participants