Skip to content

Add public-safe preview release readiness gate to dependency-flow skill - #36268

Merged
PureWeen merged 1 commit into
mainfrom
pureween-preview-release-readiness-gate
Jul 5, 2026
Merged

Add public-safe preview release readiness gate to dependency-flow skill#36268
PureWeen merged 1 commit into
mainfrom
pureween-preview-release-readiness-gate

Conversation

@PureWeen

@PureWeen PureWeen commented Jul 1, 2026

Copy link
Copy Markdown
Member

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

What this adds

A public-safe "Preview release readiness" capability in the dependency-flow skill. This is a docs/skill + script authoring change only — no API changes, no dependency bumps.

Two files:

  1. .github/skills/dependency-flow/scripts/Get-PreviewReleaseReadiness.ps1 — a deterministic access gate. It runs a single local gh api repos/dotnet/release --silent read probe and checks the user's personal ~/.copilot/settings.json for an enabled dotnet-release-tracker plugin (tolerant of any marketplace suffix). It emits exactly one status line to stdout and always exits 0 so it can never fail its caller:

    • RELEASE_TRACKER_STATUS=NO_ACCESS
    • RELEASE_TRACKER_STATUS=AVAILABLE_ENABLED
    • RELEASE_TRACKER_STATUS=AVAILABLE_NOT_ENABLED
  2. .github/skills/dependency-flow/SKILL.md — a new "## Preview release readiness" section (appended; existing content untouched) documenting how to run the gate and how to branch on the token.

A/B/C tiering

  • AVAILABLE_ENABLED → use the private dotnet-release-tracker plugin as the authoritative source.
  • AVAILABLE_NOT_ENABLED → the user appears to have access; offer a personal opt-in snippet for their own ~/.copilot/settings.json (never commit plugin enablement to the repo).
  • NO_ACCESS → silently fall back to the existing public BAR/Maestro workflow, without ever revealing that a private source exists.

Privacy / security guardrails

  • No secrets, no internal URLs, no AAD/tenant GUIDs, no endpoint hostnames anywhere in either file. The only private coordinate referenced is the repo name dotnet/release, used purely for the access probe.
  • No fetch-and-exec — the gate only runs a local gh api read check and prints a status token; the probe body and stderr are discarded so no private data leaks to stdout.
  • Access is double-gated (GitHub read on dotnet/release + an authorized AAD identity handled by the plugin, not by this script).
  • The private source is invisible to no-access users.

Honest public-data limitation

Public data (BAR/Maestro + dotnet/core metadata) can surface build/channel/coherency and subscription health, but cannot identify which staged build is "blessed" as the official preview — that determination lives in the authoritative source. The doc calls this out explicitly for the no-access and un-enabled tiers.

Testing performed

  • Real run on a machine with access, plugin not enabled → AVAILABLE_NOT_ENABLED, exit 0, exactly one line.
  • No-access simulation (temp copy probing a nonexistent repo) → NO_ACCESS, exit 0, one line; original script untouched.
  • Missing gh on PATHNO_ACCESS, exit 0.
  • Enabled-detection across settings variations (any marketplace suffix / no suffix / value false / unrelated plugin / missing file / malformed JSON / no enabledPlugins key) — all correct and always exit 0.
  • PowerShell parse check: no syntax errors.
  • Guardrail scan: no suspicious URLs and no GUIDs in the new files.

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

Adds a deterministic access gate script and a 'Preview release readiness'
section to the dependency-flow skill. The gate probes local GitHub read
access to dotnet/release and reports one of three status tokens
(NO_ACCESS / AVAILABLE_ENABLED / AVAILABLE_NOT_ENABLED), emitting no
private data and always exiting 0.

The skill documents A/B/C tiering: use the private dotnet-release-tracker
plugin when enabled, offer a personal opt-in when access is present but
the plugin is not enabled, and silently fall back to public BAR/Maestro
data (without revealing the private source) when there is no access.

No secrets, endpoints, or AAD GUIDs are introduced; no fetch-and-exec.

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

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

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

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

Or

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

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Skill Validation Results

@PureWeen — new skill validation results are available based on this last commit: 7018b1b.
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 Results7018b1b · Add public-safe preview release readiness gate to dependency-flow skill · 2026-07-01T09:11:34Z

✅ 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 “Preview release readiness” access-gating workflow to the dependency-flow skill so the skill can deterministically choose between an authoritative source (when available) and a public BAR/Maestro fallback.

