Skip to content

[automated] Merge branch 'main' => 'net11.0' - #36684

Closed
github-actions[bot] wants to merge 215 commits into
net11.0from
merge/main-to-net11.0
Closed

[automated] Merge branch 'main' => 'net11.0'#36684
github-actions[bot] wants to merge 215 commits into
net11.0from
merge/main-to-net11.0

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

I detected changes in the main branch which have not been merged yet to net11.0. I'm a robot and am configured to help you automatically keep net11.0 up to date, so I've opened this PR.

This PR merges commits made on main by the following committers:

  • kubaflo
  • PureWeen
  • dependabot[bot]
  • github-actions[bot]
  • sheiksyedm
  • TamilarasanSF4853
  • jfversluis
  • BagavathiPerumal
  • devanathan-vaithiyanathan
  • Dhivya-SF4094
  • SubhikshaSf4851
  • SyedAbdulAzeemSF4852
  • praveenkumarkarunanithi
  • Vignesh-SF3580
  • HarishwaranVijayakumar
  • HarishKumarSF4517

Instructions for merging from UI

This PR will not be auto-merged. When pull request checks pass, complete this PR by creating a merge commit, not a squash or rebase commit.

merge button instructions

If this repo does not allow creating merge commits from the GitHub UI, use command line instructions.

Instructions for merging via command line

Run these commands to merge this pull request from the command line.

git fetch
git checkout main
git pull --ff-only
git checkout net11.0
git pull --ff-only
git merge --no-ff main

# If there are merge conflicts, resolve them and then run git merge --continue to complete the merge
# Pushing the changes to the PR branch will re-trigger PR validation.
git push https://github.com/dotnet/maui HEAD:merge/main-to-net11.0
or if you are using SSH
git push git@github.com:dotnet/maui HEAD:merge/main-to-net11.0

After PR checks are complete push the branch

git push

Instructions for resolving conflicts

⚠️ If there are merge conflicts, you will need to resolve them manually before merging. You can do this using GitHub or using the command line.

Instructions for updating this pull request

Contributors to this repo have permission update this pull request by pushing to the branch 'merge/main-to-net11.0'. This can be done to resolve conflicts or make other changes to this pull request before it is merged.
The provided examples assume that the remote is named 'origin'. If you have a different remote name, please replace 'origin' with the name of your remote.

git fetch
git checkout -b merge/main-to-net11.0 origin/net11.0
git pull https://github.com/dotnet/maui merge/main-to-net11.0
(make changes)
git commit -m "Updated PR with my changes"
git push https://github.com/dotnet/maui HEAD:merge/main-to-net11.0
or if you are using SSH
git fetch
git checkout -b merge/main-to-net11.0 origin/net11.0
git pull git@github.com:dotnet/maui merge/main-to-net11.0
(make changes)
git commit -m "Updated PR with my changes"
git push git@github.com:dotnet/maui HEAD:merge/main-to-net11.0

Contact .NET Core Engineering (dotnet/dnceng) if you have questions or issues.
Also, if this PR was generated incorrectly, help us fix it. See https://github.com/dotnet/arcade/blob/main/.github/workflows/scripts/inter-branch-merge.ps1.

KarthikRajaKalaimani and others added 30 commits June 2, 2026 14:33
)

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

### Issue Details:

Horizontalspacing / Verticalspacing is not not applied to the first
column in GridItemLayout using CollectionView on Android platform.
        
### Root Cause:

The grid spacing was not being distributed symmetrically across the
active layout implementations, so edge items did not fully participate
when spacing changed at runtime.

### Description of Change:

- On Android, the fix in MauiRecyclerView.cs changes how RecyclerView
padding is handled for GridItemsLayout. Android was already using
SpacingItemDecoration, which applies half-spacing on all four sides of
each item. Previously, negative RecyclerView padding canceled that
spacing at the control edges. The branch keeps that negative-padding
behavior for non-grid layouts, but disables it for GridItemsLayout,
allowing the grid’s half-spacing to remain visible at the outer
perimeter. This makes the first row and first column visually respond
when spacing changes, but it also changes the grid behavior from spacing
only between items to spacing around the outside edges as well.

**Tested the behavior in the following platforms:**

- [x] Android
- [x] Windows
- [ ] iOS
- [ ] Mac

### Reference:

N/A

### Issues Fixed:

Fixes  #34257      

### Screenshots
| Before  | After  |
|---------|--------|
| <Video
src="https://github.com/user-attachments/assets/578dda69-1d60-474c-a6d8-23b3f9d29a50"
Width="300" Height="600"> | <Video
src="https://github.com/user-attachments/assets/7f3826e6-5922-4b6f-a6b9-de581b7db6c3"
Width="300" Height="600"> |
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Description of Change

<!-- Enter description of the fix in this section -->
This pull request standardizes the namespace declarations for several
test case files related to issues in the test suite. The changes ensure
that all files use the correct `Microsoft.Maui.TestCases.Tests.Issues`
namespace, improving consistency and maintainability.

**Namespace corrections:**

* Changed the namespace in `Issue22075.cs` from
`Microsoft.Maui.TestCases.Tests.Issue` to
`Microsoft.Maui.TestCases.Tests.Issues` to match the naming convention.
* Updated the namespace in `Issue28968.cs` from
`Microsoft.Maui.TestCases.Tests.Tests.Issues` to
`Microsoft.Maui.TestCases.Tests.Issues`.
* Updated the namespace in `Issue29588.cs` from
`Microsoft.Maui.TestCases.Tests.Tests.Issues` to
`Microsoft.Maui.TestCases.Tests.Issues`.
* Changed the namespace in `Issue33227.cs` from
`Maui.Controls.TestCases.Tests.Issues` to
`Microsoft.Maui.TestCases.Tests.Issues`.
…35092)

<!-- Please keep the note below for people who 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 whether this change resolves your
issue. Thank you!<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

This pull request addresses an issue where the `WebView` rendered blank
when both a `HybridWebView` and a regular `WebView` coexisted in the
same Windows (UWP) app. The main fix ensures that `HybridWebView` only
creates a custom `CoreWebView2Environment` if custom settings are
provided, allowing both controls to share the default environment and
preventing conflicts. Additionally, new test cases have been added to
verify the fix.
### Description of Change
**Bug fix for WebView and HybridWebView coexistence:**

