---
description: "Guidance for GitHub Copilot when working on the .NET MAUI repository."
---

# GitHub Copilot Development Environment Instructions

This document provides specific guidance for GitHub Copilot when working on the .NET MAUI repository. It serves as context for understanding the project structure, development workflow, and best practices.

## Code Review Instructions

When performing a code review on PRs that change functional code, run the pr-finalize skill to verify that the PR title and description accurately match the actual implementation. This ensures proper documentation and helps maintain high-quality commit messages.

## Repository Overview

**.NET MAUI** is a cross-platform framework for creating mobile and desktop applications with C# and XAML. This repository contains the core framework code that enables development for Android, iOS, iPadOS, macOS, and Windows from a single shared codebase.

### Key Technologies

- **.NET SDK** - Version is **ALWAYS** defined in `global.json` at repository root
  - **main branch**: Latest stable .NET version
  - **Feature branches**: Each `netN.0` branch targets the .NET N SDK. By convention, the highest `netN.0` branch is the current development branch for new features and API changes.
- **Cake build system** for compilation and packaging (`dotnet cake`)
- **MSBuild** with custom build tasks (must build `Microsoft.Maui.BuildTasks.slnf` first)
- **Testing frameworks**:
  - **xUnit** - Unit tests (`*.UnitTests.csproj`)
  - **NUnit** - UI tests (`TestCases.Shared.Tests`)
  - **Appium WebDriver** - UI test automation

## Development Environment Setup

This guidance assumes:
- Repository is already cloned and tools are restored (`dotnet tool restore` completed)
- Build tasks are compiled (`Microsoft.Maui.BuildTasks.slnf` built successfully)
- Correct .NET SDK version installed (verify with `dotnet --version` against `global.json`)

### Platform-Specific Requirements

- **Android**: OpenJDK 17 + Android SDK (install via `android` command after `dotnet tool restore`)
- **iOS/macOS**: Xcode (current stable version)
- **Windows**: Windows SDK

## Project Structure

### Important Directories
- `src/Core/` - Core MAUI framework code
- `src/Controls/` - UI controls and components
- `src/Essentials/` - Platform APIs and essentials
- `src/TestUtils/` - Testing utilities and infrastructure
- `docs/` - Development documentation
- `eng/` - Build engineering and tooling
- `.github/` - GitHub workflows and configuration

### Platform-Specific Code Organization
- **Android** specific code is inside folders labeled `Android`
- **iOS** specific code is inside folders labeled `iOS`
- **MacCatalyst** specific code is inside folders named `MacCatalyst`
- **Windows** specific code is inside folders named `Windows`

### Platform-Specific File Extensions

Platform-specific files use naming conventions to control compilation:

**File extension patterns**:
- `.windows.cs` - Windows TFM only
- `.android.cs` - Android TFM only
- `.ios.cs` - iOS and MacCatalyst TFMs (both)
- `.maccatalyst.cs` - MacCatalyst TFM only (does NOT compile for iOS)

**Important**: Both `.ios.cs` and `.maccatalyst.cs` files compile for MacCatalyst. There is no precedence mechanism that excludes one when the other exists.

**Example**: If you have both `CollectionView.ios.cs` and `CollectionView.maccatalyst.cs`, both will compile for MacCatalyst builds. The `.maccatalyst.cs` file won't compile for iOS, but the `.ios.cs` file will compile for both iOS and MacCatalyst.

### Sample Projects

- `src/Controls/samples/Maui.Controls.Sample` - Full gallery sample with all controls and features
- `src/Controls/samples/Maui.Controls.Sample.Sandbox` - Empty project for testing/reproduction
- `src/Essentials/samples/Essentials.Sample` - Essentials API demonstrations (non-UI MAUI APIs)
- `src/BlazorWebView/samples/` - BlazorWebView sample applications

## Development Workflow

### Testing

