diff --git a/.agents/skills/issue-triage/SKILL.md b/.agents/skills/issue-triage/SKILL.md index f3ea7b1502e..fa17da9ca20 100644 --- a/.agents/skills/issue-triage/SKILL.md +++ b/.agents/skills/issue-triage/SKILL.md @@ -134,7 +134,7 @@ Write brief internal analysis (3–5 sentences), classify the type, then read [r > 3. **Follow these critical constraints:** > - `meta.schemaVersion` must be `"1.0"` > - **Optional fields:** OMIT entirely if not applicable. Do NOT set to `null`. -> - **String Arrays:** `platforms`, `backends`, `tenets` are simple string arrays (no confidence wrapper). +> - **String Arrays:** `platforms`, `backends`, `tenets`, `perf` are simple string arrays (no confidence wrapper). Any `perf/*` value implies `tenet/performance` — include it in `tenets`. > - **Investigation:** `analysis.codeInvestigation` is MANDATORY. At least one entry for bugs, two for close-* actions. > - **Rationale:** `analysis.rationale` is a single summary string (not per-field). > - **Validation:** No extra properties allowed (`additionalProperties: false`). diff --git a/.agents/skills/issue-triage/references/labels.md b/.agents/skills/issue-triage/references/labels.md index 3c06a95f490..a51c1c7e670 100644 --- a/.agents/skills/issue-triage/references/labels.md +++ b/.agents/skills/issue-triage/references/labels.md @@ -11,6 +11,7 @@ Values are **full GitHub labels** (e.g., `type/bug`, `os/Linux`) — use exactly | `os/` | **0–N** | All platforms affected (omit if cross-platform) | | `backend/` | **0–N** | All rendering backends affected (omit if not backend-specific) | | `tenet/` | **0–N** | Quality tenets that apply | +| `perf/` | **0–N** | Performance sub-type — **each implies `tenet/performance`** (add both) | | `partner/` | **0–1** | Third-party partner flag | ## Valid Labels @@ -32,6 +33,22 @@ Pick the most specific match. SKCanvasView in MAUI → `area/SkiaSharp.Views.Mau ### tenet/ (optional, multiple) `tenet/compatibility` · `tenet/performance` · `tenet/reliability` +### perf/ (optional, multiple) +Performance **sub-type** — a finer axis under the `tenet/performance` umbrella. Apply one or +more when an issue is about speed, memory, or size, **and always add `tenet/performance` too**. + +`perf/memory-leak` · `perf/allocations` · `perf/interop` · `perf/rendering` · `perf/throughput` · `perf/startup` · `perf/size` + +| Label | Use when… | Example | +|-------|-----------|---------| +| `perf/memory-leak` | memory grows unbounded — leaked native handles, undisposed objects, GPU memory not released | "SKGLView leaks memory over time" | +| `perf/allocations` | excessive managed allocations / per-frame GC or heap churn | "per-frame heap allocations in the swapchain" | +| `perf/interop` | P/Invoke marshalling or native interop overhead (incl. lock contention) | "remove native interop in SKMatrix" | +| `perf/rendering` | slow drawing, rendering, or GPU frame performance | "DrawText is slow", "matrix changes drop draw perf" | +| `perf/throughput` | an operation is slower than expected — encode/decode, file loading, pixel/format conversion, readback | "JPEG decode 3× slower than libjpeg-turbo" | +| `perf/startup` | slow initialization or first-use latency | "SKTypeface.CreateDefault very slow on first call" | +| `perf/size` | binary, package, or output-file size | "reduce SkiaSharp dll size", "PDF output too large" | + ### partner/ (optional, single) `partner/maui` · `partner/tizen` · `partner/unoplatform` @@ -43,4 +60,5 @@ Pick the most specific match. SKCanvasView in MAUI → `area/SkiaSharp.Views.Mau - **`type/feature-request`**: Adding completely new functionality — e.g., "add PDF export", "support new image format" - **Platform parity gap**: API exists cross-platform but a specific platform handler is missing → `type/bug` (the API contract is broken). Only use `type/enhancement` if no platform implements it yet. - `question` asks "how?"; `documentation` says "we need docs for X" +- **Performance issues:** whenever you apply any `perf/*` label, also apply `tenet/performance` — `perf/*` is the sub-type, `tenet/performance` is the umbrella tenet. A **memory leak** (growing native memory, undisposed handles) → `tenet/performance` + `perf/memory-leak` (this is what the `memory-leak-fixer` workflow files). Pick multiple `perf/*` only if genuinely distinct concerns apply. - When behavior is correct but easy to misuse (disposal ordering, threading, etc.), keep `type/bug` and suggest `close-as-not-a-bug` with a workaround. The `status/by-design` label communicates that the behavior is intentional. diff --git a/.agents/skills/issue-triage/references/schema-cheatsheet.md b/.agents/skills/issue-triage/references/schema-cheatsheet.md index 0d4add2b182..432045917ed 100644 --- a/.agents/skills/issue-triage/references/schema-cheatsheet.md +++ b/.agents/skills/issue-triage/references/schema-cheatsheet.md @@ -22,11 +22,13 @@ Read this BEFORE generating JSON. Full schema: `references/triage-schema.json`. "platforms": ["", ...], // optional, plain strings NOT objects "backends": ["", ...], // optional, plain strings NOT objects "tenets": ["", ...], // optional, plain strings NOT objects + "perf": ["", ...], // optional, plain strings NOT objects; if non-empty add tenet/performance "partner": "" // optional, plain string NOT object } ``` -⚠️ `platforms`, `backends`, `tenets` are **plain string arrays** — NOT `{value, confidence}` objects. +⚠️ `platforms`, `backends`, `tenets`, `perf` are **plain string arrays** — NOT `{value, confidence}` objects. +⚠️ Any `perf/*` label implies `tenet/performance` — when `perf` is non-empty, include `tenet/performance` in `tenets`. ## Evidence & Analysis Fields @@ -53,6 +55,8 @@ Read this BEFORE generating JSON. Full schema: `references/triage-schema.json`. | **classifiedArea** | `area/Build`, `area/Docs`, `area/HarfBuzzSharp`, `area/SkiaSharp`, `area/SkiaSharp.HarfBuzz`, `area/SkiaSharp.Views`, `area/SkiaSharp.Views.Blazor`, `area/SkiaSharp.Views.Forms`, `area/SkiaSharp.Views.Maui`, `area/SkiaSharp.Views.Uno`, `area/SkiaSharp.Workbooks`, `area/libHarfBuzzSharp.native`, `area/libSkiaSharp.native` | | **classifiedPlatform** | `os/Android`, `os/Linux`, `os/Tizen`, `os/WASM`, `os/Windows-Classic`, `os/Windows-Nano-Server`, `os/Windows-Universal-UWP`, `os/Windows-WinUI`, `os/iOS`, `os/macOS`, `os/tvOS`, `os/watchOS` | | **classifiedBackend** | `backend/Direct3D`, `backend/Metal`, `backend/OpenGL`, `backend/PDF`, `backend/Raster`, `backend/SVG`, `backend/Vulkan`, `backend/XPS` | +| **classifiedTenet** | `tenet/compatibility`, `tenet/performance`, `tenet/reliability` | +| **classifiedPerf** | `perf/allocations`, `perf/interop`, `perf/memory-leak`, `perf/rendering`, `perf/size`, `perf/startup`, `perf/throughput` — sub-type of `tenet/performance`; if any is set, also set `tenet/performance` | | **suggestedAction** | `needs-info`, `needs-reproduction`, `needs-investigation`, `ready-to-fix`, `keep-open`, `close-as-fixed`, `close-as-duplicate`, `close-as-not-a-bug`, `close-as-external` | | **errorType** | `crash`, `exception`, `memory-leak`, `build-error`, `wrong-output`, `missing-output`, `missing-api`, `performance`, `platform-specific`, `other` | | **severity** | `critical`, `high`, `medium`, `low` | diff --git a/.agents/skills/issue-triage/references/triage-schema.json b/.agents/skills/issue-triage/references/triage-schema.json index 208ded94622..095f31165d1 100644 --- a/.agents/skills/issue-triage/references/triage-schema.json +++ b/.agents/skills/issue-triage/references/triage-schema.json @@ -91,6 +91,19 @@ ], "description": "Quality tenet label. Omit tenets array entirely if no tenets apply." }, + "classifiedPerf": { + "type": "string", + "enum": [ + "perf/allocations", + "perf/interop", + "perf/memory-leak", + "perf/rendering", + "perf/size", + "perf/startup", + "perf/throughput" + ], + "description": "Performance sub-type label under the tenet/performance umbrella. perf/memory-leak=unbounded memory growth / leaked native handles / undisposed objects. perf/allocations=excessive managed allocations or per-frame GC/heap churn. perf/interop=P/Invoke marshalling or native interop overhead. perf/rendering=slow drawing/render/GPU frame performance. perf/throughput=operation slower than expected (encode/decode, file loading, pixel/format conversion, readback). perf/startup=slow initialization or first-use latency. perf/size=binary/package/output size. Any perf/* label implies tenet/performance — include tenet/performance in tenets whenever perf is non-empty. Omit perf array entirely if not a performance concern." + }, "classifiedPartner": { "type": "string", "enum": [ @@ -254,7 +267,15 @@ "$ref": "#/$defs/classifiedTenet", "description": "A tenet label." }, - "description": "Quality tenet labels, e.g. ['tenet/reliability']. Omit if no tenets apply." + "description": "Quality tenet labels, e.g. ['tenet/reliability']. Omit if no tenets apply. Include 'tenet/performance' whenever the perf array is non-empty." + }, + "perf": { + "type": "array", + "items": { + "$ref": "#/$defs/classifiedPerf", + "description": "A performance sub-type label." + }, + "description": "Performance sub-type labels under the tenet/performance umbrella, e.g. ['perf/memory-leak']. Omit if not a performance concern. When non-empty, also include 'tenet/performance' in tenets." }, "partner": { "$ref": "#/$defs/classifiedPartner", diff --git a/.agents/skills/issue-triage/scripts/triage-report.md.jinja2 b/.agents/skills/issue-triage/scripts/triage-report.md.jinja2 index a527e09a31c..481cfde8b7a 100644 --- a/.agents/skills/issue-triage/scripts/triage-report.md.jinja2 +++ b/.agents/skills/issue-triage/scripts/triage-report.md.jinja2 @@ -25,6 +25,7 @@ | Platforms | {{ classification.platforms | fmt_list }} | | Backends | {{ classification.backends | fmt_list }} | | Tenets | {{ classification.tenets | fmt_list }} | +| Perf | {{ classification.perf | fmt_list }} | | Partner | {{ classification.partner | d('—') }} | {% if meta.currentLabels %} | Current labels | {{ meta.currentLabels | join(', ') }} | diff --git a/.agents/skills/memory-leak-fixer/SKILL.md b/.agents/skills/memory-leak-fixer/SKILL.md index ad8e7c46858..da63b5cee4c 100644 --- a/.agents/skills/memory-leak-fixer/SKILL.md +++ b/.agents/skills/memory-leak-fixer/SKILL.md @@ -20,10 +20,10 @@ description: > # Memory Leak Fixer Proactively **find** and **fix** memory leaks in SkiaSharp — a thin managed -wrapper over native Skia, so its recurring, high-impact leak family is **native +wrapper over native Skia, so its recurring, high-impact class of leaks is **native ownership / disposal correctness** — the C# binding failing to dispose, own, pin, or root a native object correctly — not the managed view-retention leaks a pure-managed app framework -worries about. This skill hunts that family and produces a validated fix. +worries about. This skill hunts that class of leaks and produces a validated fix. **Scope: managed C# only.** Work only in the code SkiaSharp owns — the C# bindings (`binding/**`) and view layers (`source/**`). Everything under `externals/skia/**` is @@ -34,10 +34,10 @@ Read [`documentation/dev/memory-management.md`](../../../documentation/dev/memor first — it is the authoritative model (pointer types, `owns:` flag, ref-count rules, the same-instance-return contract). This skill assumes that model. -The leak catalogue this skill scans against — **11 real families**, each with a description, +The leak catalogue this skill scans against — **11 real focus areas**, each with a description, why it's bad, a leak→fix code example, and a leak-specific anti-pattern — is in [`references/types-of-leaks.md`](references/types-of-leaks.md). Read it before scanning -(Phase 1) and consult the matching family when writing a fix (Phase 3). +(Phase 1) and consult the matching focus area when writing a fix (Phase 3). ## Golden rules (non-negotiable) @@ -93,32 +93,32 @@ Run the phases in order. The skill has two entry points: ## Phase 1 — Scan (find ONE candidate) ### 1.1 Choose a focus area (round-robin across runs) -A full 11-family sweep every run is wasteful and the surface is mostly hardened, so start from -ONE focus family and widen only if it's exhausted. +A full 11-area sweep every run is wasteful and the surface is mostly hardened, so start from +ONE focus area and widen only if it's exhausted. -**Override first.** If the run supplies an explicit focus family (a bare number 0–10 — e.g. a -maintainer testing one family on demand), use that number directly as `FOCUS` and **skip the -rotation below**. Otherwise rotate the *starting* family on a -time-based **round-robin** so consecutive runs cover different families. This needs only +**Override first.** If the run supplies an explicit focus area (a bare number 0–10 — e.g. a +maintainer testing one area on demand), use that number directly as `FOCUS` and **skip the +rotation below**. Otherwise rotate the *starting* area on a +time-based **round-robin** so consecutive runs cover different areas. This needs only `date`, so it behaves identically locally and in CI — no `$GITHUB_RUN_NUMBER` / `$RANDOM` (which don't exist or aren't deterministic outside GitHub Actions): ```bash -# Round-robin: advance one family every hour, cycling through all 11. +# Round-robin: advance one focus area every hour, cycling through all 11. DOY=$(date -u +%j); HOUR=$(date -u +%H) # day-of-year + hour, both zero-padded FOCUS=$(( (10#$DOY * 24 + 10#$HOUR) % 11 )) # 10# forces base-10 -echo "focus family: $FOCUS" +echo "focus area: $FOCUS" ``` The `10#` prefix is **required**: `date` zero-pads `%j`/`%H`, and `$(( 08 ))` is an invalid-octal error without it. For a targeted local run, skip the rotation and just name the -family you want. +focus area you want. -Every family is drawn from a **real, historical SkiaSharp leak fix**. Now open -**[references/types-of-leaks.md](references/types-of-leaks.md)** and load family `#FOCUS`: its +Every focus area is drawn from a **real, historical SkiaSharp leak fix**. Now open +**[references/types-of-leaks.md](references/types-of-leaks.md)** and load focus area `#FOCUS`: its **Where to look** line gives the path + grep starting points, and the rest of the entry is the -description, why-it's-bad, a leak→fix example, and the per-family anti-pattern. **Read that -family before scanning.** If it's exhausted (its leaks are already open issues/PRs — see 1.3), -advance to the next index and load that family. +description, why-it's-bad, a leak→fix example, and the per-area anti-pattern. **Read that +focus area before scanning.** If it's exhausted (its leaks are already open issues/PRs — see 1.3), +advance to the next index and load that focus area. ### 1.2 Establish the retention/ownership path For each candidate write the precise path **with `file:line` citations**: @@ -153,7 +153,7 @@ gh pr list --repo "$GITHUB_REPOSITORY" --search 'Blob.FromStream in:title,bod A candidate is OUT only if an **open** issue/PR already covers the same handle / ownership path (by our prefix OR by the api/type name). A candidate whose only prior item is CLOSED may be re-filed. **Worked example:** the `HarfBuzzSharp.Blob.FromStream` -`fixed`-pointer leak (family 4) is a genuine, still-present bug — but open PR #3473 "Make +`fixed`-pointer leak (area 4) is a genuine, still-present bug — but open PR #3473 "Make Blob.FromStream GC safe" already fixes it, so it is OUT: stand down, do **not** open a duplicate PR, emit a `noop`. @@ -217,9 +217,9 @@ dotnet test tests/SkiaSharp.Tests.Console/SkiaSharp.Tests.Console.csproj --filte If a test you *expected* to be red is green, your hypothesis is wrong — go back to Phase 1. ### 3.2 Implement the minimal idiomatic fix -Apply the **Fix (✓)** for the matching family in -[`references/types-of-leaks.md`](references/types-of-leaks.md) — every family has a worked -before/after there. Then re-read that family's **Watch out (❌ don't):** note: it names the +Apply the **Fix (✓)** for the matching focus area in +[`references/types-of-leaks.md`](references/types-of-leaks.md) — every focus area has a worked +before/after there. Then re-read that focus area's **Watch out (❌ don't):** note: it names the specific *wrong fix* that turns one leak into another (an unconditional `Dispose`, flipping `owns:` blind, nulling a field before disposing, a pinned `GCHandle` where a plain field suffices, …). @@ -249,7 +249,7 @@ instead of pushed and reverted. If any box can't be ticked, **fix it or stand do - [ ] The fix is inside `binding/**` / `source/**` only — no `*.generated.cs`, no `externals/skia/**`, no native / upstream change. - [ ] **No public signature changed** — overloads / internals only (ABI stable). -- [ ] The matching family's **Watch out (❌ don't):** note in +- [ ] The matching focus area's **Watch out (❌ don't):** note in [`references/types-of-leaks.md`](references/types-of-leaks.md) does **not** describe what you just did (no unconditional same-instance `Dispose`, no blind `owns:` flip, no field nulled before dispose, no pinned `GCHandle` where a plain field suffices, …). @@ -267,25 +267,35 @@ All ticked ⇒ proceed to Phase 4 (file the finding, then open the PR). Any unti A confirmed, managed-C#-fixable leak produces **two linked safe outputs** so the *finding* and the *fix* are tracked separately and the issue **auto-closes when the PR merges**. +**Labels (both the issue and the PR):** a memory leak is a performance concern, so tag both +outputs with `tenet/performance` (the quality-tenet umbrella) **and** `perf/memory-leak` (the +performance sub-type). When this skill runs from the `memory-leak-fixer` workflow these labels +are applied automatically by its `safe-outputs` config; when filing by hand, add them yourself. +The `perf/*` taxonomy is defined in the issue-triage skill +([`references/labels.md`](../issue-triage/references/labels.md)). + ### 4.1 The issue — the finding Emit a `create_issue` that describes the **leak, not the fix**. Give it a `temporary_id` (format `aw_` + 3–8 alphanumeric characters — no underscores or other symbols — e.g. `aw_leak1`) so the PR can reference it before its real number exists. Body (markdown): - **AI-generated banner** naming this workflow + the `memory-leak-fixer` skill. -- **Family**, and the **retention/ownership path** with `file:line` citations. +- **Focus area**, and the **retention/ownership path** with `file:line` citations. - **Evidence**: the Phase 2 proof — the probe you ran and its alive/collected counts. - **Scope note**: framework bug vs footgun; empirically-proven vs statically-reasoned; ABI impact. +- **Labels**: `tenet/performance` + `perf/memory-leak`. ### 4.2 The PR — the fix Create a feature branch (`dev/memory-leak-`), commit the test + fix, and open a **draft** `create_pull_request`. Body (markdown): - **AI-generated banner** naming this workflow + skill. -- **The fix**: what changed and why it is the idiomatic pattern (point at the family's `Fix ✓`). +- **The fix**: what changed and why it is the idiomatic pattern (point at the focus area's `Fix ✓`). - **Proof (red→green)**: the failing-then-passing test and the exact `dotnet test` commands. - **A closing keyword on its own line so merging auto-closes the finding:** `Fixes #` — e.g. `Fixes #aw_leak1` (the id you gave the issue in 4.1). gh-aw rewrites it to the real issue number once the issue is created. +- **Labels**: `tenet/performance` + `perf/memory-leak`. + ### 4.3 Out of scope (native / upstream only) If the leak is real but the only correct fix lives under `externals/skia/**` (incl. the C @@ -296,7 +306,7 @@ proposed native fix — so nothing is lost. ## Phase 5 — Report -Write a short summary: which family, the candidate (`file:line`), the proof result, and the +Write a short summary: which focus area, the candidate (`file:line`), the proof result, and the resulting issue + PR links. When run from the agentic workflow, append this to the run's step summary. diff --git a/.agents/skills/memory-leak-fixer/references/types-of-leaks.md b/.agents/skills/memory-leak-fixer/references/types-of-leaks.md index 92f86db0704..5d650fd4595 100644 --- a/.agents/skills/memory-leak-fixer/references/types-of-leaks.md +++ b/.agents/skills/memory-leak-fixer/references/types-of-leaks.md @@ -1,30 +1,30 @@ # SkiaSharp leak types — reference -The catalogue the `memory-leak-fixer` skill scans against. **Every family below is drawn -from a real, historical SkiaSharp fix** (issue/PR cited), so the hunt targets patterns that -have actually shipped as bugs in this repo — not hypotheticals. +The catalogue the `memory-leak-fixer` skill scans against, split into **focus areas**. **Every +focus area below is drawn from a real, historical SkiaSharp fix** (issue/PR cited), so the hunt +targets patterns that have actually shipped as bugs in this repo — not hypotheticals. **Scope: managed C# only.** This skill hunts and fixes leaks in the code SkiaSharp owns — the C# bindings (`binding/**`) and view layers (`source/**`). The native Skia C/C++ under `externals/skia/**` (including our C shim) is **out of scope**: it is upstream, cannot be built or validated on a standard runner, and its fixes go through a different process. Every -family here is therefore something you can prove and fix from C#. +focus area here is therefore something you can prove and fix from C#. Read this alongside [`documentation/dev/memory-management.md`](../../../../documentation/dev/memory-management.md), which is the authoritative ownership model (pointer types, `owns:` flag, ref-count rules, -the `HandleDictionary`, and the same-instance-return contract). This file adds, per family: +the `HandleDictionary`, and the same-instance-return contract). This file adds, per focus area: **where to look → what it is → why it's bad → a leaking example → the idiomatic fix → a watch-out.** Code samples are illustrative and trimmed to the essential lines; real wrappers add argument validation and `GC.KeepAlive`. `✓` = correct, `❌` = the bug. -Each family ends with a **Watch out (❌ don't):** note — the leak-specific *wrong fix* that +Each focus area ends with a **Watch out (❌ don't):** note — the leak-specific *wrong fix* that turns one bug into another. Re-read the matching one during the pre-PR self-review gate. -Quick index (the `#` is the rotating focus index the skill uses): +Quick index (the `#` is the rotating focus-area index the skill uses): -| # | Family | One-line signature | +| # | Focus area | One-line signature | |--:|---|---| | 0 | Undisposed native handle | owned/ref-counted `SKObject` escapes a factory/cache and is never disposed | | 1 | Wrong `owns:` flag | borrowed pointer wrapped `owns:true` (double-free) or owned handle `owns:false` (leak) | @@ -73,8 +73,8 @@ foreach (var frame in frames) { For a cache, dispose evicted entries and clear the cache on teardown. **Watch out (❌ don't):** don't slap `using`/`Dispose` on a handle you don't actually own — -a *borrowed getter* result (family 1), a *same-instance return* (family 2), or a -*process-wide singleton* (family 7). Confirm the object is genuinely owned before disposing, +a *borrowed getter* result (area 1), a *same-instance return* (area 2), or a +*process-wide singleton* (area 7). Confirm the object is genuinely owned before disposing, or you convert a leak into a double-free. **Real cases:** the general class behind many reports; see `documentation/dev/memory-management.md`. @@ -114,7 +114,7 @@ accessors return borrowed → `owns:false`. Getting this backwards just swaps a crash. When the contract is genuinely unclear from the managed side, file an issue rather than flipping blind. -**Real cases:** the counterpart of family 7 (dispose-protected singletons); verify each new +**Real cases:** the counterpart of area 7 (dispose-protected singletons); verify each new getter against whether it returns a fresh ref or a borrowed pointer. --- @@ -291,7 +291,7 @@ type DO keep such a field (e.g. one iterator/cursor stores `private readonly SKP another doesn't). The odd one out is the prime suspect. Prove it before believing it (Phase 2). **Watch out (❌ don't):** don't root the parent with a pinned `GCHandle` — a plain managed -field is enough and a pinned handle is its own leak (family 9). And don't lean on +field is enough and a pinned handle is its own leak (area 9). And don't lean on `GC.KeepAlive` for a *long-lived* child (an iterator you hold across calls); KeepAlive only covers the current method, so a stored child needs the field. diff --git a/.github/workflows/auto-triage.lock.yml b/.github/workflows/auto-triage.lock.yml index 9ad5c477bc7..cc1ae2a1808 100644 --- a/.github/workflows/auto-triage.lock.yml +++ b/.github/workflows/auto-triage.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"4ce97952369965fc49fc398afda00d8596512d008f246a40d87f57b3ec42b779","compiler_version":"v0.71.5","strict":true,"agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"c8afd782113b7c4abf2d4985d1455a361dd3e1d25767403b8c911fdc436378f1","compiler_version":"v0.71.5","strict":true,"agent_id":"copilot"} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_WRITE_PROJECT_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"b8068426813005612b960b5ab0b8bd2c27142323","version":"v0.71.5"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.40","digest":"sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.25.40@sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40","digest":"sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40@sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.40","digest":"sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.25.40@sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -220,20 +220,20 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_f075a76a24979b24_EOF' + cat << 'GH_AW_PROMPT_ce661d6e560410d6_EOF' - GH_AW_PROMPT_f075a76a24979b24_EOF + GH_AW_PROMPT_ce661d6e560410d6_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_f075a76a24979b24_EOF' + cat << 'GH_AW_PROMPT_ce661d6e560410d6_EOF' - Tools: add_labels(max:10), update_project, missing_tool, missing_data, noop + Tools: add_labels(max:12), update_project, missing_tool, missing_data, noop - GH_AW_PROMPT_f075a76a24979b24_EOF + GH_AW_PROMPT_ce661d6e560410d6_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_f075a76a24979b24_EOF' + cat << 'GH_AW_PROMPT_ce661d6e560410d6_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -262,12 +262,12 @@ jobs: {{/if}} - GH_AW_PROMPT_f075a76a24979b24_EOF + GH_AW_PROMPT_ce661d6e560410d6_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_f075a76a24979b24_EOF' + cat << 'GH_AW_PROMPT_ce661d6e560410d6_EOF' {{#runtime-import .github/workflows/auto-triage.md}} - GH_AW_PROMPT_f075a76a24979b24_EOF + GH_AW_PROMPT_ce661d6e560410d6_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -466,15 +466,15 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << GH_AW_SAFE_OUTPUTS_CONFIG_8df8d05c931120e8_EOF - {"add_labels":{"max":10,"target":"*"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{},"update_project":{"github-token":"${GH_AW_WRITE_PROJECT_TOKEN}","max":1,"project":"https://github.com/orgs/mono/projects/1"}} - GH_AW_SAFE_OUTPUTS_CONFIG_8df8d05c931120e8_EOF + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << GH_AW_SAFE_OUTPUTS_CONFIG_40031c1d914fc1c2_EOF + {"add_labels":{"max":12,"target":"*"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{},"update_project":{"github-token":"${GH_AW_WRITE_PROJECT_TOKEN}","max":1,"project":"https://github.com/orgs/mono/projects/1"}} + GH_AW_SAFE_OUTPUTS_CONFIG_40031c1d914fc1c2_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | { "description_suffixes": { - "add_labels": " CONSTRAINTS: Maximum 10 label(s) can be added. Target: *.", + "add_labels": " CONSTRAINTS: Maximum 12 label(s) can be added. Target: *.", "update_project": " CONSTRAINTS: Maximum 1 project operation(s) can be performed. Default project URL: \"https://github.com/orgs/mono/projects/1\"." }, "repo_params": {}, @@ -696,7 +696,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_c1290a4923f498ef_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_e602b2c681b95a5a_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "github": { @@ -746,7 +746,7 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_c1290a4923f498ef_EOF + GH_AW_MCP_CONFIG_e602b2c681b95a5a_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1459,7 +1459,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,conda.anaconda.org,conda.binstar.org,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,files.pythonhosted.org,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_labels\":{\"max\":10,\"target\":\"*\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{},\"update_project\":{\"github-token\":\"${{ secrets.GH_AW_WRITE_PROJECT_TOKEN }}\",\"max\":1,\"project\":\"https://github.com/orgs/mono/projects/1\"}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_labels\":{\"max\":12,\"target\":\"*\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{},\"update_project\":{\"github-token\":\"${{ secrets.GH_AW_WRITE_PROJECT_TOKEN }}\",\"max\":1,\"project\":\"https://github.com/orgs/mono/projects/1\"}}" GH_AW_PROJECT_URL: "https://github.com/orgs/mono/projects/1" GH_AW_PROJECT_GITHUB_TOKEN: ${{ secrets.GH_AW_WRITE_PROJECT_TOKEN }} with: diff --git a/.github/workflows/auto-triage.md b/.github/workflows/auto-triage.md index d7724faa8d9..a32b2ce830c 100644 --- a/.github/workflows/auto-triage.md +++ b/.github/workflows/auto-triage.md @@ -61,7 +61,7 @@ network: - python safe-outputs: add-labels: - max: 10 + max: 12 target: "*" update-project: project: "https://github.com/orgs/mono/projects/1" @@ -90,6 +90,7 @@ Read the triage JSON file and extract the classification labels. Apply each labe - Each entry in `classification.platforms[]` — platform labels (e.g. `os/Android`) - Each entry in `classification.backends[]` — backend labels (e.g. `backend/Metal`) - Each entry in `classification.tenets[]` — quality tenet labels (e.g. `tenet/compatibility`) +- Each entry in `classification.perf[]` — performance sub-type labels (e.g. `perf/memory-leak`). Whenever `perf[]` is non-empty, ensure `tenet/performance` is also applied (it should already be present in `tenets[]`). - `classification.partner` — partner label if present (e.g. `partner/maui`) After applying classification labels, also add the `triage/triaged` label to mark the issue as processed. diff --git a/.github/workflows/memory-leak-fixer.lock.yml b/.github/workflows/memory-leak-fixer.lock.yml index e977cefa67d..d45c55e6739 100644 --- a/.github/workflows/memory-leak-fixer.lock.yml +++ b/.github/workflows/memory-leak-fixer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"9479b6f3e100a822b5a2202c2d58cbf5c91f4a7788827a8c5821937011db232a","compiler_version":"v0.71.5","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"1859cefafc8ea29e03a321de99c30e38e8e8392420c2909f96b66db3a0239e2f","compiler_version":"v0.71.5","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8"} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"b8068426813005612b960b5ab0b8bd2c27142323","version":"v0.71.5"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.40","digest":"sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.25.40@sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40","digest":"sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40@sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.40","digest":"sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.25.40@sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -69,9 +69,9 @@ name: "Fixer - Memory Leak" description: Do the full scan→prove→fix locally but do NOT open a PR/issue. required: false type: boolean - focus_family: + focus_area: default: "" - description: Force a specific leak family 0-10 (see references/types-of-leaks.md) instead of the time-based round-robin. Leave blank to rotate. + description: Force a specific leak focus area 0-10 (see references/types-of-leaks.md) instead of the time-based round-robin. Leave blank to rotate. required: false type: string @@ -204,7 +204,7 @@ jobs: GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} GH_AW_GITHUB_EVENT_INPUTS_DRY_RUN: ${{ github.event.inputs.dry_run }} - GH_AW_GITHUB_EVENT_INPUTS_FOCUS_FAMILY: ${{ github.event.inputs.focus_family }} + GH_AW_GITHUB_EVENT_INPUTS_FOCUS_AREA: ${{ github.event.inputs.focus_area }} GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} GH_AW_GITHUB_EVENT_NAME: ${{ github.event_name }} GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} @@ -215,23 +215,23 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_1b69b3eaace3a8c4_EOF' + cat << 'GH_AW_PROMPT_e30ca7fc8be46111_EOF' - GH_AW_PROMPT_1b69b3eaace3a8c4_EOF + GH_AW_PROMPT_e30ca7fc8be46111_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_1b69b3eaace3a8c4_EOF' + cat << 'GH_AW_PROMPT_e30ca7fc8be46111_EOF' Tools: create_issue, create_pull_request, missing_tool, missing_data, noop - GH_AW_PROMPT_1b69b3eaace3a8c4_EOF + GH_AW_PROMPT_e30ca7fc8be46111_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md" - cat << 'GH_AW_PROMPT_1b69b3eaace3a8c4_EOF' + cat << 'GH_AW_PROMPT_e30ca7fc8be46111_EOF' - GH_AW_PROMPT_1b69b3eaace3a8c4_EOF + GH_AW_PROMPT_e30ca7fc8be46111_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_1b69b3eaace3a8c4_EOF' + cat << 'GH_AW_PROMPT_e30ca7fc8be46111_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -263,12 +263,12 @@ jobs: - **Note**: If a branch you need is not in the list above and is not listed as an additional fetched ref, it has NOT been checked out. For private repositories you cannot fetch it without proper authentication. If the branch is required and not available, exit with an error and ask the user to add it to the `fetch:` option of the `checkout:` configuration (e.g., `fetch: ["refs/pulls/open/*"]` for all open PR refs, or `fetch: ["main", "feature/my-branch"]` for specific branches). - GH_AW_PROMPT_1b69b3eaace3a8c4_EOF + GH_AW_PROMPT_e30ca7fc8be46111_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_1b69b3eaace3a8c4_EOF' + cat << 'GH_AW_PROMPT_e30ca7fc8be46111_EOF' {{#runtime-import .github/workflows/memory-leak-fixer.md}} - GH_AW_PROMPT_1b69b3eaace3a8c4_EOF + GH_AW_PROMPT_e30ca7fc8be46111_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -276,7 +276,7 @@ jobs: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_ENGINE_ID: "copilot" GH_AW_GITHUB_EVENT_INPUTS_DRY_RUN: ${{ github.event.inputs.dry_run }} - GH_AW_GITHUB_EVENT_INPUTS_FOCUS_FAMILY: ${{ github.event.inputs.focus_family }} + GH_AW_GITHUB_EVENT_INPUTS_FOCUS_AREA: ${{ github.event.inputs.focus_area }} GH_AW_GITHUB_EVENT_NAME: ${{ github.event_name }} with: script: | @@ -292,7 +292,7 @@ jobs: GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} GH_AW_GITHUB_EVENT_INPUTS_DRY_RUN: ${{ github.event.inputs.dry_run }} - GH_AW_GITHUB_EVENT_INPUTS_FOCUS_FAMILY: ${{ github.event.inputs.focus_family }} + GH_AW_GITHUB_EVENT_INPUTS_FOCUS_AREA: ${{ github.event.inputs.focus_area }} GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} GH_AW_GITHUB_EVENT_NAME: ${{ github.event_name }} GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} @@ -316,7 +316,7 @@ jobs: GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, GH_AW_GITHUB_EVENT_INPUTS_DRY_RUN: process.env.GH_AW_GITHUB_EVENT_INPUTS_DRY_RUN, - GH_AW_GITHUB_EVENT_INPUTS_FOCUS_FAMILY: process.env.GH_AW_GITHUB_EVENT_INPUTS_FOCUS_FAMILY, + GH_AW_GITHUB_EVENT_INPUTS_FOCUS_AREA: process.env.GH_AW_GITHUB_EVENT_INPUTS_FOCUS_AREA, GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, GH_AW_GITHUB_EVENT_NAME: process.env.GH_AW_GITHUB_EVENT_NAME, GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, @@ -474,16 +474,16 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_b0dd1747ba89d376_EOF' - {"create_issue":{"allowed_labels":["agentic-workflows"],"labels":["agentic-workflows"],"max":1,"title_prefix":"[memory-leak] "},"create_pull_request":{"allowed_base_branches":["main"],"draft":true,"labels":["agentic-workflows"],"max":1,"max_patch_files":100,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"title_prefix":"[memory-leak] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_b0dd1747ba89d376_EOF + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_a167662a82c85250_EOF' + {"create_issue":{"allowed_labels":["tenet/performance","perf/memory-leak"],"labels":["tenet/performance","perf/memory-leak"],"max":1,"title_prefix":"[memory-leak] "},"create_pull_request":{"allowed_base_branches":["main"],"draft":true,"labels":["tenet/performance","perf/memory-leak"],"max":1,"max_patch_files":100,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"title_prefix":"[memory-leak] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"report_incomplete":{}} + GH_AW_SAFE_OUTPUTS_CONFIG_a167662a82c85250_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | { "description_suffixes": { - "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[memory-leak] \". Labels [\"agentic-workflows\"] will be automatically added. Only these labels are allowed: [\"agentic-workflows\"].", - "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[memory-leak] \". Labels [\"agentic-workflows\"] will be automatically added. PRs will be created as drafts." + "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[memory-leak] \". Labels [\"tenet/performance\" \"perf/memory-leak\"] will be automatically added. Only these labels are allowed: [\"tenet/performance\" \"perf/memory-leak\"].", + "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[memory-leak] \". Labels [\"tenet/performance\" \"perf/memory-leak\"] will be automatically added. PRs will be created as drafts." }, "repo_params": {}, "dynamic_tools": [] @@ -716,7 +716,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_69f5409922e9fb5d_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_8e59def73e77f8bd_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "github": { @@ -762,7 +762,7 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_69f5409922e9fb5d_EOF + GH_AW_MCP_CONFIG_8e59def73e77f8bd_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -1502,7 +1502,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "*.blob.core.windows.net,*.githubusercontent.com,*.vsblob.vsassets.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,builds.dotnet.microsoft.com,ci.dot.net,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,dist.nuget.org,docs.github.com,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkgs.dev.azure.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,www.microsoft.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"allowed_labels\":[\"agentic-workflows\"],\"labels\":[\"agentic-workflows\"],\"max\":1,\"title_prefix\":\"[memory-leak] \"},\"create_pull_request\":{\"allowed_base_branches\":[\"main\"],\"draft\":true,\"labels\":[\"agentic-workflows\"],\"max\":1,\"max_patch_files\":100,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"title_prefix\":\"[memory-leak] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"allowed_labels\":[\"tenet/performance\",\"perf/memory-leak\"],\"labels\":[\"tenet/performance\",\"perf/memory-leak\"],\"max\":1,\"title_prefix\":\"[memory-leak] \"},\"create_pull_request\":{\"allowed_base_branches\":[\"main\"],\"draft\":true,\"labels\":[\"tenet/performance\",\"perf/memory-leak\"],\"max\":1,\"max_patch_files\":100,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"title_prefix\":\"[memory-leak] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}" GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }} with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/memory-leak-fixer.md b/.github/workflows/memory-leak-fixer.md index fb1d509a78a..1fa73353992 100644 --- a/.github/workflows/memory-leak-fixer.md +++ b/.github/workflows/memory-leak-fixer.md @@ -5,7 +5,7 @@ # Periodic AI-driven workflow that SCANS the repo's managed C# for a native # ownership / disposal memory leak, PROVES it with a red→green regression test, # FIXES it, and opens a DRAFT PR. Adapted from dotnet/maui's leak scanner+fixer -# idea to our real leak family (undisposed SKObject handles, wrong +# idea to our real leak focus areas (undisposed SKObject handles, wrong # `owns:` flags, same-instance double-dispose, unremoved view/handler # subscriptions, `fixed`-pointer lifetime). Scope: managed C# only — the native # Skia under externals/skia/** is upstream and out of scope. @@ -28,8 +28,8 @@ engine: # -- Triggers ---------------------------------------------------------- # Every 12h + manual + PR-driven self-test. Every run does the same full # scan→prove→fix→file pipeline; the knobs are `dry_run` (do everything but open -# nothing) and `focus_family` (force one leak family instead of the time-based -# rotation — for testing a specific family on demand). A `pull_request` that edits THIS workflow or its skill re-runs +# nothing) and `focus_area` (force one leak focus area instead of the time-based +# rotation — for testing a specific focus area on demand). A `pull_request` that edits THIS workflow or its skill re-runs # the whole pipeline in FORCED DRY-RUN (see Step 2.6) so we can iterate on the # prompt/skill and watch the run without ever opening a real PR/issue. on: @@ -41,8 +41,8 @@ on: required: false default: false type: boolean - focus_family: - description: "Force a specific leak family 0-10 (see references/types-of-leaks.md) instead of the time-based round-robin. Leave blank to rotate." + focus_area: + description: "Force a specific leak focus area 0-10 (see references/types-of-leaks.md) instead of the time-based round-robin. Leave blank to rotate." required: false default: "" type: string @@ -113,16 +113,21 @@ network: # finding) + a draft fix PR that closes it on merge (Fixes #, resolved # by gh-aw to the real number). When the only correct fix is native / upstream # Skia (out of scope here), the issue is filed alone. Quiet/dry runs emit a noop. +# +# Labels: a memory leak is a performance concern, so both the issue and the PR +# carry `tenet/performance` (the quality-tenet umbrella) + `perf/memory-leak` +# (the performance sub-type). See the perf/* taxonomy in the issue-triage skill +# (.agents/skills/issue-triage/references/labels.md). safe-outputs: create-pull-request: title-prefix: "[memory-leak] " - labels: [agentic-workflows] + labels: [tenet/performance, perf/memory-leak] draft: true allowed-base-branches: [main] create-issue: title-prefix: "[memory-leak] " - labels: [agentic-workflows] - allowed-labels: [agentic-workflows] + labels: [tenet/performance, perf/memory-leak] + allowed-labels: [tenet/performance, perf/memory-leak] max: 1 noop: report-as-issue: false @@ -150,7 +155,7 @@ alone. **One finding per run.** Everything under `externals/skia/**` (including our C shim) is upstream Skia: not checked out, not buildable here (native tests use pre-built packages via `externals-download`), and out of scope. A leak whose only correct fix is native is **issue-only** (see 2.4). -- **Timebox the scan.** Do one focused pass over the leak families, pick the single strongest +- **Timebox the scan.** Do one focused pass over the leak focus areas, pick the single strongest candidate early, and stop. If nothing clears the bar in that pass, emit the `noop` and finish — do not launch open-ended sub-agent explorations that may not return within the budget. @@ -165,10 +170,10 @@ leak, prove it, fix it, then file the finding issue + linked fix PR. There is no mode. The only variation is dry-run — forced on `pull_request`, opt-in via the `dry_run` input (see Guardrail 6). -**Focus family override:** `${{ github.event.inputs.focus_family }}` — if that shows a bare -number **0–10**, pass it to the skill's Phase 1.1 as the focus family and **skip the round-robin** +**Focus area override:** `${{ github.event.inputs.focus_area }}` — if that shows a bare +number **0–10**, pass it to the skill's Phase 1.1 as the focus area and **skip the round-robin** computation. If it is blank (schedule / PR / dispatch without the input), use the normal -time-based round-robin. This is only a testing knob to target a specific family on demand; it +time-based round-robin. This is only a testing knob to target a specific focus area on demand; it changes nothing else about the run. Persist all intermediate state (the `/tmp/leakprobe` project, notes) under `/tmp/gh-aw/agent/`. @@ -211,7 +216,7 @@ Each bash call is a fresh subshell — re-`cd` as needed. ## Step 3 — Report Append a short summary to `/tmp/gh-aw/agent/step-summary.md` (this file is symlinked to the -run's step summary — do **not** use `$GITHUB_STEP_SUMMARY`): the leak family, the candidate +run's step summary — do **not** use `$GITHUB_STEP_SUMMARY`): the leak focus area, the candidate (with `file:line`), the proof result (alive/collected counts or red→green status), and the resulting issue + PR links — or "no convincing candidate this run" for a quiet run.