* Updated `HybridWebViewHandler.Windows.cs` so that a custom
`CoreWebView2Environment` is created only if the user provides custom
settings; otherwise, the default shared environment is used. This
prevents conflicts when both `HybridWebView` and `WebView` are present
in the same app, resolving the blank rendering issue.
[[1]](diffhunk://#diff-9adaedb7571e93283664d8e3db8d34930d748bbe1207eb004f53e25e08eaaaeaR310-R325)
[[2]](diffhunk://#diff-9adaedb7571e93283664d8e3db8d34930d748bbe1207eb004f53e25e08eaaaeaR337-R341)

**Test coverage improvements:**

* Added a new UI test in
`TestCases.Shared.Tests/Tests/Issues/Issue34558.cs` to verify that the
regular `WebView` renders content successfully when coexisting with a
`HybridWebView`.
* Introduced a new test case page in
`TestCases.HostApp/Issues/Issue34558.cs` that sets up both controls and
provides UI elements for automated verification.



<!-- Enter description of the fix in this section -->

### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes #34558 
### Tested the behavior in the following platforms

- [x] Windows
- [ ] Android
- [ ] iOS
- [ ] Mac

| Before Issue Fix | After Issue Fix |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/4ba3435a-99af-4e7e-82be-6dbb588e07b6">
| <video
src="https://github.com/user-attachments/assets/fb5b1e22-b317-4421-829c-0f9b15c84e77">
|
<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->
…PI changes) (#35095)

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

### Description of Change

Enables third-party platform backends (for example Maui.Gtk) to provide
their own `DisplayAlertAsync` / `DisplayActionSheetAsync` /
`DisplayPromptAsync` implementations without MAUI exposing any new
public API. This complements #33267: because that PR introduces a new
public interface, it must wait for .NET 11. This PR solves the same
problem using already-shipped public argument types, so it is shippable
in .NET 10.

`AlertManager.Subscribe()` now keeps the existing explicit internal
`IAlertManagerSubscription` path, then checks for keyed delegate
registrations before falling back to the platform default. The delegate
signatures use only existing public types:

```csharp
Func<Page, AlertArguments,       Task<bool>>
Func<Page, ActionSheetArguments, Task<string>>
Func<Page, PromptArguments,      Task<string>>
```

The registrations are keyed so MAUI does not accidentally consume
unrelated `Func<>` services:

| Dialog | Service key |
|---|---|
| Alert | `Microsoft.Maui.Controls.DisplayAlert` |
| Action sheet | `Microsoft.Maui.Controls.DisplayActionSheet` |
| Prompt | `Microsoft.Maui.Controls.DisplayPrompt` |

Consumer usage:

```csharp
builder.Services.AddKeyedSingleton<Func<Page, AlertArguments, Task<bool>>>(
    "Microsoft.Maui.Controls.DisplayAlert",
    async (page, args) =>
    {
        return await MyGtkDialog.ShowAsync(args.Title, args.Message, args.Accept, args.Cancel);
    });
```

If any keyed delegate is registered, MAUI wraps it in a new internal
`DelegateAlertSubscription`. Registered operations dispatch to their
delegate; unregistered operations fall through to the platform default.
The delegate returns the dialog result and MAUI completes the
corresponding `AlertArguments`, `ActionSheetArguments`, or
`PromptArguments` internally. If the delegate returns `null`, faults, or
cancels, the caller observes that through the original `Display*Async`
task instead of hanging silently.

Precedence order in `Subscribe()`:

1. Explicit `IAlertManagerSubscription` service (existing internal path,
unchanged)
2. Keyed result-returning delegate convention (new)
3. Platform default subscription (existing fallback)

### Notes for reviewers

- Zero public API surface: no `PublicAPI.*.txt` changes. The new
`DelegateAlertSubscription` class and key constants are internal;
consumers use literal string keys documented on the existing argument
types.
- Keyed registrations are intentional. Unkeyed `Func<>` services are
ignored to avoid accidental collisions.
- The delegate returns the dialog result instead of calling
`args.SetResult(...)`. This matches the built-in async platform pattern
where MAUI completes the argument after awaiting the native dialog
result.
- Per-operation fall-through is intentional. A backend can override just
alerts and keep the platform action sheet/prompt, for example.
- `OnPageBusy` is excluded from the convention (obsolete in .NET 10,
removed in .NET 11) and always routes to the fallback.
- When .NET 11 ships a proper public
`IAlertManager`/`IAlertDialogProvider`, this delegate convention can
stay as a lightweight alias or be deprecated. Either way, .NET 10
consumers are unblocked now.

### Tests

Unit coverage in `AlertManagerTests.cs` includes:

- Alert, action sheet, and prompt delegate dispatch
- Returned delegate results completing the original `Display*Async`
caller
- Unregistered operation fall-through
- Unkeyed delegate services being ignored
- Explicit `IAlertManagerSubscription` precedence
- Synchronous and asynchronous delegate faults
- Delegate cancellation forwarding
- Null task contract violation

Focused `AlertManagerTests` pass locally: 21/21.

### Issues Fixed

Fixes #34104

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
…ing ToPlatform and subsequent property changes (#31159)

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

<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Description of Change

- The iOS Label performance was improved in PR #30864. In that PR, the
Label and Entry Feature Matrix test sample and script were modified,
which caused discrepancies in the expected images due to changes which
is due to test sample's default property values. In this PR, I updated
the test sample and re-saved the images accordingly.

- Windows - The Entry is now unfocused, so I re-saved the latest image.
- Android - I modified the test sample by altering the default values
and re-saved two images.

Additionally, while working on the test sample changes, I identified and
fixed issues in FormattedStringExtensions.

These updates improve formatted text rendering on iOS by correctly
propagating span properties (font, character spacing, and line-break
settings) from the label to each span. The layout logic is also more
robust, falling back to MAUI’s calculated size when iOS has not yet
provided a valid label size, preventing incorrect text positioning and
rendering issues.

### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Contributing to #30864

---------

Co-authored-by: KarthikRajaKalaimani <92777139+KarthikRajaKalaimani@users.noreply.github.com>
Co-authored-by: Jakub Florkowski <kubaflo123@gmail.com>
Co-authored-by: albyrock87 <albyrock87@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
# Conflicts:
#	src/Controls/tests/TestCases.HostApp/Issues/Issue34671.cs
The Visual Studio signing scan flags unsigned files inside MAUI workload
pack MSIs. This PR adds signing configuration for two categories of
unsigned files:

### 1. Unsigned `.cab` files (75 files across 75 payloads)

Every MAUI workload pack MSI contains an embedded `cab1.cab.cab` cabinet
archive that is currently unsigned. This affects 69 `maui*` payloads and
6 `aspnetcorewebviewmaui*` payloads across net9.0/net10.0 ×
arm64/x64/x86.

**Fix:** Add `FileExtensionSignInfo` for `.cab` with `Microsoft400`
(which Arcade auto-converts to `MicrosoftDotNet500` since
`UseDotNetCertificate` is `true`).

**Affected payloads (cab):**
- `mauicontrols{10020100200,90120901200}{arm64,x64,x86}`
- `mauicontrolsbuildtasks{10020100200,90120901200}{arm64,x64,x86}`
- `mauicontrolscompatibility90120901200{arm64,x64,x86}`
- `mauicontrolscore{10020100200,90120901200}{arm64,x64,x86}`
- `mauicontrolsxaml{10020100200,90120901200}{arm64,x64,x86}`
- `mauicore{10020100200,90120901200}{arm64,x64,x86}`
- `mauiessentials{10020100200,90120901200}{arm64,x64,x86}`
- `mauigraphics{10020100200,90120901200}{arm64,x64,x86}`
- `mauigraphicswindows{10020100200,90120901200}{arm64,x64,x86}`
- `mauiresizetizer{10020100200,90120901200}{arm64,x64,x86}`
- `mauisdknet{10,9}{10020100200,90120901200}{arm64,x64,x86}`
- `mauitemplatesnet{10,9}{10020100200,90120901200}{arm64,x64,x86}`
- `aspnetcorewebviewmaui{10020100200,90120901200}{arm64,x64,x86}`

### 2. Unsigned `ReconnectModal.razor.js` (3 files)

The Blazor reconnect modal script from
`Microsoft.AspNetCore.Components.Web` is packed into
`mauitemplatesnet10` workload MSIs without a signature.

**Fix:** Add `FileSignInfo` for `ReconnectModal.razor.js` with
`Microsoft400`.

**Affected payloads (js):**
- `mauitemplatesnet1010020100200{arm64,x64,x86}`
…latform (#35179)

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

### Issue Details:

DragGestureRecognizer.DropCompleted event not firing in Android platform
       
### Root Cause:

DragAndDropGestureHandler.SetupHandlerForDrop() only called
SetOnDragListener(this) when the view had a DropGestureRecognizer. On
Android, DragAction.Ended is only delivered to views that have a drag
listener registered. A view with only a DragGestureRecognizer (no
DropGestureRecognizer) never registered a listener → never received
DragAction.Ended → HandleDropCompleted never called → DropCompleted
event never fired.

### Description of Change:

The fix is in
src/Controls/src/Core/Platform/Android/DragAndDropGestureHandler.cs.
Modified the listener registration for drag-source-only views (those
with no DropGestureRecognizer) is now done just before
StartDragAndDrop() is called, scoping it to the active drag and
preventing sibling views in the same layout from accidentally becoming
drag listeners. In the DragAction.Ended handler, the fix always
dispatches DropCompleted to the tracked dragSourceElement regardless of
which view received the event — since on Android, DragAction.Ended can
arrive on a non-source view first. A one-shot DropCompletedSent flag on
the local drag state prevents the event from firing more than once. Once
the source view receives DragAction.Ended, the temporary listener is
unregistered to restore the original state.

**Tested the behavior in the following platforms: **

- [x] Android
- [ ] Windows
- [ ] iOS
- [ ] Mac

### Reference:

N/A

### Issues Fixed:

Fixes  #17554          

### Screenshots
| Before  | After  |
|---------|--------|
| <Video width="300" height="600"
src="https://github.com/user-attachments/assets/243dec05-a560-4d26-87dd-cb6e8b99ab27"
/> | <Video width="300" height="600"
src="https://github.com/user-attachments/assets/3137deb3-4f45-476e-8e96-15661609c546"
/> |
- ICrossPlatformLayout.CrossPlatformArrange can call LayoutButton with a
null button so it should be prepared for that.

Fixes #31048

---------
…35086)

<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

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


<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Issue Details
TabbedPage tab titles get truncated on Android when there are more than
3 tabs (e.g., "New Tab 1" shows as "N..."). Tabs should scroll
horizontally and show full titles.

### Root Cause
TabbedPageManager.cs creates the Android TabLayout with TabMode =
TabLayout.ModeFixed, which divides screen width equally among all tabs.

### Description of Change
Changed TabMode from ModeFixed to ModeScrollable in which sizes tabs to
fit their content and enables horizontal scrolling.
 

Validated the behavior in the following platforms
 
- [x] Android
- [x] Windows
- [x] iOS
- [x] Mac
 
### Issues Fixed
  
Fixes #16470 

### Output  ScreenShot

|Before|After|
|--|--|
| <video
src="https://github.com/user-attachments/assets/1e984577-08aa-4091-b1fc-53bac5511154"
>| <video
src="https://github.com/user-attachments/assets/8c3ef77b-5d1a-480c-8e33-c3f485d04b53">|
…ebViewHandler in AddControlsHandlers (#34868)

`AOTTemplateTest.PublishNativeAOT` and
`PublishNativeAOTRootAllMauiAssemblies` fail on Android NativeAOT
because the Android ILC (SDK 36.99.0-preview.3.10) emits unexpected
`IL3050` warnings for `HybridWebViewHandler` constructors—despite the
call site already being correctly guarded by a
`[FeatureGuard]`-annotated feature switch.

## Why the warnings appear

`HybridWebViewHandler` carries
`[RequiresDynamicCode]`/`[RequiresUnreferencedCode]` because it uses
dynamic `System.Text.Json` serialization. The registration in
`AddControlsHandlers` is inside a proper feature guard:

```csharp
// RuntimeFeature.IsHybridWebViewSupported has:
//   [FeatureGuard(typeof(RequiresDynamicCodeAttribute))]
//   [FeatureGuard(typeof(RequiresUnreferencedCodeAttribute))]
// MauiHybridWebViewSupported=false is set by MSBuild targets when PublishAot=true
if (RuntimeFeature.IsHybridWebViewSupported)
{
    handlersCollection.AddHandler<HybridWebView, HybridWebViewHandler>();
}
```

The iOS/macCatalyst NativeAOT ILC correctly honors `[FeatureGuard]` and
suppresses the warnings. The Android NativeAOT ILC does not, so
`IL3050`/`IL2026` leak through and break the test's strict
warning-baseline check.

## Fix

Explicitly suppress with `#pragma` at the affected call site—the same
pattern used elsewhere in the codebase (`ResourceDictionaryHelpers.cs`,
etc.) for ILC warnings on code that is provably safe via a feature
guard:

```csharp
if (RuntimeFeature.IsHybridWebViewSupported)
{
    // NOTE: not registered under NativeAOT or TrimMode=Full scenarios.
    // IL2026/IL3050 suppressed because IsHybridWebViewSupported has [FeatureGuard] annotations
    // for both RequiresUnreferencedCode and RequiresDynamicCode. The Android NativeAOT ILC does
    // not honor [FeatureGuard] for warning suppression (unlike iOS/macCatalyst), so suppress explicitly.
#pragma warning disable IL2026, IL3050
    handlersCollection.AddHandler<HybridWebView, HybridWebViewHandler>();
#pragma warning restore IL2026, IL3050
}
```

No baseline changes needed—the pragma suppression is respected by the
ILC so the warnings are no longer emitted.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: mattleibow <1096616+mattleibow@users.noreply.github.com>
…ide the Border (#30408)

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

### Root Cause of the issue



- On Windows, when a ContentView with a clip applied during the
SizeChanged event is placed inside a Border, both the ContentView
(through WrapperView) and the Border's content (via ContentPanel)
attempt to apply their own clips to the same visual element.

- The clip set in the ContentPanel.UpdateClip method of the Border is
applied after the WrapperView has already set its clip, resulting in the
Border overwriting the clip geometry originally applied.



### Description of Change



- The fix introduces a condition in ContentPanel.UpdateClip that checks:
- Whether the content's visual already has a clip (visual.Clip is not
null)
- Whether the content is wrapped by a WrapperView (Content.Parent is
WrapperView)
- If both conditions are met, the ContentPanel skips applying its own
clip to the content, allowing the ContentView’s clip (set by the
WrapperView) to be preserved.



### Issues Fixed



Fixes #30404 



### Tested the behaviour in the following platforms



- [x] Android
- [x] Windows
- [x] iOS
- [x] Mac



### Screenshot



| Before Issue Fix | After Issue Fix |
|----------|----------|
| <img
src="https://github.com/user-attachments/assets/04381895-5d99-4ac8-9a2e-7d35aaa03dd4">
| <img
src="https://github.com/user-attachments/assets/04938f65-5c0a-4a4b-9676-c34c3ac30378">
|
…nt Width (#35213)

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

### Issue Details:

CollectionView's Header / Footer is not expanded to its content width in
iOS and Mac platform
       
### Root Cause:

In the iOS/MacCatalyst CollectionView2 implementation,
StructuredItemsViewController2 and GroupableItemsViewController2 both
have a GetViewForSupplementaryElement() method responsible for creating
header and footer views. When a TemplatedCell2 is dequeued as a
supplementary view, its ScrollDirection property was never set — it
silently defaulted to UICollectionViewScrollDirection.Vertical. This was
an oversight, because regular item cells in GetCell() already correctly
assigned ScrollDirection. The missing assignment fed through to
GetMeasureConstraints() in TemplatedCell2, which uses ScrollDirection to
decide how to constrain the cell during measurement: for a horizontal
grid, a Vertical direction incorrectly constrains the cell's width to
the estimated value (~30pt from LayoutFactory2) instead of leaving it
unconstrained, causing header/footer labels to be clipped to a tiny
width and become inaccessible in the UI tree.

### Description of Change:

The fix is a one-line addition in each of the two affected view
controllers, mirroring the pattern already used in GetCell(). In
StructuredItemsViewController2.GetViewForSupplementaryElement(),
templatedCell.ScrollDirection = ScrollDirection is set before calling
UpdateTemplatedSupplementaryView. The same line is added in
GroupableItemsViewController2.GetViewForSupplementaryElement() for group
header/footer cells. With ScrollDirection correctly propagated,
GetMeasureConstraints() leaves the width unconstrained for horizontal
grids, allowing headers and footers to self-size to their full content
width. Vertical layouts are unaffected since they already defaulted to
Vertical, and the change has no impact on Android or Windows as Items2
is iOS/MacCatalyst-only.

**Tested the behavior in the following platforms: **

- [ ] Android
- [ ] Windows
- [x] iOS
- [x] Mac

### Reference:

N/A

### Issues Fixed:

Fixes #35113            

### Screenshots
| Before  | After  |
|---------|--------|
| <img width="400" height="500"
src="https://github.com/user-attachments/assets/319d97f0-a4b9-4b00-bc9a-670fd44fd86f"
/> | <img width="400" height="500" alt="After_31553"
src="https://github.com/user-attachments/assets/710a0fca-0c92-48fd-a91f-697d9e0d6b86"
/> |
…ible=false (#34621)

### Description

On Android, navigating from a page with the soft keyboard (IME) open to
a page with `Shell.NavBarIsVisible="False"` causes the destination page
to initially render under the status bar and then jump into the correct
position.

This behavior is more noticeable when the destination page performs
heavier UI work, and in some cases the layout may remain incorrectly
positioned.

### Root Cause

During `ShellRenderer.SwitchFragment`, the fragment transaction is
committed while the IME is still visible. Android continues to report
IME-related `WindowInsets`, causing the new layout to be measured with
incorrect top insets.

Once the IME state stabilizes, the layout is corrected, resulting in a
visible jump.

### Fix

Dismiss the soft keyboard before performing the fragment transaction:

- Detect IME visibility using `IsSoftInputShowing`
- Call `HideSoftInput()` prior to `FragmentTransaction`

This ensures that `WindowInsets` are stable before the new layout is
measured.

### Result

- Eliminates layout jump when navigating with keyboard open
- Ensures correct layout positioning from initial render
- Improves Shell navigation consistency on Android

### Testing

Added a UI test (`Issue34584`) that:

- Opens the keyboard by focusing an `Entry`
- Navigates to a page with `Shell.NavBarIsVisible="False"`
- Verifies that content is laid out below the status bar (`Y > 0`)

Note: The test validates final layout correctness, not visual animation.

### Related Issues

- Fixes #34584
- Related to #34060
… Shell (#35072)

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

<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Issue Details:
#8296 - ContentPage.OnBackButtonPressed not invoked on iOS/MacCatalyst
when the native navigation bar back button is tapped inside a
NavigationPage.

#9095 - Shell.OnBackButtonPressed not invoked when the toolbar back
button is tapped on Android and iOS Shell pages

### Root Cause
#8296
- ShouldPopItem in NavigationRenderer.cs (the
navigationBar:shouldPopItem: UIKit delegate callback) fires when the
user taps the native iOS back button.
- Previously it unconditionally set _uiRequestedPop = true and returned
true, allowing the native pop without ever notifying MAUI's page model.
As a result, ContentPage.OnBackButtonPressed was never called on iOS
when the native back button was tapped inside a NavigationPage.

#9095 -  iOS (ShellSectionRenderer.cs)
- Same issue — tracker.Value.Page?.SendBackButtonPressed() skipped the
Shell chain and went directly to the ContentPage.

### Description of change

#8296  **NavigationRenderer.cs**
- In ShouldPopItem, call NavPage?.CurrentPage?.SendBackButtonPressed()
before allowing the native pop. If it returns true (the page
handled/cancelled back navigation), reset _uiRequestedPop and return
false to prevent the native pop.

#9095 **ShellSectionRenderer.cs** (iOS Shell)

- Route through _context.Shell?.SendBackButtonPressed() instead of
tracker.Value.Page?.SendBackButtonPressed(), so the Shell back button
tap goes through the same chain as the system back button.

**Note** on _sendPopPending = false in the BackButtonBehavior command
path: On iOS 26+, _sendPopPending is set to true unconditionally at the
start of SendPop(), before any BackButtonBehavior checks. When a
BackButtonBehavior.Command executes and returns false (preventing
navigation), ViewDidDisappear never fires — so without an explicit
reset, the flag stays true permanently and silently blocks all
subsequent back presses. The _sendPopPending = false after command
execution (line 188) is therefore an intentional and necessary fix to
prevent the back button becoming permanently unresponsive after a
command-handled back press on iOS 26+.

### Test results 
#### Shell — `OnBackButtonPressed` (fixes #9095)

| Platform | Navigation | `OnBackButtonPressed` |
|---|---|---|
| iOS / MacCatalyst | `GoToAsync` | ✅ Triggered |
| Android | `GoToAsync` | ✅ Triggered (app back button and emulator back
button) |
| Windows | `GoToAsync` | ✅ Triggered |

>`OnBackButtonPressed` is triggered for both the `Shell` and the
contained `ContentPage`.


#### NavigationPage — `OnBackButtonPressed` (fixes #8296)

| Platform | Navigation | `OnBackButtonPressed` | Notes |
|---|---|---|---|
| iOS / MacCatalyst | `PushAsync` | ✅ Triggered | |
| iOS / MacCatalyst | `PushModalAsync` | — | Back button not visible in
modal navigation |
| Android | `PushAsync` | ✅ Triggered (app back button and emulator back
button) | |
| Android | `PushModalAsync` | ✅ Triggered | Called on the modal
ContentPage |
| Windows | `PushAsync` | ✅ Triggered | |
| Windows | `PushModalAsync` | — | Back button not visible in modal
navigation |

> On Android and Windows, `OnBackButtonPressed` is triggered for both
the `NavigationPage` and the contained `ContentPage`.

#### TabbedPage — `OnBackButtonPressed`

| Platform | Navigation | `OnBackButtonPressed` | Notes |
|---|---|---|---|
| iOS / MacCatalyst | `PushAsync` | ✅ Triggered | |
| iOS / MacCatalyst | `PushModalAsync` | — | Back button not visible in
modal navigation |
| Android | `PushAsync` | ✅ Triggered | |
| Android | `PushModalAsync` | ✅ Triggered | |
| Windows | `PushAsync` | ✅ Triggered | |
| Windows | `PushModalAsync` | — | Back button not visible in modal
navigation |

#### FlyoutPage — `OnBackButtonPressed`

| Platform | Navigation | `OnBackButtonPressed` | Notes |
|---|---|---|---|
| iOS / MacCatalyst | `PushAsync` | ✅ Triggered | |
| iOS / MacCatalyst | `PushModalAsync` | — | Back button not visible in
modal navigation |
| Android | `PushAsync` | ✅ Triggered | |
| Android | `PushModalAsync` | ✅ Triggered | |
| Windows | `PushAsync` | ✅ Triggered | |
| Windows | `PushModalAsync` | — | Back button not visible in modal
navigation |


### Validated the behaviour in the following platforms
- [ ] Android
- [ ] Windows
- [x] iOS
- [x] Mac

### Issue Fixes
Fixes #8296 

### Screenshots
|  | Before  | After |
|--|---------|--------|
|8296|  <video
src="https://github.com/user-attachments/assets/13887369-e9e9-42df-a2a0-db88f0e6b38f">
|   <video
src="https://github.com/user-attachments/assets/d0ccfb01-59e8-44bc-8878-54c5d0f6fb7e"> 
|
|9095 |  <video
src="https://github.com/user-attachments/assets/664e0b7f-3964-4a1b-a4f6-46b1db758b48">
|   <video
src="https://github.com/user-attachments/assets/3ff159ec-93b0-4a91-854a-ca23211ff204"> 
|

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…s changed to default value (#35215)

<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

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


<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Issue Details
On iOS/MacCatalyst, dynamically changing IndicatorView.IndicatorSize
back to the default value (6) at runtime has no visual effect —
indicators remain stuck at the previously set non-default size.

### Root Cause
The platform control has a hard-coded early return when the indicator
size equals the default value, which skips resetting the visual
transform even when indicators are currently scaled to a different size.
Additionally, the field tracking the last applied size was initialized
to -1 instead of the actual default.

### Description of Change
Replaced the hard-coded default value check with a comparison against
the last applied size, so the update is only skipped when the size is
truly unchanged. Initialized the tracking field to the default size to
correctly represent the initial state.
 

Validated the behavior in the following platforms
 
- [x] Android
- [x] Windows
- [x] iOS
- [x] Mac
 
### Issues Fixed
  
Fixes #35214 

### Output  ScreenShot

|Before|After|
|--|--|
| <video
src="https://github.com/user-attachments/assets/30f6d250-1a65-4061-986f-16d3940ac243"
>| <video
src="https://github.com/user-attachments/assets/759cc5af-9f5f-4d95-8b2a-262499928d0d">|
…ent) IsEnabled changed. (#31540)

### Issue Details:

The user has defined an implicit style for the BackgroundColor property
of a Grid, with the selected state value set to green. However, when the
IsEnabled property of the CollectionView's parent Grid is changed from
false to true immediately on the button click, the background color of
the currently selected item (which is also a Grid) is lost.
        
### Root Cause:

This happens because changing the IsEnabled property triggers the
ChangeVisualState() method in the base VisualElement class. As a result,
the visual state transitions to "Normal", overriding the previously
applied "Selected" state and its associated background color.

### Description of Change:

To address the issue, a selection check has been added to ensure that
the "Normal" state is not forced when the element is currently in the
"Selected" state. This change preserves the visual appearance of
selected items by preventing the "Selected" state from being overridden
during IsEnabled property changes.

**Tested the behavior in the following platforms.**

- [x] Android
- [x] Windows
- [x] iOS
- [x] Mac

### Reference:

N/A

### Issues Fixed:

Fixes  #20615 

### Screenshots
| Before  | After  |
|---------|--------|
| <Video
src="https://github.com/user-attachments/assets/14ead580-bb70-4779-8c6e-00c8e7e1d425"
Width="300" Height="600"> | <Video
src="https://github.com/user-attachments/assets/d5699514-40e7-417b-b0c6-d22ebc101a91"
Width="300" Height="600"> |

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

Fixed a small typo in Clipboard.shared.cs. Nothing exciting, really.

### Issues Fixed

Given that this is an extremely small typo fix only, I thought I'd just
go ahead and file a PR.

---------
<!-- Please keep the note below for people who 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 whether this change resolves your
issue. Thank you!
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

This pull request fixes a regression where LinearGradientBrush with
transparent stops rendered as opaque black boxes on Android starting in
10.0.60. The fix preserves per-stop alpha values so gradients render
correctly.

### Root Cause : 

- Introduced by #31567 (Android drawable perf), which rewrote
MauiDrawable.Android.cs to use a Java-side PlatformDrawable.
- In that refactor, calls to GetGradientData were written as
GetGradientData(1.0f), which forces every gradient stop's alpha to fully
opaque (255), overriding any Transparent stop colors defined by the
developer.
  
### Description of Change
**Bug fix: Gradient transparency on Android**

* Updated `MauiDrawable.Android.cs` so that both linear and radial
gradient paints now call `GetGradientData(null)` instead of
`GetGradientData(1.0f)`, preserving per-stop alpha values for
backgrounds and borders. This prevents gradients with transparent stops
from rendering as solid black.
[[1]](diffhunk://#diff-9962b2ab4d4a0eb4922307e668bfe7d868c4fac7919a9463a3cba1859a6de86fL115-R115)
[[2]](diffhunk://#diff-9962b2ab4d4a0eb4922307e668bfe7d868c4fac7919a9463a3cba1859a6de86fL129-R129)
[[3]](diffhunk://#diff-9962b2ab4d4a0eb4922307e668bfe7d868c4fac7919a9463a3cba1859a6de86fL250-R250)
[[4]](diffhunk://#diff-9962b2ab4d4a0eb4922307e668bfe7d868c4fac7919a9463a3cba1859a6de86fL264-R264)

**Testing: New test case and UI test**

* Added a new issue test page `Issue35280` that displays an image with a
linear gradient overlay fading from black to transparent, to visually
confirm the fix.
* Introduced a corresponding UI test in `Issue35280.cs` to verify that
the gradient overlay is rendered correctly and not as an opaque black
box.

<!-- Enter description of the fix in this section -->

### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes #35280 
### Tested the behavior in the following platforms

- [ ] Windows
- [x] Android
- [ ] iOS
- [ ] Mac

| Before Issue Fix | After Issue Fix |
|----------|----------|
| <img width="1080" height="1920" alt="Screenshot_1777904296"
src="https://github.com/user-attachments/assets/a3ed29f1-9f85-44e7-9d4a-14e343aa8bda"
/> | <img width="1080" height="1920" alt="Screenshot_1777904172"
src="https://github.com/user-attachments/assets/c1f4be01-7af1-4e68-a36b-766ef167062f"
/> |
<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->
…acing is applied (#35309)

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

### Issue Details
On iOS, when an Editor has CharacterSpacing applied through a Slider,
the Editor becomes scrollable once the content exceeds its frame size.
After rotating the device (portrait → landscape → portrait), the Editor
loses scrollability because a re-measurement cascade expands it to the
full content height.

**Regression PR:** #30629
 
### Root Cause
PR #30629 added ValidateSafeArea() to MauiView.LayoutSubviews, which
calls InvalidateConstraintsCache() during rotation. This clears the
measurement cache (_lastMeasuredSize = null), causing HasBeenMeasured()
= false and needsMeasure = true for Pages.
As a result, a full re-measurement cascade occurs, where the Editor is
measured with infinite height. SizeThatFits then returns the full
content height (for example, 134px), causing the Editor to grow to fit
its content and lose its scrollable behavior.
 
### Description of Change
Updated EditorHandler.GetDesiredSize to cap the measured height to the
current frame height when the Editor content exceeds its frame
(scrollable state).
An AllowAutoGrowth flag was added to MauiTextView so Editors using
AutoSize = TextChanges are excluded from the cap and can continue
growing to fit their content.

### Issues Fixed
Fixes #35114

### Screenshots

| Before Issue Fix | After Issue Fix |
|----------|----------|
| <video width="300" height="600"
src="https://github.com/user-attachments/assets/4f1b08f7-abb2-40ae-84d8-f05cd063339c">
| <video width="300" height="600"
src="https://github.com/user-attachments/assets/42850642-087c-4198-91be-fc9b09b8dd43">
|
…ker.FocusChange (#29939)

Check if _editText is null before detaching the event.
)

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


<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Issue Details
VisualStateManager permanently breaks when attempting to set a control's
Style property during state transitions. When properties like IsEnabled
change, VSM automatically transitions states - but if that state
contains a Style setter, the control loses all VSM functionality and can
no longer respond to any state changes.

### Root Cause
The old code set the Style property by replacing the entire Style
object. This removed the original Style that contained the
VisualStateGroups attached property. Once the VSM attachment is lost,
the control cannot transition to other states - VSM is permanently
broken for that control.

### Description of Change
Added special handling when Setter values are Styles, using the
IStyle.Apply() and IStyle.UnApply() methods. Apply() applies the Style's
individual setters without replacing the Style property, keeping the VSM
connection intact. UnApply() properly removes the Style's setters when
the state changes, preventing conflicts between different states.

Validated the behavior in the following platforms
 
- [x] Android
- [x] Windows
- [x] iOS
- [x] Mac
 
### Issues Fixed
  
Fixes #17175  

### Output  ScreenShot

|Before|After|
|--|--|
| <video
src="https://github.com/user-attachments/assets/a5a2a482-73a2-4e84-a133-afacaf1c3872"
>| <video
src="https://github.com/user-attachments/assets/a1079f90-06cb-4083-8202-2517200b5346">|
…35208)

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

### Issue details
When a virtual XAML event handler is wired inside a DataTemplate (for
example, SwipeItem.Invoked) and the handler is defined in a base class
and overridden in a derived class, the app crashes in Release builds on
iOS/macOS and may also behave incorrectly on Android. This occurs due to
a bug in XamlC's IL code generator (SetPropertiesVisitor.cs), where the
ldvirtftn instruction receives the wrong vtable object (the anonymous
DataTemplate class instead of the root XAML element), causing runtime
failure with Full AOT compilation.

### Root Cause

The issue occurs because of incorrect IL generation in
SetPropertiesVisitor.ConnectEvent() when wiring a virtual event handler
inside a DataTemplate.

When XamlC compiles a DataTemplate, it generates an anonymous nested
class (e.g., <InitializeComponent>_anonXamlCDataTemplate_1) with a
LoadDataTemplate() method where event wiring happens. For virtual
handlers, XamlC must emit ldvirtftn, which requires the actual page
object as the vtable source to resolve the correct overridden method.

The bug was that the code always pushed Ldarg_0 as the vtable object.
Inside LoadDataTemplate(), Ldarg_0 is the anonymous class, not the root
page. This causes ldvirtftn to look up the virtual method on the wrong
vtable, so the override on the generic subclass is never found. On iOS
Full AOT, this causes a hard crash; on Android and Windows, the JIT
silently calls the base class method instead of the override.
 
### Description of Change

The fix involves replacing the extra Ldarg_0 push before ldvirtftn with
a Dup instruction, which reuses the delegate target object that is
already correctly loaded on the stack just above this code.

The delegate target (the root page) is already on the stack, correctly
resolved via context.Root for both the top-level and DataTemplate
contexts. Dup copies that same object for ldvirtftn, ensuring virtual
dispatch always targets the actual page's vtable, and the overridden
method on the generic subclass is found correctly on all platforms.
 
**Windows platform behavior:**
 
The issue does not occur on the Windows platform. Based on analysis,
even though Windows does not crash, the behavior before the fix was
technically undefined, as it relied on the JIT being lenient. If the
method resolution happened to pick the base class version instead of the
override, the logic would silently be incorrect without any error. The
fix makes the IL correct and explicit for all platforms.

Tested the behavior in the following platforms.
 
- [x] iOS
- [x] Mac
- [ ] Android
- [ ] Windows

### Issues Fixed

Fixes #18055

### Output

| Before Issue Fix | After Issue Fix |
|----------|----------|
| <video width="270" height="600"
src="https://github.com/user-attachments/assets/6bc99f45-7114-4ce5-9a75-d98d3c014882">
| <video width="270" height="600"
src="https://github.com/user-attachments/assets/00cd3e91-cb1b-4a9a-9df9-1a2538d2ec56">
|
…ding (#34845)

<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->
### Issue Details
When set IsRefreshing = true in navigated page, refreshview is not
visible

### Root Cause
OnLoaded unsubscribed itself after firing once (refreshControl.Loaded -=
OnLoaded). So when the user navigated
back to the page, the Loaded event fired again but had no handler —
UpdateIsRefreshing() was never called, and
the refresh indicator never appeared.

### Description of Change

<!-- Enter description of the fix in this section -->
Removed Self-unsubscribe line refreshControl.Loaded -= OnLoaded so the
handler stays active across.

### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes #30535 

<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->

**Tested the behavior in the following platforms.**
- [x] Android
- [x] Windows
- [x] iOS
- [x] Mac

| Before  | After  |
|---------|--------|
| **Windows**<br> <video
src="https://github.com/user-attachments/assets/408e35ed-61ed-4ad2-9104-f08635c98af2"
width="600" height="300"> | **Windows**<br> <video
src="https://github.com/user-attachments/assets/c16e311b-44c0-4571-af71-b3fa316fbea2"
width="600" height="300"> |
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

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


<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Issue Details
Build fails with InvalidOperationException when using an empty (but
valid) SVG as the app icon. The SVG has width, height, and viewBox but
no drawn shapes inside.

### Root Cause
PR #33194 moved the size.IsEmpty check to run before both code paths in
DrawUnscaled, but size (from CullRect) is only needed in the downscale
path. Empty SVGs have a zero CullRect even with valid declared
dimensions, so the check incorrectly blocks the upscale path that never
uses size.

### Description of Change
Moved the size.IsEmpty check from before both branches into only the
else (downscale) branch where size is actually used. The scale >= 1 path
calls DrawPicture directly, which harmlessly draws nothing for empty
SVGs, producing a valid transparent image.
 

Validated the behavior in the following platforms
 
- [x] Android
- [x] Windows
- [x] iOS
- [x] Mac
 
### Issues Fixed
  
Fixes #35293 

### Output  ScreenShot

|Before|After|
|--|--|
| <video
src="https://github.com/user-attachments/assets/cf8040d6-9b0f-4867-bd4a-1713ea41d1d5"
>| <video
src="https://github.com/user-attachments/assets/97c09d5e-b2cc-4a13-b5a3-dc056dacba78">|
#32674)

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

<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->
### Issue Details:

The FlowDirection property is not being applied to the EmptyView content
within a CollectionView.

### Root Cause
FlowDirection was not applied correctly to EmptyView because
EffectiveUserInterfaceLayoutDirection
returned incorrect values, causing RTL detection logic to fail. After
using the correct
property to update the isRTL boolean, the flip logic produced inverted
visuals and did not properly
propagate FlowDirection changes.

### Description of Change

- Replaced transform-based RTL handling with proper FlowDirection
propagation using the MAUI ItemsView.FlowDirection.
- For View-based and DataTemplate-based EmptyViews, the old
transform-based RTL handling was removed. The platform view now updates
its layout direction through UpdateFlowDirection().
- For string-based EmptyViews rendered as a UILabel, the text alignment
is set to center to provide a better user experience.

### Validated the behaviour in the following platforms
- [x] Android
- [x]  Windows
- [x] iOS
- [x] Mac

### Issues Fixed:

Fixes #32404 
Fixes #34522

### Screenshots
| Before  | After |
|---------|--------|
|  <video
src="https://github.com/user-attachments/assets/6423b8a9-8861-4a11-b825-00b4efa28fbb">
|   <video
src="https://github.com/user-attachments/assets/1c851a9b-e772-45ea-8fd5-485e081e53ed"> 
|
…g background (#34997)

<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

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


<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Issue Details
On Android, switching from a gradient to a solid color is not worked —
the gradient persists visually.
On Windows, gradients are never applied, and setting Background to null
doesn't clear previously applied colors.

### Root Cause
On Android, BorderDrawable.SetBackground() doesn't clear the gradient
shader when switching to a solid color or null — the stale shader
persists on the underlying paint object.

On Windows,
[UpdateBackground()](vscode-file://vscode-app/Applications/Visual%20Studio%20Code.app/Contents/Resources/app/out/vs/code/electron-browser/workbench/workbench.html)
only handled
[SolidPaint](vscode-file://vscode-app/Applications/Visual%20Studio%20Code.app/Contents/Resources/app/out/vs/code/electron-browser/workbench/workbench.html),
so gradients were ignored and null backgrounds didn't trigger resource
cleanup.

### Description of Change
On Android, clear the shader via platformPaint.SetShader(null) in
BorderDrawable.SetBackground() before applying the solid color.
On Windows, use
[button.Background?.ToPlatform()](vscode-file://vscode-app/Applications/Visual%20Studio%20Code.app/Contents/Resources/app/out/vs/code/electron-browser/workbench/workbench.html)
to handle all paint types and allow null to remove theme overrides.
Ensure
[RefreshThemeResources()](vscode-file://vscode-app/Applications/Visual%20Studio%20Code.app/Contents/Resources/app/out/vs/code/electron-browser/workbench/workbench.html)
always executes.

Validated the behavior in the following platforms
 
- [x] Android
- [x] Windows
- [ ] iOS
- [ ] Mac
 
### Issues Fixed
  
Fixes #34993  

### Output  ScreenShot
Android
|Before|After|
|--|--|
| <video
src="https://github.com/user-attachments/assets/a48e6fd4-46df-443e-90bf-c2ce41b3a000"
>| <video
src="https://github.com/user-attachments/assets/575b452a-9b68-4568-a5ec-ea14acce17c4">|


Windows
|Before|After|
|--|--|
| <video
src="https://github.com/user-attachments/assets/0b10cb79-2910-42ad-94e7-ef64283a8dc2"
>| <video
src="https://github.com/user-attachments/assets/74d41e8d-3734-4374-b7b7-b6e668234dfb">|
…Android in .NET 10 (#35295)

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

### Issue details
On Android (.NET MAUI 10), Shell toolbar colors are applied before
navigation completes during tab/page transitions, causing the current
page to briefly display the destination page’s colors. This is a
regression from .NET MAUI 9 and results in incorrect visual behavior
during navigation.

### Root Cause
The issue occurs because of the toolbar appearance update being
triggered before the tab navigation is fully completed when switching
tabs in a Shell with TabBar. During the tab change, the destination tab
colors are applied immediately while the previous tab content is still
visible on screen, which causes a brief color flash. This is especially
noticeable when different child pages use different toolbar colors. The
behavior started after an earlier change where the appearance update was
added during tab switching but executed before the section change was
committed.
 
### Description of Change
The fix involves changing the execution order so the section change is
committed first, and the appearance update happens only after the tab
switch is successfully accepted. This ensures the Shell internal state
is updated to the correct tab before applying the toolbar colors,
preventing premature color changes while the previous tab is still
visible. The update is also skipped when the tab switch is rejected or
cancelled, so colors continue to refresh correctly on every tab switch,
but now at the correct time.

### Why Tests were not added:

**Regarding the test case:** No automated test case was added for this
fix, as the issue is a brief visual color flash that occurs during the
tab-switch animation on Android and cannot be reliably captured through
the existing automated test infrastructure.
 
Screenshot-based tests cannot consistently capture the exact transition
frame where the issue occurs, and the current baseline comparison
approach is unable to accurately differentiate correct and incorrect
toolbar color timing during the animation.

Tested the behavior in the following platforms.
 
- [x] Android
- [ ] Mac
- [ ] iOS
- [ ] Windows

**Regression PR:** The PR
[25870](#25870) as the primary
regression source. PR [32882](#32882)
appears to be a later overlapping change affecting Shell color timing.

The PR [25870](#25870) as the primary
regression source. PR [32882](#32882)
appears to be a later overlapping change affecting Shell color timing.

### Issues Fixed

Fixes #35060

### Output

| Before Issue Fix | After Issue Fix |
|----------|----------|
| <video width="270" height="600"
src="https://github.com/user-attachments/assets/44ea19d0-7aca-42b1-b936-506eb4609f33">
| <video width="270" height="600"
src="https://github.com/user-attachments/assets/f1c8b6b4-44b5-4f6b-9daa-988daa33b90c">
|
Appears to be unused, but trips up analyzers when investigating other
issues.

Fixes: xamarin/Xamarin.Forms#1749

---------
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Issue Details
In FlyoutPage, RTL FlowDirection is not working properly at initial and
runtime changes.

### Description of Changes
* Added an `UpdateFlowDirection` method to set the correct
`SemanticContentAttribute` on the root view, child controller views, and
navigation bar, ensuring proper mirroring for RTL layouts and correct
inheritance by child handlers.
* Updated property change handling to listen for `FlowDirection` changes
and trigger both `UpdateFlowDirection` and `UpdateLeftBarButton` when
flow direction is updated at runtime.
* Called `UpdateFlowDirection` during gesture recognizer setup to ensure
the correct flow direction is applied during initialization.

### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes #34830 

<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->

**Tested the behavior in the following platforms.**
- [x] Android
- [x] Windows
- [x] iOS
- [x] Mac

| Before  | After  |
|---------|--------|
| **iOS**<br> <video
src="https://github.com/user-attachments/assets/f9a9cc1a-3320-4e8b-9c6c-3702c585b5c2"
width="300" height="600"> | **iOS**<br> <video
src="https://github.com/user-attachments/assets/7da0ba09-557f-46bd-81b7-2fed1295b61d"
width="300" height="600"> |
…dler disconnect (#35314)

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

### Description of Change

Fixes #27101.

When a `Page` is navigated away from on Windows, the focused control's
handler is disconnected.
`ElementHandler.IElementHandler.DisconnectHandler()` (in
`src/Core/src/Handlers/Element/ElementHandler.cs`) sets `PlatformView =
null` **before** running the platform-specific disconnect chain.

On Windows, that chain calls `UpdateIsFocused(false)`
(`src/Core/src/Handlers/View/ViewHandler.Windows.cs`), which then sets
`virtualView.IsFocused = false`. That property change cascades through:

```
BindableObject.SetValue
  → OnIsFocusedPropertyChanged
    → ChangeVisualState
      → VisualStateManager.GoToState
        → Setter.Apply (e.g. background color)
          → BindableObject.SetValue (mapped property)
            → Handler.UpdateValue("Background")
              → Property mapper
                → handler.PlatformView ← throws "PlatformView cannot be null here"
```

The strongly-typed `PlatformView` accessor (`ViewHandlerOfT.cs`,
`ElementHandlerOfT.cs`) throws `InvalidOperationException("PlatformView
cannot be null here")` because we already nulled it.

### Fix

Introduce a new `Disconnecting` state on `ElementHandlerState` and set
it for the duration of `DisconnectHandler(oldPlatformView)`.
`ElementHandler.UpdateValue` short-circuits when the handler is in that
state, so property fan-outs during teardown no longer hit the mapper
(and therefore no longer touch the released platform view).

The state is restored in a `finally` block so a throwing platform
disconnect cannot leave the handler stuck in `Disconnecting`.

Logical state on the `BindableObject` itself still gets updated by VSM
setters (so reused/rebound elements remain correct — when a recycled
element is reconnected, `SetVirtualView` re-pushes all current property
values via `_mapper.UpdateProperties`).

### Why fix here, not in `VisualElement.ChangeVisualState()` (or
`Page.cs`)?

Several controls override `ChangeVisualState()` and call
`VisualStateManager.GoToState` directly, bypassing the base
implementation (`Button`, `ImageButton`, `CheckBox`, `RadioButton`,
`Switch`). Guarding in `ChangeVisualState` would miss those paths, and a
naive `Handler is null` check would also regress legitimate pre-handler
XAML init scenarios (`VisualStateGroupsPropertyChanged`,
`VisualStateGroupList.OnStatesChanged`).

`ElementHandler.UpdateValue` is the central choke point that all
property cascades flow through, so the guard there:
- covers every code path on every platform,
- still lets VSM logical state settle on the `BindableObject`,
- requires no changes in any control-specific override.

### Tests

- **Unit test**:
`Microsoft.Maui.UnitTests.AbstractViewHandlerTests.UpdateValueIsSkippedWhileHandlerIsDisconnecting`
simulates the property fan-out from inside `DisconnectHandler` and
asserts the mapper is not invoked. Verified that the test fails on the
unmodified code (mapper count goes from 0 to 1) and passes after the
fix.
- **UI test**:
`Microsoft.Maui.TestCases.Tests.Issues.Issue27101.NoCrashWhenNavigatingBackFromPageWithFocusedButton`
repeatedly navigates a `NavigationPage` containing a focused `Button`
styled with VSM (Normal/Focused/Disabled/PointerOver/Pressed) to surface
the focus race.

### Relation to #27877

This PR supersedes #27877. That PR wrapped the post-disconnect
`ChangeVisualState` call in `try { ... } catch (ObjectDisposedException)
{ }` inside `Page.cs`. Two issues with that approach:
1. The actual exception thrown is `InvalidOperationException`, not
`ObjectDisposedException`, so the catch likely never fires for the
reported scenario (matches the unanswered review feedback from MartyIX
on 2025‑05‑30).
2. Even with the right exception type, that's a band-aid: it lets the
failing call happen, swallows the symptom, and only patches one of many
code paths that could trip the same issue (any subclass that triggers a
property change during disconnect).

### Issues Fixed

Fixes #27101
Supersedes #27877

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PureWeen and others added 24 commits July 7, 2026 12:41
…36410)

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

## Why
The release-readiness regression classifier could report a **CLOSED**
issue as an active `open-on-main` regression (Tier 2 — "wait for main
merge, then backport"). That's contradictory: an unmerged OPEN PR cannot
have closed a completed issue. It happens when a giant still-open
"Candidate" changelog PR `Fixes`-lists dozens of issues — its OPEN state
gets attributed to an already-completed issue. Real-world: issue #35615
(CLOSED/COMPLETED) surfaced under `open-on-main` in the SR9 tracker
while candidate PR #35716 was still open.

## What
`Classify-RegressionCandidate` now enforces: **never emit `open-on-main`
for a CLOSED issue.** When the aggregated best verdict is exactly
`open-on-main` and the issue is CLOSED:
1. First try the same comment-prose recovery the empty-candidate path
uses — a merged fix verifiably present on the SR wins →
`closed-fix-unlinked` (Tier 3, "no ship risk; add a closing reference
for traceability").
2. Otherwise fall to the honest `no-fix-yet` (Tier 3 for a CLOSED issue
via the existing `Get-OverallVerdict` downgrade) — the automation can't
pin a verified fix on this SR and the open candidate hasn't merged.

The shared recovery logic is extracted into a new
`Resolve-ClosedFixUnlinked` helper, called from both the
empty-`strongPrs` CLOSED path and the new guard, preserving the
fix-phrase gate, merged-on-SR gate, revert guards, and tooling-only
skips exactly.

**Strictly scoped:** only `open-on-main` + CLOSED is contradictory.
Every other verdict (`merged-on-main-no-backport`,
`backport-in-progress`, `rejected-from-sr`, `in-sr-*`,
`needs-human-review`) is unchanged even for CLOSED issues — those remain
legitimately actionable (the SR may still need the backport).
Genuinely-OPEN issues still get `open-on-main`.

## Tests
Three new unit tests in `Test-ReleaseReadiness.ps1`:
1. CLOSED issue + OPEN candidate on main + no comment fix →
`no-fix-yet`, proven non-blocking (🟢) via `Get-OverallVerdict`.
2. CLOSED issue + OPEN candidate + comment-cited merged fix on SR →
`closed-fix-unlinked` (recovery wins).
3. Regression guard: OPEN issue + OPEN candidate → stays `open-on-main`.

Full suite green (829/0).

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

---------

Co-authored-by: PureWeen <223556219+Copilot@users.noreply.github.com>
<!-- 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!

> [!NOTE]
> 🤖 **AI-generated PR.** These workflows, and this description, were
produced with AI assistance. Please review carefully before merging.

## Summary

Adds two [gh-aw](https://github.com/github/gh-aw) agentic workflows that
proactively **find** and **fix** managed, cross-platform memory leaks in
MAUI — turning the AI-driven leak-hunting flow several contributors
already run by hand (scan → repro → measure → file → fix) into a
repeatable, reviewable pipeline.

**Design principle: only empirically-proven leaks.** The scanner files
an issue **only** when a plain `dotnet test` demonstrates the leak; the
fixer opens a PR **only** with a red→green regression test proving the
fix. There is no "you're missing a test" / coverage-gap mode — every
artifact is backed by a passing/failing test.

**Key property:** neither workflow needs an emulator/simulator or a MAUI
source build to *detect* a leak — the leak signature is proven by a
`dotnet test` against the **shipped `Microsoft.Maui.Controls` NuGet
package** on the plain library TFM, so detection runs on a standard
GitHub-hosted runner.

Only 4 files are added — the workflow source (`.md`) and its compiled
artifact (`.lock.yml`) for each:

```
.github/workflows/daily-leak-hunter.md   + .lock.yml
.github/workflows/leak-fixer.md          + .lock.yml
```

---

## 1. `daily-leak-hunter` — the scanner (files `[leak-scan]` issues)

**Trigger:** `schedule: every 12h` + manual (`workflow_dispatch`). Files
**only empirically-proven `[leak-scan]` issues** — up to **8 per run**.

1. **Sweeps every focus area** of the managed surface (`src/Core/src`,
`src/Controls/src/Core`, `src/Essentials/src`) in one run — it does not
stop after the first find.
2. Each focus area is **seeded with proven MAUI leak signatures** (from
the community's known-leak catalog) so the hunt targets where leaks
actually live: shared publisher → strong
`CollectionChanged`/`PropertyChanged` (e.g. `Picker.ItemsSource`,
`TableRoot`, `SelectedItems`, `GradientStops`), shared `ICommand` →
`CanExecuteChanged` (`ListView.RefreshCommand`, `SwipeItemView.Command`,
`BackButtonBehavior.Command`), VSM state triggers →
`DeviceDisplay.MainDisplayInfoChanged`, and static/`ResourceDictionary`
roots.
3. For each candidate it writes a standalone control / leaky /
mitigation xUnit repro (referencing the shipped package, no source
build) and measures retention with `WeakReference` + a forced GC. All
candidates go in **one test project** (one `[Fact]` each, one
restore/run).
4. It files a `[leak-scan]` issue **only for each leak the test
confirms** — the leaky scenario retains while **both** the control and
the mitigation release. A false positive is treated as worse than a
quiet run; unconfirmed candidates are dropped. **If a run proves
nothing, it files nothing** (no fallback mode).

**De-dup & safety:** skips a leak already covered by one of *its own*
open `[leak-scan]` issues; never touches product or test code (read-only
+ `create-issue`); `noop: report-as-issue: false`.

---

## 2. `leak-fixer` — the fixer (opens draft `[leak-fix]` PRs)

**Trigger:** `schedule: every 12h` + manual (`workflow_dispatch`). At
most **one action per run**, with de-dup + a 3-attempt cap. It picks the
**higher-priority** of two tracks:

### Track C — respond to review feedback (checked first)
Before doing new work, it checks whether one of *its own* open
`[leak-fix]` PRs (**hosted on this repo** — fork-hosted heads it can't
push to are left for a human) has an **unaddressed `CHANGES_REQUESTED`
review**. If so it works on that PR: **applies** the valid requested
changes (pushes a commit, re-validated so the PR's own test still holds)
and/or **posts a comment pushing back** on any request that is wrong,
would regress behaviour, or asks for a mute. A loop-guard (act only on a
review newer than the PR's last commit *and* the workflow's last
comment) prevents re-processing the same review.

### Track A — `[leak-scan]` runtime leak → `[leak-fix]` PR
For the selected proven leak it:
1. Writes a focused regression test in `Controls.Core.UnitTests`.
2. Builds MAUI **from source** and confirms the test **FAILS on `main`**
— proving it catches the leak.
3. Implements the minimal, idiomatic managed fix — a weak subscription /
teardown mirroring the existing `WeakEventManager` / `WeakNotify*Proxy`
patterns.
4. Rebuilds and confirms the **same test now PASSES**, no neighbouring
regressions.
5. Opens a **draft `[leak-fix]` PR** with `Fixes #N` and the red→green
evidence.

### Safety
- Enforces red→green **in both directions** — a fix without a
demonstrated failing-then-passing test is rejected.
- **Never** mutes / skips / disables a test; rejects its own attempt if
the only thing that goes green is a mute — **including when a review
asks for one** (it pushes back with a comment instead).
- **One action per run** — Track C review-response *or* one new Track A
`[leak-fix]` PR, never both.
- Writes only via scoped safe-outputs (agent job is read-only); PR
branches are limited to `leak-fix/**`, changes to managed `src/**` (+
`PublicAPI.Unshipped.txt`).
- Skips cleanly if already fixed on `main`, out of scope (needs a device
fix), or attempt-capped.

---

## Validated end-to-end on **dotnet/maui itself** (pre-merge)

Both workflows were executed on this repo's own Actions infrastructure
(via short-lived `push`-triggered test branches, since
`workflow_dispatch` only lights up once a workflow is on `main`). Real
outputs, all on **dotnet/maui**:

- **Multi-leak in one run** — a single hunter run filed **5** distinct
proven `[leak-scan]` issues via the catalog-seeded sweep: #36343
`SwipeItemView.Command`, #36344 `ListView.RefreshCommand`, #36345 `Shell
BackButtonBehavior.Command` (all `ICommand.CanExecuteChanged`), #36346
`Picker.ItemsSource`, #36347 `IndicatorView.ItemsSource`
(`CollectionChanged`). #36344 and #36345 are leaks not previously
tracked upstream.
- **Proven-only** — a later run filed `[leak-scan]` #36350
`CollectionView.SelectedItems` and (correctly) **nothing else**, having
de-duped the already-filed leaks.
- **Track A fix** — #36309 `[leak-fix] Fix ResourceDictionary
MergedDictionaries memory leak` (regression test red→green + managed
fix).
- **Track C review-response** — on #36253 the fixer independently
re-read the code, agreed with the reviewer's findings, validated the
reviewer's `WeakEventManager` approach (24/24, red→green), and posted
the concrete fix.

---

## Notes for reviewers

- Everything the workflows file/open is **AI-generated** and clearly
labelled as such; treat them as high-quality drafts for human review.
- The workflows are self-contained (`.md` source + compiled
`.lock.yml`); merging them enables the `schedule` triggers on `main`.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Backport of #35049 to main

/cc @PureWeen

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Updated [Magick.NET-Q8-AnyCPU](https://github.com/dlemstra/Magick.NET)
from 14.13.1 to 14.14.0.

<details>
<summary>Release notes</summary>

_Sourced from [Magick.NET-Q8-AnyCPU's
releases](https://github.com/dlemstra/Magick.NET/releases)._

## 14.14.0

### What's Changed
- Renamed `ExifParts` to `ExifIfds` and renamed `ExifProfile.Parts` to
`ExifProfile.AllowedIfds` and mark the old names as obsolete.
- Store the exif ifd in the ExifTag to make prevent marking a tag as
duplicate when it is found in another ifd (#​2017)
- Fixed Regression: Clip() doesn't take the correct bounding box
(#​2019)
- Fixed Regression: ColorType is no longer persisted (#​2020)
- Fixed Native DLLs (Magick.Native-Q16-*.dll) appear at project root in
Solution Explorer on .NET Framework 4.8 (#​2029)

### Related changes in ImageMagick since the last release of Magick.NET:
- Heap Buffer Underwrite in Floyd-Steinberg depth dithering
(GHSA-2hhq-c99x-492r)
- Stack Overflow in MVG decoder
(GHSA-h36c-3666-h489)
- Infinite Loop in subimage-search with crafted image
(GHSA-5v62-8fq6-cp9m)
- Policy Bypass in DCM decoder could result in image with invalid
dimensions
(GHSA-8pj9-6897-74xc)
- Policy Bypass can read disallowed files
(GHSA-xcjm-wqff-m669)
- Heap Buffer Over-Write in MAT decoder on 32-bit systems
(GHSA-4v89-6mgq-6rgc)
- Policy Bypass can trigger out-of-Memory condition
(GHSA-q62c-h75r-2xhc)
- Heap Buffer Over-Write in ICON decoder due to incorrect loop
(GHSA-g22q-f7gc-5jhr)
- Use-After-Free when allocation in CheckPrimitiveExtent fails
(GHSA-px7q-ggqj-hcf2)
- Null Pointer Dereference in distort operation when passing incorrect
arguments
(GHSA-p9rq-q46c-g4x6)
- Memory Leak in wand option parser when providing invalid arguments
(GHSA-j989-f892-2335)
- Heap Buffer Over-Write in SF3 encoder when writing multi-frame image
(GHSA-44cp-c3ww-9rv5)

### Library updates:
- ImageMagick 7.1.2-25 (2026-06-04)
- aom 3.14.1 (2026-05-22)
- libde265 1.1.0 (2026-05-26)
- openexr 3.4.12 (2026-05-25)
- libheif 1.23.0 (2026-05-29)

**Full Changelog**:
dlemstra/Magick.NET@14.13.1...14.14.0

Commits viewable in [compare
view](dlemstra/Magick.NET@14.13.1...14.14.0).
</details>

[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=Magick.NET-Q8-AnyCPU&package-manager=nuget&previous-version=14.13.1&new-version=14.14.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts page](https://github.com/dotnet/maui/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…empt human hand-off) (#36317)

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

Today the `ci-status-fix` agentic workflows (`main` + `net11.0`) open
**one** `[ci-fix]` PR per failing-CI tracking issue and then **hand off
to a human forever** — nobody re-checks whether the fix actually turned
CI green, and if it didn't, the loop never tries again.

This PR turns them into a **self-watching loop**: on each scheduled poll
the workflow re-reads the CI of the PR it already opened, and — when the
failure is caused by its own fix — pushes a **fresh follow-up commit
onto the same PR**, up to **10 attempts**, with **no human intervention
between attempts**. It stays **one PR per issue, forever** (never a
second PR), and stops + hands off to humans only after 10 attempts.

Round 1 keeps a human in the loop for exactly one thing: typing `/azp
run maui-pr` to kick CI on each new commit (a `GITHUB_TOKEN` push
doesn't fire CI). Everything else — watch, classify, re-fix — is
autonomous. Auto-CI-triggering is deferred to Phase 2 (see the
`GH_AW_CI_TRIGGER_TOKEN` note under **Validation & operation**).

The loop also **responds to maintainer review feedback** (Track C): when
a human maintainer (OWNER/MEMBER/COLLABORATOR relationship) formally
requests changes on the open PR, it applies the valid findings and
pushes back on the rest — one push + one comment — rather than sitting
idle waiting for a human to also make the edit.

## How it works

```mermaid
flowchart TD
    A[Scheduled poll every 12h] --> B[pre_activation job:<br/>Query-CiFixPRs.ps1 prefetch]
    B --> C{Open ci-fix PR<br/>for this issue?}
    C -- No --> D[FRESH: open ONE draft PR<br/>seed marker 1/10]
    C -- Yes --> E{Watch state machine}
    E -- Maintainer (OWNER/MEMBER/<br/>COLLABORATOR) requested changes<br/>gate 0, highest priority --> M[Track C: apply valid findings,<br/>push back on rest —<br/>one push + comment]
    E -- Human commented / pushed / reviewed --> F[Skip — defer to human]
    E -- CI still pending --> G[Wait, re-check next poll]
    E -- CI green --> H[Surface + comment, don't advance]
    E -- CI red --> I{Classify red on<br/>the PR's OWN build}
    I -- Unrelated flake --> J[Annotate + comment,<br/>don't burn an attempt]
    I -- Caused by the fix & attempt < 10 --> K[ADVANCE: push follow-up commit<br/>to SAME branch, bump marker N+1/10]
    I -- attempt = 10 --> L[Hand off: needs-human]
```

### 1. Deterministic prefetch — `.github/scripts/Query-CiFixPRs.ps1`
(new)
Runs in a pre-agent `pre_activation` job (not by the model), so it never
executes PR-controlled code. It lists open `ci-fix/**` PRs and emits
JSON per PR: number, head ref/SHA, `refsIssue`, `isDraft`,
settled/overall CI conclusion, per-leg failures, a `humanEngaged` flag
(reviews/comments/non-bot pushes), and the parsed `<!-- ci-fix-attempts:
N/10 -->` marker. The agent consumes this via
`needs.pre_activation.outputs.ci_fix_candidates` — no blind re-querying.

### 2. Attempt counter = a body marker
Each PR body carries `<!-- ci-fix-attempts: N/10 -->`. FRESH seeds
`1/10`; every ADVANCE bumps it via `update-pull-request`. If the marker
is ever missing, the agent reconstructs the count from the bot commits
already on the branch.

### 3. Keep-ONE-PR advance (`push-to-pull-request-branch`)
When a red build is classified as caused by the fix, the agent checks
out the **existing** PR branch at its remote tip (guarded by a head-SHA
equality check against the classified commit), commits **one new,
distinct** fix, and uses `push-to-pull-request-branch` to add it to the
same PR — plus `update-pull-request` (marker bump) and `add-comment`
(attempt note). It never opens a second PR.

### 4. The watch state machine (per open PR)
Ordered gates: human-engaged → skip · CI pending/unknown → wait · green
→ surface + comment (don't advance) · red → classify on the PR's **own**
build (`maui-pr` for `C.headSha`): unrelated-flake → annotate, don't
burn an attempt; caused-by-fix → advance if `attempt < 10`, else hand
off `[ci-fix][needs-human]`.

### 5. `net11.0` twin
`ci-status-fix-net11.md` mirrors the redesign for the `net11.0` branch:
base/label/scanner token swaps, cross-reference inversions, and the
net11-only `checkout.fetch: [net11.0]` + transport-patch-cap rationale
(a `main`-based patch for a `net11.0` fix would exceed gh-aw's 10 MB
cap, which is why the two are separate workflows).

### 6. Review-response (Track C — respond to a maintainer's
change-request)
Gate 0 of the watch state machine (highest priority, checked before
"human engaged"): a `CHANGES_REQUESTED` review is the ONE case where a
human touching the PR means *"act on my instruction"*, not *"back off"*.
When the open PR carries an un-addressed change request from a **human
maintainer with an established repo relationship**
(OWNER/MEMBER/COLLABORATOR), the agent reads the findings, classifies
each as **APPLY** (correct + in-bounds) or **PUSH BACK** (wrong / out of
area bounds / would weaken a test), applies only the APPLY items in
place, and emits one push + one comment listing what it did and why it
declined the rest. Guards:
- **Author filter (the hardening):** only reviews where `state ==
"CHANGES_REQUESTED"` **and** `user.type == "User"` (and the author is
not in the bot denylist) **and** `author_association ∈ {OWNER, MEMBER,
COLLABORATOR}` are actionable. Bot reviews and drive-by reviews from
`CONTRIBUTOR`/`FIRST_TIME_CONTRIBUTOR`/`NONE` accounts are ignored. Note
`author_association` is a **coarse relationship signal, not a per-repo
write-permission check** — it is a cheap pre-filter, not an
authorization gate. The real safety comes from the narrow APPLY bounds
below (`src/**` + PublicAPI only, never mute a test), a defense-in-depth
carve-out to Hard-Rule 10 ("review content is untrusted by default").
- **Per-review idempotency:** each maintainer review is answered at most
once. The agent stamps the answered review id into its response comment
(`<!-- ci-fix-track-c-responded: <RID> -->`); the deterministic prefetch
fully paginates the PR's comments and hands the agent the set of
already-answered ids (`respondedTrackCReviewIds`), so a review is
actionable only if its id is **not** in that set. A secondary
`submitted_at >` last-commit guard remains on the APPLY path. This
replaces the earlier "newer than the last bot comment" timestamp
heuristic, which could silently re-fire the same decline every run on a
PR with more than one page (100) of comments.
- **Attempt accounting:** an APPLY commit counts toward the same `≤ 10`
bot-commits-per-PR ceiling; a PUSH-BACK-only response makes no commit
and consumes no attempt. In-bounds means `src/**` + PublicAPI only,
never muting a test.
- Reuses the existing ADVANCE emit trio (push + marker bump + comment) —
**no new safe-output, permission, or config surface** (the compiled-lock
delta is `body_hash` only).

## Safety &amp; scoping

The loop runs **live** (`safe-outputs.staged` is not set) and holds
`pull-requests: write`, so every write path is scoped at the **handler**
level, not just by prompt text:

| Safe output | Handler-level scope |
|---|---|
| `push-to-pull-request-branch` (code) | `required-title-prefix:
"[ci-fix] "` + `required-labels: [agentic-workflows]` + `allowed-files`
allowlist |
| `add-comment` | `required-title-prefix: "[ci-fix] "` +
`required-labels: [agentic-workflows]`, `discussions: false` |
| `update-pull-request` | `title: false` → `allow_title:false` (no
retitles); body-marker edits only |
| `create-pull-request` | `[ci-fix]` title prefix + `agentic-workflows`
label; `allow-empty` intentionally unset |

- **Reads / triggers are integrity-gated** — `min-integrity: approved`
filters PR/issue content to trusted authors (OWNER/MEMBER/COLLABORATOR)
before the agent ever sees it.
- **Review-response is relationship-gated (Track C):** the sole path
that treats review *content* as an instruction requires a
`CHANGES_REQUESTED` review from a human (`user.type == "User"`, not in
the bot denylist) whose `author_association ∈ {OWNER, MEMBER,
COLLABORATOR}` — a coarse relationship pre-filter, **not** a per-repo
write-permission check — and even then may only touch `src/**` +
PublicAPI and may never mute a test. Bot and
outside/first-time-contributor reviews stay non-actionable.
- **`update-pull-request` caveat (documented inline in both twins):**
gh-aw v0.79.8's compiler silently drops
`required-title-prefix`/`required-labels` from *this one* handler —
verified against the compiled `.lock.yml`, unlike `add-comment` /
`push-to-pull-request-branch` which do emit them. So it can't be
prefix-locked the same way; we remove the sharpest capability instead
(`title: false`), leaving only body-marker edits on an already
`min-integrity:approved` target.
- **Keep-ONE-PR / no duplicates:** `create-pull-request` does not set
`allow-empty`, and the fixer dedups by `Refs: dotnet/maui#<N>` + issue
number (the `ci-scan-fingerprint` marker is *optional* and never the
dedup key), so a re-poll advances the existing branch instead of
spawning a second — or empty — PR.
- **No cost guardrail:** `max-ai-credits` / `max-daily-ai-credits` are
`-1` (unlimited). A live auto-fixer must not silently stall mid-loop on
a budget cap — a deliberate maintainer trade-off, not an oversight.

## Validation &amp; operation (round 1)

1. **Scoped dispatch:** Actions → "CI Failure Fixer (main)" → Run
workflow → `issue_number` (e.g. `36180`) drives one issue; empty runs
the full scheduled sweep. A `dry_run` input gives a live-but-write-free
canary.
2. **Exercise the commit:** when the loop pushes, a maintainer types
`/azp run maui-pr` (+ the gated `maui-pr-uitests` /
`maui-pr-devicetests` legs when relevant). A bare `/azp run` does
**not** count as human engagement, so the loop keeps watching.
3. **Open item — `GH_AW_CI_TRIGGER_TOKEN`:** a bot
`push-to-pull-request-branch` uses `GITHUB_TOKEN`, which does **not**
re-fire the AzDO `maui-pr` webhook build — that's why the manual `/azp
run` exists in round 1. A provisioned PAT/OIDC trigger token (Phase 2)
would remove that human step; **until then the token is a harmless
no-op** and the `/azp run` step is the expected round-1 behavior.

## Files
| File | Change |
|------|--------|
| `.github/scripts/Query-CiFixPRs.ps1` | **new** — deterministic
prefetch |
| `.github/workflows/ci-status-fix.md` (+`.lock.yml`) | keep-one loop +
watch state machine + Track C review-response + scoped safe-outputs |
| `.github/workflows/ci-status-fix-net11.md` (+`.lock.yml`) | net11.0
mirror |

Both `.lock.yml` were recompiled in the same commit as their `.md`
source (per pipeline security rules). No `cgmanifest.json` /
`templatestrings.json` changes.

---------

Co-authored-by: PureWeen <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…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>
…r) (#36460)

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

## Description

Adds **two net-new** [GitHub Agentic Workflows](https://gh.io/gh-aw)
that keep dotnet/maui's own gh-aw setup current. This is
**maintenance-only automation** — it does **not** touch the existing 11
gh-aw workflows, product code, or CI. Both are **credential-free** on
the write path (they use only the default `GITHUB_TOKEN` via
safe-outputs); the Copilot PAT pool is used solely for model
**inference**, exactly like every existing MAUI agentic workflow.

### Workflow A — `aw-actions-update.md` ("Action Pin Refresh")
Low-risk action-pin refresher.
- **Triggers:** weekly `schedule` + `workflow_dispatch`, pinned to
`main` (`checkout: ref: main`), fork-guarded.
- **What it does:** installs the gh-aw CLI **pinned to v0.80.9** (the
same `compiler_version` as the committed locks, so `actions-lock.json`
stays consistent with them — no version skew; failing closed if that
exact version can't be installed), runs `gh aw update`, then opens a PR
**only** when `.github/aw/actions-lock.json` changes (the SHA-pin
lockfile for the actions our workflows use).
- **Blast radius pinned:** `allowed-files` restricts the write to that
single file, and `protected-files: allowed` is scoped to just that one
path under `.github/`. Generated `.github/workflows/*.lock.yml` and any
source-workflow drift are explicitly discarded (including brand-new
**untracked** files, via a scoped `git clean`) and verified out with
`git status --porcelain` — this workflow can *only* touch the actions
lock.
- De-dupes against its **own** open `[actions]` PRs (scoped to `--author
"app/github-actions"`); no-ops quietly when there's nothing to update.

> **Known behavior (by design, not a defect):**
> - The PR only edits `.github/aw/actions-lock.json`. Because each
`.lock.yml` embeds its resolved action SHAs, the refreshed pins take
runtime effect only after the workflows are **recompiled** (`gh aw
compile`) — which is Workflow B's maintainer runbook. Pushing
regenerated `.lock.yml` files would require a GitHub-App `workflows:
write` token this credential-free workflow deliberately avoids.
> - Because the PR is created with the default `GITHUB_TOKEN`, it will
**not** auto-trigger CI (GitHub platform rule: `GITHUB_TOKEN` pushes
don't fire workflow events). A maintainer may need to nudge CI (e.g.,
close/reopen the PR or push an empty commit).

### Workflow B — `aw-version-update.md` ("Agentic Workflow Version
Auto-Update")
gh-aw version updater, **detect → file issue** design (fully automatic,
credential-free).
- **Triggers:** weekly `schedule` (live) + `workflow_dispatch`, pinned
to `main`, fork-guarded.
- **What it does:** a **read-only** detector (`permissions: { contents:
read, issues: read }`) installs/refreshes the gh-aw CLI **to the latest
release** (detecting newer versions is its whole purpose), runs `gh aw
upgrade` + `gh aw compile`, and checks whether that produces a non-empty
change set (`git status --porcelain`, so brand-new untracked locks are
caught). It then hard-resets its worktree so nothing leaks out of the
detection run.
- **If an upgrade is pending,** it emits exactly **one** `create-issue`
— a `[Auto Update]` tracking issue asking a maintainer to run `gh aw
upgrade` locally and open the PR. The issue is filed with the default
`GITHUB_TOKEN` (`issues: write`, granted to the separate safe-outputs
job). Steady state is `noop` (no issue, no noise).
- **De-dupes** against its **own** open `[Auto Update]` issues (scoped
to `--author "app/github-actions"`), and runs with `min-integrity:
approved` so community content stays out of the agent's view.

> **Why an issue instead of an automated PR (deliberate design):** `gh
aw upgrade` / `gh aw compile` regenerate `.github/workflows/*.lock.yml`,
which the default `GITHUB_TOKEN` cannot push (GitHub platform rule) and
which would otherwise require a Copilot-licensed / `workflow`-scoped
agent token this repo intentionally does **not** provision. Filing an
issue keeps the workflow fully automatic and credential-free; a
maintainer performs the actual upgrade. There is **no**
`create-agent-session`, `GH_AW_AGENT_TOKEN`, or staged-delegation step.

Both workflows reuse MAUI's existing Copilot PAT-pool import +
`copilot-pat-pool` environment (inference only), carry a fork guard
(won't run in forks), and were compiled with **gh-aw v0.80.9** — the
same `compiler_version` as all 11 existing locks, so no other lock files
are bumped.

## Validation
- ✅ `gh aw compile` — **0 errors, 0 warnings** on both.
- ✅ `gh aw lint` — clean across all workflows.
- ✅ gh-aw security review (gh-aw-guide scanner) — clean on both.
- ✅ Scope check — only the 4 new workflow files (2 `.md` + 2 generated
`.lock.yml`); the existing 11 workflows are untouched. No auto-generated
files (`cgmanifest.json`, `templatestrings.json`) changed.

## Rollout
- **Workflow A** can be enabled immediately — low-risk, single-file,
human-reviewed PRs.
- **Workflow B** is safe to run live on merge — its only possible write
is a single tracking issue for a maintainer; the actual upgrade is
always human-performed. No secrets to provision.

## Notes for reviewers
- No hardcoded PATs; both use the standard `COPILOT_PAT_0..9` pool via
the existing `shared/pat_pool.md` import (inference only).
- No auto-merge — every PR/issue these produce is
human-reviewed/actioned.
- `.lock.yml` files are auto-generated; regenerate via `gh aw compile
<name>`, never hand-edit.

<sub>Co-authored-by: Copilot App</sub>

---------

Co-authored-by: PureWeen <223556219+Copilot@users.noreply.github.com>
<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

## Summary

- Reverts the iOS layout changes from #34936 for `Default` and `Fixed`
`FlyoutHeaderBehavior`.
- Restores the prior overlapping ScrollView frame and top content inset,
allowing flyout items to scroll behind a semi-transparent header.
- Removes the regression test that asserted the reverted behavior.
- After merge, backport this commit to `release/10.0.1xx-sr9` for
10.0.90.

Fixes #36249

## Test plan

- `pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1
-Project Controls -Platform ios -TestFilter "Category=Shell"` (203
passed)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
<!-- 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!

Fixes #36154.

Forward-ports the final implementation and regression coverage from
#36231 to `main`, preserving the newer WebView lifecycle handling
already present on `main`.

The Android SwipeView now yields to a nested WebView while it can scroll
in the gesture direction, and handles the gesture at the WebView edge.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

## Summary

- Advances `main` from the SR9 `10.0.90` cycle to the SR10 `10.0.100`
cycle.
- Keeps `SdkBandVersion` and prerelease settings unchanged.
- Matches the previous cycle bumps in #35433 and #35879.

This clears the `Main bumped to SR10 cycle` release-readiness blocker
for 10.0.90.

_This PR was created by GitHub Copilot CLI on behalf of @kubaflo._

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

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

Follow-up to #36317 (the self-watching `ci-status-fix` loop). Today,
when a `[ci-fix]` draft PR's CI comes back green — or red only on
**unrelated** flakes — the loop posts a comment but **leaves the PR as a
draft forever**. A maintainer has to notice it, confirm the specific
fixed test actually passed, and flip it to ready. In practice these
validated-green draft PRs sit unreviewed for weeks.

This PR adds a **target-test verification + mark-ready gate (Step 3.6)**
to both twins (`main` + `net11.0`). When a draft `[ci-fix]` /
`[ci-fix-net11]` PR reaches the green-surface or unrelated-flake branch,
the loop now drills into the PR's **own** AzDO test-results for the
**specific test(s)** the fix targeted. If every target test is `Passed`
on ≥1 leg and `Failed` on none (VALIDATED-GREEN), it:

- posts a `🎯 Target test validated green on <headSha>` comment (naming
the test + legs + buildId), and
- transitions the draft PR to **ready for review**.

This is a **state transition only** — it never approves and never
merges; a human still reviews and merges. Overall red on **unrelated**
legs no longer keeps a validated fix parked as a draft.

## How it works

- **Preconditions** (all required): PR `isDraft == true`; it is
unmistakably this workflow's own PR (`[ci-fix]`/`[ci-fix-net11]` title
prefix **and** `agentic-workflows` label); and it was reached from the
green or **unrelated-flake** path (never from caused-by-fix — that path
advances an attempt instead).
- **T1 — identify target test(s)** from the `[ci-scan]` issue signature
+ the PR diff. If no specific test can be identified (e.g. a product
build-break), it records a skip — build-only fixes are validated by
overall-green, which the existing green branch already handles.
- **T2 — drill AzDO test-results** for the build(s) on the PR's current
head SHA, filtered to the target test's `testCaseTitle`. A test that
never ran (e.g. an `/azp`-gated `maui-pr-uitests`/`maui-pr-devicetests`
leg that wasn't kicked) is **not** validated — the loop records an
honest "not yet executed" skip and does **not** mark ready. No
overclaiming: a green *sibling* leg is not the target test.
- **T3 — mark ready + report**, guarded by a per-head-SHA idempotency
marker and the existing `dry_run` gate (dry-run emits nothing and
tallies `would-mark-ready`).

## Safe-output

Adds the `mark-pull-request-as-ready-for-review` safe-output to both
twins (`max: 3`, `target: "*"`, `required-title-prefix` +
`required-labels`). Unlike `update-pull-request`, this output's
`required-*` guards **do** survive the gh-aw v0.80.9 compile (verified
against the generated locks), so which-PR scoping is enforced at the
handler level in addition to the Step 3.6 preconditions and
`min-integrity: approved`. No gh-aw version bump is required — the
capability already exists at our pinned v0.80.9.

## Enabling fix — the loop's own create-PR commit no longer counts as
"human engaged"

While validating this feature against #36429 I found the mark-ready path
was **unreachable for every loop-owned draft PR**, and traced it to a
regression from #36317's own review-hardening.

gh-aw's `create_pull_request` builds a PR's initial commit through the
GitHub API, which stamps `author=github-actions[bot]` but
**`committer=web-flow`**. Commit `2f6b77b330` (in #36317) removed
`web-flow` from the prefetch's bot-login denylist so a maintainer
"Update branch" would correctly hand the PR off — but that also made
`Test-AnyHumanCommitActor`'s committer check read the loop's **own**
first commit as human engagement. Result: every freshly opened
`[ci-fix]` draft PR computed `humanEngaged=true` from commit #1, so the
watch loop skipped it forever — never surfacing green, never marking
ready. This regression is live on `main` today (all four open loop-owned
draft PRs have `committer=web-flow`).

Fix (`Query-CiFixPRs.ps1`): a human **committer** only trips the
hand-off when the commit **author** is not one of this workflow's own
bot identities (`$LoopBotCommitAuthors`). A human **author** still
counts unconditionally, so maintainer direct commits and
web-flow-authored "Update branch" merges continue to hand off correctly.
Unit-tested across all six author/committer shapes.

## Validation

- Both twins recompiled with `gh aw compile` (0 errors / 0 warnings);
locks show no action-SHA or `compiler_version` drift (only
frontmatter/body hash + the new handler config).
- `mark_pull_request_as_ready_for_review` config confirmed present in
**both** locks with the correct `[ci-fix] ` / `[ci-fix-net11] ` prefixes
and `agentic-workflows` label; the `safe_outputs` job carries
`pull-requests: write`.
- Twin symmetry preserved (only `[ci-fix]` ↔ `[ci-fix-net11]` /
`ci-scan` ↔ `ci-scan-net11` token differences).
- **Live dry-run against #36429** (scoped `workflow_dispatch`,
`dry_run=true`, this branch): the workflow prefetch now computes
`humanEngaged=false` for #36429 (was `true` pre-fix), and the agent's
gate walk correctly advances **past** the human-engaged gate to the
CI-pending WAIT gate — `checksSettled=false` because #36429's macOS
`SafeAreaEdges` leg is still queued. Once that leg settles green, the
same run path reaches Step 3.6 and marks the PR ready. Emitted zero
writes (dry-run).

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

---------

Co-authored-by: PureWeen <223556219+Copilot@users.noreply.github.com>
…le grouped comments (#36533)

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

The `/review tests` **Test Failure Analysis** workflow classifies
whether a PR's CI failures are PR-caused or unrelated. Its deterministic
gate computed `legsRegressedVsBase` — the "Likely PR-caused" headline —
by comparing each red PR leg against **a single most-recent base
build**. Flaky UI-test legs that happened to be green on that one base
build produced false **"regressed-vs-base / Likely PR-caused"**
positives, so the generated comments carried little signal.

This PR makes the regression diff sample **several recent base builds of
the PR's own base branch** (`main` or `net11.0`) and only calls a leg a
regression when it is green across enough of them and red on none — and
switches the LLM engine to Opus. It also **replaces the per-test result
table with a skimmable, root-cause-grouped bullet list** (the
deep-UI-test-analysis style) so a run with hundreds of failures
collapses to a handful of readable groups inside the existing compact
badge + collapsible header instead of a giant table.

### Changes

- **`Gather-TestFailureContext.ps1`** — new `Get-AggregatedBaseLegMap`
aggregator + params `RegressionBaseBuilds=5` and
`MinBaseGreenSamples=2`. A leg is a regression only if it is green on
**≥ `MinBaseGreenSamples`** base builds **and red on NONE**. Per base
build, a leg that failed even one attempt counts RED (a retry that later
passed does not clear a base flake). New leg states `flaky-on-base` /
`succeeded-on-base-unconfirmed` and new per-failure fields
`baseSampleCount` / `baseGreenCount` / `baseFailedCount`.
- **Asymmetric conservatism (preserved)** — multi-build sampling widens
**only** the false-RED (assert-a-regression) side. The test-level
dismissal / false-GREEN side stays single-build-strict on purpose, so
this change never turns a real failure green.
- **`copilot-review-tests.md` / `.lock.yml`** — engine model
`claude-sonnet-4.6` → `claude-opus-4.8`; **replaced only the per-test
Markdown table** (an unreadable wall of text on runs with hundreds of
failures) with a deep-UI-test-analysis style **root-cause-grouped**
bullet list using subtle tokens (`✗ PR-related` / `ℹ Uncertain` / `●
Unrelated`). The compact **badge row and the `Test Failure Review:
[verdict] - click to expand` collapsible are kept** so the collapsed
comment stays a one-glance summary; the badge set now surfaces the key
multi-build signal — `Overall` / `Failures` / **`Regressed vs base`**
(replacing the old `Platform` badge) / `Baseline on base`. Everything
detailed (verdict sentence, grouped bullets, coverage counts, this-PR +
base-sampling build links, recommended action) lives inside the
collapsible. `img.shields.io` stays in the network allowlist. Lock
recompiled (gh-aw v0.80.9).
- **`SKILL.md` / `maui-ci-facts.md`** — document the multi-build
sampling, the new states/fields, and soften the "green on base is proof"
wording to require green across several base builds and red on none.

### Validation

Ran the gatherer locally against **PR #36478** (base `main`), before vs
after:

| | `legsRegressedVsBase` | Verdict |
|---|---|---|
| **Before** (single base build) | **14** | Not ready |
| **After** (5 base builds) | **0** | Needs human investigation |

The `ValidateDynamic*` (×9), `CollectionViewInfiniteScroll`, and
`Issue17400` legs — each green on 4/5 base builds and red on 1/5 — are
now correctly classified as `flaky-on-base` instead of PR regressions.

### Notes

- Extracted as a focused, standalone change from the broader
`improved-reviewer` work (#36473).
- CI-tooling-only change (`.github/`); no framework/runtime code is
touched.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… twin) (#36601)

> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

## Problem

The **net11 twin** of the CI Failure Fixer (`ci-status-fix-net11`) has
been failing in its `safe_outputs` job on most scheduled runs since
~2026-07-10 (red on 07-10, 07-12, 07-13, 07-14, and 07-15 ×2). Every
failure is the same error:

```
Cannot create pull request: patch modifies files outside the allowed-files list
(src/AI/tests/Essentials.AI.UnitTests/...)
```

`net11.0` gained a new **`src/AI/`** source tree (Essentials.AI /
streaming-JSON) that **does not exist on `main`** (it 404s there). The
agent repeatedly finds a flaky `Essentials.AI.UnitTests` test, builds a
valid fix, and emits `create_pull_request` — but `src/AI/**` was never
added to the enforced `allowed-files` allowlist. The handler rejects the
patch, so:

- the whole scheduled run goes **red** every time,
- that AI-test flake can **never** be filed, and
- one create-PR slot is **burned** each sweep.

The `main` twin is unaffected because there is no `src/AI/` tree on
`main`.

## Fix

Add `src/AI/**` to **both** `allowed-files` blocks
(`create-pull-request` and `push-to-pull-request-branch`) in **both**
twins:

- On **net11.0** it unblocks the failing PR creation.
- On **main** it is a harmless no-op (no `src/AI/` tree exists there to
match) and keeps the two workflow files byte-identical per the
established mirror invariant — future-proofing a forward-port of the AI
tree.

Both `.lock.yml` files were recompiled with `gh aw compile` (0 errors /
0 warnings). The only substantive lock delta is the prepended
`"src/AI/**"` entry in the two `allowed_files` arrays (plus the metadata
hash line). No other handler config changed — `max` values, `add_labels
allowed:[p/0]`, `mark_pull_request_as_ready_for_review`,
`protected_files`, `base_branch`, and `title_prefix` are all unchanged.

## Verification

- `gh aw compile ci-status-fix` and `gh aw compile ci-status-fix-net11`
→ **0 errors / 0 warnings** each.
- Twin-symmetry invariant preserved (transformed-net11 vs main diff =
**124 lines**, the pre-existing cosmetic delta — unchanged by this PR).
- `gh aw lint` → no lint issues.
- Confirmed via lock diff that the sole config change is `"src/AI/**"`
prepended to the two `allowed_files` arrays in each twin.

Co-authored-by: PureWeen <223556219+Copilot@users.noreply.github.com>
…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>
<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

## Summary

The live manual validation of Action Pin Refresh ([run
29052735587](https://github.com/dotnet/maui/actions/runs/29052735587))
correctly produced a no-op: the runner had gh-aw v0.81.6 preinstalled,
but the v0.80.9-compiled workflow needed its matching CLI. `gh extension
remove gh-aw` could not authenticate because the agent had only the
Copilot inference PAT, and direct binary download was blocked.

This follow-up supplies the agent's `GH_TOKEN` from the workflow's
existing read-only `${{ github.token }}` permissions, leaving the
Copilot PAT limited to inference. It also reads and validates the
required gh-aw version from `aw-actions-update.lock.yml` metadata,
avoiding manual pin drift when the dedicated version-updater changes the
compiler version.

## Scope

- `.github/workflows/aw-actions-update.md`
- Regenerated `.github/workflows/aw-actions-update.lock.yml` (compiled
with gh-aw v0.80.9)

The safe-output remains constrained to `.github/aw/actions-lock.json`;
permissions and write scope are unchanged.

## Validation

- `gh aw compile aw-actions-update`
- gh-aw workflow security scanner: `aw-actions-update.md` passed
- `gh aw lint .github/workflows/aw-actions-update.lock.yml` reported no
lint issues; its local actionlint integration exited 125 due to a
tooling error.

Co-authored-by: PureWeen <223556219+Copilot@users.noreply.github.com>
…ry inline-findings.json write (#36002)

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

Fixes to the Copilot PR-reviewer pipeline, driven by analyzing **64
recent `maui-copilot` PR-reviewer CI sessions** (most-recent run per PR,
last 7 days). Three independent analysis agents mined disjoint batches
and **converged on the same top two systemic issues** — strong signal
these are structural, not anecdotal. Fixes #1 and #2 are
**prompt/instruction-only**; the PR additionally ships
**regression-guard evals** for both, plus a **token-auth migration** of
the eval harness workflow (see Scope & safety).

### 1. `gh` CLI is unauthenticated by design, but the prompt doesn't say
so — ~61/64 sessions
The `CopilotReview` task intentionally strips all GitHub tokens
(`copilot ...
--secret-env-vars=GH_TOKEN,COPILOT_GITHUB_TOKEN,GITHUB_TOKEN`), so `gh
pr view` / `gh issue view` / `gh api` always fail inside the agent run.
But `pr-preflight.md` still *leads* with those commands, so nearly every
session burns 1-3 turns watching them fail and pivoting — and several
then **wrongly log an "environment blocker" or lower review
confidence**.

**Fix:** `pr-preflight.md` now opens with an explicit "Environment &
Authentication" note stating that unauthenticated `gh` is **expected,
not a blocker** (don't stop, don't reduce confidence), and provides
**local-`git` + unauthenticated public-REST (`curl`)** recipes that work
in CI. The original `gh` recipes are retained for local `pr-review` runs
where a token is present.

### 2. Agent refuses to write the REQUIRED `inline-findings.json` —
~34/64 sessions
The expert-review step over-generalizes a host "do not write output
files" guardrail and refuses to write `inline-findings.json`, dumping
the JSON into chat instead. There is **no fallback** —
`post-inline-review.ps1` finds no file (`Test-Path` fails) and **inline
review comments are silently dropped** in roughly half of sessions. This
is the most damaging correctness issue found.

**Fix:** the STEP 5b prompt in `Review-PR.ps1` and the
`maui-expert-reviewer` agent now **explicitly authorize and mandate**
writing the file, state that the general "don't write review output"
guidance does **not** apply to this required artifact, and forbid
substituting a chat-text dump (with an orchestrator-writes-it-itself
fallback instruction).

## Files
- `.github/pr-review/pr-preflight.md` — environment note + local-first /
`curl` recipes (#1)
- `.github/scripts/Review-PR.ps1` — strengthened STEP 5b inline-findings
mandate (#2, prompt string only)
- `.github/agents/maui-expert-reviewer.md` — reinforce required file
write, no chat-dump (#2)
- `.github/skills/pr-review/tests/eval.gh-auth.vally.yaml` — **new**
regression guard for #1
- `.github/skills/code-review/tests/eval.inline-findings.vally.yaml` —
**new** regression guard for #2
- `.github/workflows/skill-validation.yml` — **token-auth migration**
for the eval harness (see Scope & safety)

## Scope & safety
- **Fixes #1 and #2 are prompt/instruction text only.** No pipeline
control-flow or security-boundary changes — the #1 note merely
*documents* the existing token-stripping (reinforces it, never weakens
it). `Review-PR.ps1`'s change is confined to a here-string prompt;
`pwsh` parse check passes and `$PRNumber` interpolation is preserved.
- **`skill-validation.yml` is a token-provisioning migration (not prompt
text).** The prior `COPILOT_GITHUB_TOKEN*` secrets were rotated out with
the removal of `gh-aw-agents`, so the eval harness now sources Copilot
auth from the **`copilot-pat-pool`** environment (`COPILOT_PAT_0..9`,
index/run-id modulo selection, masked before `GITHUB_OUTPUT`). This is a
**like-for-like** auth-source swap — same `pull_request_target` exposure
model as before, no new secret exposure and no security loosening. ⚠️
**Merge prerequisite:** the `copilot-pat-pool` environment must be
populated with `COPILOT_PAT_0..9` (and must NOT carry
required-reviewer/wait-timer protection rules, which would hang the
`pull_request_target` job) so eval goes green immediately after merge.

## Evals (regression guards for these fixes)
Now that we run
[`vally`](https://www.npmjs.com/package/@microsoft/vally-cli) eval
suites, each fix ships with a guard that reproduces the exact failure
mode it fixes. Both pass `vally lint --strict` and run on this PR (each
lives under its skill's `tests/` dir, which flags that skill as
changed).

- `.github/skills/pr-review/tests/eval.gh-auth.vally.yaml` (#1) — the
vally eval step is already tokenless, so it **natively reproduces** the
CI condition where `gh` is unauthenticated. The agent must classify that
as **expected, not a blocker**, pivot to the local-first
`git`/anonymous-REST path, and **not lower its review confidence**.
Structural floor: the agent must end with `GH_AUTH_BLOCKER: no` — a
*necessary, not sufficient* signal — with an LLM judge scoring the
reasoning.
- `.github/skills/code-review/tests/eval.inline-findings.vally.yaml`
(#2) — pins a worktree to a real regression commit so the agent has a
genuine diff, then must **write** `inline-findings.json` (canonical
`path`/`line`/`body` schema) to the path the pipeline reads from disk
and prove it landed (read back, echo `FILE_OK:[…`). An agent that
refuses (`"prohibited"`) and chat-dumps the JSON has no file to echo and
fails. An LLM judge scores finding quality, non-refusal, and requires
transcript evidence of an actual write tool call (not just a fabricated
`FILE_OK:` line).

Each suite keeps two graders — one structural floor + one LLM judge.
Both floors are *satisfiable by a partial regression* (the prompt hands
the agent the giveaway token), so `scoring.threshold` is set to **0.7**
(above the house 0.6): with the unweighted `mean(floor, judge_norm)`
aggregate, a floor-1.0-but-judge-failing run scores `(1.0 + 0.25)/2 =
0.625`, which 0.6 would **pass** but 0.7 **fails**. This makes the **LLM
judge load-bearing** (must reach ≥3/5) rather than letting the spoofable
floor decide. Live-validated: good path 1.00, partial-regression 0.625.

## Follow-ups (identified, not in this PR)
Same analysis surfaced: PowerShell-vs-bash redirection footguns in
`try-fix` recipes; test-result classification keyed off exit code
instead of TRX `failed=` count; winners crowned with no regression
evidence; gate=FAILED conflating real regressions with
wrong-platform/ineffective tests; and the agent re-deriving the true PR
diff base each run. These can be addressed in separate PRs.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](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 this adds

A reusable, **local-only** dotnet/maui skill —
`.github/skills/analyze-sessions` — that analyzes a contributor's
Copilot CLI sessions to drive iterative improvements to the **PR-review
agent** (and other agents/skills/instruction files).

It automates the loop the team has been running by hand: **select →
extract → score → judge → cluster → propose → emit-eval**. The emit-eval
step is what makes it iterative — every recurring failure mode becomes a
`vally` guard-eval, the exact mechanism PR #36002 used by hand, now run
over the whole fleet of local sessions.

## Architecture — one engine, two front doors

```
 local front door  ─┐
 -Repository/-Last   │   scripts/Get-SessionAnalysis.ps1  (deterministic, NO LLM)
 -SessionId         ─┤     select → extract → score → digest → redact
                     │       • dotnet-replay --summary --json  (normalization)
 CI front door      ─┤       • thin raw events.jsonl scan      (success/tokens/…)
 -EventsPath/-Dir   ─┘     emits: session-analysis.md + JSON contract
                                     │
                                     ▼  redacted digests + ranking
                          agent, in the contributor's OWN session:
                          judge → cluster → propose → emit guard-eval
```

- **Deterministic shared core** (`Get-SessionAnalysis.ps1`, no LLM) does
select/extract/score/digest + redaction. It **wraps `dotnet-replay`
v0.9.1** for normalization and adds a thin raw `events.jsonl` scan for
the signals replay's `--json` omits (per-tool `success`, `outputTokens`,
truncations/compactions, aborts, retries, subagent failures).
- **Local front door:** `-Repository` / `-Last` / `-SessionId` select
from `session-store.db`.
- **CI front door:** `-EventsPath` / `-EventsDir` point the *same*
engine at already-downloaded AzDO `events.jsonl` artifacts — one engine
reused by the existing CI-session pipeline, inside its artifact
boundary.
- The **judge / cluster / propose / emit-eval** steps run in the
contributor's **own Copilot session** (no third-party endpoint), driven
by `SKILL.md`.

## Privacy / safety

- **Local-only by default** — reads `~/.copilot/…`, writes a report into
the session workspace. It **never** opens a gist and **never** POSTs a
transcript.
- **Redaction on by default** — home paths → `~`, tokens
(`ghp_`/`gho_`/`Bearer`/`password=`/`key=`), and emails are stripped
from the report **and** any emitted eval.
- The LLM-judge runs through the contributor's own auth/quota;
cross-machine sharing is explicit, manual, opt-in.

## Deliverables

| File | Purpose |
|------|---------|
| `SKILL.md` | Triggers, 6-phase workflow, judge rubric, learn-from-pr
proposal taxonomy, #36002 emit-eval template, privacy model,
when-NOT-to-use |
| `scripts/Get-SessionAnalysis.ps1` | The deterministic shared core
(PowerShell — matches every other repo skill script) |
| `references/design-rationale.md` | Cites `dotnet-replay` (+ the
`--json` gap), the `events.jsonl` 35-event schema, the privacy model,
the two-front-doors architecture, and the hand-run proof-of-concept |
| `tests/eval.vally.yaml` | Capability + privacy suite: privacy floor
(`SHARE_ACTION: none`), capability (`PROPOSED_EVAL: yes`), and
negative-trigger — each a refutation-proof structural floor + LLM judge
|

## Verification

- Core validated end-to-end against real local maui sessions; ranking is
driven by genuine inefficiency (failures, retries, truncations, tokens)
rather than calendar span (resumed-session wall-clock is capped for
scoring).
- Metrics spot-checked against raw `events.jsonl` (tool-failure count,
`outputTokens`, compactions all matched exactly).
- Both front doors exercised (local DB select **and** `-EventsPath`);
`-Json` contract is valid JSON; redaction confirmed against synthetic
secrets.
- `npx -y @microsoft/vally-cli@0.6.0 lint --eval-spec
.github/skills/analyze-sessions/tests/eval.vally.yaml --strict`
**passes**.

No production code changes — this is additive tooling under
`.github/skills/`.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

## Summary

- Process every prefetched CI-fix watch candidate before broad `ci-scan`
discovery, with actionable candidates first.
- Keep an open CI-fix PR autonomous until it is closed; eligible
`CHANGES_REQUESTED` reviews continue through Track C.
- Allow `src/AI/**` so the net11 fixer can address that source subtree,
while keeping the main twin behaviorally aligned.
- Regenerate both gh-aw lock files.

## Validation

- `gh aw compile ci-status-fix`
- `gh aw compile ci-status-fix-net11`
- PowerShell parser check for `Query-CiFixPRs.ps1`
- `git diff --check`
- Live prefetch confirms #36404 remains an actionable candidate.

---------

Co-authored-by: PureWeen <223556219+Copilot@users.noreply.github.com>
<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

## Summary
- Upgrade the Skill Validation workflow from
`@microsoft/vally-cli@0.6.0` to `0.10.0`.
- Preserve MAUI's explicit `*.vally.yaml` enumeration because Vally
directory discovery recognizes only `eval.yaml` and `eval.yml` by
default.
- Update all 0.6-specific scoring guidance: these specs intentionally
omit `scoring.weights`, retaining Vally 0.10's equal-weight aggregation
and existing thresholds.

## Validation
- Ran strict lint with `@microsoft/vally-cli@0.10.0` for all 10 MAUI
eval specs.
- Parsed `.github/workflows/skill-validation.yml` as YAML and ran `git
diff --check`.

## Scope
This PR is limited to the Vally 0.10.0 upgrade. It intentionally
excludes the Trim/AOT fixture-workflow changes from #34962.

Co-authored-by: PureWeen <223556219+Copilot@users.noreply.github.com>
<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

## Summary

Add Trim/NativeAOT annotation-chain guidance to the active MAUI expert
reviewer and cover it with hermetic Vally scenarios.

## Motivation

The HybridWebView NativeAOT investigation showed that IL2026/IL3050
warnings require annotation-chain and target-toolchain analysis. A
blanket rule that every suppression is wrong is inaccurate: #34868 uses
a documented, narrowly scoped Android NativeAOT ILC workaround, while
#34958 demonstrates the structural annotated-helper alternative on
`net11.0`.

## What changed

- **`maui-expert-reviewer.md`**: expands the Trimming/AOT dimension to
require tracing the annotated member, generic/DAM hops, feature guard,
reachability, and target analyzer. It distinguishes broad suppression
from a documented, scoped toolchain workaround and covers
structural-helper/source-generation alternatives.
- **Dimension routing**: activates Trimming/AOT review for relevant
annotations and IL20xx/IL30xx suppressions, not only direct reflection
APIs.
- **Hermetic Vally coverage**: uses Vally 0.10 `environment.files` to
materialize frozen candidate diffs and the exact supporting source
snapshots needed to trace the HybridWebView handler annotations, generic
registration DAM hop, and runtime feature guard. No Git history, remote
repository, or custom workflow fixture setup is exposed to the reviewer.
- **Three discriminating scenarios**: covers the scoped #34868
exception, the #34958 annotated-helper approach, and a
reachable-suppression counterfactual that replaces the feature guard
with `if (true)` and must be rejected.
- **Live-eval reliability**: gives the existing multi-surface
prior-review reconciliation scenario a 10-minute cap after passing
trials took 4m01s and 4m54s and another was terminated at the former
5-minute cap.

## Evaluation scope

The separate `eval.trim-aot.vally.yaml` suite tests technical review
reasoning on inert checked-in inputs. Tool-call grading requires
inspection of the candidate patch and all annotation-chain sources while
rejecting GitHub/network access. The five-run suite retains a `0.9`
threshold so the required verdict cannot be masked by the other graders.

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
<!-- 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!

### Root Cause

The labeler treated agent skills and AI-assisted workflows as generic
infrastructure, so it never selected the more-specific `area-ai-agents`
label. Its issue-platform guidance also inferred labels too broadly and
included the unsupported `platform/tizen` label.

### Description of Change

- Routes Copilot CLI agents, agent skills, agentic workflows,
evaluations, and AI-assisted development to `area-ai-agents`.
- Keeps `area-infrastructure` for generic CI execution, authentication,
scheduling, dependency flow, and pipeline plumbing, with an explicit
dominant-subject tie-break.
- Distinguishes setup concerns such as workload availability and
target-framework recognition (`area-setup`) from debugging, editor,
build-task, and tooling behavior (`area-tooling`).
- Never applies `platform/tizen`, while still selecting the appropriate
`area-*` label for Tizen-related content.
- Infers issue platform labels only from explicitly named affected
platforms; generic claims such as "all platforms" do not infer labels,
while an explicit affected-platform list takes precedence.
- Expands the active Vally suite from 21 to 25 scenarios, including
deterministic regression coverage for `area-ai-agents`, platform
exclusion, and exact area selection.
- Explicitly activates `agentic-labeler` in every capability scenario
and requires labels-only output so the suite measures skill behavior
rather than nondeterministic skill discovery or explanatory prose.

### Validation

- Rebased onto `main` with `@microsoft/vally-cli@0.10.0` pinned by the
Skill Validation workflow.
- Full three-trial `agentic-labeler` Vally 0.10.0 evaluation passed at
99.8% (threshold: 85.0%).
- JUnit aggregate: 0 failures, 0 errors.
- Strict Vally 0.10.0 static validation passed.
- Harness hermeticity gate passed.
- Skill Validation run:
https://github.com/dotnet/maui/actions/runs/29609999793

### Issues Fixed

Follow-up to and replacement for #35570.

---------

Co-authored-by: bot <bot@test>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
<!-- 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!

### Description of Change

Retires automation targeting the unsupported `net10.0` branch:

- Removes the scheduled/event-driven `main` → `net10.0` merge workflow
and its configuration.
- Removes `net10.0` from the daily formatting workflow matrix.
- Disables the live merge workflow immediately to prevent replacement
PRs while this change is reviewed.

The matching dependency-flow cleanup is tracked by
[maestro-configuration PR
63025](https://dev.azure.com/dnceng/internal/_git/maestro-configuration/pullrequest/63025),
removing the Android, dotnet, and macios subscriptions plus the
`net10.0` default-channel mapping.

### Issues Fixed

No issue. Retires the automation responsible for generated PRs #36400
and #36641.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reset patterns:
- global.json
- NuGet.config
- eng/Version.Details.xml
- eng/Versions.props
- eng/common/*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.