Major test projects:
- **Core**: `src/Core/tests/UnitTests/Core.UnitTests.csproj`
- **Essentials**: `src/Essentials/test/UnitTests/Essentials.UnitTests.csproj`
- **Controls**: `src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj`
- **XAML**: `src/Controls/tests/Xaml.UnitTests/Controls.Xaml.UnitTests.csproj`

Find all tests: `find . -name "*.UnitTests.csproj"`

### CI Pipelines (Azure DevOps)

When referencing or triggering CI pipelines, use these current pipeline names:

| Pipeline | Name | Purpose |
|----------|------|---------|
| Overall CI | `maui-pr` | Full PR validation build |
| Device Tests | `maui-pr-devicetests` | Helix-based device tests |
| UI Tests | `maui-pr-uitests` | Appium-based UI tests |

**⚠️ Old pipeline names** (e.g., `MAUI-UITests-public`, `MAUI-public`) are **outdated** and should NOT be used. Always use the names above.

### Investigating CI Failures

**🚨 ALWAYS use the `azdo-build-investigator` skill when investigating CI failures or assessing merge readiness.** Its instructions direct you to invoke the `ci-analysis` skill first for the core investigation workflow, then apply MAUI-specific corrections (correct pipeline names, XHarness quirks, binlog guidance).

Do NOT default to manually querying AzDO APIs or rely solely on `gh pr checks` pass/fail counts.

**When to use it:**
- "How does CI look?" / "Is CI green?" / "Can we merge?"
- "What's failing?" / "Are these known failures?"
- "Is this PR safe to merge?" / "Any CI concerns?"
- After any PR push to verify the build

**Verifying specific tests:** When asked "did test X pass?" or "did the new test run?", query the **actual AzDO test results** — do NOT infer whether a test ran by inspecting code attributes. Class-level traits, base class categories, and assembly-level attributes can all cause a test to run even when the method itself has no visible category. Check the evidence, not the code.

**Anti-pattern:** Writing ad-hoc scripts to parse AzDO build timelines. The skills handle Helix work item details, known issue cross-referencing, and test result aggregation that manual approaches miss.

### Gradle / Maven Dependency Failures (CFSClean)

The official CI build uses CFSClean network isolation which blocks `repo.maven.apache.org`. All Gradle/Maven dependencies resolve through the `dotnet-public-maven` Azure Artifacts feed.

**If CI fails with Gradle 401 errors** like `"No local versions of package"` or `"Please provide authentication to save package from upstream"`, it means a Maven package hasn't been ingested into the feed yet. **Fix:** run `./eng/ingest-maven-deps.sh` locally to pre-populate the feed. See `src/Core/AndroidNative/settings.gradle` for details.

**Do NOT upgrade Gradle past 8.x** — the Android SDK's `net.android.init.gradle.kts` is incompatible with Gradle 9.x (`dotnet/android#10738`).

### Code Formatting

Always format code before committing:

```bash
dotnet format Microsoft.Maui.sln --no-restore --exclude Templates/src --exclude-diagnostics CA1822
```

## Contribution Guidelines

### Handling Existing PRs for Assigned Issues

**🚨 CRITICAL REQUIREMENT: Always develop your own solution first, then compare with existing PRs.**

1. **Develop your own solution first** - Analyze the issue independently and design your approach without looking at existing PRs
2. **Search for existing PRs** - After developing your solution, search for open PRs addressing the same issue
3. **Compare and evaluate** - Examine existing PR approaches and decide which solution better addresses the issue
4. **Document your decision** - In your PR description, compare your solution to existing PRs and explain why you chose your approach, including concerns with alternatives
5. **Improve either solution** - Whether using your solution or an existing one, enhance with better tests, code quality, error handling, or documentation

### Auto-Generated Files (Never Commit)

These files are auto-generated and must NOT be committed:
- `cgmanifest.json` - Generated during CI builds
- `templatestrings.json` - Auto-generated localization

**For AI agents:** Always reset changes to these files before committing.

