[automated] Merge branch 'net11.0' => 'release/11.0.1xx-preview7' - #36986
Merged
PureWeen merged 57 commits intoAug 3, 2026
Merged
Conversation
Adds docs/specs/shell-route-templates.md with the full design for additive
{param} path parameters in Shell routes (issue #35107 Proposal A), and a
standalone prototype at prototype/ShellRouteTemplates that demonstrates the
parser, matcher, and precedence rules independently of MAUI internals.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds support for route templates like 'product/{sku}' in Shell
navigation, allowing path parameters to be extracted from URIs and
delivered to pages via [QueryProperty] / IQueryAttributable.
Key changes:
- RouteTemplate.cs: template parser and segment helpers
- Routing.cs: detect and store template routes alongside literal routes
- RouteRequestBuilder.cs: template-aware segment matching with parameter capture
- ShellUriHandler.cs: two-pass matching (literal-first precedence),
template-aware CollapsePath
- RequestDefinition.cs: exposes PathParameters from winning route
- ShellNavigationManager.cs: seeds path params before query string params
All 5551 existing unit tests pass unchanged. 8 new tests added.
Ref: #35107
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…oss, add 9 new tests
Review findings addressed:
- Reject {sku?} optional and {*path} catch-all syntax at registration
time (not yet implemented, was silently accepted)
- Reject duplicate parameters in templates ({id}/{id})
- Fix path parameter loss in SearchForGlobalRoutes code path
- Fix path parameter loss in GenerateRoutePaths tree+global merge
- Fix GlobalRouteItem AddMatch missing _resolvedGlobalRoutes entry
- Fix IndexOutOfRange in RequestDefinition.MakeUriString
- Add ShellSection.GetOrCreateFromRoute resolved-route override for
template routes (CurrentState.Location fix)
9 new tests (17 total):
- CurrentState.Location shows resolved values, not template tokens
- Relative navigation limitation documented
- URL-encoded path parameters decoded correctly
- Second navigation with different value
- Reject optional/catch-all/duplicate template syntax
- IQueryAttributable receives path parameters
- All-template route ambiguity documented
All 5560 existing + new tests pass, 0 regressions.
Sandbox demo added with ProductPage, ReviewPage, and navigation buttons.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…t values
Full route template feature set now implemented:
- {sku?} Optional parameters (match zero or one segment)
- {*path} Catch-all (captures all remaining segments, must be last)
- {id:int} Constraints (int, long, double, bool, guid, alpha)
- {stars=5} Default values (provided when segment absent)
- product-{sku} Mixed segments (literal prefix/suffix around parameter)
- {id:int=1} Constraint + default combined
Sandbox demo updated:
- Products tab: catalog with product/{sku} route
- Orders tab: order list with order/{orderId:int} constrained route
- Product review: review/{stars=5} with default star rating
- Multi-step navigation: product → review with inherited parameters
48 template tests (31 new), 5591 total tests pass, 0 regressions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
67 template tests now cover all features in various combinations:
- Two params in single route ({cat}/{id})
- Required + optional combos
- Optional + constraint ({id:int?})
- Optional with query string fallback
- Default value + query string interaction (default wins)
- Default value + child page inheritance
- Catch-all with URL encoding
- Catch-all with empty remaining segments
- Mixed segment + constraint (item-{id:int})
- Mixed segment prefix mismatch rejection
- Constraint + literal precedence
- Two different templates in same navigation
- Template + literal route together
- Unregister template route lifecycle
- Constraint unit tests: bool, long, double, guid-reject
5610 total tests pass, 0 regressions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bugs fixed (from Opus 4.7 + GPT 5.4 reviews):
- BLOCKING: {name:int?=5} produced default '5?' instead of '5' due to
appending '?' to inner string during constraint parsing. Fixed by using
a separate boolean flag.
- Optional params in middle of route (e.g. a/{b?}/c) now rejected at
registration time (matches ASP.NET Core behavior — unmatchable).
- Default values now validated against constraint at registration time
(e.g. {id:int=hello} is now rejected).
- Multiple brace pairs in mixed segments (e.g. item-{x}-{y}) now rejected.
- IsTemplateSegment now requires { before } (rejects 'foo}bar{baz').
- Sandbox demo fixed to use absolute URIs (relative template nav unsupported).
7 new tests (74 total):
- Parse_ConstraintOptionalAndDefault_Combo — verifies {num:int?=5} combo
- RegisterRoute_RejectsDefaultThatViolatesConstraint
- RegisterRoute_RejectsOptionalInMiddle
- RegisterRoute_RejectsMultipleParamsInMixedSegment
- Parse_MalformedBraces_Rejected
- Parse_EmptyParameterName_Rejected
- Parse_ConstraintWithNoName_Rejected
5617 total tests pass, 0 regressions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…Uri tokens
Critical fixes from Opus 4.7 + GPT 5.4 round 3:
1. Template lookup bypass (Opus blocking): When CollapsePath stripped
prefix segments, GetNextSegmentMatch looked up the template by the
collapsed key (e.g. '{id:int}') instead of the registered key
('orders/{id:int}'), so constraints/defaults/catch-all were silently
bypassed. Fixed by passing RouteTemplate directly from the caller.
2. SetRoute mutation (Opus blocking, GPT medium): Mutating Page.Route
from template key to resolved value broke stack-reuse comparisons
(Routing.GetRoute(page) == globalRoutes[i] always false), causing
page recreation on re-navigation. Fixed with separate
Routing.ResolvedRouteProperty — Route keeps the template key,
ResolvedRoute stores the resolved URI for CurrentState.Location.
3. RequestDefinition.FullUri (Opus warning): Built from unresolved
template tokens ('{sku}'), causing ShellNavigationSource
misclassification. Fixed to use ResolvedGlobalRoutes when available.
4. Back navigation: Updated .. path and GetNavigationState to use
ResolvedRoute ?? Route for correct URI reconstruction.
3 new tests (77 total):
- TemplateRoute_RenavigationPreservesPageInstance
- Constraint_EnforcedWhenShellContentMatchesPrefix
- Constraint_AcceptedWhenShellContentMatchesPrefix
5620 total tests pass, 0 regressions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…te params, FullUri
Fixes from Opus 4.7 + GPT 5.4 round 4:
1. IL2111 trimmer error (Opus blocking): ResolvedRouteProperty field
initializer caused trimmer warning→error. Fixed by wrapping in
CreateResolvedRouteProperty() with [UnconditionalSuppressMessage].
2. Stale ResolvedRoute on reused pages (GPT high): When re-navigating
to same template route with different value, the reused page kept
the old ResolvedRoute. Fixed by updating ResolvedRoute in the
page-reuse check at PrepareCurrentStackForBeingReplaced.
3. Intermediate page parameter delivery (GPT high): Non-last pages
in navigation chain now receive route-prefixed path params
(e.g. 'product/{sku}.sku') for prefix-filtered ApplyQueryAttributes.
4. Path params overwriting caller params (Opus medium): Changed from
unconditional overwrite to 'only add if not present', matching
SetQueryStringParameters semantics.
5. RequestDefinition.FullUri (Opus medium): Only substitute resolved
route when the route is actually a template (IsTemplateRoute check),
preventing multi-segment literal route truncation.
6. ExpandOutGlobalRoutes cast (Opus medium): Replaced fragile
'as IDictionary' cast with MergePathParameters() helper.
4 new tests (81 total):
- IntermediatePage_ReceivesOwnPathParameter
- ReusedPage_ResolvedRouteUpdated
- OptionalParam_WithCollapsedPrefix_NavigationSucceeds
- DefaultParam_WithCollapsedPrefix_NavigationSucceeds
5624 total tests pass, 0 regressions.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ram precedence Fixes from Opus 4.7 + GPT 5.4 round 5: 1. Tautological tests fixed: IntermediatePage, DefaultWithChild, OptionalParam_WithCollapsedPrefix, DefaultParam_WithCollapsedPrefix all now assert meaningful conditions instead of always-true checks. 2. Modal-stack reuse path: ResolvedRoute is now also updated in the pop/reapply loop (ShellSection.cs line 417 path), not just the non-modal PrepareCurrentStackForBeingReplaced path. 3. Route-prefixed param seeding uses 'only add if not present' semantics, matching the same precedence rule as unprefixed params. 5624 total tests pass, 0 regressions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Samples integration tests build with TreatWarningsAsErrors=true. Backing fields for QueryProperty properties in ProductPage, ReviewPage, and OrderDetailPage were non-nullable, causing CS8618 warnings→errors. Fixed by making all backing fields and properties nullable (string?). 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 - inject canonical CI-scan fingerprint, match-count, and trusted evidence-key markers in the deterministic publisher instead of relying on agent-authored HTML comments - apply the same complete-manifest, frozen-evidence, all-or-nothing publisher architecture to both `ci-status-main` and `ci-status-net11` - separate countable raw failure evidence from synthetic provenance framing and bind canonical recurrence to publisher-derived full raw-evidence lines - normalize run-specific AzDO transport timestamps only for trusted `azdo-log` evidence, while preserving timestamps in non-AzDO failure messages - reject markerless issues as authoritative coverage and remove automatic markerless adoption, preventing shared boilerplate from suppressing a distinct failure - recognize legacy pipeline lines with no suffix, `(ID N)`, or live `(definition N)` syntax using the trusted configured pipeline definition - reject pre-existing/evasive marker content and marker-like `match_pattern` variants; revalidate exact post-injection payloads at the GitHub write boundary - require complete Helix terminal evidence and bind deadletter placeholders to stable trusted work-item identity - align the merged report-only reconciler invariants with publisher-owned marker publication - add twin-aware publisher/collector execution tests and named mutation coverage for every security control ## Root cause [PR #36848](#36848) added fail-closed manifest validation to the net11 scanner and exposed a pre-existing repo-wide publication defect. In [run 30413273824](https://github.com/dotnet/maui/actions/runs/30413273824), the agent job succeeded, but `submit_ci_scan` failed before any issue write because the compiled prompt did not contain the authored HTML-comment marker template. Artifact `agent` (`8709769921`) contained 16/16 signatures with zero fingerprint-marker-prefix and zero canonical-marker matches. gh-aw strips literal HTML comments while compiling the authored prompt, so regenerating the lock or strengthening prompt prose cannot make agent-side marker emission reliable. Output-side safe-output stripping is not needed to explain this incident. Main had the same silent blast radius: sampled issues #36858, #36779, #36709, and #36689 carry no fingerprint marker, but its permissive publisher did not validate the payload. Net11's all-or-nothing gate correctly prevented every write, so the first post-merge run published zero issues. ## Architecture The shared trusted validator resolves a hard-coded scanner configuration for `ci-scan|main` or `ci-scan-net11|net11.0`. For each filed manifest entry it: 1. validates fingerprint provenance, body shape, complete manifest coverage, frozen build/log provenance, and the five-issue mutation cap 2. rejects pre-existing fingerprint/match-count/evidence-key content and marker-like `match_pattern` variants, including spacing, case, zero-width, separator, HTML-comment-like, and Unicode-homoglyph evasions 3. counts matches only in structured `.evidence.json` raw segments; rendered `.log` files retain AzDO/Helix provenance for diagnosis, but synthetic headers are not countable evidence 4. normalizes and hashes each complete raw line containing the match pattern, derives a domain-separated SHA-256 evidence key, and requires the issue body to contain a complete trusted raw-evidence line 5. injects exactly one fingerprint marker from the validated manifest, one match-count marker from the trusted recount, and one evidence-key marker from the trusted raw-line hashes 6. validates the exact post-injection body before producing the plan AzDO's log API prepends a different UTC transport timestamp to each stored line on every build. PowerShell strips that prefix only when structured provenance says the segment is `azdo-log`; Helix and other message timestamps remain identity-bearing. At the write boundary, publisher body matching computes both raw and AzDO-normalized candidates against the trusted plan hash. The same failure therefore keeps its evidence identity across builds while real non-AzDO timestamps remain distinct. Both compiled publisher jobs bind the plan to their trusted scanner ID, branch, and label; preflight every issue/reference before any mutation; preserve canonical marker retry/dedup; and revalidate GitHub's stored response. Canonical recurrence requires the exact fingerprint and evidence-key markers plus a current trusted evidence line. Markerless legacy issues no longer provide authoritative coverage. Their exact pipeline/evidence shape is still recognized for a precise migration error, including no suffix, `(ID N)`, and the live `(definition N)` suffix with the correct configured definition. An explicit markerless `existing` reference aborts before any write, and a `filed` payload never auto-adopts a markerless issue. It instead creates bounded visible canonical coverage. This is intentionally safer than silently merging two same-pipeline failures that share boilerplate such as `Build FAILED.` The frozen evidence collector treats a Helix job as complete only when the job has a terminal `Finished` value, `Waiting` and `Running` are zero, and every returned work item is terminal with valid completion evidence. Helix's cumulative `Unscheduled` counter may remain nonzero after completion and is validated but not treated as active work. AzDO build records with missing or invalid `finishTime` fail closed. Structured evidence enforces matching producer/consumer caps of 200 segments, 25 MB, and 200 distinct matching lines. A deadletter placeholder URL contains no run-specific diagnostics and is constant across Helix. The countable evidence line includes the validated stable work-item name plus that URL. This distinguishes unrelated work items while deliberately excluding job/build IDs so recurrence for the same work item remains stable across builds. Deadletters still mark their AzDO submission log as a failed leaf, so absence-only coverage remains forbidden. The branch is based on current `main` after PR #36850. Its report-only reconciler asserts that both scanner twins compile trusted validation before publisher-side exact-marker checks, rather than expecting an agent marker template. The reconciler still has no production state-marker writer, so stale-issue closure candidates remain unreachable. ## Review findings resolved - **Universal synthetic evidence header:** confirmed; synthetic framing is structurally excluded from countable evidence. - **Marker-like match replay:** confirmed; marker-like patterns fail across exact, spacing, case, zero-width, and homoglyph variants. - **Constant deadletter identity:** confirmed; fixed placeholder content is bound to trusted stable work-item identity. - **AzDO timestamp-sensitive identity:** confirmed; trusted `azdo-log` transport timestamps are removed symmetrically from PowerShell proof generation and JavaScript body matching. - **Live legacy `(definition N)` suffix:** confirmed; exact no-suffix, `(ID N)`, and `(definition N)` forms are recognized for all three configured pipelines and both twins, and a wrong definition is rejected. - **Generic markerless evidence collision:** confirmed; markerless explicit coverage and automatic adoption are disabled rather than relying on fragile length/entropy heuristics. - **Helix active counts:** confirmed defense-in-depth; terminal evidence requires zero `Waiting` and `Running` while allowing cumulative `Unscheduled`. - **Concurrency overlap note:** not reproduced. A fixed GitHub concurrency group permits one running and one pending run; `cancel-in-progress: false` preserves the active publisher instead of allowing overlap. - **Benign marker prose over-folding:** intentionally unchanged. Its false-positive mode is an all-or-nothing batch abort, not silent issue suppression. ## Tests - strict `gh aw compile` for both twins: **0 errors, 0 warnings** - focused validator/publisher/mutation Pester: **225/225 passed** - complete `.github/scripts` Pester: **1489/1489 passed** - repeated strict compilation produced unchanged lock hashes - lock-extracted Node tests execute both compiled publishers and collectors, including raw-vs-synthetic evidence, canonical cross-build recurrence, markerless no-adoption/no-write behavior, exact legacy pipeline formats, unrelated deadletter replay, Helix terminality, no-partial-write batches, retry behavior, evidence caps, and twin symmetry - named mutations cover timestamp-sensitive identity, missing `(definition N)` support, re-enabled markerless explicit coverage, re-enabled markerless auto-adoption, removed injection, untrusted fingerprint/count sourcing, pre-injection-only validation, duplicate rejection removal, synthetic framing, marker-pattern rejection, trusted-state recurrence, evidence-identity removal, constant deadletter identity, omitted twins, and empty discovery - independent final code review found no high-confidence defects There is no scanner-specific gh-aw behavioral eval runner in this repository, so deterministic Pester, lock-extracted Node execution, strict compilation, and static anti-vacuity invariants provide behavioral regression coverage. ### Residual risk Disabling markerless adoption can produce a bounded visible duplicate for a legacy issue until canonical coverage exists. This is intentional: without a publisher-owned historical identity, silently reusing a markerless issue is not a trustworthy dedup decision. Conservative marker-content and evidence-size gates may also fail an entire scan rather than truncate or publish partial evidence. These behaviors fail closed and produce zero partial writes. No real `ci-scan` or `ci-scan-net11` issue was mutated during development or validation. ### Issues Fixed No scanner tracking issue is closed by this infrastructure correction. Related incident: PR #36848 and Actions run 30413273824.
<!-- 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! Ports the `.cab` signing fix from #36879 (merged into `release/11.0.1xx-preview7`) to `main`. ### Duplicate `.cab` signing entry The internal `Pack, Sign` task fails with: ``` Sign.proj(74,5): error : Multiple certificates for extension '.cab' defined for CollisionPriorityId ''. There should be one certificate per extension per collision priority id. ``` **Cause:** PR #35026 added an explicit `.cab` `FileExtensionSignInfo` to `eng/Signing.props`, but Arcade's built-in `Sign.props` already registers `.cab` by default: ```xml <FileExtensionSignInfo Include=".dll;.exe;.mibc;.msi;.cab" CertificateName="Microsoft400" /> ``` **Fix:** Remove the duplicate entry. Cab files inside workload MSIs are still signed with `Microsoft400` via the Arcade default, so no signing coverage is lost. The `ReconnectModal.razor.js` `FileSignInfo` entry from #35026 is kept. ### Note on the second fix in #36879 #36879 also restored a missing `MicrosoftWixVersion` property in `eng/Versions.props`. **That part does not apply to `main`** — `main` has not taken the WiX 6 migration and still uses `Microsoft.Signed.WiX` / `$(MicrosoftSignedWixVersion)` in `eng/NuGetVersions.targets`. There is no `$(MicrosoftWixVersion)` reference anywhere on `main`, so adding the property would be dead config. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 515c328a-83aa-4348-9548-4d45f97760c0
<!-- 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 The `find-regression-risk` skill was missing its YAML frontmatter, so Copilot CLI refused to load it: ``` The following skills failed to load: * .github/skills/find-regression-risk/SKILL.md: missing or malformed YAML frontmatter ``` Every other `SKILL.md` under `.github/skills/` opens with a `---` block declaring at least `name` and `description`. This one was the sole exception, which made the skill invisible to the CLI skill loader — and to vally's skill linter. This PR adds a frontmatter block following the conventions used by the sibling skills (`name`, `description`, `metadata.author`, `metadata.version`, `compatibility`). The description covers the skill's purpose, trigger phrases, and "Do NOT use for" guidance, matching the style of `code-review`, `evaluate-pr-tests`, and `pr-finalize`. It also refreshes a now-stale comment in `.github/workflows/skill-validation.yml`. That comment explained why SKILL.md structural linting is skipped in the eval-spec lint gate, citing **two** pre-existing failures — the try-fix 500-line overrun and this missing frontmatter. With the frontmatter fixed, only the try-fix issue remains, so the comment now reflects reality. No behavioral change to the skill itself — `Find-RegressionRisks.ps1` and its tests are untouched. ### Issues Fixed None filed — reported directly via the Copilot CLI startup error shown above. ### Validation Linted with the exact vally version the workflow pins (`VALLY_VERSION: "0.10.0"`): ```console $ npx -y @microsoft/vally-cli@0.10.0 lint .github/skills/find-regression-risk ✅ find-regression-risk (2/2 checks passed) 1 skill(s) linted, 1 passed ``` Before the change the skill was not even discovered by the linter. The frontmatter YAML and the edited workflow YAML were both confirmed to parse. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: fedc0275-f17d-4af4-af1b-df406fa722a0
Reset patterns: - global.json - NuGet.config - eng/Version.Details.xml - eng/Versions.props - eng/common/*
<!-- 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 route template support to Shell navigation, enabling `{param}` path parameters inspired by ASP.NET Core / Blazor routing. This is **additive and non-breaking** — existing literal routes work identically, and the feature is completely dormant unless you register a route containing `{`. Fixes #35107 --- ## How It Works ### Registration Register a route with `{param}` segments. Everything inside braces becomes a path parameter: ```csharp Routing.RegisterRoute("product/{sku}", typeof(ProductDetailPage)); Routing.RegisterRoute("order/{id:int}", typeof(OrderDetailPage)); Routing.RegisterRoute("review/{stars=5}", typeof(ReviewPage)); ``` XAML works too — `{` is safe because XAML only interprets markup extensions when `{` is the **first character** of the attribute value: ```xml <ShellContent Route="product/{sku}" ContentTemplate="{DataTemplate local:ProductPage}" /> ``` ### Navigation Navigate with values in the path instead of query strings: ```csharp // Before (query string — still works) await Shell.Current.GoToAsync("product?sku=seed-tomato"); // After (path parameter) await Shell.Current.GoToAsync("//products/product/seed-tomato"); ``` ### Parameter Delivery Uses existing `[QueryProperty]` and `IQueryAttributable` — **no new attributes or interfaces needed**: ```csharp [QueryProperty(nameof(Sku), "sku")] public class ProductDetailPage : ContentPage { public string Sku { get; set; } // receives "seed-tomato" } ``` ### Supported Syntax | Syntax | What it does | Example | |--------|-------------|---------| | `{sku}` | Required parameter | `product/{sku}` matches `product/seed-tomato` | | `{sku?}` | Optional (must be last) | `product/{sku?}` matches `product` or `product/seed-tomato` | | `{stars=5}` | Default value when absent | `review/{stars=5}` → stars is "5" if you navigate to just `review` | | `{id:int}` | Constraint — rejects non-matching values | `order/{id:int}` rejects `order/abc` | | `{*path}` | Catch-all — captures remaining segments (must be last) | `files/{*path}` → `docs/report.pdf` | | `product-{sku}` | Mixed — literal around parameter | `product-seed-tomato` → sku = "seed-tomato" | | `{id:int=1}` | Combined constraint + default | Validates when present, default when absent | **Constraints:** `int`, `long`, `double`, `bool`, `guid`, `alpha` ### Route Matching Precedence Literals always win over templates (same as ASP.NET Core). Implemented via two-pass matching in `FindAndAddSegmentMatch`. ### Path vs Query String Path parameters take precedence over query strings with the same name. Both can coexist. --- ## How the Implementation Works ### Route Registration (`Routing.cs`) When `RegisterRoute` receives a route containing `{`, it parses a `RouteTemplate` and stores it in a parallel `s_routeTemplates` dictionary alongside the existing `s_routes` entry. ### Route Matching (`ShellUriHandler.cs`, `RouteRequestBuilder.cs`) `FindAndAddSegmentMatch` uses a two-pass approach: pass 0 checks literal routes only (unchanged), pass 1 checks template routes. `GetNextSegmentMatch` walks template segments in parallel with URI segments, checking type (literal, parameter, optional, catch-all, mixed) and applying constraints. The `RouteTemplate` is passed directly from the caller to handle `CollapsePath` prefix stripping. ### Parameter Delivery (`ShellNavigationManager.cs`) Path parameters are seeded into `ShellRouteParameters` **before** `SetQueryStringParameters` (path wins). Route-prefixed keys are also seeded for intermediate page delivery through Shell's prefix-based `ApplyQueryAttributes` filtering. ### CurrentState.Location (`ShellSection.cs`, `ShellNavigationManager.cs`) Resolved URIs are stored in a separate internal `ResolvedRouteProperty` on the page. `GetNavigationState` reads `GetResolvedRoute(page) ?? GetRoute(page)`. The page's `Route` keeps the template key for factory lookups and stack-reuse comparisons. --- ## Migration **None required.** To opt in, change your route string to include `{param}`. Everything else stays the same. --- ## Behavior Changes **None for existing apps.** All new types are `internal`. The two-pass matching is a no-op for routes without `{`. `CollapsePath` has one new condition that only triggers for `{`-prefixed segments. --- ## Known Limitations - **Relative navigation** with template routes not yet supported — use absolute URIs - **All-template routes** (no literal prefix) can cause ambiguous matches - **Optional/default with collapsed prefix**: navigation succeeds but defaults may not reach the page via prefix filtering Co-authored-by: Jakub Florkowski <42434498+kubaflo@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 Adds a custom-icon API for Maps cluster markers on Android and iOS/MacCatalyst. **New public API on `Map`:** - `ClusterImageProvider` (`Func<ClusterInfo, ImageSource?>?`) — dynamic, per-cluster icon with highest priority. - `ClusterImageSource` (bindable `ImageSource?`) — static icon for all clusters, used when the provider is unset or returns null. - `ClusterInfo` — read-only context (`Count`, `ClusteringIdentifier`, `Pins`, `Location`) passed to the provider. The handler consumes this through the optional `IMapClusterImageProvider` capability rather than adding a required member to `IMap`, so existing external `IMap` implementations remain source-compatible. `ClusterImageVersion` provides a change token for precise cache invalidation. **Resolution order:** provider → static source → existing default bubble. Apps that do not configure either property retain the existing behavior. Changing `ClusterImageSource` or `ClusterImageProvider`, including mutating an existing image source, rebuilds current clusters immediately. `ClusterImageSource` follows the standard MAUI image lifecycle: parenting, inherited binding context, `SourceChanged` updates, and cancellation when replaced. Both platforms use a bounded LRU cache keyed by stable image content. Same-key concurrent loads are coalesced into one decode/rasterization, ordinary pin updates preserve warm entries, and cache invalidation tracks both the owning map and `ClusterImageVersion`. URI sources honor `CachingEnabled` and positive `CacheValidity`. On iOS/MacCatalyst, image-service results and scaled `UIImage` ownership are disposed deterministically across annotation reuse, uncached loads, eviction, cleanup, and pooled map reuse. **iOS bug found and fixed along the way:** on the iOS 26.5/.NET 11 preview 5 bindings, MapKit can hand `GetViewForAnnotation` a cluster annotation wrapped as a generic `MapKit.MKAnnotationWrapper` instead of the concrete `MKClusterAnnotation` subclass. The implementation falls back to `Runtime.GetNSObject<MKClusterAnnotation>(annotation.Handle)` only when the native object is a cluster annotation, preserving custom rendering, count glyphs, and cluster selection. The `ClusteringGallery` sample includes **Custom Cluster Icon (provider)** and **Static Cluster Icon** actions to exercise both modes. ### What NOT to Do (for future agents) - Do not add a required `GetClusterImage` member to `IMap`; it breaks external implementations. - Do not use a default interface implementation; the Maps projects target `netstandard2.0`, where it fails with CS8701. - Do not maintain separate dictionary and FIFO-order structures; concurrent loads can make them diverge and evict live entries. - Do not clear all cluster images for ordinary pin collection changes. #### Testing - 98 focused `MapTests` pass. - `Controls.Maps` builds for `netstandard2.0`, `net11.0-android37.0`, `net11.0-ios26.5`, and `net11.0-maccatalyst26.5`. - Manually verified by the author on an Android tablet and iPad: static/provider icons render, update live, and survive re-zoom. ### Issues Fixed Fixes #36335 > **Note:** targeting `net11.0`, not `main` — pin clustering (#33831) currently exists on `net11.0`. --------- Co-authored-by: Kévin Baumeyer <kbaumeyer@divalto.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7c7f8afa-6548-4a5c-aa86-946e2db624ef Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
I detected changes in the main branch which have not been merged yet to net11.0. I'm a robot and am configured to help you automatically keep net11.0 up to date, so I've opened this PR. This PR merges commits made on main by the following committers: * kubaflo * rmarinho * PureWeen ## 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. <img alt="merge button instructions" src="https://i.imgur.com/GepcNJV.png" width="300" /> 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. ``` sh git fetch git checkout main git pull --ff-only git checkout net11.0 git pull --ff-only git merge --no-ff main # If there are merge conflicts, resolve them and then run git merge --continue to complete the merge # Pushing the changes to the PR branch will re-trigger PR validation. git push https://github.com/dotnet/maui HEAD:merge/main-to-net11.0 ``` <details> <summary>or if you are using SSH</summary> ``` git push git@github.com:dotnet/maui HEAD:merge/main-to-net11.0 ``` </details> After PR checks are complete push the branch ``` git push ``` ## Instructions for resolving conflicts :warning: If there are merge conflicts, you will need to resolve them manually before merging. You can do this [using GitHub][resolve-github] or using the [command line][resolve-cli]. [resolve-github]: https://help.github.com/articles/resolving-a-merge-conflict-on-github/ [resolve-cli]: https://help.github.com/articles/resolving-a-merge-conflict-using-the-command-line/ ## Instructions for updating this pull request Contributors to this repo have permission update this pull request by pushing to the branch 'merge/main-to-net11.0'. This can be done to resolve conflicts or make other changes to this pull request before it is merged. The provided examples assume that the remote is named 'origin'. If you have a different remote name, please replace 'origin' with the name of your remote. ``` git fetch git checkout -b merge/main-to-net11.0 origin/net11.0 git pull https://github.com/dotnet/maui merge/main-to-net11.0 (make changes) git commit -m "Updated PR with my changes" git push https://github.com/dotnet/maui HEAD:merge/main-to-net11.0 ``` <details> <summary>or if you are using SSH</summary> ``` git fetch git checkout -b merge/main-to-net11.0 origin/net11.0 git pull git@github.com:dotnet/maui merge/main-to-net11.0 (make changes) git commit -m "Updated PR with my changes" git push git@github.com:dotnet/maui HEAD:merge/main-to-net11.0 ``` </details> 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.
<!-- 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 This PR improves incremental XAML Hot Reload source generation and expands deterministic coverage for generator, compilation, and in-process metadata-update behavior. - Preserves supported complex properties such as `CollectionView.ItemTemplate` when an update replaces a root element. - Buffers diagnostics from speculative source generation so rejected attempts do not leak or duplicate diagnostics. - Conservatively emits the existing explicit `skipped (not yet supported)` marker for inline-resource and `StaticResource`-dependent complex-property subtrees that cannot be resolved safely in the isolated speculative context. - Adds a reusable source-generator Hot Reload harness for strict or diagnostic-tolerant generation, compilation, live metadata updates, chained deltas, multiple XAML documents, optional stable C# inputs, multiple retained instances, and application-host resource/theme scenarios. - Reorganizes the larger AI-assisted behavior suites into logical partial-class files while preserving their fully qualified test names. ### Root Cause and Wave-1 Fix `UpdateComponentCodeWriter` could create a newly added replacement root, but it skipped element-valued properties instead of applying the same value-creation and property-setting pipeline used by `InitializeComponent`. That silently dropped properties such as `CollectionView.ItemTemplate` in #36256. The replacement-root path now speculatively emits supported complex properties and assigns them to the existing live page. The #36256 coverage verifies both generated source/compilation and a real metadata update on the same page instance, including a non-null `CollectionView.ItemTemplate` whose content contains the updated label. Speculative generation introduced two safety constraints: - Diagnostics must remain isolated until emission is accepted. `SourceGenContext` now supports explicit diagnostic buffering, flush, and discard behavior. - A fresh speculative context cannot safely resolve ancestor resources or run the complete resource pipeline. Inline resources and direct, nested-markup, or element-form `StaticResource` references are therefore declined explicitly instead of producing a compileable false-success update that fails or resolves to `null` at runtime. Only the expected unsupported `InvalidOperationException` falls back to the skip marker; unexpected exceptions continue to propagate. #36157 is covered only at the generator boundary: malformed-to-repaired input recomputes generator diagnostics and the repaired output compiles. This does not claim IDE Error List, Roslyn edit-session, or `dotnet watch` recovery. #36156 remains deferred to an IDE/Roslyn host test because its rude-edit session-poisoning behavior cannot be reproduced faithfully by a generator unit test. ### Reusable Hot Reload Harness The harness: - keeps one incremental generator driver and advances the Roslyn `EmitBaseline` through successive deltas; - gives each scenario unique assembly, XAML path, and collectible `AssemblyLoadContext` identities; - supports generation-only, compile-only, and full live-update paths; - supports diagnostic-tolerant versions, multiple XAML documents, optional stable C# sources, and multiple retained roots; - uses metadata-aware fact and theory attributes so live-update tests skip, rather than fail, when the runtime does not support `MetadataUpdater.ApplyUpdate`; - resets `XamlHotReloadState`, restores `Application.Current`, unregisters roots from `XamlComponentRegistry`, disposes metadata/streams, and unloads the collectible context. Generated `x:Name` fields still require manual C# stubs because `CodeBehindCodeWriter` is outside this harness. ### Wave-2 Coverage The living AI-assisted index accounts for 55 test methods across 35 behavior IDs/capabilities covering: - dynamic and merged resources, styles, themes, and application resources; - visual states and behaviors; - data/control templates, selectors, compiled bindings, and `BindableLayout`; - bindings, markup extensions, and `MultiBinding`; - nested generated controls and namescopes; - multi-document and cross-assembly generator invalidation. The metadata-enabled AI-assisted run reports **41 passed, 18 intentionally skipped, and 0 failed**. Theory rows affect the reported case count. Passing tests are classified as live, construction, generator/compile guards, or explicit decline guards. Each skip-gated RED-PROBE names a nearby executable passing guard. The 18 skipped probes encode desired behavior for known gaps; they are not claimed as passing runtime coverage. The larger resource/theme, visual-state, template, binding/markup, and nested-control suites are split into behavior-named partial files for reviewability without changing xUnit discovery, attributes, fixtures, or method identities. ### Known Limitations and Deferred Lanes - A compiled `ResourceDictionary Source=` payload is unavailable in the in-memory collectible load context. Multi-document and `Source=` tests therefore verify tracking, generation, and compilation; their live payload probes remain skip-gated. - Cross-assembly tests cover incremental-generator reference invalidation and caching only. Applying deltas to a separately loaded runtime assembly remains an integration lane. - IDE/Hot Reload host behavior, Roslyn rude-edit recovery, `dotnet watch`, devices, native handlers, rendered output, and lifecycle behavior remain integration/host lanes. - The AppThemeBinding live probe remains skip-gated because the current update writer supplies an `IProvideValueTarget` with a null `TargetProperty`; its passing guard proves generated branch capture only. - Future keyed-template and selector factories remain tracked by #36482. Passing construction/source guards do not prove post-update future realization. - Complex-property and collection reconciliation remains tracked by #36732. Explicit decline guards prove that unsupported updates are skipped and compile, not that live reconciliation succeeds. ### What NOT to Do - Do not send speculative visitor diagnostics directly to the production diagnostic sink; rejected attempts must discard them. - Do not emit resource-dependent subtrees from an isolated context and treat successful compilation as proof of runtime resolution. - Do not catch broad exceptions and silently degrade unexpected generator failures. - Do not use generated-source markers as evidence of live runtime behavior. - Do not treat generator-only recovery coverage as proof of IDE or `dotnet watch` recovery. ### Issues Fixed Fixes #36256 Related coverage and roadmap issues: - #36157: generator diagnostic recomputation coverage only; host recovery is not fixed here. - #36156: deferred IDE/Roslyn host scenario. - #36482: future template/selector factory roadmap; not fixed by this PR. - #36732: complex-property and collection reconciliation roadmap; not fixed by this PR. ### Test Coverage ```bash DOTNET_MODIFIABLE_ASSEMBLIES=debug dotnet test src/Controls/tests/SourceGen.UnitTests/SourceGen.UnitTests.csproj -c Debug DOTNET_MODIFIABLE_ASSEMBLIES=debug dotnet test src/Controls/tests/SourceGen.UnitTests/SourceGen.UnitTests.csproj -c Release env -u DOTNET_MODIFIABLE_ASSEMBLIES dotnet test src/Controls/tests/SourceGen.UnitTests/SourceGen.UnitTests.csproj -c Debug ``` - `SourceGen.UnitTests` Debug: **497 passed / 18 skipped / 0 failed** (515 total). - `SourceGen.UnitTests` Release: **497 passed / 18 skipped / 0 failed** (515 total). - `XamlIncrementalHotReloadE2ETests`: **13 passed / 0 skipped / 0 failed**. - `HotReload.AiAssisted`: **41 passed / 18 skipped / 0 failed**. - Default Debug with metadata updates explicitly unavailable: **475 passed / 37 skipped / 0 failed** (512 total). xUnit reports each skip-gated theory once rather than once per data row. --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1b9bf3f3-3e24-49bf-b3eb-c075858ee563 Copilot-Session: 8dc5778b-13c3-40aa-8b21-76617ea71b69 Copilot-Session: f093ec6b-e912-40c6-acae-f308f79ee558 Copilot-Session: 8191a632-a704-4949-865b-a38bd8133f6d Copilot-Session: 96ebbad1-0079-4d43-a464-bb0b9ebadd89 Copilot-Session: d5e424fa-b2af-4909-9a4d-3b2114ac147d Copilot-Session: ba10e73c-e9b1-46c7-8bf6-c7cd0a2fa15c Copilot-Session: 3954ca4a-026e-46b1-a878-8ed0ba5224be Copilot-Session: 3aecbe3b-4302-428f-a397-9f0923d19656 Copilot-Session: 60c67cff-beec-4a4b-848d-fafbe39b837b Copilot-Session: 53ff611b-9d3e-4671-8950-c2c0c3e93d64
…6970) This PR removes `Controls.Sample.Sandbox` changes that were unintentionally bundled with the route-template work, so only the intended feature code remains in the original stream. The Sandbox sample is restored to its prior baseline behavior. - **Scope correction** - Reverts Sandbox-only edits that were part of the earlier PR payload but not part of the intended deliverable. - **Sandbox app restoration** - Restores previous startup mode and Shell layout in: - `App.xaml.cs` - `SandboxShell.xaml` - `SandboxShell.xaml.cs` - `MainPage.xaml.cs` - **Removal of unintended sample artifacts** - Removes route-template demo pages that should not have shipped in this change set: - `ProductPage.*` - `ReviewPage.*` - `OrdersPage.*` - `OrderDetailPage.*` ```csharp // Restored baseline startup behavior in Sandbox bool useShell = false; ``` Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: sheiksyedm <23059975+sheiksyedm@users.noreply.github.com>
…ompile filtering (#36957) <!-- 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 This focused follow-up addresses two empirically validated gaps found by the GPT-5.6 Sol post-merge verification of #36654. 1. `BackendIdentity` now defaults `ActivationValue` even when `TargetPlatformIdentifier` or `TargetPlatformIdentifiers` is also present. A single registration can therefore activate through a recognized TPI or through `MauiActiveBackend` on a neutral TFM, while explicit `ActivationValue` and `ActivationProperty` metadata still win. 2. platform Compile removal now starts from the `Compile` items in the `ExcludeFromCurrentConfiguration=true` metadata batch and intersects them with physical `Platforms/**` candidates. A fresh filesystem glob can no longer pull an explicitly false Compile item from another metadata bucket into the removal set. The helper-item removal pattern remains in place, preserving active item order and avoiding remove/re-add duplication. ## Before and After Before this change, a registration such as: ```xml <MauiPlatformSpecificFolder Include="Platforms/MacOS/" TargetPlatformIdentifiers="macos" BackendIdentity="macos" /> ``` worked for recognized TPI `macos` but did not activate on a neutral TFM with `MauiActiveBackend=macos`. It now supports both paths and remains inactive for another recognized TPI or a mismatched backend. Before this change, the presence of any true Compile metadata batch caused a fresh `Platforms/**` glob to include physical files explicitly marked false by a downstream target. Removal is now constrained to the actual true Compile identities under the platform folder. ## Tests The real shipping SingleProject targets are covered by regressions for: - one dual-path macOS registration through recognized TPI and neutral backend activation, plus recognized-TPI and backend mismatch negatives; - two physical platform Compile files in different metadata buckets with no folder allow-list, asserting only the true item is removed and the surviving list is ordered and duplicate-free. Validation: - complete `MSBuildTests.SingleProject_*` matrix: **46 passed, 0 failed**; - new regression matrix: **7 passed, 0 failed**; - `Controls.Build.Tasks.csproj` build: **succeeded with 0 warnings and 0 errors**; - targeted `dotnet format`: completed successfully. The unfiltered local graph is unavailable on this Mac because the platform project graph requires the iOS workload (`NETSDK1147`) and the full BuildTasks solution filter includes .NET Framework 4.7.2 projects without local reference assemblies (`MSB3644`). The workload-neutral matrix above used the repository-pinned .NET 11 SDK with platform TFMs disabled and imported the exact shipping targets. ### Candidate selection and final review #36957 is the selected implementation. Alternatives #36955 and #36956 both fail their own new Windows Helix XAML regression by removing the explicit-false Compile item. This branch uses FullPath/PathLike intersection, passes the expanded 46-case matrix, preserves metadata and duplicate count, handles absolute Compile identities, and skips the platform filesystem glob when no removal batch exists. Neutral activation of built-in backend identities is intentional: #36654 documented that built-ins use the same registration shape and that ActivationValue defaults from BackendIdentity. A dedicated regression now locks that contract. ## Issues Fixed Part of #34099 Part of #35021 Fixes #36650 Follow-up to #36654 --------- Co-authored-by: PureWeen <223556219+Copilot@users.noreply.github.com> Copilot-Session: ac6265fe-09a0-473c-b3f9-9ec6d6dd97cd Copilot-Session: 66f84348-6476-4097-8b7f-f240338e85c3
…ntent property changes at runtime (#36902) <!-- 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! **Faiilure Test on android hanlder path** : _**ShellContentShouldUpdateWhenContentPropertyChanges**_ This pull request introduces significant improvements to how `ShellSectionHandler` on Android manages and updates its `ShellContent` items, especially in response to property changes and collection modifications. The main goals are to ensure proper event subscription management, keep the UI in sync when content changes, and make adapter updates more robust. The property-change tracking and invalidation approach added here mirrors what was already implemented for the legacy renderer in [#34630](#34630), bringing the same `ShellContent.PropertyChanged` subscription/invalidation pattern to the new handler-based Android Shell architecture, since the handler previously had no equivalent logic and silently ignored `Content` changes at runtime. **ShellContent property change tracking and adapter updates:** * Added tracking of currently-subscribed `ShellContent` items via a `_subscribedItems` list, ensuring that `PropertyChanged` events are always wired/unwired correctly as items are added/removed. [[1]](diffhunk://#diff-46fc3573f39a6e0c764825dc5fac89c15fa741e4eb17cdc979aca5ed2acba144R47) [[2]](diffhunk://#diff-46fc3573f39a6e0c764825dc5fac89c15fa741e4eb17cdc979aca5ed2acba144R205-R211) [[3]](diffhunk://#diff-46fc3573f39a6e0c764825dc5fac89c15fa741e4eb17cdc979aca5ed2acba144R439-R446) [[4]](diffhunk://#diff-46fc3573f39a6e0c764825dc5fac89c15fa741e4eb17cdc979aca5ed2acba144R522-R597) * Implemented `OnShellContentPropertyChanged` to handle updates when a `ShellContent`'s content changes, including invalidating the adapter and syncing the toolbar state if the active tab is affected. * Added `UpdateContentPropertyChangedSubscriptions` to efficiently update event subscriptions in response to collection changes, including handling collection resets, additions, and removals. **Adapter robustness and notification improvements:** * Introduced `InvalidateShellContent` and `SafeNotifyDataSetChanged` methods to the adapter, ensuring that UI updates are safely triggered even if `ViewPager2` is in a layout pass, and logging a warning if updates get stuck. * Replaced direct calls to `NotifyDataSetChanged` with the safer `SafeNotifyDataSetChanged` throughout the handler, reducing the risk of UI inconsistencies or crashes. [[1]](diffhunk://#diff-46fc3573f39a6e0c764825dc5fac89c15fa741e4eb17cdc979aca5ed2acba144R510-R513) [[2]](diffhunk://#diff-46fc3573f39a6e0c764825dc5fac89c15fa741e4eb17cdc979aca5ed2acba144L531-R623) [[3]](diffhunk://#diff-46fc3573f39a6e0c764825dc5fac89c15fa741e4eb17cdc979aca5ed2acba144R829-R864) **Other improvements:** * Added missing `using` directives for required types such as `System.ComponentModel`, `Microsoft.Extensions.Logging`, and `Microsoft.Maui.Platform`. [[1]](diffhunk://#diff-46fc3573f39a6e0c764825dc5fac89c15fa741e4eb17cdc979aca5ed2acba144R5) [[2]](diffhunk://#diff-46fc3573f39a6e0c764825dc5fac89c15fa741e4eb17cdc979aca5ed2acba144R16-R20) These changes collectively make tab content updates more reliable and maintainable, especially when dynamic changes occur in the `ShellSection`, and bring the handler-based Android Shell in line with the equivalent renderer-based fix already merged in [#34630](#34630). ### Why no tests added : - No new automated test was added in this PR because the existing UI test `ShellContentShouldUpdateWhenContentPropertyChanges` already covers this behavior — it was the test that started failing in net11 CI, which is what prompted this fix. Rather than adding a new test, this PR restores the passing behavior of that existing test <!-- 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 #36809 ### Tested the behavior in the following platforms - [ ] Windows - [x] Android - [ ] iOS - [ ] Mac | Before Issue Fix | After Issue Fix | |----------|----------| | <video src="https://github.com/user-attachments/assets/cdcb02d6-a663-47fc-a85d-2e1143198070"> | <video src="https://github.com/user-attachments/assets/04e05f85-9b78-4a56-8503-76b1e5fe7b1a"> | <!-- Are you targeting main? All PRs should target the main branch unless otherwise noted. -->
This PR addresses UI test failures in the Net11 branch and includes updates to improve rendering and test stability across platforms. - The **BottomSheetDetentHeightIsCorrectWhenCollectionViewIsMeasuredBeforeMount** test passed locally in both CV1 and CV2. However, in CI, it fails in CV1 but passes in CV2. To avoid flaky issues, the sample was changed to use CollectionView2.
Reset patterns: - global.json - NuGet.config - eng/Version.Details.xml - eng/Versions.props - eng/common/*
|
Azure Pipelines: Successfully started running 1 pipeline(s). There may be pipelines that require an authorized user to comment /azp run to run. |
<!-- 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 Auto-approve and enable GitHub native auto-merge for immutable snapshots of these exact forward-merge targets: ```text main => net11.0 net11.0 => release/11.0.1xx-preview7 net11.0 => release/11.0.1xx-rc1 net11.0 => release/11.0.1xx-rc2 ``` ### Immutable snapshot design Each caller workflow checks for its generated merge PR before invoking Arcade: ```text no open merge PR -> run Arcade and create a fresh snapshot open merge PR -> leave its branch unchanged while CI runs ``` The checks use exact head/base pairs and require the PR authoring App to be `github-actions` with `isCrossRepository == false`. The release workflow resolves the current `MergeToBranch` from `github-merge-flow-release-11.jsonc` on `net11.0`, and passes `configuration_file_branch: net11.0` to Arcade so the gate and merge implementation use the same source of truth. Workflow runs are serialized with distinct concurrency groups so simultaneous push/schedule/manual runs cannot both pass the check and create or update the same PR. Because scheduled workflows start from the default branch, the release schedule uses a schedule-only job to dispatch `merge-net11-to-release.yml` at ref `net11.0`; the dispatched run is not a schedule event and cannot recurse. During rollout, the schedule job first compares the parsed safety-critical sections (`concurrency`, `CheckForOpenMergePullRequest`, and `Merge`) between the workflow on `main` and `net11.0`. It skips the dispatch until the full immutable-snapshot gate has propagated, while allowing unrelated branch-specific workflow differences. Fetch or parse failures fail visibly instead of dispatching an unknown definition. The read-only snapshot checks use read-only `GITHUB_TOKEN` permissions. Only the reusable Arcade merge job retains content and pull-request write access. New source commits that arrive while a merge PR is open wait for the next generated PR. Once the current PR merges or closes, the next push or daily schedule creates a fresh snapshot containing the remaining commits. This intentionally stops using Arcade's existing "fast-forward the open merge PR" behavior. The generated PR head does not change during CI, so source-branch pushes do not invalidate its approval or restart CI. ### Policy Service rule The rule runs only on `Opened`; `Synchronize` is not accepted. It requires: - event sender `github-actions[bot]` - event sender is also the PR author - exact target branch - exact, fully anchored generated title Human conflict-resolution pushes do not trigger reapproval. Bot-attributed `/rebase` synchronization also does not trigger the policy, closing the review-bypass path identified in the adversarial review. ### Review behavior and accepted limitation `MAUI protection` intentionally remains: ```text dismiss_stale_reviews_on_push: true require_last_push_approval: false required_approving_review_count: 1 ``` This preserves the repository's human-review workflow: a maintainer who pushes a fix to another person's PR can provide the subsequent approval without requiring a third reviewer. This PR does not modify repository rulesets or add a bypass. The generated merge PR remains safe under these settings. Policy Service approves only the initial `Opened` snapshot, the caller workflow refuses to invoke Arcade while the exact bot-authored PR remains open, and any out-of-band head push dismisses the approval with no automatic reapproval path. Ordinary target-branch advancement does not require the generated PR to update because the required status-check rules use `strict_required_status_checks_policy: false`. If the immutable head remains conflict-free, its approval remains valid, and required checks pass, auto-merge can complete against the advanced base. In the narrower case where base activity actually changes the reviewed diff or merge base, GitHub can dismiss the approval and safely stall the PR. That fail-closed limitation is accepted for this automation. ### Exact branch and title allow-list ```text net11.0 + ^[automated] Merge branch 'main' => 'net11.0'$ release/...-preview7 + ^[automated] Merge branch 'net11.0' => 'release/...-preview7'$ release/...-rc1 + ^[automated] Merge branch 'net11.0' => 'release/...-rc1'$ release/...-rc2 + ^[automated] Merge branch 'net11.0' => 'release/...-rc2'$ ``` Each entry in the file contains the full literal branch and anchored regex. Targets outside this allow-list remain manual. ### Merge behavior ```yaml - enableAutoMerge: mergeMethod: merge ``` This always creates a true merge commit, never squash or rebase. Arcade relies on merge ancestry to determine what remains to flow. GitHub completes auto-merge only when the PR has no merge conflict and required checks pass. ### Required checks | ruleset | checks | Policy Service bypass | | --- | --- | --- | | `MAUI required CI checks` | `maui-pr` | **none** | | `MAUI device and UI test checks` | `maui-pr-devicetests`, `maui-pr-uitests` | pull requests only | A failing or pending `maui-pr` blocks the merge. Device/UI checks do not. `MAUI protection` has no Policy Service bypass. It requires one ordinary approval; Policy Service supplies it for the exact authenticated `Opened` events above. ### CODEOWNERS PR #36890 removes the invalid CODEOWNERS file. `Require review from Code Owners` is disabled in `MAUI protection`; the ordinary one-approval requirement remains. ### Accepted trust boundary An initial `Opened` event authorizes on exact title/base plus `github-actions[bot]` as both sender and PR author. A collaborator with repository push access could deliberately create a same-repository workflow and matching PR. Real `maui-pr` from Azure Pipelines integration 9426 must still pass, but there is no additional human review under the intentionally ordinary one-review policy. This tradeoff is accepted for these exact forward-merge target pairs. The immutable-snapshot gate prevents later human content from being reapproved through synchronization. No new App is installed and no bypass is added to `MAUI protection` or the `maui-pr` ruleset. ### Verification - Both caller workflows pass `actionlint`. - All three changed YAML files parse successfully. - The live target resolver returns `release/11.0.1xx-preview7`. - Exact App/head/base/same-repository queries identify #36886 (`main => net11.0`) and #36880 (`net11.0 => release/11.0.1xx-preview7`). - The semantic rollout check rejects the current old `net11.0` workflow and accepts matching safety-critical sections. - Official GitHub documentation confirms that `workflow_dispatch` events created with `GITHUB_TOKEN` start workflow runs; the dispatched event skips the schedule-only job. - The Policy Service file parses and GitOps schema validation runs on every update. - The live `MAUI protection` settings and effective non-strict required-status-check rules were reverified on 2026-07-31. ### Issues Fixed None; infrastructure automation. --------- Co-authored-by: PureWeen <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1b2b2dd6-bf5f-4179-8a8f-c4c85b9bce26
PureWeen
enabled auto-merge
July 31, 2026 16:45
PureWeen
previously approved these changes
Jul 31, 2026
<!-- !!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING MAIN. !!!!!!! --> <!-- 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 #35302 ### Description of Change This pull request introduces a new caching mechanism for `ImmutableBrush` instances, specifically optimizing color-to-brush conversions in the `Brush` class. It adds a two-stage cache (simple and LRU) to reduce allocations and improve performance when repeatedly converting colors to brushes. Additionally, a suite of benchmarks is included to measure the impact of these changes on property-change propagation in common controls. **Caching improvements for brush creation:** * Introduced a new `CacheWithSwitch` class that provides a two-stage caching strategy for `ImmutableBrush` instances keyed by `Color`. It starts with a simple dictionary cache and automatically promotes to an LRU cache when capacity is reached, reducing memory allocations and improving brush reuse. (`src/Controls/src/Core/Internals/CacheWithSwitch.cs`) * Added an `ICache<TKey, TValue>` interface to standardize cache implementations used for brush caching. (`src/Controls/src/Core/Internals/ICache.cs`) * Implemented an `LRUBrushCache` class for least-recently-used caching of brushes, used as the second stage of `CacheWithSwitch`. (`src/Controls/src/Core/Internals/LRUBrushCache.cs`) **Integration with Brush class:** * Updated the `Brush` class to use the new cache for implicit conversions from `Color` and `SolidPaint`, ensuring brush instances are reused whenever possible. (`src/Controls/src/Core/Brush/Brush.cs`) [[1]](diffhunk://#diff-383e75280c95c1ee1bf2545184c1c002f647d95eacae4b3230bf2c2a2461f138R2) [[2]](diffhunk://#diff-383e75280c95c1ee1bf2545184c1c002f647d95eacae4b3230bf2c2a2461f138R15-R23) [[3]](diffhunk://#diff-383e75280c95c1ee1bf2545184c1c002f647d95eacae4b3230bf2c2a2461f138L103-R109) **Benchmarking and performance validation:** * Added a new benchmark suite to measure property-change propagation performance and allocations for `Label`, `Button`, and `Entry` controls, with a focus on background color changes that exercise the new brush caching logic. (`src/Core/tests/Benchmarks/Benchmarks/PropertyChangePropagationBenchmarker.cs`) ## Brush cache benchmark: branch vs `main` **Setup:** `main` has no brush cache — every `Paint → Brush` conversion allocates `new SolidColorBrush { Color = c }`. This branch adds `Lru64ColorVectorInlineBrushCache` (inline-array + SIMD lookup) and wires it into `Brush.cs`. Benchmarked over identical color sets, ShortRun, Apple M4 Max. > **Caveat:** `main` targets net10 (ran on .NET 10.0.9) and this branch targets net11 (ran on .NET 11.0-preview.6) — the runtimes differ because the TFMs differ. As a control, the branch `NullCache` (allocate-every-time, ~68 ns / 1024 B) closely matches main's no-cache (~84 ns / 1116 B), so the runtime gap is small (~15 ns) and the cache wins are real. ### Production path — `InlineLruCache` (branch) vs no-cache (`main`) | Scenario | main (no cache) | branch InlineLruCache | Time Δ | main alloc | branch alloc | Alloc Δ | |---|---|---|---|---|---|---| | 40 colors (fits in cap 50) | 84.36 ns | **16.92 ns** | **≈5.0× faster (−80%)** | 1116 B | **105 B** | **−90.6%** | | Weighted (20 hot ×10 + 40 cold) | 80.53 ns | **24.88 ns** | **≈3.2× faster (−69%)** | 1116 B | **180 B** | **−83.9%** | | 60 colors (> cap 50, thrashes) | 84.24 ns | 87.06 ns | ≈even (+3%) | 1116 B | 1024 B | −8% | ### Full branch run (net11, ShortRun) | Scenario | LruCache (dict+LL) | InlineLruCache (new) | NullCache (≈main) | |---|---|---|---| | 40 colors | 36.87 ns / 119 B | **16.92 ns / 105 B** | 68.19 ns / 1024 B | | 60 colors | 137.85 ns / 1.05 KB | **87.06 ns / 1 KB** | 75.61 ns / 1 KB | | Weighted | 36.77 ns / 190 B | **24.88 ns / 180 B** | 68.02 ns / 1024 B | ### Takeaways - **vs main:** for realistic workloads (colors fit in the cap), the new cache is **3–5× faster and allocates ~6–11× less** — main allocates a full `SolidColorBrush` (~1 KB) on every conversion, the cache returns a shared instance. - **vs the old LRU in this PR:** InlineLruCache is also **1.5–2.1× faster** than the dict+linked-list `LRUBrushCache`. - **The 60-color row is pathological:** 60 distinct colors exceed the capacity-50 benchmark, so it thrashes (every access misses + allocates). In production the cap is 64 and typical apps reuse a handful of colors, so the 40-color / weighted rows are representative. --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Resolve release SDK and dependency alignment conflicts by preserving the Preview 7 branch pins. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3b38a29c-edf8-4e9a-a247-e270e643cc54
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
Contributor
|
/azp run maui-pr |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
Contributor
|
/azp run maui-pr |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
Use a test-specific NuGet configuration that permits only Avalonia.Controls.Maui packages from NuGet.org while retaining the existing approved feeds for the rest of the dependency graph. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 37faae15-0e4c-46fd-9bef-f3006c8aaee8
Contributor
|
/azp run maui-pr |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
Keep NuGet.org restricted by package-source mapping while including the Avalonia transitive dependency graph required by the template build. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 37faae15-0e4c-46fd-9bef-f3006c8aaee8
Contributor
|
/azp run maui-pr |
|
Azure Pipelines will not run the associated pipelines, because the pull request was updated after the run command was issued. Review the pull request again and issue a new run command. |
Include the non-Avalonia-named MicroCom runtime dependency in the test-only NuGet.org package-source mapping while keeping all other packages on approved feeds. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 37faae15-0e4c-46fd-9bef-f3006c8aaee8
Contributor
|
/azp run maui-pr |
|
Azure Pipelines will not run the associated pipelines, because the pull request was updated after the run command was issued. Review the pull request again and issue a new run command. |
Contributor
|
/azp run maui-pr |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
Prevent the MainThread bridge and dispatcher tests from clearing their shared process-global DispatcherProvider state while another test is running. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 37faae15-0e4c-46fd-9bef-f3006c8aaee8
Contributor
|
/azp run maui-pr |
|
Azure Pipelines will not run the associated pipelines, because the pull request was updated after the run command was issued. Review the pull request again and issue a new run command. |
kubaflo
approved these changes
Aug 3, 2026
PureWeen
approved these changes
Aug 3, 2026
PureWeen
enabled auto-merge
August 3, 2026 13:14
PureWeen
disabled auto-merge
August 3, 2026 13:17
PureWeen
pushed a commit
that referenced
this pull request
Aug 3, 2026
<!-- 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 Flows the remaining CI fixes discovered while validating the automated `net11.0` to Preview 7 merge in #36986 back to `net11.0`. - Adds the missing iOS hosting namespace so the TabbedPage device tests compile. - Makes Avalonia template tests skip template post-action restore and use a test-specific NuGet configuration. Existing approved feeds remain mapped to all packages; NuGet.org is limited to the external `Avalonia*` and `MicroCom.*` dependency families. - Serializes `DispatcherTests` and `MainThreadBridgeTests`, which both mutate the process-global `DispatcherProvider`, preventing the Helix race observed in build 1536641. The modal device-test override fix was removed from this PR after it landed independently in #37030. These remaining fixes produced a successful aggregate `maui-pr` run for #36986 in [build 1536659](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1536659). The source-only version-property merge hardening remains separately tracked by #37019. ## Testing - `Core.UnitTests`: 22 targeted `DispatcherTests` and `MainThreadBridgeTests` passed on .NET 11 RC1 after rebasing. - iOS device-test compilation was validated while fixing #36986. - Avalonia integration scenarios restored through the restricted package-source mapping in successful `maui-pr` build 1536659. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Vally Fixture <vally-fixture@example.invalid> Copilot-Session: 37faae15-0e4c-46fd-9bef-f3006c8aaee8
Vignesh-SF3580
pushed a commit
to Vignesh-SF3580/maui
that referenced
this pull request
Aug 10, 2026
<!-- 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 Flows the remaining CI fixes discovered while validating the automated `net11.0` to Preview 7 merge in dotnet#36986 back to `net11.0`. - Adds the missing iOS hosting namespace so the TabbedPage device tests compile. - Makes Avalonia template tests skip template post-action restore and use a test-specific NuGet configuration. Existing approved feeds remain mapped to all packages; NuGet.org is limited to the external `Avalonia*` and `MicroCom.*` dependency families. - Serializes `DispatcherTests` and `MainThreadBridgeTests`, which both mutate the process-global `DispatcherProvider`, preventing the Helix race observed in build 1536641. The modal device-test override fix was removed from this PR after it landed independently in dotnet#37030. These remaining fixes produced a successful aggregate `maui-pr` run for dotnet#36986 in [build 1536659](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1536659). The source-only version-property merge hardening remains separately tracked by dotnet#37019. ## Testing - `Core.UnitTests`: 22 targeted `DispatcherTests` and `MainThreadBridgeTests` passed on .NET 11 RC1 after rebasing. - iOS device-test compilation was validated while fixing dotnet#36986. - Avalonia integration scenarios restored through the restricted package-source mapping in successful `maui-pr` build 1536659. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Vally Fixture <vally-fixture@example.invalid> Copilot-Session: 37faae15-0e4c-46fd-9bef-f3006c8aaee8
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 net11.0 branch which have not been merged yet to release/11.0.1xx-preview7. I'm a robot and am configured to help you automatically keep release/11.0.1xx-preview7 up to date, so I've opened this PR.
This PR merges commits made on net11.0 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/net11.0-to-release/11.0.1xx-preview7'. 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.