Commit 99ad6d1
Add AI memory-leak scanner + fixer agentic workflows (dotnet#36342)
<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!
> [!NOTE]
> 🤖 **AI-generated PR.** These workflows, and this description, were
produced with AI assistance. Please review carefully before merging.
## Summary
Adds two [gh-aw](https://github.com/github/gh-aw) agentic workflows that
proactively **find** and **fix** managed, cross-platform memory leaks in
MAUI — turning the AI-driven leak-hunting flow several contributors
already run by hand (scan → repro → measure → file → fix) into a
repeatable, reviewable pipeline.
**Design principle: only empirically-proven leaks.** The scanner files
an issue **only** when a plain `dotnet test` demonstrates the leak; the
fixer opens a PR **only** with a red→green regression test proving the
fix. There is no "you're missing a test" / coverage-gap mode — every
artifact is backed by a passing/failing test.
**Key property:** neither workflow needs an emulator/simulator or a MAUI
source build to *detect* a leak — the leak signature is proven by a
`dotnet test` against the **shipped `Microsoft.Maui.Controls` NuGet
package** on the plain library TFM, so detection runs on a standard
GitHub-hosted runner.
Only 4 files are added — the workflow source (`.md`) and its compiled
artifact (`.lock.yml`) for each:
```
.github/workflows/daily-leak-hunter.md + .lock.yml
.github/workflows/leak-fixer.md + .lock.yml
```
---
## 1. `daily-leak-hunter` — the scanner (files `[leak-scan]` issues)
**Trigger:** `schedule: every 12h` + manual (`workflow_dispatch`). Files
**only empirically-proven `[leak-scan]` issues** — up to **8 per run**.
1. **Sweeps every focus area** of the managed surface (`src/Core/src`,
`src/Controls/src/Core`, `src/Essentials/src`) in one run — it does not
stop after the first find.
2. Each focus area is **seeded with proven MAUI leak signatures** (from
the community's known-leak catalog) so the hunt targets where leaks
actually live: shared publisher → strong
`CollectionChanged`/`PropertyChanged` (e.g. `Picker.ItemsSource`,
`TableRoot`, `SelectedItems`, `GradientStops`), shared `ICommand` →
`CanExecuteChanged` (`ListView.RefreshCommand`, `SwipeItemView.Command`,
`BackButtonBehavior.Command`), VSM state triggers →
`DeviceDisplay.MainDisplayInfoChanged`, and static/`ResourceDictionary`
roots.
3. For each candidate it writes a standalone control / leaky /
mitigation xUnit repro (referencing the shipped package, no source
build) and measures retention with `WeakReference` + a forced GC. All
candidates go in **one test project** (one `[Fact]` each, one
restore/run).
4. It files a `[leak-scan]` issue **only for each leak the test
confirms** — the leaky scenario retains while **both** the control and
the mitigation release. A false positive is treated as worse than a
quiet run; unconfirmed candidates are dropped. **If a run proves
nothing, it files nothing** (no fallback mode).
**De-dup & safety:** skips a leak already covered by one of *its own*
open `[leak-scan]` issues; never touches product or test code (read-only
+ `create-issue`); `noop: report-as-issue: false`.
---
## 2. `leak-fixer` — the fixer (opens draft `[leak-fix]` PRs)
**Trigger:** `schedule: every 12h` + manual (`workflow_dispatch`). At
most **one action per run**, with de-dup + a 3-attempt cap. It picks the
**higher-priority** of two tracks:
### Track C — respond to review feedback (checked first)
Before doing new work, it checks whether one of *its own* open
`[leak-fix]` PRs (**hosted on this repo** — fork-hosted heads it can't
push to are left for a human) has an **unaddressed `CHANGES_REQUESTED`
review**. If so it works on that PR: **applies** the valid requested
changes (pushes a commit, re-validated so the PR's own test still holds)
and/or **posts a comment pushing back** on any request that is wrong,
would regress behaviour, or asks for a mute. A loop-guard (act only on a
review newer than the PR's last commit *and* the workflow's last
comment) prevents re-processing the same review.
### Track A — `[leak-scan]` runtime leak → `[leak-fix]` PR
For the selected proven leak it:
1. Writes a focused regression test in `Controls.Core.UnitTests`.
2. Builds MAUI **from source** and confirms the test **FAILS on `main`**
— proving it catches the leak.
3. Implements the minimal, idiomatic managed fix — a weak subscription /
teardown mirroring the existing `WeakEventManager` / `WeakNotify*Proxy`
patterns.
4. Rebuilds and confirms the **same test now PASSES**, no neighbouring
regressions.
5. Opens a **draft `[leak-fix]` PR** with `Fixes #N` and the red→green
evidence.
### Safety
- Enforces red→green **in both directions** — a fix without a
demonstrated failing-then-passing test is rejected.
- **Never** mutes / skips / disables a test; rejects its own attempt if
the only thing that goes green is a mute — **including when a review
asks for one** (it pushes back with a comment instead).
- **One action per run** — Track C review-response *or* one new Track A
`[leak-fix]` PR, never both.
- Writes only via scoped safe-outputs (agent job is read-only); PR
branches are limited to `leak-fix/**`, changes to managed `src/**` (+
`PublicAPI.Unshipped.txt`).
- Skips cleanly if already fixed on `main`, out of scope (needs a device
fix), or attempt-capped.
---
## Validated end-to-end on **dotnet/maui itself** (pre-merge)
Both workflows were executed on this repo's own Actions infrastructure
(via short-lived `push`-triggered test branches, since
`workflow_dispatch` only lights up once a workflow is on `main`). Real
outputs, all on **dotnet/maui**:
- **Multi-leak in one run** — a single hunter run filed **5** distinct
proven `[leak-scan]` issues via the catalog-seeded sweep: dotnet#36343
`SwipeItemView.Command`, dotnet#36344 `ListView.RefreshCommand`, dotnet#36345 `Shell
BackButtonBehavior.Command` (all `ICommand.CanExecuteChanged`), dotnet#36346
`Picker.ItemsSource`, dotnet#36347 `IndicatorView.ItemsSource`
(`CollectionChanged`). dotnet#36344 and dotnet#36345 are leaks not previously
tracked upstream.
- **Proven-only** — a later run filed `[leak-scan]` dotnet#36350
`CollectionView.SelectedItems` and (correctly) **nothing else**, having
de-duped the already-filed leaks.
- **Track A fix** — dotnet#36309 `[leak-fix] Fix ResourceDictionary
MergedDictionaries memory leak` (regression test red→green + managed
fix).
- **Track C review-response** — on dotnet#36253 the fixer independently
re-read the code, agreed with the reviewer's findings, validated the
reviewer's `WeakEventManager` approach (24/24, red→green), and posted
the concrete fix.
---
## Notes for reviewers
- Everything the workflows file/open is **AI-generated** and clearly
labelled as such; treat them as high-quality drafts for human review.
- The workflows are self-contained (`.md` source + compiled
`.lock.yml`); merging them enables the `schedule` triggers on `main`.
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>1 parent 580ef8a commit 99ad6d1
4 files changed
Lines changed: 4118 additions & 0 deletions
File tree
- .github/workflows
0 commit comments