### PublicAPI.Unshipped.txt File Management

When working with public API changes:
- **Never disable analyzers** to bypass PublicAPI.Unshipped.txt issues
- **Always add correct API entries** to PublicAPI.Unshipped.txt files
- **Use `dotnet format analyzers`** if having trouble
- **If files are incorrect**: Revert all changes, then add only the necessary new API entries

### Branching
- `main` - For bug fixes without API changes
- The highest `netN.0` branch (by convention) - For new features and API changes. To find it, run `git fetch origin` then: `git for-each-ref --sort=-version:refname --count=1 --format='%(refname:lstrip=3)' refs/remotes/origin/net*.0`

### Git Workflow (Copilot CLI Rules)

**🚨 CRITICAL Git Rules for Copilot CLI:**

1. **NEVER commit directly to `main`** - Always create a feature branch for your work. Direct commits to `main` are strictly prohibited.

2. **When amending an existing PR, work on the PR's branch directly** - Do NOT create a separate branch off a PR branch. The PR branch already IS a feature branch. Creating a new branch off it means CI won't run on the original PR, defeating the purpose. Use `gh pr checkout` to switch to the PR branch, make your changes, commit, **then** ask before pushing so the user can review locally first.

3. **Do NOT rebase, squash, or force-push** unless explicitly requested by the user. These operations rewrite git history and can cause problems for other contributors. Default behavior should be regular commits and pushes.

**Safe Git Workflow:**
```bash
# Create a feature branch (NEVER work directly on main)
git checkout -b feature/issue-12345

# Make commits normally
git add .
git commit -m "Fix: Description of the change"

# Push to remote (for new branches)
git push -u origin feature/issue-12345

# For subsequent pushes on the same branch
git push
```

**When asked to update an existing PR:**
```bash
# Check out the PR branch directly (do NOT create a new branch off it)
gh pr checkout 12345

# Make fixes and commit to the PR branch
git add .
git commit -m "Fix: Description of the change"
```
1. **STOP and ask the user** before pushing: "Changes are committed locally. Would you like me to push these changes to the PR?"
2. Exception: If the user's instructions explicitly include pushing, proceed without asking.

### Documentation
- Update XML documentation for public APIs
- Follow existing code documentation patterns
- Update relevant docs in `docs/` folder when needed

### Opening PRs

All PRs are required to have this at the top of the description:

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

Always put that at the top, without the block quotes. Without it, users will NOT be able to try the PR and your work will have been in vain!



## Custom Agents and Skills

The repository includes specialized custom agents and reusable skills for specific tasks.

### Skills vs Agents

| Aspect | Skills | Agents |
|--------|--------|--------|
| **Invoke** | `/skill-name` or direct request | Delegate to agent |
| **Output** | Analysis, recommendations | Actions, changes applied |
| **Interaction** | Interactive discussion | Autonomous workflow |
| **Example** | `/learn-from-pr` → recommendations | learn-from-pr agent → applies changes |

### Available Custom Agents

1. **pr** - Sequential 4-phase workflow for reviewing and working on PRs
   - **Use when**: A PR already exists and needs review or work, OR an issue needs a fix
   - **Capabilities**: PR review, test verification, fix exploration, alternative comparison
   - **Trigger phrases**: "review PR #XXXXX", "work on PR #XXXXX", "fix issue #XXXXX", "continue PR #XXXXX"
   - **Do NOT use for**: Just running tests manually → Use `sandbox-agent`

2. **write-tests-agent** - Agent for writing tests. Determines test type (UI vs XAML) and invokes the appropriate skill (`write-ui-tests`, `write-xaml-tests`)
   - **Use when**: Creating new tests for issues or PRs
   - **Capabilities**: Test type determination (UI and XAML), skill invocation, test verification
   - **Trigger phrases**: "write tests for #XXXXX", "create tests", "add test coverage"