Changes:

  • Appends a new “Preview release readiness” section to the dependency-flow skill docs, including token-based branching guidance.
  • Introduces a PowerShell gate script that probes gh api repos/dotnet/release access and checks ~/.copilot/settings.json for an enabled plugin, emitting a single status line.
Show a summary per file
File Description
.github/skills/dependency-flow/SKILL.md Documents the new preview readiness workflow, including the gate script invocation and tiered behavior.
.github/skills/dependency-flow/scripts/Get-PreviewReleaseReadiness.ps1 Implements the deterministic status-token gate (repo access + personal settings plugin check).

Copilot's findings

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

Comment on lines +398 to +402
`release/X.0.Yxx-previewN` branch, the best available answer depends on whether
that contributor has access to an authoritative, sometimes-embargoed release
source. This section defines a tiered workflow that lets contributors **with
access** benefit automatically while contributors **without access** fall back
safely to public BAR/Maestro data — and never learn that a private source exists.
Comment on lines +409 to +411
- **The private source is invisible to no-access users.** In the `NO_ACCESS`
tier you must NOT mention that any private plugin or embargoed data source
exists. Answer purely from public data.
Comment on lines +1 to +2
#!/usr/bin/env pwsh
#Requires -Version 7.0
Comment on lines +446 to +450
```jsonc
// ~/.copilot/settings.json
"extraKnownMarketplaces": { "dotnet-release": { "source": { "source": "github", "repo": "dotnet/release" } } },
"enabledPlugins": { "dotnet-release-tracker@dotnet-release": true }
```
@github-actions github-actions Bot added the area-infrastructure CI, Maestro / Coherency, upstream dependencies/versions label Jul 1, 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 (ran the gate under every failure path).

✅ LGTM (high confidence) — hardened rework of #36213

