[automated] Merge branch 'main' => 'net11.0' - #36684
Closed
github-actions[bot] wants to merge 215 commits into
Closed
[automated] Merge branch 'main' => 'net11.0'#36684github-actions[bot] wants to merge 215 commits into
github-actions[bot] wants to merge 215 commits into
Conversation
) <!-- 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>
…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>
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> [](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 & 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 & 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) <!-- 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 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`. > **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 <account>` 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/*
This was referenced Jul 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
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.
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.
or if you are using SSH
After PR checks are complete push the branch
Instructions for resolving conflicts
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.
or if you are using SSH
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.