3. **sandbox-agent** - Specialized agent for working with the Sandbox app for testing, validation, and experimentation
   - **Use when**: User wants to manually test PR functionality or reproduce issues
   - **Capabilities**: Sandbox app setup, Appium-based manual testing, PR functional validation
   - **Trigger phrases**: "test this PR", "validate PR #XXXXX in Sandbox", "reproduce issue #XXXXX", "try out in Sandbox"
   - **Do NOT use for**: Code review (use pr agent), writing automated tests (use write-tests-agent)

4. **learn-from-pr** - Extracts lessons from PRs and applies improvements to the repository
   - **Use when**: After complex PR, want to improve instruction files/skills based on lessons learned
   - **Capabilities**: Analyzes PR, identifies failure modes, applies improvements to instruction files, skills, code comments
   - **Trigger phrases**: "learn from PR #XXXXX and apply improvements", "improve repo based on what we learned", "update skills based on PR"
   - **Output**: Applied changes to instruction files, skills, architecture docs, code comments
   - **Do NOT use for**: Analysis only without applying changes → Use `/learn-from-pr` skill instead

5. **release-readiness-agent** - Assesses ship-readiness for a .NET MAUI release branch — both **SR** (`release/*-srN`) and **Preview** (`release/*-previewN`)
   - **Use when**: A release (SR or Preview) is approaching ship date and you need a synthesized verdict with WorkIQ/MCP enrichment on top of the deterministic report — **or** for a portfolio question across all active releases ("status on releases", "what needs attention across releases") where the user may not know which releases exist
   - **Capabilities**: Resolves the branch (SR or Preview) from natural language, picks the right script (`Get-ReleaseReadiness.ps1` for SR, `Get-PreviewReadiness.ps1` for Preview), enriches `rejected-from-sr` candidates with WorkIQ context (SR lane), patches `UNKNOWN` ship-check rows via MCP (`maestro_default_channels`, `maestro_builds`), presents an overall verdict
   - **Trigger phrases**: "is SR7 ready to ship", "release readiness for release/10.0.1xx-sr7", "survey the SR8 branch", "how does net11 preview6 look", "is preview6 ready to cut", "release readiness for release/11.0.1xx-preview6" — **plus portfolio / cross-release questions with no specific release named**: "give me a status on releases", "release status overview", "what's the status across all releases", "what needs attention across releases", "what's next for MAUI releases"
   - **Output**: Verdict (Ready / Conditionally Ready / Not Ready) + per-candidate classification (SR) or per-section table (Preview) + actionable next steps
   - **Do NOT use for**: Programmatic / scripted consumers that just need the raw JSON — use the `release-readiness` skill directly. Reviewing a single PR (use **pr**). Running tests manually (use **sandbox-agent**).

### Reusable Skills

Skills are modular capabilities that can be invoked directly or used by agents. Located in `.github/skills/`:

#### User-Facing Skills

1. **pr-review** (`.github/skills/pr-review/SKILL.md`)
   - **Purpose**: End-to-end PR review orchestrator — 3 phases: pr-preflight, try-fix, pr-report. Gate runs separately before this skill via Review-PR.ps1.
   - **Trigger phrases**: "review PR #XXXXX", "work on PR #XXXXX", "fix issue #XXXXX", "continue PR #XXXXX"
   - **Capabilities**: Multi-model fix exploration, alternative comparison, PR review recommendation
   - **Do NOT use for**: Just running tests manually → Use `sandbox-agent`
   - **Phase instructions** (in `.github/pr-review/`):
     - `pr-preflight.md` — Context gathering from issue/PR
     - `pr-report.md` — Final recommendation
   - **Phase skill**: `try-fix` — Multi-model fix exploration
   - **Note**: Gate (test verification) runs as a script step in `Review-PR.ps1` before this skill is invoked. Gate result is passed in the prompt.