This is the public-safe rework of #36213, and it cleanly addressed every hardening point from that review:

  • gh probe wrapped in try/catch + explicit $PSNativeCommandUseErrorActionPreference = $false — the always-exit-0 contract now holds regardless of session preferences. (my #36213 finding #2)
  • Dropped the committable project-scope .github/copilot/settings.json scan — now only the personal ~/.copilot/settings.json. (my #36213 finding #3)
  • Emits only a single status token — removed the old # access=… reason=… diagnostic line and the -Json dump. (my #36213 finding #4)

Empirically verified (Opus ran the script under every failure path)

  • Leak audit CLEAN — no real Azure AD GUIDs, api:// audiences, hostnames, endpoints, or secrets; only the sanctioned dotnet/release repo + dotnet-release-tracker plugin names. The sole stdout emitter prints exactly one of three hardcoded literals (45 bytes / 0 stderr on a real run). (The dnceng/maestro-configuration URLs GPT-5.5 flagged are pre-existing — 0 lines added by this PR — and are AzDO repo paths, not secrets.)
  • Fail-closed confirmed under gh-missing, unauthenticated, bad-token (401), unreachable network, malformed JSON, JSONC comments, trailing comma, substring-injection, null/empty/missing settings, and HOME-unset — every path → NO_ACCESS (or not-enabled), exit 0, one token. AVAILABLE_* is unreachable without a genuine HTTP 200 from the private dotnet/release.
Model Verdict Confidence
Claude Opus 4.8 LGTM high
Gemini 3.1 Pro LGTM high
GPT-5.5 NEEDS_CHANGES high (false-positive — see above)

⚠️ Action item (unanimous across all 3 models)

Close #36213. It's still open on the identical two files, same author/base; #36268 is a strict hardening superset and fully supersedes it. Leaving both open risks a double-merge / conflict / reviewer confusion.

💡 Non-blocking suggestions

  • Test-ReleaseTrackerEnabled matches on $prop.Value truthiness, so a non-boolean like "…": "yes" resolves to AVAILABLE_ENABLED. Harmless (still gated by genuine private-repo read access; it's only the user's own opt-in signal), but use $prop.Value -eq $true if strict boolean semantics are intended. (Opus)
  • Join-Path -Path $HOME … (L73) sits just outside the try/catch — verified safe today (HOME-unset still → NO_ACCESS/exit-0), but moving it inside would defensively cover any future $HOME edge case. (Opus)

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


foreach ($prop in $enabled.PSObject.Properties) {
# Match the plugin name with or without a marketplace suffix.
if ($prop.Name -match '^dotnet-release-tracker(@.*)?$' -and $prop.Value) {

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-ReleaseTrackerEnabled matches on $prop.Value truthiness, so a non-boolean opt-in value like "dotnet-release-tracker@dotnet-release": "yes" (or a non-zero number) resolves to AVAILABLE_ENABLED. Harmless — the tier is still gated by genuine private-repo read access, and this only reflects the user's own opt-in signal — but if strict boolean semantics are intended, use $prop.Value -eq $true. (Opus 4.8)

# Returns $true when the personal settings file enables a
# dotnet-release-tracker plugin under any marketplace suffix
# (e.g. dotnet-release-tracker@dotnet-release) with a truthy value.
$settingsPath = Join-Path -Path $HOME -ChildPath '.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 (defensive nit)

Join-Path -Path $HOME … sits just outside the try/catch. Verified safe today (with ErrorActionPreference=SilentlyContinue and this path only reached when repo access is true, a HOME-unset run still yields NO_ACCESS/exit-0/no crash), but moving it inside the try block would guard any future $HOME edge case from producing a stray non-terminating error line. (Opus 4.8)

@PureWeen
PureWeen merged commit 89a4e02 into main Jul 5, 2026
22 of 23 checks passed
@PureWeen
PureWeen deleted the pureween-preview-release-readiness-gate branch July 5, 2026 15:36
@github-actions github-actions Bot added this to the .NET 10 SR9 milestone Jul 5, 2026
PureWeen added a commit that referenced this pull request Jul 5, 2026
Resolves add/add conflicts on the preview release-readiness gate
(Get-PreviewReleaseReadiness.ps1 + dependency-flow SKILL.md section)
by keeping this branch's richer implementation, which supersedes the
simpler version merged via #36268.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PureWeen added a commit that referenced this pull request Jul 5, 2026
Addresses consensus findings from a 3-model review (gpt-5.5,
claude-opus-4.6, gemini-3.1-pro) comparing this branch's gate against
the simpler version merged via #36268:

* Always-exit-0 contract: restore native-command safety by pinning
  $PSNativeCommandUseErrorActionPreference = $false and wrapping the
  main body in try/catch that falls back to NO_ACCESS. Prevents a
  failing 'gh api' on the common NO_ACCESS path from throwing on hosts
  where the PowerShell 7.4+ native-error default is $true.
* Plugin detection: make the marketplace @suffix optional again
  ('dotnet-release-tracker' as well as 'dotnet-release-tracker@...'),
  matching the behavior of the merged version, and anchor the regex to
  line start so commented-out JSONC entries no longer false-positive.

Verified on pwsh 7.4.5: parses clean; NO_ACCESS path emits a line and
exits 0 even with the native-error preference forced true; bare-name
and suffixed enables detected; commented-out and explicit-false not.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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 pushed a commit that referenced this pull request Jul 8, 2026
…ption-wiring & feed-drift checks (evolve #36268)

Adds the preview lane to the release-readiness skill: an authoritative
blessed-build source (.NET Release Tracker) behind a deterministic,
privacy-preserving access gate, plus subscription-wiring (Check A),
feed-vs-branch drift (Check B), and component-pin coherence (Check C)
checks. Dependency-flow PRs (darc + human-authored component bumps) are
detected via Test-IsDependencyFlowPr and scoped to the target branch so a
branched preview tracker no longer reports net<major>.0 inflight bumps.

Rebased onto main (squash of the release-readiness-skill branch, 15
commits) with conflicts reconciled:

- Get-PreviewReadiness.ps1: kept main's two-hop merge-up structure
  (Raw target+inflight, $mergeUpChainLabel) and layered the PR's
  dependency-flow detection + blessed-build / component-pin rendering on
  top.
- Test-ReleaseReadiness.ps1: adopted the PR's drift-proof SR E2E rewrite
  and updated it for main's 3-mode detector (shipped refresh tracker +
  in-flight + candidate), so the suite stays green across SR cuts/ships
  with no per-cut edit.

Review fixes folded in:
- Document ACCESS_ON_INACTIVE_ACCOUNT in the example status-token comments.
- Gate script no longer emits the caller's settings path in the reason
  line (emits a fixed user-settings scope label instead).

Full suite: 880 passed / 0 failed (incl. live-repo E2E).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
PureWeen added a commit that referenced this pull request Jul 9, 2026
…ption-wiring & feed-drift checks (evolve #36268) (#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
#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 (#35364) trap prose in
`SKILL.md`.

&gt; **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. #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 #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.

---------

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>
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 5, 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.

3 participants