2. **issue-triage** (`.github/skills/issue-triage/SKILL.md`)
   - **Purpose**: Query and triage open issues that need milestones, labels, or investigation
   - **Trigger phrases**: "find issues to triage", "show me old Android issues", "what issues need attention"
   - **Scripts**: `init-triage-session.ps1`, `query-issues.ps1`, `record-triage.ps1`

2. **find-reviewable-pr** (`.github/skills/find-reviewable-pr/SKILL.md`)
   - **Purpose**: Finds open PRs in dotnet/maui and dotnet/docs-maui that need review
   - **Trigger phrases**: "find PRs to review", "show milestoned PRs", "find partner PRs"
   - **Scripts**: `query-reviewable-prs.ps1`
   - **Categories**: P/0, milestoned, partner, community, recent, docs-maui

3. **pr-finalize** (`.github/skills/pr-finalize/SKILL.md`)
   - **Purpose**: Verifies PR title and description match actual implementation, AND performs code review for best practices before merge.
   - **Trigger phrases**: "finalize PR #XXXXX", "check PR description for #XXXXX", "review commit message"
   - **Used by**: Before merging any PR, when description may be stale
   - **Note**: Does NOT require agent involvement or session markdown - works on any PR
   - **🚨 CRITICAL**: NEVER use `--approve` or `--request-changes` - only post comments. Approval is a human decision.

4. **code-review** (`.github/skills/code-review/SKILL.md`)
   - **Purpose**: Reviews PR code changes for correctness, safety, and consistency with MAUI conventions. Walks through a MAUI-specific checklist covering handler lifecycle, platform code, safe area, threading, public API, and test patterns.
   - **Trigger phrases**: "review code for PR #XXXXX", "code review PR #XXXXX", "review this PR's code"
   - **Note**: Standalone skill — uses independence-first assessment (reads code before PR description to avoid anchoring bias). Can be used by any agent or invoked directly.
   - **🚨 CRITICAL**: NEVER use `--approve` or `--request-changes` — only post comments. Approval is a human decision.

5. **learn-from-pr** (`.github/skills/learn-from-pr/SKILL.md`)
   - **Purpose**: Analyzes completed PR to identify repository improvements (analysis only, no changes applied)
   - **Trigger phrases**: "what can we learn from PR #XXXXX?", "how can we improve agents based on PR #XXXXX?"
   - **Used by**: After complex PRs, when agent struggled to find solution
   - **Output**: Prioritized recommendations for instruction files, skills, code comments
   - **Note**: For applying changes automatically, use the learn-from-pr agent instead

6. **write-ui-tests** (`.github/skills/write-ui-tests/SKILL.md`)
   - **Purpose**: Creates UI tests for GitHub issues and verifies they reproduce the bug
   - **Trigger phrases**: "write UI tests for #XXXXX", "create UI test for issue", "add UI test coverage"
   - **Output**: Test files that fail without fix, pass with fix

7. **write-xaml-tests** (`.github/skills/write-xaml-tests/SKILL.md`)
   - **Purpose**: Creates XAML unit tests for XAML parsing, compilation, and source generation
   - **Trigger phrases**: "write XAML tests for #XXXXX", "test XamlC behavior", "reproduce XAML parsing bug"
   - **Output**: Test files for Controls.Xaml.UnitTests

9. **verify-tests-fail-without-fix** (`.github/skills/verify-tests-fail-without-fix/SKILL.md`)
   - **Purpose**: Verifies tests catch the bug before fix and pass with fix. Auto-detects test type (UI, device, unit, XAML) and dispatches to the appropriate runner.
   - **Two modes**: Verify failure only (test creation) or full verification (test + fix)
   - **Used by**: After creating tests, before considering PR complete

10. **run-integration-tests** (`.github/skills/run-integration-tests/SKILL.md`)
   - **Purpose**: Build, pack, and run .NET MAUI integration tests locally
   - **Trigger phrases**: "run integration tests", "test templates locally", "run macOSTemplates tests", "run RunOniOS tests"
   - **Categories**: Build, WindowsTemplates, macOSTemplates, Blazor, MultiProject, Samples, AOT, RunOnAndroid, RunOniOS
   - **Note**: **ALWAYS use this skill** instead of manual `dotnet test` commands for integration tests

11. **dependency-flow** (`.github/skills/dependency-flow/SKILL.md`)
    - **Purpose**: MAUI-specific dependency flow rules, channel conventions, and feed lookup workflows
    - **Trigger phrases**: "feeds for .NET MAUI X.Y.Z", "where is MAUI build", "promote build to public feed", "what channels is MAUI on", "subscription health for MAUI"
    - **Wraps**: `maestro-cli` skill (from `dotnet-dnceng@dotnet-arcade-skills` plugin) and maestro MCP tools
    - **Note**: Provides MAUI-specific guardrails on top of core Maestro/darc operations — channel naming, safety deny-list, input validation, and prompt injection defense

12. **release-readiness** (`.github/skills/release-readiness/SKILL.md`)
    - **Purpose**: Deterministic ship-readiness engine for .NET MAUI release branches — both **SR** (`release/*-srN`) and **Preview** (`release/*-previewN`). Surveys CI, computes what's actually shipping, classifies open regressions, identifies port candidates and rejected backports
    - **Trigger phrases**: "release readiness for SRN", "is SR7 ready to ship", "survey the SR branch", "release readiness for preview6", "how does preview6 look (deterministic)", "status across all releases" (reads the live `[Release Readiness]` tracker issues by body marker — no survey re-run needed)
    - **Scripts**: `Get-ReleaseReadiness.ps1` (SR lane), `Get-PreviewReadiness.ps1` (Preview lane), `Find-ReleaseReadinessTrackers.ps1` (tracker discovery)
    - **Output**: JSON + Markdown report, list of source PRs, classification of regression issues (in-sr-active, rejected-from-sr, no-fix-yet, etc.)
    - **Note**: Deterministic and reproducible — no MCP, no LLM judgment. Use **this skill directly** when you need raw output for a script, dashboard, cron job, or programmatic consumer. For natural-language verdict synthesis with WorkIQ enrichment, use the **`release-readiness-agent`** instead.

#### Internal Skills (Used by Agents)

13. **try-fix** (`.github/skills/try-fix/SKILL.md`)
   - **Purpose**: Proposes ONE independent fix approach, applies it, tests, records result with failure analysis, then reverts
   - **Used by**: pr agent Phase 3 (Fix phase) - rarely invoked directly by users
   - **Behavior**: Reads prior attempts to learn from failures. Max 5 attempts per session.
   - **Output**: Updates session markdown with attempt results and failure analysis

### Using Custom Agents

**Delegation Policy**: When user request matches agent trigger phrases, **ALWAYS delegate to the appropriate agent immediately**. Do not ask for permission or explain alternatives unless the request is ambiguous.

**Examples of correct delegation**:
- User: "Review PR #12345" → Immediately invoke **pr** agent
- User: "Test this PR" → Immediately invoke **sandbox-agent**
- User: "Fix issue #67890" (no PR exists) → Suggest using `/delegate` command
- User: "Write tests for issue #12345" → Immediately invoke **write-tests-agent**
- User: "Is SR7 ready to ship?" → Immediately invoke **release-readiness-agent**
- User: "How does net11 preview6 look?" → Immediately invoke **release-readiness-agent**
- User: "Give me a status on releases / what needs attention across releases?" → Immediately invoke **release-readiness-agent** (portfolio mode — it enumerates active releases by reading the `[Release Readiness]` tracker issues; don't ask "which release?")
- User: "Give me the raw release-readiness JSON for SR8" → Use the **release-readiness** skill directly (no enrichment needed)

**When NOT to delegate**:
- User asks "What does PR #12345 do?" → Informational query, handle yourself
- User asks "How do I test PRs?" → Documentation query, handle yourself
- User has follow-up questions after agent completes → Continue the conversation yourself