From f556bc5c25a9100f1c2ec557beb5f0e1b9bb511d Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Sat, 11 Jul 2026 22:43:16 +0200 Subject: [PATCH 01/20] Run Memory Leak Fixer every 3h instead of every 12h Increase the scheduled cadence of the Memory Leak Fixer agentic workflow from every 12h to every 3h so open [leak-scan] issues get a [leak-fix] PR (and review-feedback responses) turned around faster. Only the fixer changes; the Daily Memory Leak Hunter stays at 12h. Edited the `schedule:` directive in leak-fixer.md and recompiled the lock file with gh aw (v0.80.9): cron "20 */12 * * *" -> "20 */3 * * *". Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1257a460-520a-4b7a-91d5-61f39054ea59 --- .github/workflows/leak-fixer.lock.yml | 6 +++--- .github/workflows/leak-fixer.md | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/leak-fixer.lock.yml b/.github/workflows/leak-fixer.lock.yml index 82b423ba13de..1b2f54783de2 100644 --- a/.github/workflows/leak-fixer.lock.yml +++ b/.github/workflows/leak-fixer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"93286c516b276831faadc4da37017ed32b01e64520b1becf2e87407bd286ae25","body_hash":"6bcdee91cfbce25619b6641e2f3f5979d7b90fe996e90533a5913f7a21a73ed9","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"9ecc84f8fb1e8a9f80dcb4db783e0e60a50a64b5d7ee7f889469f07d534fb2bf","body_hash":"6bcdee91cfbce25619b6641e2f3f5979d7b90fe996e90533a5913f7a21a73ed9","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -85,8 +85,8 @@ name: "Memory Leak Fixer" on: # permissions: {} # Permissions applied to pre-activation job schedule: - - cron: "20 */12 * * *" - # Friendly format: every 12h (scattered) + - cron: "20 */3 * * *" + # Friendly format: every 3h (scattered) workflow_dispatch: inputs: aw_context: diff --git a/.github/workflows/leak-fixer.md b/.github/workflows/leak-fixer.md index c321becede71..e26c5b6e3cf1 100644 --- a/.github/workflows/leak-fixer.md +++ b/.github/workflows/leak-fixer.md @@ -36,7 +36,7 @@ imports: environment: copilot-pat-pool on: - schedule: every 12h + schedule: every 3h workflow_dispatch: inputs: issue_number: @@ -152,7 +152,7 @@ safe-outputs: target: "*" # agent supplies the leak PR number required-title-prefix: "[leak-fix]" # Track C only comments on this workflow's own [leak-fix] PRs max: 1 - # Most 12h runs are idle (every open leak already has a [leak-fix] PR). Without this, + # Most 3h runs are idle (every open leak already has a [leak-fix] PR). Without this, # gh-aw's auto-injected default (noop: report-as-issue: true) files a "no action taken" # issue every idle run. Mirror the hunter, which already opts out. noop: From 00edaf1f0ce2b1fc251b42a7a1cf0492c304271d Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Sat, 11 Jul 2026 22:49:22 +0200 Subject: [PATCH 02/20] Change Memory Leak Fixer cadence to every 6h (4x/day) Adjust the fixer schedule from every 3h to every 6h per feedback, so it runs 4 times a day. Recompiled the lock file with gh aw: cron "20 */3 * * *" -> "20 */6 * * *". Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1257a460-520a-4b7a-91d5-61f39054ea59 --- .github/workflows/leak-fixer.lock.yml | 6 +++--- .github/workflows/leak-fixer.md | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/leak-fixer.lock.yml b/.github/workflows/leak-fixer.lock.yml index 1b2f54783de2..a454c30afb83 100644 --- a/.github/workflows/leak-fixer.lock.yml +++ b/.github/workflows/leak-fixer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"9ecc84f8fb1e8a9f80dcb4db783e0e60a50a64b5d7ee7f889469f07d534fb2bf","body_hash":"6bcdee91cfbce25619b6641e2f3f5979d7b90fe996e90533a5913f7a21a73ed9","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"eb3ab9f3c4156e6f870ad3a604c4e52ccfe7b584d5497d6cdfd13779a6fe98d3","body_hash":"6bcdee91cfbce25619b6641e2f3f5979d7b90fe996e90533a5913f7a21a73ed9","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -85,8 +85,8 @@ name: "Memory Leak Fixer" on: # permissions: {} # Permissions applied to pre-activation job schedule: - - cron: "20 */3 * * *" - # Friendly format: every 3h (scattered) + - cron: "20 */6 * * *" + # Friendly format: every 6h (scattered) workflow_dispatch: inputs: aw_context: diff --git a/.github/workflows/leak-fixer.md b/.github/workflows/leak-fixer.md index e26c5b6e3cf1..1af81eae07c7 100644 --- a/.github/workflows/leak-fixer.md +++ b/.github/workflows/leak-fixer.md @@ -36,7 +36,7 @@ imports: environment: copilot-pat-pool on: - schedule: every 3h + schedule: every 6h workflow_dispatch: inputs: issue_number: @@ -152,7 +152,7 @@ safe-outputs: target: "*" # agent supplies the leak PR number required-title-prefix: "[leak-fix]" # Track C only comments on this workflow's own [leak-fix] PRs max: 1 - # Most 3h runs are idle (every open leak already has a [leak-fix] PR). Without this, + # Most 6h runs are idle (every open leak already has a [leak-fix] PR). Without this, # gh-aw's auto-injected default (noop: report-as-issue: true) files a "no action taken" # issue every idle run. Mirror the hunter, which already opts out. noop: From b9c61b5ad26e250ab643034927da168ca59ce7f5 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Sun, 12 Jul 2026 18:16:02 +0200 Subject: [PATCH 03/20] Add de-dup/auto-close + fixed-on-main guard to leak workflows Beyond the 6h fixer cadence, teach the two agentic memory-leak workflows to stop re-processing leaks that are already fixed on main. Leak Fixer (leak-fixer.md): - New close-issue safe-output (issues:write scoped to the safe-outputs job). - Step 3 gate (d): if a MERGED [leak-fix] PR already covers the same rooting Type.Member (or refs the issue), close the [leak-scan] issue instead of rebuilding MAUI from source. Merged fixes often reference an upstream issue number, so GitHub never auto-closed the scan issue and it was re-picked and rebuilt every run. - Step 6: when the regression test is already green on unpatched main, close the scan issue (already fixed) instead of silently skipping. - Step 10: require Fixes # to be the [leak-scan] number (upstream refs go as Refs:, never a second Fixes:) so the scan issue always auto-closes on merge. Daily Leak Hunter (daily-leak-hunter.md): - Step 2: build a fixed-on-main Type.Member set from merged [leak-fix] PRs and [leak-scan] issues closed as completed; skip re-filing those. Prevents a re-file loop, since the hunter tests the shipped 10.0.0 package where a fixed-on-main leak still reproduces. - Tighten Hard Rule 5 / Step 6 wording: only re-file leaks closed as NOT PLANNED, never those fixed on main. Recompiled both .lock.yml via gh aw compile. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1257a460-520a-4b7a-91d5-61f39054ea59 --- .github/workflows/daily-leak-hunter.lock.yml | 2 +- .github/workflows/daily-leak-hunter.md | 50 ++++++++++++---- .github/workflows/leak-fixer.lock.yml | 50 +++++++++++----- .github/workflows/leak-fixer.md | 62 +++++++++++++++++--- 4 files changed, 129 insertions(+), 35 deletions(-) diff --git a/.github/workflows/daily-leak-hunter.lock.yml b/.github/workflows/daily-leak-hunter.lock.yml index 607c75c1913d..a980e61c944b 100644 --- a/.github/workflows/daily-leak-hunter.lock.yml +++ b/.github/workflows/daily-leak-hunter.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"745be3f30003b219d6dd9e5e3c43ff0c843c6c60231bff9d7ea0ecb4ed2d668b","body_hash":"03dd04b37a8427233cf1d403b6d08b394ab52f81e16e1d7d8ce99e22153a0192","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"745be3f30003b219d6dd9e5e3c43ff0c843c6c60231bff9d7ea0ecb4ed2d668b","body_hash":"c6059f80c42d3843e47edf2cf070a3d0c274b5679a5054970deaf85436b74cf1","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/daily-leak-hunter.md b/.github/workflows/daily-leak-hunter.md index 29f656b28127..37926b4a9caa 100644 --- a/.github/workflows/daily-leak-hunter.md +++ b/.github/workflows/daily-leak-hunter.md @@ -132,11 +132,15 @@ Never push, never open a PR, never comment, never edit product or test code in t device tests and are out of scope. 4. **Skip weak-proxied code.** If the suspect uses `WeakEventManager`, `ConditionalWeakTable`, `WeakReference`, or any `Weak*Proxy`, it does not leak — move on. -5. **De-dup against THIS SCANNER's own OPEN issues.** Before filing, fetch this workflow's open - `[leak-scan]` issues and skip a leak already covered by one (same rooting API / retention - path). Do NOT suppress a candidate because AdamEssenmacher (or anyone else) has a repro/issue - for it — duplicating those is fine. A - candidate whose only prior issue from this scanner is CLOSED may be re-filed. +5. **De-dup against THIS SCANNER's own OPEN issues AND leaks already fixed on `main`.** Before + filing, fetch this workflow's open `[leak-scan]` issues and skip a leak already covered by one + (same rooting API / retention path). ALSO skip any leak whose rooting `Type.Member` is already + fixed on `main` — a merged `[leak-fix]` PR or a `[leak-scan]` issue closed as completed (Step 2 + builds this set). Such leaks still reproduce against the SHIPPED package until the fix ships, + so the empirical test alone can't tell — de-dup is the only guard. Do NOT suppress a candidate + because AdamEssenmacher (or anyone else) has a repro/issue for it — duplicating those is fine. + A candidate whose only prior issue from this scanner is CLOSED **as not planned** (never fixed) + may be re-filed; one whose leak is fixed on `main` may not. 6. **Never weaken or disable anything, and never commit code.** You only READ repo source and (Pass A) ADD a throwaway test under `/tmp`. Never edit product code, never `[ActiveIssue]`/skip/mute existing tests, never push. @@ -176,6 +180,24 @@ jq -r '.[].title' /tmp/gh-aw/agent/my-open-leakscan.json \ | sort -u \ > /tmp/gh-aw/agent/already-filed-apis.txt echo "already-filed rooting APIs:"; cat /tmp/gh-aw/agent/already-filed-apis.txt + +# ── ALSO skip leaks ALREADY FIXED on main ────────────────────────────────────────────────── +# Your Step 5 confirmation runs against the SHIPPED NuGet package (pinned 10.0.0), where an +# already-MERGED fix has NOT shipped yet — so a leak that is fixed on `main` STILL reproduces +# (goes red) and would be re-filed every run forever. De-dup is the ONLY guard. Build a +# "fixed-on-main" rooting-Type.Member set from (a) every MERGED `[leak-fix]` PR title and +# (b) every `[leak-scan]` issue this scanner closed as COMPLETED (fix landed). +{ + gh pr list --repo "$GITHUB_REPOSITORY" --state merged --search '"[leak-fix]" in:title' \ + --limit 200 --json title -q '.[].title' | grep -E '^\[leak-fix\] ' | sed -E 's/^\[leak-fix\] *(Fix +)?//' + gh issue list --repo "$GITHUB_REPOSITORY" --search '"[leak-scan]" in:title' \ + --state closed --label agentic-workflows --limit 300 --json title,stateReason \ + -q '.[] | select(.stateReason == "COMPLETED") | .title' | sed -E 's/^\[leak-scan\] *//' +} \ + | awk '{ if (match($0, /[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)+/)) { chain=substr($0,RSTART,RLENGTH); n=split(chain,seg,"."); print seg[n-1]"."seg[n] } }' \ + | sort -u \ + > /tmp/gh-aw/agent/fixed-on-main-apis.txt +echo "fixed-on-main (do NOT re-file) rooting APIs:"; cat /tmp/gh-aw/agent/fixed-on-main-apis.txt ``` - A candidate is **OUT** if its rooting `Type.Member` (e.g. `SwipeItemView.Command`, @@ -183,8 +205,15 @@ echo "already-filed rooting APIs:"; cat /tmp/gh-aw/agent/already-filed-apis.txt otherwise covers the same rooting API / retention path. **Check this for EVERY candidate before you write its test** — re-filing a leak this scanner already has open (even with different title wording) is the #1 failure mode, so be strict about matching the `Type.Member`. +- A candidate is **ALSO OUT** if its rooting `Type.Member` is in `fixed-on-main-apis.txt` + (already fixed on `main` by a merged `[leak-fix]` PR, or a `[leak-scan]` issue closed as + completed). The shipped-package test in Step 5 will STILL go red for these because the fix + hasn't shipped in a release yet — that is expected. Do NOT re-file: the fix is already on + `main` and will ship. Re-filing just recreates an issue that was deliberately closed. -A candidate whose only prior issue from this scanner is CLOSED may be re-filed. +A candidate whose only prior `[leak-scan]` issue was closed as **not planned** (i.e. never +fixed — wontfix / invalid / duplicate) may be re-filed if it still reproduces. A candidate whose +leak is in `fixed-on-main-apis.txt` (fixed on `main`) must **not** be re-filed. # ===================== RUNTIME LEAK HUNT ===================== @@ -305,10 +334,11 @@ no MAUI source build, no emulator. ## Step 6 — File the issues (Pass A — one per confirmed leak) For **every** leak Step 5 confirmed, emit a `create-issue` safe-output (up to the 8 cap) — one -issue per distinct leak. De-dup each against open `[leak-scan]` issues AND against the other -issues you're filing this run (no two issues for the same rooting API). Each title MUST be of the -form **`[leak-scan] .`** — it MUST **lead with the canonical -rooting `Type.Member`** immediately after the tag (e.g. `[leak-scan] SwipeItemView.Command — non-weak +issue per distinct leak. De-dup each against open `[leak-scan]` issues, against +`fixed-on-main-apis.txt` (Step 2 — never re-file a leak already fixed on `main`), AND against the +other issues you're filing this run (no two issues for the same rooting API). Each title MUST be +of the form **`[leak-scan] .`** — it MUST **lead with the +canonical rooting `Type.Member`** immediately after the tag (e.g. `[leak-scan] SwipeItemView.Command — non-weak ICommand.CanExecuteChanged retains the control`). De-dup (Step 2) matches on that leading `Type.Member`, so keep it stable and canonical — do not reword it run-to-run. Body (markdown): diff --git a/.github/workflows/leak-fixer.lock.yml b/.github/workflows/leak-fixer.lock.yml index a454c30afb83..d0424bda4426 100644 --- a/.github/workflows/leak-fixer.lock.yml +++ b/.github/workflows/leak-fixer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"eb3ab9f3c4156e6f870ad3a604c4e52ccfe7b584d5497d6cdfd13779a6fe98d3","body_hash":"6bcdee91cfbce25619b6641e2f3f5979d7b90fe996e90533a5913f7a21a73ed9","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"a50dcc63c4432c53ac7b3c0fbcd6d90d156a40d3744c8e0cb854b143fce7fbba","body_hash":"164e4b72f756dd75431b0757b47e9e99c37863899a471e83ccb25fd04deb6b6c","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -235,24 +235,24 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_1915128a947746b1_EOF' + cat << 'GH_AW_PROMPT_54a86f49a2a70409_EOF' - GH_AW_PROMPT_1915128a947746b1_EOF + GH_AW_PROMPT_54a86f49a2a70409_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_1915128a947746b1_EOF' + cat << 'GH_AW_PROMPT_54a86f49a2a70409_EOF' - Tools: add_comment, create_pull_request, push_to_pull_request_branch, missing_tool, missing_data, noop - GH_AW_PROMPT_1915128a947746b1_EOF + Tools: add_comment, close_issue, create_pull_request, push_to_pull_request_branch, missing_tool, missing_data, noop + GH_AW_PROMPT_54a86f49a2a70409_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_push_to_pr_branch.md" - cat << 'GH_AW_PROMPT_1915128a947746b1_EOF' + cat << 'GH_AW_PROMPT_54a86f49a2a70409_EOF' - GH_AW_PROMPT_1915128a947746b1_EOF + GH_AW_PROMPT_54a86f49a2a70409_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_1915128a947746b1_EOF' + cat << 'GH_AW_PROMPT_54a86f49a2a70409_EOF' The following GitHub context information is available for this workflow: {{#if github.actor}} @@ -294,12 +294,12 @@ jobs: stop immediately and report the limitation rather than spending turns trying to work around it. - GH_AW_PROMPT_1915128a947746b1_EOF + GH_AW_PROMPT_54a86f49a2a70409_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_1915128a947746b1_EOF' + cat << 'GH_AW_PROMPT_54a86f49a2a70409_EOF' {{#runtime-import .github/workflows/leak-fixer.md}} - GH_AW_PROMPT_1915128a947746b1_EOF + GH_AW_PROMPT_54a86f49a2a70409_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -516,15 +516,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_782651390b58dac1_EOF' - {"add_comment":{"max":1,"required_title_prefix":"[leak-fix]","target":"*"},"create_pull_request":{"allowed_base_branches":["main"],"allowed_branches":["leak-fix/**"],"allowed_files":["src/Controls/src/**","src/Controls/tests/Core.UnitTests/**","src/Controls/tests/DeviceTests/**","src/Core/src/**","src/Essentials/src/**","**/PublicAPI.Unshipped.txt"],"base_branch":"main","draft":true,"labels":["agentic-workflows","perf/memory-leak 💦"],"max":1,"max_patch_files":100,"max_patch_size":4096,"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"],"protected_files_policy":"request_review"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"push_to_pull_request_branch":{"allowed_files":["src/Controls/src/**","src/Controls/tests/Core.UnitTests/**","src/Controls/tests/DeviceTests/**","src/Core/src/**","src/Essentials/src/**","**/PublicAPI.Unshipped.txt"],"if_no_changes":"ignore","max":1,"max_patch_size":4096,"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"],"target":"*","title_prefix":"[leak-fix]"},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_782651390b58dac1_EOF + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_01e0409969dac555_EOF' + {"add_comment":{"max":1,"required_title_prefix":"[leak-fix]","target":"*"},"close_issue":{"max":1,"target":"*"},"create_pull_request":{"allowed_base_branches":["main"],"allowed_branches":["leak-fix/**"],"allowed_files":["src/Controls/src/**","src/Controls/tests/Core.UnitTests/**","src/Controls/tests/DeviceTests/**","src/Core/src/**","src/Essentials/src/**","**/PublicAPI.Unshipped.txt"],"base_branch":"main","draft":true,"labels":["agentic-workflows","perf/memory-leak 💦"],"max":1,"max_patch_files":100,"max_patch_size":4096,"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"],"protected_files_policy":"request_review"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"push_to_pull_request_branch":{"allowed_files":["src/Controls/src/**","src/Controls/tests/Core.UnitTests/**","src/Controls/tests/DeviceTests/**","src/Core/src/**","src/Essentials/src/**","**/PublicAPI.Unshipped.txt"],"if_no_changes":"ignore","max":1,"max_patch_size":4096,"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"],"target":"*","title_prefix":"[leak-fix]"},"report_incomplete":{}} + GH_AW_SAFE_OUTPUTS_CONFIG_01e0409969dac555_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | { "description_suffixes": { "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added. Target: *. Supports reply_to_id for discussion threading.", + "close_issue": " CONSTRAINTS: Maximum 1 issue(s) can be closed. Target: *.", "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Labels [\"agentic-workflows\" \"perf/memory-leak 💦\"] will be automatically added. Only these labels are allowed: [\"agentic-workflows\" \"perf/memory-leak 💦\"]. PRs will be created as drafts.", "push_to_pull_request_branch": " CONSTRAINTS: Maximum 1 push(es) can be made. The target pull request title must start with \"[leak-fix]\"." }, @@ -555,6 +556,23 @@ jobs: } } }, + "close_issue": { + "defaultMax": 1, + "fields": { + "body": { + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "issue_number": { + "optionalPositiveInteger": true + }, + "repo": { + "type": "string", + "maxLength": 256 + } + } + }, "create_pull_request": { "defaultMax": 1, "fields": { @@ -1795,7 +1813,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,dev.azure.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,patch-diff.githubusercontent.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: "{\"add_comment\":{\"max\":1,\"required_title_prefix\":\"[leak-fix]\",\"target\":\"*\"},\"create_pull_request\":{\"allowed_base_branches\":[\"main\"],\"allowed_branches\":[\"leak-fix/**\"],\"allowed_files\":[\"src/Controls/src/**\",\"src/Controls/tests/Core.UnitTests/**\",\"src/Controls/tests/DeviceTests/**\",\"src/Core/src/**\",\"src/Essentials/src/**\",\"**/PublicAPI.Unshipped.txt\"],\"base_branch\":\"main\",\"draft\":true,\"labels\":[\"agentic-workflows\",\"perf/memory-leak 💦\"],\"max\":1,\"max_patch_files\":100,\"max_patch_size\":4096,\"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\"],\"protected_files_policy\":\"request_review\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_to_pull_request_branch\":{\"allowed_files\":[\"src/Controls/src/**\",\"src/Controls/tests/Core.UnitTests/**\",\"src/Controls/tests/DeviceTests/**\",\"src/Core/src/**\",\"src/Essentials/src/**\",\"**/PublicAPI.Unshipped.txt\"],\"if_no_changes\":\"ignore\",\"max\":1,\"max_patch_size\":4096,\"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\"],\"target\":\"*\",\"title_prefix\":\"[leak-fix]\"},\"report_incomplete\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1,\"required_title_prefix\":\"[leak-fix]\",\"target\":\"*\"},\"close_issue\":{\"max\":1,\"target\":\"*\"},\"create_pull_request\":{\"allowed_base_branches\":[\"main\"],\"allowed_branches\":[\"leak-fix/**\"],\"allowed_files\":[\"src/Controls/src/**\",\"src/Controls/tests/Core.UnitTests/**\",\"src/Controls/tests/DeviceTests/**\",\"src/Core/src/**\",\"src/Essentials/src/**\",\"**/PublicAPI.Unshipped.txt\"],\"base_branch\":\"main\",\"draft\":true,\"labels\":[\"agentic-workflows\",\"perf/memory-leak 💦\"],\"max\":1,\"max_patch_files\":100,\"max_patch_size\":4096,\"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\"],\"protected_files_policy\":\"request_review\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_to_pull_request_branch\":{\"allowed_files\":[\"src/Controls/src/**\",\"src/Controls/tests/Core.UnitTests/**\",\"src/Controls/tests/DeviceTests/**\",\"src/Core/src/**\",\"src/Essentials/src/**\",\"**/PublicAPI.Unshipped.txt\"],\"if_no_changes\":\"ignore\",\"max\":1,\"max_patch_size\":4096,\"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\"],\"target\":\"*\",\"title_prefix\":\"[leak-fix]\"},\"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/leak-fixer.md b/.github/workflows/leak-fixer.md index 1af81eae07c7..0c34b7b77eca 100644 --- a/.github/workflows/leak-fixer.md +++ b/.github/workflows/leak-fixer.md @@ -152,6 +152,12 @@ safe-outputs: target: "*" # agent supplies the leak PR number required-title-prefix: "[leak-fix]" # Track C only comments on this workflow's own [leak-fix] PRs max: 1 + # Close a [leak-scan] issue whose leak is ALREADY FIXED on main (Step 3 gate d / Step 6) so it + # isn't re-picked and rebuilt from source every run. The agent supplies the issue number + a + # closing comment linking the merged fix. Grants issues:write only to the safe-output job. + close-issue: + target: "*" + max: 1 # Most 6h runs are idle (every open leak already has a [leak-fix] PR). Without this, # gh-aw's auto-injected default (noop: report-as-issue: true) files a "no action taken" # issue every idle run. Mirror the hunter, which already opts out. @@ -189,9 +195,11 @@ writes are the safe-outputs (`create-pull-request`, `push-to-pull-request-branch - **Track C (review response):** any code you push must keep the PR's tests valid — re-run the affected test so Track A stays red→green. Never push a change that breaks the PR's own test just to satisfy a review. -2. **If a Track A leak is already fixed on `main`, open NO PR.** When your faithful regression - test passes on the *unpatched* source, the leak no longer reproduces — record - `skipped: already fixed on main (test green without fix)` and stop. +2. **If a Track A leak is already fixed on `main`, open NO PR — close the scan issue instead.** + When your faithful regression test passes on the *unpatched* source, or a **merged** `[leak-fix]` + PR already covers the same rooting `Type.Member` (Step 3 gate d), the leak no longer needs a + fix. Emit a `close-issue` on the `[leak-scan]` issue (AI-attributed comment linking the fix) + and record `skipped: already fixed on main`. Do NOT leave it open to be re-picked and rebuilt. 3. **Managed scope for product fixes.** Any *product* change (Track A / a Track C code fix) must live in managed cross-platform code (`src/Controls/src`, `src/Core/src`, `src/Essentials/src`). If a `[leak-scan]` leak can only be reproduced with a platform handler / native peer, it is out @@ -205,8 +213,8 @@ writes are the safe-outputs (`create-pull-request`, `push-to-pull-request-branch add the missing `-=` / teardown on the unload path. Prefer the minimal, idiomatic change that matches how the surrounding code already hardens similar subscriptions. 6. **One action per run.** Either respond to ONE PR's review (Track C) OR open ONE new draft PR - (Track A/B) — never both, never two of a kind. Honour the de-dup / attempt-cap / already- - addressed gates so you never repeat yourself. + (Track A/B) OR close ONE already-fixed `[leak-scan]` issue (Step 3d / Step 6) — never two. + Honour the de-dup / attempt-cap / already-addressed gates so you never repeat yourself. 7. **AI attribution.** Every PR body and every comment must clearly state it was generated by this workflow. @@ -405,8 +413,30 @@ gh pr list --repo "$GITHUB_REPOSITORY" --state closed --search '"[leak-fix]" in: | jq --arg n "$N" '[.[] | select(((.body // "") | test("(Fixes|Refs)[^0-9]*#"+$n+"\\b")) and (.mergedAt == null))]' \ > /tmp/gh-aw/agent/closed-fix-prs.json jq 'length' /tmp/gh-aw/agent/closed-fix-prs.json +# (d) MERGED [leak-fix] PR already fixing this issue number OR the SAME rooting Type.Member? +# Merged fixes often reference an UPSTREAM issue (e.g. "Fixes #35775") instead of this +# [leak-scan] number, so GitHub never auto-closed this scan issue — leaving it to be +# re-picked and rebuilt from source every run. Detect the merged fix by BOTH the issue-ref +# AND the rooting Type.Member so we can close this issue instead of rebuilding. +gh pr list --repo "$GITHUB_REPOSITORY" --state merged --search '"[leak-fix]" in:title' \ + --json number,title,body \ + | jq --arg n "$N" --arg api "$API_RE" \ + '[.[] | select(((.body // "") | test("(Fixes|Refs)[^0-9]*#"+$n+"\\b")) or (.title | test("Fix +"+$api+"([. ]|$)")))]' \ + > /tmp/gh-aw/agent/merged-fix-prs.json +jq -r '.[] | "merged fix for this leak: #\(.number) \(.title)"' /tmp/gh-aw/agent/merged-fix-prs.json ``` +- If a **merged** `[leak-fix]` PR already fixes this issue number OR the same rooting + `Type.Member` (d) → the leak is **already on `main`**. Do NOT create a branch or rebuild. + Emit exactly one `close-issue` safe-output on THIS `[leak-scan]` issue `#` with a closing + comment (AI-attributed, linking the merged PR), e.g.: + > 🔍 **AI-generated action** (Memory Leak Fixer) — this leak is already fixed on `main` by + > # (``). That PR's `Fixes:` line referenced a different issue + > number, so this `[leak-scan]` issue stayed open and kept being re-processed. Closing it to + > stop the rebuild loop. Note: it may still reproduce in the shipped NuGet package until the + > fix ships in a release. + + This is the run's single action — stop after emitting `close-issue`. - If an **open** fix PR already refs this issue (a) OR already fixes the same rooting `Type.Member` (b) → `skipped: leak already being fixed` and stop (or, if `issue_number` was explicit, just stop). Also double-check the leak isn't already fixed on `main` (Step 8 gate). @@ -484,7 +514,16 @@ Interpret: - **Test FAILS** (the `WaitForCollect` assert trips: object still alive) → good, the test catches the leak. Proceed to Step 7. - **Test PASSES on unpatched source** → the leak is already fixed on `main`. Per Hard Rule 2, - open NO PR: record `skipped: already fixed on main (test green without fix)` and stop. + open NO PR. This issue must not be re-picked and rebuilt every run, so emit exactly one + `close-issue` safe-output on the `[leak-scan]` issue `#` with an AI-attributed closing + comment — e.g.: + > 🔍 **AI-generated action** (Memory Leak Fixer) — the regression test for this leak is + > already GREEN on unpatched `main` (no fix applied), so this leak is already fixed. Closing + > this `[leak-scan]` issue so it isn't re-processed and rebuilt from source each run. Note: it + > may still reproduce in the shipped NuGet package until the fix ships in a release. + + Then record `skipped: already fixed on main (test green without fix)` and stop. Emitting the + `close-issue` is this run's single action. - **Restore 403 / feed unreachable** → should not happen now (`pkgs.dev.azure.com`, `*.blob.core.windows.net`, `*.nuget.org` are allowlisted). If it still does, record `skipped: build env cannot restore (feed blocked)` and stop — do NOT emit a PR. @@ -570,7 +609,10 @@ If nothing was committed (count `0`) → `skipped: no commit produced` and stop. Emit exactly one `create-pull-request` safe-output. Do NOT set a `base` field (the `base-branch: main` config pins it). Source `branch` MUST be `leak-fix/issue-`. Title MUST start with the literal tag **`[leak-fix] `** followed by e.g. `Fix memory leak -(Fixes #)`. +(Fixes #)`, where **`` is the `[leak-scan]` issue number you selected in Step 2** — not a +pre-existing upstream issue number. The `Fixes #` join key MUST point at the `[leak-scan]` +issue so GitHub auto-closes it on merge; otherwise the scan issue is orphaned and the fixer +re-picks and rebuilds it every run. The body MUST start with the required MAUI testing note (verbatim, no block-quote on the first two lines as shown), then the details: @@ -585,6 +627,9 @@ two lines as shown), then the details: Fixes # Refs: /# + Target branch: main Attempt: /3 @@ -612,7 +657,8 @@ PublicAPI.Unshipped.txt entries added). ``` Before emitting, re-read your body and confirm the `Target branch:` line says `main` and a -`Fixes #` line is present. If not, drop the attempt: `skipped: PR self-check failed`. +`Fixes #` line is present **whose `` is the `[leak-scan]` issue number selected in Step 2** +(not an upstream issue). If not, drop the attempt: `skipped: PR self-check failed`. If no PR was emitted this run (already-fixed, gated out, or validation failed), produce no output — that is an acceptable outcome. Otherwise you have produced exactly one validated, From 3966e53cfe5b5816fde9fb8a0b52e1c9cdd19b52 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Sun, 12 Jul 2026 18:49:04 +0200 Subject: [PATCH 04/20] Gate new leak-fixer close-issue emissions behind dry_run The two auto-close paths added in this PR (Step 3 gate (d) merged-PR de-dup, and Step 6 test-green-on-main) emitted close-issue without an explicit dry-run gate, unlike Track C (Step R) and Step 10 which each restate one. A dry_run could therefore still close a scan issue. Add the same '> Dry-run gate:' guard to both paths and extend the global dry_run rule to list 'close', so dry_run is a true no-side-effect mode. Recompiled leak-fixer.lock.yml (body_hash only; prose is read from the committed .md at runtime). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1257a460-520a-4b7a-91d5-61f39054ea59 --- .github/workflows/leak-fixer.lock.yml | 2 +- .github/workflows/leak-fixer.md | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/leak-fixer.lock.yml b/.github/workflows/leak-fixer.lock.yml index d0424bda4426..625d4bc6de31 100644 --- a/.github/workflows/leak-fixer.lock.yml +++ b/.github/workflows/leak-fixer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"a50dcc63c4432c53ac7b3c0fbcd6d90d156a40d3744c8e0cb854b143fce7fbba","body_hash":"164e4b72f756dd75431b0757b47e9e99c37863899a471e83ccb25fd04deb6b6c","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"a50dcc63c4432c53ac7b3c0fbcd6d90d156a40d3744c8e0cb854b143fce7fbba","body_hash":"6137686c25d38cc70111bfc1d44cd88f380fadc3374c4c34e3064835c450f603","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/leak-fixer.md b/.github/workflows/leak-fixer.md index 0c34b7b77eca..28840a5b2146 100644 --- a/.github/workflows/leak-fixer.md +++ b/.github/workflows/leak-fixer.md @@ -224,7 +224,7 @@ writes are the safe-outputs (`create-pull-request`, `push-to-pull-request-branch (Track A). If blank (e.g. the scheduled run), do Track C first (Step R), then auto-pick a `[leak-scan]` target in Step 2. - `dry_run` (optional, default false): if `"true"`, do the full local work but **emit no - safe-output** — print what you *would* push/comment/open to the run log instead. + safe-output** — print what you *would* push/comment/open/close to the run log instead. ```bash mkdir -p /tmp/gh-aw/agent @@ -436,6 +436,9 @@ jq -r '.[] | "merged fix for this leak: #\(.number) \(.title)"' /tmp/gh-aw/agent > stop the rebuild loop. Note: it may still reproduce in the shipped NuGet package until the > fix ships in a release. + > **Dry-run gate:** if `dry_run == "true"`, do NOT emit — print `DRY RUN — would close #` + > and the closing comment to the run log instead, then stop. + This is the run's single action — stop after emitting `close-issue`. - If an **open** fix PR already refs this issue (a) OR already fixes the same rooting `Type.Member` (b) → `skipped: leak already being fixed` and stop (or, if `issue_number` was @@ -522,6 +525,9 @@ Interpret: > this `[leak-scan]` issue so it isn't re-processed and rebuilt from source each run. Note: it > may still reproduce in the shipped NuGet package until the fix ships in a release. + > **Dry-run gate:** if `dry_run == "true"`, do NOT emit — print `DRY RUN — would close #` + > and the closing comment to the run log instead, then stop. + Then record `skipped: already fixed on main (test green without fix)` and stop. Emitting the `close-issue` is this run's single action. - **Restore 403 / feed unreachable** → should not happen now (`pkgs.dev.azure.com`, From 218f4656ba44233e6920449c886f62baddb20667 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 16:30:59 +0200 Subject: [PATCH 05/20] Address review: provenance-gate leak-workflow issue closure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Respond to the Copilot + adversarial review on the leak-fixer / daily-leak-hunter tuning PR: - close-issue safe-output now carries deterministic guards (required-title-prefix "[leak-scan] " + required-labels [agentic-workflows]), so a hallucinated/injected issue number pointing at an unrelated issue is rejected by the validator, not merely bounded by max:1. (Security) - Fixer Step 6: a regression test that is green on unpatched main no longer closes the [leak-scan] issue — a single green result from a freshly-authored repro is not proof. Automatic closure is reserved for the provenance-backed path only (Step 3 gate (d): a merged [leak-fix] PR). (Regression/data-loss) - Hunter fixed-on-main set is now built from MERGED [leak-fix] PRs ONLY; dropped the "[leak-scan] closed as COMPLETED" source (a close reason is not proof a fix landed and could permanently suppress a still-valid leak). (Data-loss) - Hunter Type.Member extractor keeps a normalized-title fallback key for off-contract titles instead of silently dropping them. - Fixer de-dup/auto-close gates: add --limit 200 to gh pr list so older merged [leak-fix] PRs aren't missed (default page size is 30). - Reworded the issues:write comment (write access lives in the isolated safe-outputs/conclusion jobs; the agent stays read-only). - Clarified the PR-body Refs/Fixes placeholder (Refs points at an OPTIONAL separate upstream issue , never the [leak-scan] #). Recompiled both workflows with gh aw v0.80.9 (0 errors, 0 warnings); lock diffs are the close_issue guard (fixer) and the prompt body_hash (both) only. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 767d2d21-494b-41fb-8e8b-4410f9cc1b4b --- .github/workflows/daily-leak-hunter.lock.yml | 2 +- .github/workflows/daily-leak-hunter.md | 66 ++++++++++-------- .github/workflows/leak-fixer.lock.yml | 12 ++-- .github/workflows/leak-fixer.md | 70 +++++++++++--------- 4 files changed, 86 insertions(+), 64 deletions(-) diff --git a/.github/workflows/daily-leak-hunter.lock.yml b/.github/workflows/daily-leak-hunter.lock.yml index a980e61c944b..2310a1890217 100644 --- a/.github/workflows/daily-leak-hunter.lock.yml +++ b/.github/workflows/daily-leak-hunter.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"745be3f30003b219d6dd9e5e3c43ff0c843c6c60231bff9d7ea0ecb4ed2d668b","body_hash":"c6059f80c42d3843e47edf2cf070a3d0c274b5679a5054970deaf85436b74cf1","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"745be3f30003b219d6dd9e5e3c43ff0c843c6c60231bff9d7ea0ecb4ed2d668b","body_hash":"0472288bf924dd8dd383a9e8f15b25c2357260f8cc19035b903a3fc96194dedd","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/daily-leak-hunter.md b/.github/workflows/daily-leak-hunter.md index 37926b4a9caa..126b58b7a78d 100644 --- a/.github/workflows/daily-leak-hunter.md +++ b/.github/workflows/daily-leak-hunter.md @@ -135,12 +135,14 @@ Never push, never open a PR, never comment, never edit product or test code in t 5. **De-dup against THIS SCANNER's own OPEN issues AND leaks already fixed on `main`.** Before filing, fetch this workflow's open `[leak-scan]` issues and skip a leak already covered by one (same rooting API / retention path). ALSO skip any leak whose rooting `Type.Member` is already - fixed on `main` — a merged `[leak-fix]` PR or a `[leak-scan]` issue closed as completed (Step 2 - builds this set). Such leaks still reproduce against the SHIPPED package until the fix ships, - so the empirical test alone can't tell — de-dup is the only guard. Do NOT suppress a candidate - because AdamEssenmacher (or anyone else) has a repro/issue for it — duplicating those is fine. - A candidate whose only prior issue from this scanner is CLOSED **as not planned** (never fixed) - may be re-filed; one whose leak is fixed on `main` may not. + fixed on `main` by a **merged `[leak-fix]` PR** (Step 2 builds this set — a merged fix PR is + durable, workflow-owned proof a fix landed; a close *reason* alone is not). Such leaks still + reproduce against the SHIPPED package until the fix ships, so the empirical test alone can't + tell — this provenance-gated de-dup is the only guard. Do NOT suppress a candidate because + AdamEssenmacher (or anyone else) has a repro/issue for it — duplicating those is fine. A + candidate whose only prior issue from this scanner is CLOSED with **no merged `[leak-fix]` PR** + (any close reason) may be re-filed if it still reproduces; one whose leak is fixed on `main` by + a merged `[leak-fix]` PR may not. 6. **Never weaken or disable anything, and never commit code.** You only READ repo source and (Pass A) ADD a throwaway test under `/tmp`. Never edit product code, never `[ActiveIssue]`/skip/mute existing tests, never push. @@ -181,20 +183,31 @@ jq -r '.[].title' /tmp/gh-aw/agent/my-open-leakscan.json \ > /tmp/gh-aw/agent/already-filed-apis.txt echo "already-filed rooting APIs:"; cat /tmp/gh-aw/agent/already-filed-apis.txt -# ── ALSO skip leaks ALREADY FIXED on main ────────────────────────────────────────────────── +# ── ALSO skip leaks ALREADY FIXED on main — MERGED [leak-fix] PRs ONLY ─────────────────────── # Your Step 5 confirmation runs against the SHIPPED NuGet package (pinned 10.0.0), where an # already-MERGED fix has NOT shipped yet — so a leak that is fixed on `main` STILL reproduces -# (goes red) and would be re-filed every run forever. De-dup is the ONLY guard. Build a -# "fixed-on-main" rooting-Type.Member set from (a) every MERGED `[leak-fix]` PR title and -# (b) every `[leak-scan]` issue this scanner closed as COMPLETED (fix landed). -{ - gh pr list --repo "$GITHUB_REPOSITORY" --state merged --search '"[leak-fix]" in:title' \ - --limit 200 --json title -q '.[].title' | grep -E '^\[leak-fix\] ' | sed -E 's/^\[leak-fix\] *(Fix +)?//' - gh issue list --repo "$GITHUB_REPOSITORY" --search '"[leak-scan]" in:title' \ - --state closed --label agentic-workflows --limit 300 --json title,stateReason \ - -q '.[] | select(.stateReason == "COMPLETED") | .title' | sed -E 's/^\[leak-scan\] *//' -} \ - | awk '{ if (match($0, /[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)+/)) { chain=substr($0,RSTART,RLENGTH); n=split(chain,seg,"."); print seg[n-1]"."seg[n] } }' \ +# (goes red) and would be re-filed every run forever. De-dup is the ONLY guard. Build the +# "fixed-on-main" rooting-Type.Member set from **MERGED `[leak-fix]` PR titles ONLY** — a merged +# fix PR is durable, workflow-owned provenance that a fix ACTUALLY LANDED on `main`. Do NOT trust +# an issue's close *reason*: a `[leak-scan]` issue closed as COMPLETED records only that SOMEONE +# closed it, not that a fix merged (a maintainer can close a still-reproducing issue as +# completed), so treating "closed as completed" as fixed would permanently suppress a still-valid +# leak. Worst case here (a human-fixed leak with no [leak-fix] PR) is a single bounded re-file +# that the OPEN-issue de-dup above then catches — far safer than permanent data loss. +gh pr list --repo "$GITHUB_REPOSITORY" --state merged --search '"[leak-fix]" in:title' \ + --limit 300 --json title -q '.[].title' \ + | grep -E '^\[leak-fix\] ' | sed -E 's/^\[leak-fix\] *(Fix +)?//' \ + | awk '{ + if (match($0, /[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)+/)) { + chain=substr($0,RSTART,RLENGTH); n=split(chain,seg,"."); print seg[n-1]"."seg[n] + } else { + # Off-contract title with no dotted Type.Member: keep a durable normalized-title + # fallback key (prefixed "title:") instead of SILENTLY DROPPING it, so the known-fixed + # leak still surfaces with a de-dup identity for the agent to match against. + key=tolower($0); gsub(/[^a-z0-9]+/, "-", key); gsub(/^-+|-+$/, "", key); + if (key != "") print "title:" key + } + }' \ | sort -u \ > /tmp/gh-aw/agent/fixed-on-main-apis.txt echo "fixed-on-main (do NOT re-file) rooting APIs:"; cat /tmp/gh-aw/agent/fixed-on-main-apis.txt @@ -206,14 +219,15 @@ echo "fixed-on-main (do NOT re-file) rooting APIs:"; cat /tmp/gh-aw/agent/fixed- before you write its test** — re-filing a leak this scanner already has open (even with different title wording) is the #1 failure mode, so be strict about matching the `Type.Member`. - A candidate is **ALSO OUT** if its rooting `Type.Member` is in `fixed-on-main-apis.txt` - (already fixed on `main` by a merged `[leak-fix]` PR, or a `[leak-scan]` issue closed as - completed). The shipped-package test in Step 5 will STILL go red for these because the fix - hasn't shipped in a release yet — that is expected. Do NOT re-file: the fix is already on - `main` and will ship. Re-filing just recreates an issue that was deliberately closed. - -A candidate whose only prior `[leak-scan]` issue was closed as **not planned** (i.e. never -fixed — wontfix / invalid / duplicate) may be re-filed if it still reproduces. A candidate whose -leak is in `fixed-on-main-apis.txt` (fixed on `main`) must **not** be re-filed. + (already fixed on `main` by a **merged `[leak-fix]` PR**). The shipped-package test in Step 5 + will STILL go red for these because the fix hasn't shipped in a release yet — that is expected. + Do NOT re-file: the fix is already on `main` and will ship. + +A candidate whose only prior `[leak-scan]` issue was **closed with no merged `[leak-fix]` PR** +(closed as not planned — wontfix / invalid / duplicate — OR closed as completed by a maintainer +without a landed fix) may be re-filed if it still reproduces: a close *reason* is not proof the +leak is gone. A candidate whose leak is in `fixed-on-main-apis.txt` (fixed on `main` by a merged +`[leak-fix]` PR) must **not** be re-filed. # ===================== RUNTIME LEAK HUNT ===================== diff --git a/.github/workflows/leak-fixer.lock.yml b/.github/workflows/leak-fixer.lock.yml index 625d4bc6de31..77267a0453e5 100644 --- a/.github/workflows/leak-fixer.lock.yml +++ b/.github/workflows/leak-fixer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"a50dcc63c4432c53ac7b3c0fbcd6d90d156a40d3744c8e0cb854b143fce7fbba","body_hash":"6137686c25d38cc70111bfc1d44cd88f380fadc3374c4c34e3064835c450f603","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"8ffde7d60b6ed1b713a0adb98cfa70e4ae3539a006f1c2ba01696e3c475cb965","body_hash":"344b22f01c459a945bb42691467c137756074256f842f7090617c10d20c95108","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -516,16 +516,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_01e0409969dac555_EOF' - {"add_comment":{"max":1,"required_title_prefix":"[leak-fix]","target":"*"},"close_issue":{"max":1,"target":"*"},"create_pull_request":{"allowed_base_branches":["main"],"allowed_branches":["leak-fix/**"],"allowed_files":["src/Controls/src/**","src/Controls/tests/Core.UnitTests/**","src/Controls/tests/DeviceTests/**","src/Core/src/**","src/Essentials/src/**","**/PublicAPI.Unshipped.txt"],"base_branch":"main","draft":true,"labels":["agentic-workflows","perf/memory-leak 💦"],"max":1,"max_patch_files":100,"max_patch_size":4096,"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"],"protected_files_policy":"request_review"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"push_to_pull_request_branch":{"allowed_files":["src/Controls/src/**","src/Controls/tests/Core.UnitTests/**","src/Controls/tests/DeviceTests/**","src/Core/src/**","src/Essentials/src/**","**/PublicAPI.Unshipped.txt"],"if_no_changes":"ignore","max":1,"max_patch_size":4096,"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"],"target":"*","title_prefix":"[leak-fix]"},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_01e0409969dac555_EOF + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_7f124cc8961e87c6_EOF' + {"add_comment":{"max":1,"required_title_prefix":"[leak-fix]","target":"*"},"close_issue":{"max":1,"required_labels":["agentic-workflows"],"required_title_prefix":"[leak-scan] ","target":"*"},"create_pull_request":{"allowed_base_branches":["main"],"allowed_branches":["leak-fix/**"],"allowed_files":["src/Controls/src/**","src/Controls/tests/Core.UnitTests/**","src/Controls/tests/DeviceTests/**","src/Core/src/**","src/Essentials/src/**","**/PublicAPI.Unshipped.txt"],"base_branch":"main","draft":true,"labels":["agentic-workflows","perf/memory-leak 💦"],"max":1,"max_patch_files":100,"max_patch_size":4096,"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"],"protected_files_policy":"request_review"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"push_to_pull_request_branch":{"allowed_files":["src/Controls/src/**","src/Controls/tests/Core.UnitTests/**","src/Controls/tests/DeviceTests/**","src/Core/src/**","src/Essentials/src/**","**/PublicAPI.Unshipped.txt"],"if_no_changes":"ignore","max":1,"max_patch_size":4096,"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"],"target":"*","title_prefix":"[leak-fix]"},"report_incomplete":{}} + GH_AW_SAFE_OUTPUTS_CONFIG_7f124cc8961e87c6_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | { "description_suffixes": { "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added. Target: *. Supports reply_to_id for discussion threading.", - "close_issue": " CONSTRAINTS: Maximum 1 issue(s) can be closed. Target: *.", + "close_issue": " CONSTRAINTS: Maximum 1 issue(s) can be closed. Target: *. Only issues with title prefix \"[leak-scan] \" can be closed.", "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Labels [\"agentic-workflows\" \"perf/memory-leak 💦\"] will be automatically added. Only these labels are allowed: [\"agentic-workflows\" \"perf/memory-leak 💦\"]. PRs will be created as drafts.", "push_to_pull_request_branch": " CONSTRAINTS: Maximum 1 push(es) can be made. The target pull request title must start with \"[leak-fix]\"." }, @@ -1813,7 +1813,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,dev.azure.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,patch-diff.githubusercontent.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: "{\"add_comment\":{\"max\":1,\"required_title_prefix\":\"[leak-fix]\",\"target\":\"*\"},\"close_issue\":{\"max\":1,\"target\":\"*\"},\"create_pull_request\":{\"allowed_base_branches\":[\"main\"],\"allowed_branches\":[\"leak-fix/**\"],\"allowed_files\":[\"src/Controls/src/**\",\"src/Controls/tests/Core.UnitTests/**\",\"src/Controls/tests/DeviceTests/**\",\"src/Core/src/**\",\"src/Essentials/src/**\",\"**/PublicAPI.Unshipped.txt\"],\"base_branch\":\"main\",\"draft\":true,\"labels\":[\"agentic-workflows\",\"perf/memory-leak 💦\"],\"max\":1,\"max_patch_files\":100,\"max_patch_size\":4096,\"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\"],\"protected_files_policy\":\"request_review\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_to_pull_request_branch\":{\"allowed_files\":[\"src/Controls/src/**\",\"src/Controls/tests/Core.UnitTests/**\",\"src/Controls/tests/DeviceTests/**\",\"src/Core/src/**\",\"src/Essentials/src/**\",\"**/PublicAPI.Unshipped.txt\"],\"if_no_changes\":\"ignore\",\"max\":1,\"max_patch_size\":4096,\"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\"],\"target\":\"*\",\"title_prefix\":\"[leak-fix]\"},\"report_incomplete\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1,\"required_title_prefix\":\"[leak-fix]\",\"target\":\"*\"},\"close_issue\":{\"max\":1,\"required_labels\":[\"agentic-workflows\"],\"required_title_prefix\":\"[leak-scan] \",\"target\":\"*\"},\"create_pull_request\":{\"allowed_base_branches\":[\"main\"],\"allowed_branches\":[\"leak-fix/**\"],\"allowed_files\":[\"src/Controls/src/**\",\"src/Controls/tests/Core.UnitTests/**\",\"src/Controls/tests/DeviceTests/**\",\"src/Core/src/**\",\"src/Essentials/src/**\",\"**/PublicAPI.Unshipped.txt\"],\"base_branch\":\"main\",\"draft\":true,\"labels\":[\"agentic-workflows\",\"perf/memory-leak 💦\"],\"max\":1,\"max_patch_files\":100,\"max_patch_size\":4096,\"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\"],\"protected_files_policy\":\"request_review\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_to_pull_request_branch\":{\"allowed_files\":[\"src/Controls/src/**\",\"src/Controls/tests/Core.UnitTests/**\",\"src/Controls/tests/DeviceTests/**\",\"src/Core/src/**\",\"src/Essentials/src/**\",\"**/PublicAPI.Unshipped.txt\"],\"if_no_changes\":\"ignore\",\"max\":1,\"max_patch_size\":4096,\"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\"],\"target\":\"*\",\"title_prefix\":\"[leak-fix]\"},\"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/leak-fixer.md b/.github/workflows/leak-fixer.md index 28840a5b2146..e11765724607 100644 --- a/.github/workflows/leak-fixer.md +++ b/.github/workflows/leak-fixer.md @@ -152,11 +152,18 @@ safe-outputs: target: "*" # agent supplies the leak PR number required-title-prefix: "[leak-fix]" # Track C only comments on this workflow's own [leak-fix] PRs max: 1 - # Close a [leak-scan] issue whose leak is ALREADY FIXED on main (Step 3 gate d / Step 6) so it - # isn't re-picked and rebuilt from source every run. The agent supplies the issue number + a - # closing comment linking the merged fix. Grants issues:write only to the safe-output job. + # Close an ORPHANED [leak-scan] issue whose leak is ALREADY FIXED on main by a MERGED + # [leak-fix] PR (Step 3 gate (d)) so it isn't re-picked and rebuilt from source every run. + # The agent supplies the issue number + a closing comment linking the merged fix. Write + # access lives ONLY in the isolated safe-outputs/conclusion jobs — the agent itself stays + # read-only. Deterministic guards: the safe-output validator will ONLY close an issue whose + # title starts with "[leak-scan] " AND that carries this workflow's own `agentic-workflows` + # label, so a hallucinated or injected number pointing at an unrelated issue is rejected + # outright (not merely bounded by max:1). close-issue: target: "*" + required-title-prefix: "[leak-scan] " + required-labels: [agentic-workflows] max: 1 # Most 6h runs are idle (every open leak already has a [leak-fix] PR). Without this, # gh-aw's auto-injected default (noop: report-as-issue: true) files a "no action taken" @@ -195,11 +202,14 @@ writes are the safe-outputs (`create-pull-request`, `push-to-pull-request-branch - **Track C (review response):** any code you push must keep the PR's tests valid — re-run the affected test so Track A stays red→green. Never push a change that breaks the PR's own test just to satisfy a review. -2. **If a Track A leak is already fixed on `main`, open NO PR — close the scan issue instead.** - When your faithful regression test passes on the *unpatched* source, or a **merged** `[leak-fix]` - PR already covers the same rooting `Type.Member` (Step 3 gate d), the leak no longer needs a - fix. Emit a `close-issue` on the `[leak-scan]` issue (AI-attributed comment linking the fix) - and record `skipped: already fixed on main`. Do NOT leave it open to be re-picked and rebuilt. +2. **If a Track A leak is already fixed on `main`, open NO PR.** Close the scan issue **only** + when a **merged** `[leak-fix]` PR already covers the same rooting `Type.Member` (Step 3 gate + (d)) — that merged PR is the provenance a fix actually landed. Emit a `close-issue` on the + `[leak-scan]` issue (AI-attributed comment linking the merged fix) and record + `skipped: already fixed on main`. If instead your freshly-authored regression test merely + passes on the *unpatched* source (Step 6) with **no** merged fix PR, that is NOT proof the + leak is gone — do NOT close it; record `skipped: test green on unpatched main (issue left + open)` and move on. 3. **Managed scope for product fixes.** Any *product* change (Track A / a Track C code fix) must live in managed cross-platform code (`src/Controls/src`, `src/Core/src`, `src/Essentials/src`). If a `[leak-scan]` leak can only be reproduced with a platform handler / native peer, it is out @@ -213,7 +223,7 @@ writes are the safe-outputs (`create-pull-request`, `push-to-pull-request-branch add the missing `-=` / teardown on the unload path. Prefer the minimal, idiomatic change that matches how the surrounding code already hardens similar subscriptions. 6. **One action per run.** Either respond to ONE PR's review (Track C) OR open ONE new draft PR - (Track A/B) OR close ONE already-fixed `[leak-scan]` issue (Step 3d / Step 6) — never two. + (Track A/B) OR close ONE already-fixed `[leak-scan]` issue (Step 3 gate (d)) — never two. Honour the de-dup / attempt-cap / already-addressed gates so you never repeat yourself. 7. **AI attribution.** Every PR body and every comment must clearly state it was generated by this workflow. @@ -395,20 +405,20 @@ echo "target rooting API: $API" # a LITERAL "Type.Member" — otherwise "BackButtonBehavior.Command" would also match "BackButtonBehaviorXCommand". API_RE=$(printf '%s' "$API" | sed -E 's/[][(){}.^$*+?|\\]/\\&/g') # (a) Open [leak-fix] PR already addressing THIS issue number? -gh pr list --repo "$GITHUB_REPOSITORY" --state open --search '"[leak-fix]" in:title' \ +gh pr list --repo "$GITHUB_REPOSITORY" --state open --search '"[leak-fix]" in:title' --limit 200 \ --json number,title,body \ | jq --arg n "$N" '[.[] | select((.body // "") | test("(Fixes|Refs)[^0-9]*#"+$n+"\\b"))]' \ > /tmp/gh-aw/agent/open-fix-prs.json jq 'length' /tmp/gh-aw/agent/open-fix-prs.json # (b) Open [leak-fix] PR already fixing the SAME rooting Type.Member (any issue number)? # [leak-fix] PR titles are "Fix . memory leak". -gh pr list --repo "$GITHUB_REPOSITORY" --state open --search '"[leak-fix]" in:title' \ +gh pr list --repo "$GITHUB_REPOSITORY" --state open --search '"[leak-fix]" in:title' --limit 200 \ --json number,title \ | jq --arg api "$API_RE" '[.[] | select(.title | test("Fix +"+$api+"([. ]|$)"))]' \ > /tmp/gh-aw/agent/same-api-prs.json jq -r '.[] | "same-API open fix PR: #\(.number) \(.title)"' /tmp/gh-aw/agent/same-api-prs.json # (c) Closed-unmerged attempts for this issue (attempt cap = 3). -gh pr list --repo "$GITHUB_REPOSITORY" --state closed --search '"[leak-fix]" in:title' \ +gh pr list --repo "$GITHUB_REPOSITORY" --state closed --search '"[leak-fix]" in:title' --limit 200 \ --json number,title,body,mergedAt \ | jq --arg n "$N" '[.[] | select(((.body // "") | test("(Fixes|Refs)[^0-9]*#"+$n+"\\b")) and (.mergedAt == null))]' \ > /tmp/gh-aw/agent/closed-fix-prs.json @@ -418,7 +428,7 @@ jq 'length' /tmp/gh-aw/agent/closed-fix-prs.json # [leak-scan] number, so GitHub never auto-closed this scan issue — leaving it to be # re-picked and rebuilt from source every run. Detect the merged fix by BOTH the issue-ref # AND the rooting Type.Member so we can close this issue instead of rebuilding. -gh pr list --repo "$GITHUB_REPOSITORY" --state merged --search '"[leak-fix]" in:title' \ +gh pr list --repo "$GITHUB_REPOSITORY" --state merged --search '"[leak-fix]" in:title' --limit 200 \ --json number,title,body \ | jq --arg n "$N" --arg api "$API_RE" \ '[.[] | select(((.body // "") | test("(Fixes|Refs)[^0-9]*#"+$n+"\\b")) or (.title | test("Fix +"+$api+"([. ]|$)")))]' \ @@ -516,20 +526,16 @@ Interpret: - **Test FAILS** (the `WaitForCollect` assert trips: object still alive) → good, the test catches the leak. Proceed to Step 7. -- **Test PASSES on unpatched source** → the leak is already fixed on `main`. Per Hard Rule 2, - open NO PR. This issue must not be re-picked and rebuilt every run, so emit exactly one - `close-issue` safe-output on the `[leak-scan]` issue `#` with an AI-attributed closing - comment — e.g.: - > 🔍 **AI-generated action** (Memory Leak Fixer) — the regression test for this leak is - > already GREEN on unpatched `main` (no fix applied), so this leak is already fixed. Closing - > this `[leak-scan]` issue so it isn't re-processed and rebuilt from source each run. Note: it - > may still reproduce in the shipped NuGet package until the fix ships in a release. - - > **Dry-run gate:** if `dry_run == "true"`, do NOT emit — print `DRY RUN — would close #` - > and the closing comment to the run log instead, then stop. - - Then record `skipped: already fixed on main (test green without fix)` and stop. Emitting the - `close-issue` is this run's single action. +- **Test PASSES on unpatched source** → your regression test does not reproduce the leak on + `main`. Per Hard Rule 2, open NO PR. **Do NOT close the `[leak-scan]` issue here.** A single + green result from a test *you just authored* is **not** proof the leak is fixed — the repro may + simply have failed to recreate the retention path (wrong root, too little GC pressure, a typo'd + assertion). Automatic issue closure is reserved for the **provenance-backed** path only — + Step 3 gate (d), where a **merged `[leak-fix]` PR** for the same leak is the evidence. Here, + just record `skipped: test green on unpatched main (no PR, issue left open)` and stop. If the + leak really is fixed on `main`, gate (d) will close the scan issue once the merged fix is + detected; if the test was a bad repro, leaving the issue open lets a later run try again — far + safer than closing a still-live leak on weak evidence. - **Restore 403 / feed unreachable** → should not happen now (`pkgs.dev.azure.com`, `*.blob.core.windows.net`, `*.nuget.org` are allowlisted). If it still does, record `skipped: build env cannot restore (feed blocked)` and stop — do NOT emit a PR. @@ -632,10 +638,12 @@ two lines as shown), then the details: > 🤖 **AI-generated PR** — produced automatically by the **Memory Leak Fixer** agentic workflow from issue #. The regression test below was observed to FAIL on unpatched `main` and PASS with this fix, on the runner. Please review carefully before merging. Fixes # -Refs: /# - + +Refs: /# Target branch: main Attempt: /3 From 25e2ec7b958bc078ab8dfd5504f07b19191c60e4 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 16:41:05 +0200 Subject: [PATCH 06/20] Fix prompt-internal consistency from Copilot re-review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address 3 Copilot re-review comments (all prompt-text internal consistency, no behavior change): - leak-fixer.md: add close-issue to the safe-output overview list (it was missing from the create-pull-request/push/add-comment sentence). - leak-fixer.md: Step 3 intro no longer claims the body carries Refs # for the [leak-scan] issue — Fixes # is the sole scan-issue join/auto-close key; Refs points at a separate upstream issue. Note gate (a) still searches Fixes|Refs for backward-compat with older fix PRs. - daily-leak-hunter.md: candidate-OUT prose now documents matching the title: fallback keys via the SAME normalization used to build them, so off-contract fixed leaks are actually de-duped (previously the title: keys were unused). Recompiled with gh aw v0.80.9: 0 errors/0 warnings, body_hash-only lock change (frontmatter_hash unchanged, no permission drift). Normalization parity between the awk key-writer and the documented candidate-normalization verified empirically. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 767d2d21-494b-41fb-8e8b-4410f9cc1b4b --- .github/workflows/daily-leak-hunter.lock.yml | 2 +- .github/workflows/daily-leak-hunter.md | 6 +++++- .github/workflows/leak-fixer.lock.yml | 2 +- .github/workflows/leak-fixer.md | 15 +++++++++------ 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.github/workflows/daily-leak-hunter.lock.yml b/.github/workflows/daily-leak-hunter.lock.yml index 2310a1890217..a5d3d03b3ee7 100644 --- a/.github/workflows/daily-leak-hunter.lock.yml +++ b/.github/workflows/daily-leak-hunter.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"745be3f30003b219d6dd9e5e3c43ff0c843c6c60231bff9d7ea0ecb4ed2d668b","body_hash":"0472288bf924dd8dd383a9e8f15b25c2357260f8cc19035b903a3fc96194dedd","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"745be3f30003b219d6dd9e5e3c43ff0c843c6c60231bff9d7ea0ecb4ed2d668b","body_hash":"4b3e261bc72f7594f02ee47620e70e5365cc709da277b9d4782d986cda877c25","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/daily-leak-hunter.md b/.github/workflows/daily-leak-hunter.md index 126b58b7a78d..27356b378857 100644 --- a/.github/workflows/daily-leak-hunter.md +++ b/.github/workflows/daily-leak-hunter.md @@ -221,7 +221,11 @@ echo "fixed-on-main (do NOT re-file) rooting APIs:"; cat /tmp/gh-aw/agent/fixed- - A candidate is **ALSO OUT** if its rooting `Type.Member` is in `fixed-on-main-apis.txt` (already fixed on `main` by a **merged `[leak-fix]` PR**). The shipped-package test in Step 5 will STILL go red for these because the fix hasn't shipped in a release yet — that is expected. - Do NOT re-file: the fix is already on `main` and will ship. + Do NOT re-file: the fix is already on `main` and will ship. For an **off-contract candidate + whose title has no dotted `Type.Member`**, apply the SAME normalization the merged-PR list uses + (lower-case, replace each run of non-alphanumerics with `-`, trim leading/trailing `-`) and + treat the candidate as OUT if `title:` is in `fixed-on-main-apis.txt`. That is how the + `title:` fallback keys are matched, so off-contract fixed leaks aren't re-filed either. A candidate whose only prior `[leak-scan]` issue was **closed with no merged `[leak-fix]` PR** (closed as not planned — wontfix / invalid / duplicate — OR closed as completed by a maintainer diff --git a/.github/workflows/leak-fixer.lock.yml b/.github/workflows/leak-fixer.lock.yml index 77267a0453e5..ef961cb7d685 100644 --- a/.github/workflows/leak-fixer.lock.yml +++ b/.github/workflows/leak-fixer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"8ffde7d60b6ed1b713a0adb98cfa70e4ae3539a006f1c2ba01696e3c475cb965","body_hash":"344b22f01c459a945bb42691467c137756074256f842f7090617c10d20c95108","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"8ffde7d60b6ed1b713a0adb98cfa70e4ae3539a006f1c2ba01696e3c475cb965","body_hash":"6670be959ebf55ccf6e39af856eaadcf14031c3151a92a9ee5a8f563f7227065","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/leak-fixer.md b/.github/workflows/leak-fixer.md index e11765724607..0627340ddf21 100644 --- a/.github/workflows/leak-fixer.md +++ b/.github/workflows/leak-fixer.md @@ -190,8 +190,8 @@ You produce **only `[leak-fix]` PRs for empirically-proven leaks** — there is The PR base is always `main`. You build MAUI **from source** to validate. All scratch state goes under `/tmp/gh-aw/agent/` (each bash call is a fresh subshell; persist what you need). Your only -writes are the safe-outputs (`create-pull-request`, `push-to-pull-request-branch`, `add-comment`) -— never push with raw git, never edit anything outside the fix/test. +writes are the safe-outputs (`create-pull-request`, `push-to-pull-request-branch`, `add-comment`, +`close-issue`) — never push with raw git, never edit anything outside the fix/test. ## Hard rules — non-negotiable @@ -387,10 +387,13 @@ Read the chosen issue's body in full (`gh issue view --json title,body`). Ex ## Step 3 — De-dup + attempt cap (live GitHub searches) -A fix PR carries `Fixes #` (and `Refs: /#`) in its body — that is the join -key. But the same underlying leak can be filed under MULTIPLE issue numbers (duplicate -`[leak-scan]` issues, or a pre-existing upstream issue), so also de-dup by the **rooting -`Type.Member`** the target names — never open a second fix for a leak already being fixed. +A fix PR carries `Fixes #` in its body (where `` is the `[leak-scan]` issue) — that is the +sole scan-issue join / auto-close key. An optional `Refs: /#` line may also +be present, but it points at a SEPARATE pre-existing upstream issue, never at `#`. Because the +same underlying leak can still be filed under MULTIPLE issue numbers (duplicate `[leak-scan]` +issues, or a pre-existing upstream issue), also de-dup by the **rooting `Type.Member`** the target +names — never open a second fix for a leak already being fixed. (The gate (a) search below matches +`Fixes` **or** `Refs` against `#` for backward-compatibility with older fix PRs.) ```bash N= From e85dd9134d3159fef901f0a8edde3f7b2b00fb40 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 16:47:18 +0200 Subject: [PATCH 07/20] Align Hard Rule 2 with gate (d) closure conditions Copilot re-review: Hard Rule 2 said the fixer closes a [leak-scan] issue only when a merged [leak-fix] PR covers the same rooting Type.Member, but Step 3 gate (d) also closes when a merged PR references the scan issue NUMBER (Fixes/Refs #). Widen Hard Rule 2 to state both OR-conditions so the agent doesn't skip the auto-close path for orphaned scan issues already linked by number. Both are merged-PR-backed, so the provenance-only-closure invariant is preserved. Prompt-only: gh aw v0.80.9 recompile is 0 errors/0 warnings, body_hash-only (frontmatter_hash unchanged, no permission drift). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 767d2d21-494b-41fb-8e8b-4410f9cc1b4b --- .github/workflows/leak-fixer.lock.yml | 2 +- .github/workflows/leak-fixer.md | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/leak-fixer.lock.yml b/.github/workflows/leak-fixer.lock.yml index ef961cb7d685..75c0c087fda6 100644 --- a/.github/workflows/leak-fixer.lock.yml +++ b/.github/workflows/leak-fixer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"8ffde7d60b6ed1b713a0adb98cfa70e4ae3539a006f1c2ba01696e3c475cb965","body_hash":"6670be959ebf55ccf6e39af856eaadcf14031c3151a92a9ee5a8f563f7227065","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"8ffde7d60b6ed1b713a0adb98cfa70e4ae3539a006f1c2ba01696e3c475cb965","body_hash":"9ebec08537d884e715d50cda81f3e6712858416328df8524d092a0f94eb40746","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/leak-fixer.md b/.github/workflows/leak-fixer.md index 0627340ddf21..44a0d3136c52 100644 --- a/.github/workflows/leak-fixer.md +++ b/.github/workflows/leak-fixer.md @@ -203,8 +203,9 @@ writes are the safe-outputs (`create-pull-request`, `push-to-pull-request-branch affected test so Track A stays red→green. Never push a change that breaks the PR's own test just to satisfy a review. 2. **If a Track A leak is already fixed on `main`, open NO PR.** Close the scan issue **only** - when a **merged** `[leak-fix]` PR already covers the same rooting `Type.Member` (Step 3 gate - (d)) — that merged PR is the provenance a fix actually landed. Emit a `close-issue` on the + when a **merged** `[leak-fix]` PR either references this scan issue number (`Fixes`/`Refs #`) + OR already covers the same rooting `Type.Member` (Step 3 gate (d)) — that merged PR is the + provenance a fix actually landed. Emit a `close-issue` on the `[leak-scan]` issue (AI-attributed comment linking the merged fix) and record `skipped: already fixed on main`. If instead your freshly-authored regression test merely passes on the *unpatched* source (Step 6) with **no** merged fix PR, that is NOT proof the From c16e059828044a46da55f7dda8944c5256e0a179 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 16:48:54 +0200 Subject: [PATCH 08/20] Unify green-test skipped record string across Hard Rule 2 and Step 6 Self-audit consistency: Hard Rule 2 recorded 'skipped: test green on unpatched main (issue left open)' while Step 6 recorded '...(no PR, issue left open)' for the same condition. Unify on the Step 6 form so the agent emits one canonical record string. Prompt-only: gh aw v0.80.9 recompile 0 errors/0 warnings, body_hash-only. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 767d2d21-494b-41fb-8e8b-4410f9cc1b4b --- .github/workflows/leak-fixer.lock.yml | 2 +- .github/workflows/leak-fixer.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/leak-fixer.lock.yml b/.github/workflows/leak-fixer.lock.yml index 75c0c087fda6..1efdfdd76259 100644 --- a/.github/workflows/leak-fixer.lock.yml +++ b/.github/workflows/leak-fixer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"8ffde7d60b6ed1b713a0adb98cfa70e4ae3539a006f1c2ba01696e3c475cb965","body_hash":"9ebec08537d884e715d50cda81f3e6712858416328df8524d092a0f94eb40746","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"8ffde7d60b6ed1b713a0adb98cfa70e4ae3539a006f1c2ba01696e3c475cb965","body_hash":"d39af3e842341663a7441770ce9aee19f4d4090ae38fff20860e9cc6e0bea7c6","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/leak-fixer.md b/.github/workflows/leak-fixer.md index 44a0d3136c52..6cf4339288b5 100644 --- a/.github/workflows/leak-fixer.md +++ b/.github/workflows/leak-fixer.md @@ -209,8 +209,8 @@ writes are the safe-outputs (`create-pull-request`, `push-to-pull-request-branch `[leak-scan]` issue (AI-attributed comment linking the merged fix) and record `skipped: already fixed on main`. If instead your freshly-authored regression test merely passes on the *unpatched* source (Step 6) with **no** merged fix PR, that is NOT proof the - leak is gone — do NOT close it; record `skipped: test green on unpatched main (issue left - open)` and move on. + leak is gone — do NOT close it; record `skipped: test green on unpatched main (no PR, issue + left open)` and move on. 3. **Managed scope for product fixes.** Any *product* change (Track A / a Track C code fix) must live in managed cross-platform code (`src/Controls/src`, `src/Core/src`, `src/Essentials/src`). If a `[leak-scan]` leak can only be reproduced with a platform handler / native peer, it is out From a9231b64a97377a820d78fae11d11d8ecbeb530f Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 16:56:26 +0200 Subject: [PATCH 09/20] Scope [leak-fix] de-dup/close searches to label:agentic-workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copilot re-review (provenance): the fixer de-dup/close gates (a/b/c/d) and the hunter fixed-on-main set searched by the '[leak-fix]' title substring only, so a manually-created PR titled [leak-fix] (not workflow-owned) could false-match — blocking a real leak, inflating the attempt cap, or (worst) closing a [leak-scan] issue. Scope all six [leak-fix] PR searches to label:agentic-workflows (the label this workflow's create-pull-request output applies), so only workflow-owned fixes count as provenance. [leak-scan] issue searches are unchanged. Empirically verified: 'gh pr list --search "[leak-fix]" in:title label:agentic-workflows' returns only the 3 genuine workflow PRs, excluding non-workflow [leak-fix]-titled PRs (e.g. #36252/#35417/#22673) that the unfiltered search matched. Also (consistency, from the same review): - Step 3 intro parenthetical no longer says a green-on-unpatched-main test means 'already fixed' — aligned with Step 6 (green => no PR, but not proof, no auto-close; closure only via a merged fix PR / gate (d)). - Added provenance notes to the gate (d) and hunter fixed-on-main comments. Prompt-only + description text: gh aw v0.80.9 recompile 0 errors/0 warnings; fixer lock changes only WORKFLOW_DESCRIPTION text + body_hash (no permission/tool drift), hunter lock body_hash-only, actions-lock.json unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 767d2d21-494b-41fb-8e8b-4410f9cc1b4b --- .github/workflows/daily-leak-hunter.lock.yml | 2 +- .github/workflows/daily-leak-hunter.md | 6 ++++-- .github/workflows/leak-fixer.lock.yml | 9 +++++---- .github/workflows/leak-fixer.md | 18 ++++++++++-------- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/.github/workflows/daily-leak-hunter.lock.yml b/.github/workflows/daily-leak-hunter.lock.yml index a5d3d03b3ee7..45076fae6fe1 100644 --- a/.github/workflows/daily-leak-hunter.lock.yml +++ b/.github/workflows/daily-leak-hunter.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"745be3f30003b219d6dd9e5e3c43ff0c843c6c60231bff9d7ea0ecb4ed2d668b","body_hash":"4b3e261bc72f7594f02ee47620e70e5365cc709da277b9d4782d986cda877c25","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"745be3f30003b219d6dd9e5e3c43ff0c843c6c60231bff9d7ea0ecb4ed2d668b","body_hash":"2914430556af4e48a38fc33e89ca32f4fe21aeb6ee24d5652742e701c351414b","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/daily-leak-hunter.md b/.github/workflows/daily-leak-hunter.md index 27356b378857..c4f0f15cdf51 100644 --- a/.github/workflows/daily-leak-hunter.md +++ b/.github/workflows/daily-leak-hunter.md @@ -188,13 +188,15 @@ echo "already-filed rooting APIs:"; cat /tmp/gh-aw/agent/already-filed-apis.txt # already-MERGED fix has NOT shipped yet — so a leak that is fixed on `main` STILL reproduces # (goes red) and would be re-filed every run forever. De-dup is the ONLY guard. Build the # "fixed-on-main" rooting-Type.Member set from **MERGED `[leak-fix]` PR titles ONLY** — a merged -# fix PR is durable, workflow-owned provenance that a fix ACTUALLY LANDED on `main`. Do NOT trust +# fix PR is durable, workflow-owned provenance that a fix ACTUALLY LANDED on `main`. The query is +# scoped to `label:agentic-workflows`, so ONLY this workflow's own merged fixes count (a manually +# created `[leak-fix]`-titled PR is ignored). Do NOT trust # an issue's close *reason*: a `[leak-scan]` issue closed as COMPLETED records only that SOMEONE # closed it, not that a fix merged (a maintainer can close a still-reproducing issue as # completed), so treating "closed as completed" as fixed would permanently suppress a still-valid # leak. Worst case here (a human-fixed leak with no [leak-fix] PR) is a single bounded re-file # that the OPEN-issue de-dup above then catches — far safer than permanent data loss. -gh pr list --repo "$GITHUB_REPOSITORY" --state merged --search '"[leak-fix]" in:title' \ +gh pr list --repo "$GITHUB_REPOSITORY" --state merged --search '"[leak-fix]" in:title label:agentic-workflows' \ --limit 300 --json title -q '.[].title' \ | grep -E '^\[leak-fix\] ' | sed -E 's/^\[leak-fix\] *(Fix +)?//' \ | awk '{ diff --git a/.github/workflows/leak-fixer.lock.yml b/.github/workflows/leak-fixer.lock.yml index 1efdfdd76259..103df5787f56 100644 --- a/.github/workflows/leak-fixer.lock.yml +++ b/.github/workflows/leak-fixer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"8ffde7d60b6ed1b713a0adb98cfa70e4ae3539a006f1c2ba01696e3c475cb965","body_hash":"d39af3e842341663a7441770ce9aee19f4d4090ae38fff20860e9cc6e0bea7c6","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"a8d7de4c58f47b2eadf6bb68e4ee3471669593b2df9a8a19483cf44108ba3ee5","body_hash":"c0d381623dfaa05b7802c7ab44df6938f5856d4d39fc61ea059c99c5ae9223d0","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -31,8 +31,9 @@ # 2. Writes a focused **regression unit test** in `Controls.Core.UnitTests` (managed, # library-TFM, no workload/emulator) that asserts the transient is collected. # 3. Builds + runs that test and confirms it **FAILS** on the unpatched source — proving -# the test actually catches the leak. (If it already passes, the leak is already fixed -# on this branch: the agent opens NO PR and stops.) +# the test actually catches the leak. (If it already passes on unpatched source, the agent +# opens NO PR — but a single green run is **not** proof the leak is fixed, so it does **not** +# close the scan issue; closure happens only via a merged fix PR — see Step 6 / gate (d).) # 4. Implements the product fix (weak subscription / `WeakEventManager` / teardown on the # missing path), rebuilds, and confirms the **same test now PASSES** with no regression # in its neighbours. @@ -1437,7 +1438,7 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: WORKFLOW_NAME: "Memory Leak Fixer" - WORKFLOW_DESCRIPTION: "Turns one open `[leak-scan]` issue (filed by the Daily Memory Leak Hunter) into a\nvalidated, draft `[leak-fix]` pull request. For the selected issue the agent:\n\n1. Locates the leaking product code in the CURRENT source tree (the leak was found in\n the shipped NuGet, so it may already be fixed on this branch — that is handled).\n2. Writes a focused **regression unit test** in `Controls.Core.UnitTests` (managed,\n library-TFM, no workload/emulator) that asserts the transient is collected.\n3. Builds + runs that test and confirms it **FAILS** on the unpatched source — proving\n the test actually catches the leak. (If it already passes, the leak is already fixed\n on this branch: the agent opens NO PR and stops.)\n4. Implements the product fix (weak subscription / `WeakEventManager` / teardown on the\n missing path), rebuilds, and confirms the **same test now PASSES** with no regression\n in its neighbours.\n5. Opens ONE draft `[leak-fix]` PR (`Fixes #`) carrying the test + fix, with the\n before/after evidence in the body.\n\nDetection (the hunter) uses the shipped package and needs no source build; the FIXER\nmust build MAUI **from source** to validate a product change, so it restores from the\npublic dnceng Azure DevOps feeds (`dev.azure.com`) — hence the wider network allowlist.\nScope is the managed `[leak-scan]` issues only; native/handler leaks are out of scope." + WORKFLOW_DESCRIPTION: "Turns one open `[leak-scan]` issue (filed by the Daily Memory Leak Hunter) into a\nvalidated, draft `[leak-fix]` pull request. For the selected issue the agent:\n\n1. Locates the leaking product code in the CURRENT source tree (the leak was found in\n the shipped NuGet, so it may already be fixed on this branch — that is handled).\n2. Writes a focused **regression unit test** in `Controls.Core.UnitTests` (managed,\n library-TFM, no workload/emulator) that asserts the transient is collected.\n3. Builds + runs that test and confirms it **FAILS** on the unpatched source — proving\n the test actually catches the leak. (If it already passes on unpatched source, the agent\n opens NO PR — but a single green run is **not** proof the leak is fixed, so it does **not**\n close the scan issue; closure happens only via a merged fix PR — see Step 6 / gate (d).)\n4. Implements the product fix (weak subscription / `WeakEventManager` / teardown on the\n missing path), rebuilds, and confirms the **same test now PASSES** with no regression\n in its neighbours.\n5. Opens ONE draft `[leak-fix]` PR (`Fixes #`) carrying the test + fix, with the\n before/after evidence in the body.\n\nDetection (the hunter) uses the shipped package and needs no source build; the FIXER\nmust build MAUI **from source** to validate a product change, so it restores from the\npublic dnceng Azure DevOps feeds (`dev.azure.com`) — hence the wider network allowlist.\nScope is the managed `[leak-scan]` issues only; native/handler leaks are out of scope." HAS_PATCH: ${{ needs.agent.outputs.has_patch }} with: script: | diff --git a/.github/workflows/leak-fixer.md b/.github/workflows/leak-fixer.md index 6cf4339288b5..9b8b369d607a 100644 --- a/.github/workflows/leak-fixer.md +++ b/.github/workflows/leak-fixer.md @@ -9,8 +9,9 @@ description: | 2. Writes a focused **regression unit test** in `Controls.Core.UnitTests` (managed, library-TFM, no workload/emulator) that asserts the transient is collected. 3. Builds + runs that test and confirms it **FAILS** on the unpatched source — proving - the test actually catches the leak. (If it already passes, the leak is already fixed - on this branch: the agent opens NO PR and stops.) + the test actually catches the leak. (If it already passes on unpatched source, the agent + opens NO PR — but a single green run is **not** proof the leak is fixed, so it does **not** + close the scan issue; closure happens only via a merged fix PR — see Step 6 / gate (d).) 4. Implements the product fix (weak subscription / `WeakEventManager` / teardown on the missing path), rebuilds, and confirms the **same test now PASSES** with no regression in its neighbours. @@ -270,7 +271,7 @@ request). Otherwise, BEFORE looking for new work, see whether one of this workfl # same-repo (dotnet/maui-hosted) PRs here; skip fork-hosted [leak-fix] PRs entirely. OWNER="${GITHUB_REPOSITORY%%/*}" gh pr list --repo "$GITHUB_REPOSITORY" --state open \ - --search '"[leak-fix]" in:title' \ + --search '"[leak-fix]" in:title label:agentic-workflows' \ --json number,title,headRefName,headRepositoryOwner,url,updatedAt \ | jq --arg owner "$OWNER" '[.[] | select((.headRepositoryOwner.login // "") == $owner)] | sort_by(.number)' \ > /tmp/gh-aw/agent/my-open-prs.json @@ -409,20 +410,20 @@ echo "target rooting API: $API" # a LITERAL "Type.Member" — otherwise "BackButtonBehavior.Command" would also match "BackButtonBehaviorXCommand". API_RE=$(printf '%s' "$API" | sed -E 's/[][(){}.^$*+?|\\]/\\&/g') # (a) Open [leak-fix] PR already addressing THIS issue number? -gh pr list --repo "$GITHUB_REPOSITORY" --state open --search '"[leak-fix]" in:title' --limit 200 \ +gh pr list --repo "$GITHUB_REPOSITORY" --state open --search '"[leak-fix]" in:title label:agentic-workflows' --limit 200 \ --json number,title,body \ | jq --arg n "$N" '[.[] | select((.body // "") | test("(Fixes|Refs)[^0-9]*#"+$n+"\\b"))]' \ > /tmp/gh-aw/agent/open-fix-prs.json jq 'length' /tmp/gh-aw/agent/open-fix-prs.json # (b) Open [leak-fix] PR already fixing the SAME rooting Type.Member (any issue number)? # [leak-fix] PR titles are "Fix . memory leak". -gh pr list --repo "$GITHUB_REPOSITORY" --state open --search '"[leak-fix]" in:title' --limit 200 \ +gh pr list --repo "$GITHUB_REPOSITORY" --state open --search '"[leak-fix]" in:title label:agentic-workflows' --limit 200 \ --json number,title \ | jq --arg api "$API_RE" '[.[] | select(.title | test("Fix +"+$api+"([. ]|$)"))]' \ > /tmp/gh-aw/agent/same-api-prs.json jq -r '.[] | "same-API open fix PR: #\(.number) \(.title)"' /tmp/gh-aw/agent/same-api-prs.json # (c) Closed-unmerged attempts for this issue (attempt cap = 3). -gh pr list --repo "$GITHUB_REPOSITORY" --state closed --search '"[leak-fix]" in:title' --limit 200 \ +gh pr list --repo "$GITHUB_REPOSITORY" --state closed --search '"[leak-fix]" in:title label:agentic-workflows' --limit 200 \ --json number,title,body,mergedAt \ | jq --arg n "$N" '[.[] | select(((.body // "") | test("(Fixes|Refs)[^0-9]*#"+$n+"\\b")) and (.mergedAt == null))]' \ > /tmp/gh-aw/agent/closed-fix-prs.json @@ -431,8 +432,9 @@ jq 'length' /tmp/gh-aw/agent/closed-fix-prs.json # Merged fixes often reference an UPSTREAM issue (e.g. "Fixes #35775") instead of this # [leak-scan] number, so GitHub never auto-closed this scan issue — leaving it to be # re-picked and rebuilt from source every run. Detect the merged fix by BOTH the issue-ref -# AND the rooting Type.Member so we can close this issue instead of rebuilding. -gh pr list --repo "$GITHUB_REPOSITORY" --state merged --search '"[leak-fix]" in:title' --limit 200 \ +# AND the rooting Type.Member so we can close this issue instead of rebuilding. The search is +# scoped to label:agentic-workflows so ONLY workflow-owned merged fixes can close a scan issue. +gh pr list --repo "$GITHUB_REPOSITORY" --state merged --search '"[leak-fix]" in:title label:agentic-workflows' --limit 200 \ --json number,title,body \ | jq --arg n "$N" --arg api "$API_RE" \ '[.[] | select(((.body // "") | test("(Fixes|Refs)[^0-9]*#"+$n+"\\b")) or (.title | test("Fix +"+$api+"([. ]|$)")))]' \ From 5e47e5a864ea3cd730ef3b08dda4ff8e4cd55a61 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 17:03:23 +0200 Subject: [PATCH 10/20] Grant hunter pull-requests: read; add --limit to fixer Track C list Copilot re-review: - daily-leak-hunter now runs a 'gh pr list --state merged' call (the new fixed-on-main de-dup guard), but its permissions were only contents:read + issues:read. Without pull-requests:read the agent job's GITHUB_TOKEN can't list PRs and the guard would fail. Add pull-requests: read (the fixer already declares it). Compiled lock's agent job now carries pull-requests: read. - leak-fixer Track C own-open-PR list (Step R1) had no --limit, so gh pr list's default 30-item page could miss older open [leak-fix] PRs with pending CHANGES_REQUESTED and skip review-feedback handling. Add --limit 200 to match the other four de-dup gates. gh aw v0.80.9 recompile: 0 errors/0 warnings. Hunter lock gains only pull-requests: read on the agent job (frontmatter change); fixer lock body_hash only (prompt change); actions-lock.json unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 767d2d21-494b-41fb-8e8b-4410f9cc1b4b --- .github/workflows/daily-leak-hunter.lock.yml | 3 ++- .github/workflows/daily-leak-hunter.md | 1 + .github/workflows/leak-fixer.lock.yml | 2 +- .github/workflows/leak-fixer.md | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/daily-leak-hunter.lock.yml b/.github/workflows/daily-leak-hunter.lock.yml index 45076fae6fe1..a9e15f420030 100644 --- a/.github/workflows/daily-leak-hunter.lock.yml +++ b/.github/workflows/daily-leak-hunter.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"745be3f30003b219d6dd9e5e3c43ff0c843c6c60231bff9d7ea0ecb4ed2d668b","body_hash":"2914430556af4e48a38fc33e89ca32f4fe21aeb6ee24d5652742e701c351414b","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"900c7bbaa56bdfbbebd8069d248c510df4593b61086be8c71f3c62d396477d96","body_hash":"2914430556af4e48a38fc33e89ca32f4fe21aeb6ee24d5652742e701c351414b","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -367,6 +367,7 @@ jobs: permissions: contents: read issues: read + pull-requests: read concurrency: group: "gh-aw-copilot-${{ github.workflow }}" queue: max diff --git a/.github/workflows/daily-leak-hunter.md b/.github/workflows/daily-leak-hunter.md index c4f0f15cdf51..ce4f0654ccda 100644 --- a/.github/workflows/daily-leak-hunter.md +++ b/.github/workflows/daily-leak-hunter.md @@ -42,6 +42,7 @@ if: | permissions: contents: read issues: read + pull-requests: read engine: id: copilot diff --git a/.github/workflows/leak-fixer.lock.yml b/.github/workflows/leak-fixer.lock.yml index 103df5787f56..2da7fdd11eae 100644 --- a/.github/workflows/leak-fixer.lock.yml +++ b/.github/workflows/leak-fixer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"a8d7de4c58f47b2eadf6bb68e4ee3471669593b2df9a8a19483cf44108ba3ee5","body_hash":"c0d381623dfaa05b7802c7ab44df6938f5856d4d39fc61ea059c99c5ae9223d0","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"a8d7de4c58f47b2eadf6bb68e4ee3471669593b2df9a8a19483cf44108ba3ee5","body_hash":"88efde5002c55f4d5de170103430977868e58ea8bb32d15cfa58e63ca8fd6492","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/leak-fixer.md b/.github/workflows/leak-fixer.md index 9b8b369d607a..54f66ac80cc0 100644 --- a/.github/workflows/leak-fixer.md +++ b/.github/workflows/leak-fixer.md @@ -271,7 +271,7 @@ request). Otherwise, BEFORE looking for new work, see whether one of this workfl # same-repo (dotnet/maui-hosted) PRs here; skip fork-hosted [leak-fix] PRs entirely. OWNER="${GITHUB_REPOSITORY%%/*}" gh pr list --repo "$GITHUB_REPOSITORY" --state open \ - --search '"[leak-fix]" in:title label:agentic-workflows' \ + --search '"[leak-fix]" in:title label:agentic-workflows' --limit 200 \ --json number,title,headRefName,headRepositoryOwner,url,updatedAt \ | jq --arg owner "$OWNER" '[.[] | select((.headRepositoryOwner.login // "") == $owner)] | sort_by(.number)' \ > /tmp/gh-aw/agent/my-open-prs.json From 9bc607b0bb4cade7c0b6ec20212795a317b224ac Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 18:41:20 +0200 Subject: [PATCH 11/20] Fix cross-repo issue-ref false match in leak-fixer close gates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gates (a/c/d) matched a referenced issue with `(Fixes|Refs)[^0-9]*#`. Because `[^0-9]*` spans `: dotnet/runtime#`, a cross-repo reference such as `Refs: dotnet/runtime#5000` falsely satisfied a search for maui issue #5000. The destructive gate (d) would then close a live `[leak-scan]` issue against an unrelated upstream reference — silent data loss. Require the `#` to be either a BARE same-repo reference or an explicit `/#` qualifier (new `$REPO_RE` jq arg), rejecting arbitrary `other/repo#`. Empirically verified: `Refs: dotnet/runtime#5000` and `Fixes dotnet/runtime#5000` no longer match; `Fixes #5000`, `Refs: #5000`, and `Fixes dotnet/maui#5000` still match. Recompiled locks (body_hash only; gh-aw v0.80.9, actions-lock.json unchanged). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 767d2d21-494b-41fb-8e8b-4410f9cc1b4b --- .github/workflows/leak-fixer.lock.yml | 2 +- .github/workflows/leak-fixer.md | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/leak-fixer.lock.yml b/.github/workflows/leak-fixer.lock.yml index 2da7fdd11eae..68b7b4208f7a 100644 --- a/.github/workflows/leak-fixer.lock.yml +++ b/.github/workflows/leak-fixer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"a8d7de4c58f47b2eadf6bb68e4ee3471669593b2df9a8a19483cf44108ba3ee5","body_hash":"88efde5002c55f4d5de170103430977868e58ea8bb32d15cfa58e63ca8fd6492","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"a8d7de4c58f47b2eadf6bb68e4ee3471669593b2df9a8a19483cf44108ba3ee5","body_hash":"b3f86f6d7837a4be8374d6c0af013715060191cb32306dafb1f6d0e59adea6ec","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/leak-fixer.md b/.github/workflows/leak-fixer.md index 54f66ac80cc0..816b83874cf0 100644 --- a/.github/workflows/leak-fixer.md +++ b/.github/workflows/leak-fixer.md @@ -409,10 +409,16 @@ echo "target rooting API: $API" # Escape regex metacharacters (notably the '.' in Type.Member) so the jq test() calls below match # a LITERAL "Type.Member" — otherwise "BackButtonBehavior.Command" would also match "BackButtonBehaviorXCommand". API_RE=$(printf '%s' "$API" | sed -E 's/[][(){}.^$*+?|\\]/\\&/g') +# Escape the owner/repo so it embeds literally in the jq test() calls below. The issue-ref match +# requires "#" to be a BARE same-repo reference OR an explicit "/#" qualifier — +# NEVER an arbitrary cross-repo "other/repo#". Otherwise an unrelated upstream ref such as +# "Refs: dotnet/runtime#5000" would satisfy a search for maui #5000 and let a foreign reference +# drive the destructive close path in gate (d) against a live [leak-scan] issue. +REPO_RE=$(printf '%s' "$GITHUB_REPOSITORY" | sed -E 's/[][(){}.^$*+?|\\]/\\&/g') # (a) Open [leak-fix] PR already addressing THIS issue number? gh pr list --repo "$GITHUB_REPOSITORY" --state open --search '"[leak-fix]" in:title label:agentic-workflows' --limit 200 \ --json number,title,body \ - | jq --arg n "$N" '[.[] | select((.body // "") | test("(Fixes|Refs)[^0-9]*#"+$n+"\\b"))]' \ + | jq --arg n "$N" --arg repo "$REPO_RE" '[.[] | select((.body // "") | test("(Fixes|Refs)[^0-9]*("+$repo+"#|[^0-9A-Za-z_/]#)"+$n+"\\b"))]' \ > /tmp/gh-aw/agent/open-fix-prs.json jq 'length' /tmp/gh-aw/agent/open-fix-prs.json # (b) Open [leak-fix] PR already fixing the SAME rooting Type.Member (any issue number)? @@ -425,7 +431,7 @@ jq -r '.[] | "same-API open fix PR: #\(.number) \(.title)"' /tmp/gh-aw/agent/sam # (c) Closed-unmerged attempts for this issue (attempt cap = 3). gh pr list --repo "$GITHUB_REPOSITORY" --state closed --search '"[leak-fix]" in:title label:agentic-workflows' --limit 200 \ --json number,title,body,mergedAt \ - | jq --arg n "$N" '[.[] | select(((.body // "") | test("(Fixes|Refs)[^0-9]*#"+$n+"\\b")) and (.mergedAt == null))]' \ + | jq --arg n "$N" --arg repo "$REPO_RE" '[.[] | select(((.body // "") | test("(Fixes|Refs)[^0-9]*("+$repo+"#|[^0-9A-Za-z_/]#)"+$n+"\\b")) and (.mergedAt == null))]' \ > /tmp/gh-aw/agent/closed-fix-prs.json jq 'length' /tmp/gh-aw/agent/closed-fix-prs.json # (d) MERGED [leak-fix] PR already fixing this issue number OR the SAME rooting Type.Member? @@ -436,8 +442,8 @@ jq 'length' /tmp/gh-aw/agent/closed-fix-prs.json # scoped to label:agentic-workflows so ONLY workflow-owned merged fixes can close a scan issue. gh pr list --repo "$GITHUB_REPOSITORY" --state merged --search '"[leak-fix]" in:title label:agentic-workflows' --limit 200 \ --json number,title,body \ - | jq --arg n "$N" --arg api "$API_RE" \ - '[.[] | select(((.body // "") | test("(Fixes|Refs)[^0-9]*#"+$n+"\\b")) or (.title | test("Fix +"+$api+"([. ]|$)")))]' \ + | jq --arg n "$N" --arg api "$API_RE" --arg repo "$REPO_RE" \ + '[.[] | select(((.body // "") | test("(Fixes|Refs)[^0-9]*("+$repo+"#|[^0-9A-Za-z_/]#)"+$n+"\\b")) or (.title | test("Fix +"+$api+"([. ]|$)")))]' \ > /tmp/gh-aw/agent/merged-fix-prs.json jq -r '.[] | "merged fix for this leak: #\(.number) \(.title)"' /tmp/gh-aw/agent/merged-fix-prs.json ``` From a5fbf623690e321aa1cabb74fb17a94d52d68589 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 19:43:04 +0200 Subject: [PATCH 12/20] Raise merged [leak-fix] provenance cap to search-API ceiling + warn on truncation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses MauiBot [major] finding: the merged [leak-fix] provenance queries were capped (--limit 300 in daily-leak-hunter, --limit 200 in leak-fixer gate d). If more than that many merged leak-fix PRs exist, older durable-provenance entries silently drop out — the hunter could re-file an already-fixed leak, and the fixer could fail to close an orphaned [leak-scan] issue. Both queries now use --limit 1000 (GitHub's search-API hard ceiling; pagination cannot exceed it) and emit a loud WARNING if the raw result count reaches that ceiling, since true completeness beyond 1000 requires date-windowed enumeration. Both truncation cases remain safe-direction: hunter truncation = a single bounded re-file (open-issue de-dup then catches it); fixer gate (d) truncation = under-close (it only closes on a positive match, so a miss never wrongly closes). The data-loss fix regex (bare/current-repo issue-ref match, cross-repo excluded) is preserved verbatim; only the fetch cap, the raw-capture to a temp file, and the truncation guard changed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 767d2d21-494b-41fb-8e8b-4410f9cc1b4b --- .github/workflows/daily-leak-hunter.lock.yml | 2 +- .github/workflows/daily-leak-hunter.md | 11 +++++++++-- .github/workflows/leak-fixer.lock.yml | 2 +- .github/workflows/leak-fixer.md | 16 ++++++++++++---- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/.github/workflows/daily-leak-hunter.lock.yml b/.github/workflows/daily-leak-hunter.lock.yml index a9e15f420030..4cb3c34309ff 100644 --- a/.github/workflows/daily-leak-hunter.lock.yml +++ b/.github/workflows/daily-leak-hunter.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"900c7bbaa56bdfbbebd8069d248c510df4593b61086be8c71f3c62d396477d96","body_hash":"2914430556af4e48a38fc33e89ca32f4fe21aeb6ee24d5652742e701c351414b","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"900c7bbaa56bdfbbebd8069d248c510df4593b61086be8c71f3c62d396477d96","body_hash":"bf9da5913dc0f77df4aa54c63360f494af6be6fc4f3063ead66e1e29161badd6","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/daily-leak-hunter.md b/.github/workflows/daily-leak-hunter.md index ce4f0654ccda..7729a701161e 100644 --- a/.github/workflows/daily-leak-hunter.md +++ b/.github/workflows/daily-leak-hunter.md @@ -197,9 +197,16 @@ echo "already-filed rooting APIs:"; cat /tmp/gh-aw/agent/already-filed-apis.txt # completed), so treating "closed as completed" as fixed would permanently suppress a still-valid # leak. Worst case here (a human-fixed leak with no [leak-fix] PR) is a single bounded re-file # that the OPEN-issue de-dup above then catches — far safer than permanent data loss. +# --limit 1000 = the GitHub SEARCH API's hard result ceiling (pagination cannot exceed it). If the +# merged [leak-fix] set ever hits 1000, older fixes are TRUNCATED: because Step 5 tests the SHIPPED +# package (where a merged-but-unshipped fix still reproduces), a dropped-out fix would be re-filed +# once (then the OPEN-issue de-dup catches it), so warn loudly if we ever reach the ceiling. gh pr list --repo "$GITHUB_REPOSITORY" --state merged --search '"[leak-fix]" in:title label:agentic-workflows' \ - --limit 300 --json title -q '.[].title' \ - | grep -E '^\[leak-fix\] ' | sed -E 's/^\[leak-fix\] *(Fix +)?//' \ + --limit 1000 --json title -q '.[].title' > /tmp/gh-aw/agent/merged-leakfix-titles.txt +if [ "$(grep -c '' /tmp/gh-aw/agent/merged-leakfix-titles.txt)" -ge 1000 ]; then + echo "WARNING: fixed-on-main provenance hit the 1000 search-API ceiling; older merged [leak-fix] fixes may be TRUNCATED and their leaks could be re-filed once — switch to date-windowed enumeration." +fi +grep -E '^\[leak-fix\] ' /tmp/gh-aw/agent/merged-leakfix-titles.txt | sed -E 's/^\[leak-fix\] *(Fix +)?//' \ | awk '{ if (match($0, /[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)+/)) { chain=substr($0,RSTART,RLENGTH); n=split(chain,seg,"."); print seg[n-1]"."seg[n] diff --git a/.github/workflows/leak-fixer.lock.yml b/.github/workflows/leak-fixer.lock.yml index 68b7b4208f7a..b0c49042a1ae 100644 --- a/.github/workflows/leak-fixer.lock.yml +++ b/.github/workflows/leak-fixer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"a8d7de4c58f47b2eadf6bb68e4ee3471669593b2df9a8a19483cf44108ba3ee5","body_hash":"b3f86f6d7837a4be8374d6c0af013715060191cb32306dafb1f6d0e59adea6ec","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"a8d7de4c58f47b2eadf6bb68e4ee3471669593b2df9a8a19483cf44108ba3ee5","body_hash":"32d85d798e42248493baf6e5a47b799ea72476a2440ad3e6f0f2fe65579104ec","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/leak-fixer.md b/.github/workflows/leak-fixer.md index 816b83874cf0..daefa4a5dd88 100644 --- a/.github/workflows/leak-fixer.md +++ b/.github/workflows/leak-fixer.md @@ -440,10 +440,18 @@ jq 'length' /tmp/gh-aw/agent/closed-fix-prs.json # re-picked and rebuilt from source every run. Detect the merged fix by BOTH the issue-ref # AND the rooting Type.Member so we can close this issue instead of rebuilding. The search is # scoped to label:agentic-workflows so ONLY workflow-owned merged fixes can close a scan issue. -gh pr list --repo "$GITHUB_REPOSITORY" --state merged --search '"[leak-fix]" in:title label:agentic-workflows' --limit 200 \ - --json number,title,body \ - | jq --arg n "$N" --arg api "$API_RE" --arg repo "$REPO_RE" \ - '[.[] | select(((.body // "") | test("(Fixes|Refs)[^0-9]*("+$repo+"#|[^0-9A-Za-z_/]#)"+$n+"\\b")) or (.title | test("Fix +"+$api+"([. ]|$)")))]' \ +# --limit 1000 = the GitHub SEARCH API's hard result ceiling (pagination cannot exceed it). If +# the merged [leak-fix] set ever hits 1000, older fixes are TRUNCATED and this gate could miss a +# valid merged fix. Gate (d) stays fail-safe (it only CLOSES on a positive match, so a miss +# under-closes rather than wrongly closing), but close-coverage would be incomplete, so warn. +gh pr list --repo "$GITHUB_REPOSITORY" --state merged --search '"[leak-fix]" in:title label:agentic-workflows' --limit 1000 \ + --json number,title,body > /tmp/gh-aw/agent/merged-leakfix-all.json +if [ "$(jq 'length' /tmp/gh-aw/agent/merged-leakfix-all.json)" -ge 1000 ]; then + echo "WARNING: merged [leak-fix] provenance hit the 1000 search-API ceiling; older merged fixes may be TRUNCATED. Gate (d) stays fail-safe (under-closes) but close-coverage is incomplete — switch to date-windowed enumeration." +fi +jq --arg n "$N" --arg api "$API_RE" --arg repo "$REPO_RE" \ + '[.[] | select(((.body // "") | test("(Fixes|Refs)[^0-9]*("+$repo+"#|[^0-9A-Za-z_/]#)"+$n+"\\b")) or (.title | test("Fix +"+$api+"([. ]|$)")))]' \ + /tmp/gh-aw/agent/merged-leakfix-all.json \ > /tmp/gh-aw/agent/merged-fix-prs.json jq -r '.[] | "merged fix for this leak: #\(.number) \(.title)"' /tmp/gh-aw/agent/merged-fix-prs.json ``` From 155707cb06a225917404a9e6eddd39d3eb385e0b Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 20:44:47 +0200 Subject: [PATCH 13/20] Tighten leak-fixer issue-ref regex + fix two prompt-clarity nits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses the adversarial re-review of a5fbf623690. [correctness] The three [leak-fix] issue-reference gates (a/c/d) used '(Fixes|Refs)[^0-9]*(...#)N', where [^0-9]* greedily spans arbitrary prose and newlines. So a body like 'Fixes a bug.\nAlso see #123' matched as a closing reference to #123 — and in gate (d) (the destructive close path) that could close an unrelated live [leak-scan] issue. Replaced the separator with a strict '(?i)\b(Fixes|Refs)\b:?[ \t]*(...)': the keyword must be a whole word, followed only by an optional colon and horizontal whitespace, before the same repo-scoped '#N' matcher. Adding \b also blocks the '(?i)' false positive on words like 'prefixes #N'. Verified 12/12 on a case matrix (bare/current-repo match; cross-repo, prose-spanning, prefix-word, and #N-boundary cases all excluded). [clarity] Step-pointer fix: the description said closure happens via 'Step 6 / gate (d)', but gate (d) is in Step 3 (Step 6 is the red-test step) — a wrong pointer compiled into the prompt could send an agent to the wrong gate. Now 'Step 3 / gate (d)', matching every other reference. [clarity] The dry-run gate note was formatted as a blockquote directly after the example close-comment blockquote, so an agent could copy it into the emitted comment body. Moved it out of the quote and labeled it '(control text — NOT part of the close comment above)'. The merged-provenance close key intentionally stays at Type.Member granularity: the hunter de-dups scan issues by rooting Type.Member (Step 2), treating same-member/different-retention-path as the same leak (its #1 guarded failure mode), so there is only ever one open scan issue per Type.Member and the close key matches issue granularity. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 767d2d21-494b-41fb-8e8b-4410f9cc1b4b --- .github/workflows/leak-fixer.lock.yml | 6 +++--- .github/workflows/leak-fixer.md | 13 +++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/leak-fixer.lock.yml b/.github/workflows/leak-fixer.lock.yml index b0c49042a1ae..c55aabece459 100644 --- a/.github/workflows/leak-fixer.lock.yml +++ b/.github/workflows/leak-fixer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"a8d7de4c58f47b2eadf6bb68e4ee3471669593b2df9a8a19483cf44108ba3ee5","body_hash":"32d85d798e42248493baf6e5a47b799ea72476a2440ad3e6f0f2fe65579104ec","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"10dee7e2008b3eca441e3eae65bb9fa5231d457398852a8264def25bf28d8e5e","body_hash":"98b6c1d034e157b24ff3b1ae1de3a893943dc79e9ea5846ed5d916c3e122cb95","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -33,7 +33,7 @@ # 3. Builds + runs that test and confirms it **FAILS** on the unpatched source — proving # the test actually catches the leak. (If it already passes on unpatched source, the agent # opens NO PR — but a single green run is **not** proof the leak is fixed, so it does **not** -# close the scan issue; closure happens only via a merged fix PR — see Step 6 / gate (d).) +# close the scan issue; closure happens only via a merged fix PR — see Step 3 / gate (d).) # 4. Implements the product fix (weak subscription / `WeakEventManager` / teardown on the # missing path), rebuilds, and confirms the **same test now PASSES** with no regression # in its neighbours. @@ -1438,7 +1438,7 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: WORKFLOW_NAME: "Memory Leak Fixer" - WORKFLOW_DESCRIPTION: "Turns one open `[leak-scan]` issue (filed by the Daily Memory Leak Hunter) into a\nvalidated, draft `[leak-fix]` pull request. For the selected issue the agent:\n\n1. Locates the leaking product code in the CURRENT source tree (the leak was found in\n the shipped NuGet, so it may already be fixed on this branch — that is handled).\n2. Writes a focused **regression unit test** in `Controls.Core.UnitTests` (managed,\n library-TFM, no workload/emulator) that asserts the transient is collected.\n3. Builds + runs that test and confirms it **FAILS** on the unpatched source — proving\n the test actually catches the leak. (If it already passes on unpatched source, the agent\n opens NO PR — but a single green run is **not** proof the leak is fixed, so it does **not**\n close the scan issue; closure happens only via a merged fix PR — see Step 6 / gate (d).)\n4. Implements the product fix (weak subscription / `WeakEventManager` / teardown on the\n missing path), rebuilds, and confirms the **same test now PASSES** with no regression\n in its neighbours.\n5. Opens ONE draft `[leak-fix]` PR (`Fixes #`) carrying the test + fix, with the\n before/after evidence in the body.\n\nDetection (the hunter) uses the shipped package and needs no source build; the FIXER\nmust build MAUI **from source** to validate a product change, so it restores from the\npublic dnceng Azure DevOps feeds (`dev.azure.com`) — hence the wider network allowlist.\nScope is the managed `[leak-scan]` issues only; native/handler leaks are out of scope." + WORKFLOW_DESCRIPTION: "Turns one open `[leak-scan]` issue (filed by the Daily Memory Leak Hunter) into a\nvalidated, draft `[leak-fix]` pull request. For the selected issue the agent:\n\n1. Locates the leaking product code in the CURRENT source tree (the leak was found in\n the shipped NuGet, so it may already be fixed on this branch — that is handled).\n2. Writes a focused **regression unit test** in `Controls.Core.UnitTests` (managed,\n library-TFM, no workload/emulator) that asserts the transient is collected.\n3. Builds + runs that test and confirms it **FAILS** on the unpatched source — proving\n the test actually catches the leak. (If it already passes on unpatched source, the agent\n opens NO PR — but a single green run is **not** proof the leak is fixed, so it does **not**\n close the scan issue; closure happens only via a merged fix PR — see Step 3 / gate (d).)\n4. Implements the product fix (weak subscription / `WeakEventManager` / teardown on the\n missing path), rebuilds, and confirms the **same test now PASSES** with no regression\n in its neighbours.\n5. Opens ONE draft `[leak-fix]` PR (`Fixes #`) carrying the test + fix, with the\n before/after evidence in the body.\n\nDetection (the hunter) uses the shipped package and needs no source build; the FIXER\nmust build MAUI **from source** to validate a product change, so it restores from the\npublic dnceng Azure DevOps feeds (`dev.azure.com`) — hence the wider network allowlist.\nScope is the managed `[leak-scan]` issues only; native/handler leaks are out of scope." HAS_PATCH: ${{ needs.agent.outputs.has_patch }} with: script: | diff --git a/.github/workflows/leak-fixer.md b/.github/workflows/leak-fixer.md index daefa4a5dd88..4156f0432e8d 100644 --- a/.github/workflows/leak-fixer.md +++ b/.github/workflows/leak-fixer.md @@ -11,7 +11,7 @@ description: | 3. Builds + runs that test and confirms it **FAILS** on the unpatched source — proving the test actually catches the leak. (If it already passes on unpatched source, the agent opens NO PR — but a single green run is **not** proof the leak is fixed, so it does **not** - close the scan issue; closure happens only via a merged fix PR — see Step 6 / gate (d).) + close the scan issue; closure happens only via a merged fix PR — see Step 3 / gate (d).) 4. Implements the product fix (weak subscription / `WeakEventManager` / teardown on the missing path), rebuilds, and confirms the **same test now PASSES** with no regression in its neighbours. @@ -418,7 +418,7 @@ REPO_RE=$(printf '%s' "$GITHUB_REPOSITORY" | sed -E 's/[][(){}.^$*+?|\\]/\\&/g') # (a) Open [leak-fix] PR already addressing THIS issue number? gh pr list --repo "$GITHUB_REPOSITORY" --state open --search '"[leak-fix]" in:title label:agentic-workflows' --limit 200 \ --json number,title,body \ - | jq --arg n "$N" --arg repo "$REPO_RE" '[.[] | select((.body // "") | test("(Fixes|Refs)[^0-9]*("+$repo+"#|[^0-9A-Za-z_/]#)"+$n+"\\b"))]' \ + | jq --arg n "$N" --arg repo "$REPO_RE" '[.[] | select((.body // "") | test("(?i)\\b(Fixes|Refs)\\b:?[ \t]*("+$repo+"#|[^0-9A-Za-z_/]#)"+$n+"\\b"))]' \ > /tmp/gh-aw/agent/open-fix-prs.json jq 'length' /tmp/gh-aw/agent/open-fix-prs.json # (b) Open [leak-fix] PR already fixing the SAME rooting Type.Member (any issue number)? @@ -431,7 +431,7 @@ jq -r '.[] | "same-API open fix PR: #\(.number) \(.title)"' /tmp/gh-aw/agent/sam # (c) Closed-unmerged attempts for this issue (attempt cap = 3). gh pr list --repo "$GITHUB_REPOSITORY" --state closed --search '"[leak-fix]" in:title label:agentic-workflows' --limit 200 \ --json number,title,body,mergedAt \ - | jq --arg n "$N" --arg repo "$REPO_RE" '[.[] | select(((.body // "") | test("(Fixes|Refs)[^0-9]*("+$repo+"#|[^0-9A-Za-z_/]#)"+$n+"\\b")) and (.mergedAt == null))]' \ + | jq --arg n "$N" --arg repo "$REPO_RE" '[.[] | select(((.body // "") | test("(?i)\\b(Fixes|Refs)\\b:?[ \t]*("+$repo+"#|[^0-9A-Za-z_/]#)"+$n+"\\b")) and (.mergedAt == null))]' \ > /tmp/gh-aw/agent/closed-fix-prs.json jq 'length' /tmp/gh-aw/agent/closed-fix-prs.json # (d) MERGED [leak-fix] PR already fixing this issue number OR the SAME rooting Type.Member? @@ -450,7 +450,7 @@ if [ "$(jq 'length' /tmp/gh-aw/agent/merged-leakfix-all.json)" -ge 1000 ]; then echo "WARNING: merged [leak-fix] provenance hit the 1000 search-API ceiling; older merged fixes may be TRUNCATED. Gate (d) stays fail-safe (under-closes) but close-coverage is incomplete — switch to date-windowed enumeration." fi jq --arg n "$N" --arg api "$API_RE" --arg repo "$REPO_RE" \ - '[.[] | select(((.body // "") | test("(Fixes|Refs)[^0-9]*("+$repo+"#|[^0-9A-Za-z_/]#)"+$n+"\\b")) or (.title | test("Fix +"+$api+"([. ]|$)")))]' \ + '[.[] | select(((.body // "") | test("(?i)\\b(Fixes|Refs)\\b:?[ \t]*("+$repo+"#|[^0-9A-Za-z_/]#)"+$n+"\\b")) or (.title | test("Fix +"+$api+"([. ]|$)")))]' \ /tmp/gh-aw/agent/merged-leakfix-all.json \ > /tmp/gh-aw/agent/merged-fix-prs.json jq -r '.[] | "merged fix for this leak: #\(.number) \(.title)"' /tmp/gh-aw/agent/merged-fix-prs.json @@ -466,8 +466,9 @@ jq -r '.[] | "merged fix for this leak: #\(.number) \(.title)"' /tmp/gh-aw/agent > stop the rebuild loop. Note: it may still reproduce in the shipped NuGet package until the > fix ships in a release. - > **Dry-run gate:** if `dry_run == "true"`, do NOT emit — print `DRY RUN — would close #` - > and the closing comment to the run log instead, then stop. + **Dry-run gate** (control text — NOT part of the close comment above): if `dry_run == "true"`, + do NOT emit — print `DRY RUN — would close #` and the closing comment to the run log + instead, then stop. This is the run's single action — stop after emitting `close-issue`. - If an **open** fix PR already refs this issue (a) OR already fixes the same rooting From 5b8b853a3a78950936a3d2cef6f532775fdb15e5 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 20:59:05 +0200 Subject: [PATCH 14/20] Raise gate (c) attempt-cap query to search-API ceiling + warn on truncation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses the native Copilot reviewer finding on gate (c): the closed-unmerged [leak-fix] attempt-cap search used --limit 200. The attempt cap (3 tries per issue) is durable state, but the search is global across all closed [leak-fix] PRs, so as total history grows past the cap, older attempts for a given issue fall off the result set and the 3-attempt cap can under-count — allowing a 4th+ rebuild of a genuinely un-fixable leak. Matches the gate (d) treatment: --limit 1000 (GitHub's search-API hard ceiling; pagination cannot exceed it), capture the raw set to a temp file, and echo a loud WARNING if the count reaches the ceiling (true completeness beyond 1000 needs date-windowed enumeration). Truncation here is fail-safe (over-processing / wasted CI, never data loss). The tightened issue-ref regex from 155707cb06a is preserved verbatim; only the fetch cap, raw-capture-to-temp-file, and truncation guard changed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 767d2d21-494b-41fb-8e8b-4410f9cc1b4b --- .github/workflows/leak-fixer.lock.yml | 2 +- .github/workflows/leak-fixer.md | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/leak-fixer.lock.yml b/.github/workflows/leak-fixer.lock.yml index c55aabece459..11b220c9d997 100644 --- a/.github/workflows/leak-fixer.lock.yml +++ b/.github/workflows/leak-fixer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"10dee7e2008b3eca441e3eae65bb9fa5231d457398852a8264def25bf28d8e5e","body_hash":"98b6c1d034e157b24ff3b1ae1de3a893943dc79e9ea5846ed5d916c3e122cb95","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"10dee7e2008b3eca441e3eae65bb9fa5231d457398852a8264def25bf28d8e5e","body_hash":"54c114d608e939e1acb1a4191f680d9b24792d8c2d8b7b558bfa964aafc3be3c","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/leak-fixer.md b/.github/workflows/leak-fixer.md index 4156f0432e8d..704d76c538f6 100644 --- a/.github/workflows/leak-fixer.md +++ b/.github/workflows/leak-fixer.md @@ -429,9 +429,18 @@ gh pr list --repo "$GITHUB_REPOSITORY" --state open --search '"[leak-fix]" in:ti > /tmp/gh-aw/agent/same-api-prs.json jq -r '.[] | "same-API open fix PR: #\(.number) \(.title)"' /tmp/gh-aw/agent/same-api-prs.json # (c) Closed-unmerged attempts for this issue (attempt cap = 3). -gh pr list --repo "$GITHUB_REPOSITORY" --state closed --search '"[leak-fix]" in:title label:agentic-workflows' --limit 200 \ - --json number,title,body,mergedAt \ - | jq --arg n "$N" --arg repo "$REPO_RE" '[.[] | select(((.body // "") | test("(?i)\\b(Fixes|Refs)\\b:?[ \t]*("+$repo+"#|[^0-9A-Za-z_/]#)"+$n+"\\b")) and (.mergedAt == null))]' \ +# --limit 1000 = the GitHub SEARCH API's hard ceiling (pagination cannot exceed it). The +# attempt cap is durable state (a genuinely un-fixable leak must stop being retried), but the +# search is global: as total closed [leak-fix] history grows past the cap, older attempts for +# THIS issue could fall off the set and let the 3-attempt cap under-count (allowing a 4th+ +# rebuild). Fail-safe direction (over-processing, never data loss), but warn if we hit it. +gh pr list --repo "$GITHUB_REPOSITORY" --state closed --search '"[leak-fix]" in:title label:agentic-workflows' --limit 1000 \ + --json number,title,body,mergedAt > /tmp/gh-aw/agent/closed-leakfix-all.json +if [ "$(jq 'length' /tmp/gh-aw/agent/closed-leakfix-all.json)" -ge 1000 ]; then + echo "WARNING: closed [leak-fix] set hit the 1000 search-API ceiling; older attempts may be TRUNCATED and the 3-attempt cap could under-count for some issues — switch to date-windowed enumeration." +fi +jq --arg n "$N" --arg repo "$REPO_RE" '[.[] | select(((.body // "") | test("(?i)\\b(Fixes|Refs)\\b:?[ \t]*("+$repo+"#|[^0-9A-Za-z_/]#)"+$n+"\\b")) and (.mergedAt == null))]' \ + /tmp/gh-aw/agent/closed-leakfix-all.json \ > /tmp/gh-aw/agent/closed-fix-prs.json jq 'length' /tmp/gh-aw/agent/closed-fix-prs.json # (d) MERGED [leak-fix] PR already fixing this issue number OR the SAME rooting Type.Member? From 4715f423e84c12ebdb9c4d993b2d8874b9f17d19 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 21:11:42 +0200 Subject: [PATCH 15/20] Make fixed-on-main provenance grep robust under set -eo pipefail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses two native-reviewer findings on the hunter's fixed-on-main suppression block: 1. The leading `grep -E '^\[leak-fix\] '` in the provenance pipe exits 1 when nothing matches. Under the runner's `set -eo pipefail`, the common 'no merged [leak-fix] PRs yet' state (empty provenance — the ACTUAL current state of this brand-new workflow) would make the whole pipe abort the step. Replaced with an `awk '/^\[leak-fix\] /'` filter, which always exits 0 and yields an empty fixed-on-main-apis.txt (the intended no-op). Empirically confirmed: the grep pipe aborts (exit 1) on empty input under set -eo pipefail; the awk pipe survives (exit 0). Output is byte-identical on populated input. 2. The ceiling check used `grep -c ''` (exits 1 on an empty file). Not an actual abort (command-substitution failures don't trip set -e, and the if-condition suspends it — verified it survives), but switched to `awk 'END{print NR}'` for a clean exit-0 count and consistency. No behavior change on non-empty input (verified identical); recompiled with gh-aw v0.80.9 (0/0), only hunter body_hash changed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 767d2d21-494b-41fb-8e8b-4410f9cc1b4b --- .github/workflows/daily-leak-hunter.lock.yml | 2 +- .github/workflows/daily-leak-hunter.md | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/daily-leak-hunter.lock.yml b/.github/workflows/daily-leak-hunter.lock.yml index 4cb3c34309ff..2cccad660af2 100644 --- a/.github/workflows/daily-leak-hunter.lock.yml +++ b/.github/workflows/daily-leak-hunter.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"900c7bbaa56bdfbbebd8069d248c510df4593b61086be8c71f3c62d396477d96","body_hash":"bf9da5913dc0f77df4aa54c63360f494af6be6fc4f3063ead66e1e29161badd6","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"900c7bbaa56bdfbbebd8069d248c510df4593b61086be8c71f3c62d396477d96","body_hash":"e377109afbf21f592f887dafdc263aa5a021105e0b3acebff7a074e39e8d51f1","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/daily-leak-hunter.md b/.github/workflows/daily-leak-hunter.md index 7729a701161e..8f28ee3d79cc 100644 --- a/.github/workflows/daily-leak-hunter.md +++ b/.github/workflows/daily-leak-hunter.md @@ -203,10 +203,14 @@ echo "already-filed rooting APIs:"; cat /tmp/gh-aw/agent/already-filed-apis.txt # once (then the OPEN-issue de-dup catches it), so warn loudly if we ever reach the ceiling. gh pr list --repo "$GITHUB_REPOSITORY" --state merged --search '"[leak-fix]" in:title label:agentic-workflows' \ --limit 1000 --json title -q '.[].title' > /tmp/gh-aw/agent/merged-leakfix-titles.txt -if [ "$(grep -c '' /tmp/gh-aw/agent/merged-leakfix-titles.txt)" -ge 1000 ]; then +if [ "$(awk 'END{print NR}' /tmp/gh-aw/agent/merged-leakfix-titles.txt)" -ge 1000 ]; then echo "WARNING: fixed-on-main provenance hit the 1000 search-API ceiling; older merged [leak-fix] fixes may be TRUNCATED and their leaks could be re-filed once — switch to date-windowed enumeration." fi -grep -E '^\[leak-fix\] ' /tmp/gh-aw/agent/merged-leakfix-titles.txt | sed -E 's/^\[leak-fix\] *(Fix +)?//' \ +# awk (not grep) as the leading filter: grep exits 1 when nothing matches, which under the +# runner's `set -eo pipefail` would abort this step on the common "no merged [leak-fix] PRs yet" +# state (empty provenance is valid — it just means nothing is fixed-on-main yet). awk always +# exits 0 and yields an empty fixed-on-main-apis.txt, which is the intended no-op. +awk '/^\[leak-fix\] /' /tmp/gh-aw/agent/merged-leakfix-titles.txt | sed -E 's/^\[leak-fix\] *(Fix +)?//' \ | awk '{ if (match($0, /[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)+/)) { chain=substr($0,RSTART,RLENGTH); n=split(chain,seg,"."); print seg[n-1]"."seg[n] From 82dadbe0ba484eab0188cd56b06265262f604a68 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 21:20:23 +0200 Subject: [PATCH 16/20] Require perf/memory-leak label on the destructive close-issue guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses the native-reviewer finding on the close-issue safe-output: the agentic-workflows label is shared across multiple agentic workflows, so title-prefix [leak-scan] + agentic-workflows alone could theoretically match a manually-created or other-workflow issue if the agent output its number. Added perf/memory-leak 💦 to required-labels so the destructive close path requires BOTH labels (gh-aw close-issue required-labels is AND-semantics: 'Only close issues that have all of these labels'). This is behavior-preserving for the intended target set: the hunter stamps exactly [agentic-workflows, perf/memory-leak 💦] on every [leak-scan] issue it creates and locks that via allowed-labels, so every legitimate orphaned scan issue still qualifies — while a shared-label collision no longer can. Reinforces the design invariant that issue closure is a tightly-guarded destructive action. Recompiled with gh-aw v0.80.9 (0/0); only frontmatter_hash + the compiled close_issue config changed (body_hash, actions-lock.json, and the hunter workflow all unchanged). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 767d2d21-494b-41fb-8e8b-4410f9cc1b4b --- .github/workflows/leak-fixer.lock.yml | 10 +++++----- .github/workflows/leak-fixer.md | 11 +++++++---- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/leak-fixer.lock.yml b/.github/workflows/leak-fixer.lock.yml index 11b220c9d997..3a9430ded4df 100644 --- a/.github/workflows/leak-fixer.lock.yml +++ b/.github/workflows/leak-fixer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"10dee7e2008b3eca441e3eae65bb9fa5231d457398852a8264def25bf28d8e5e","body_hash":"54c114d608e939e1acb1a4191f680d9b24792d8c2d8b7b558bfa964aafc3be3c","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"929c73bd3fc49199da15dd587cb2467d37e1f4e9bf7620a7f36baec35e64a85a","body_hash":"54c114d608e939e1acb1a4191f680d9b24792d8c2d8b7b558bfa964aafc3be3c","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -517,9 +517,9 @@ 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_7f124cc8961e87c6_EOF' - {"add_comment":{"max":1,"required_title_prefix":"[leak-fix]","target":"*"},"close_issue":{"max":1,"required_labels":["agentic-workflows"],"required_title_prefix":"[leak-scan] ","target":"*"},"create_pull_request":{"allowed_base_branches":["main"],"allowed_branches":["leak-fix/**"],"allowed_files":["src/Controls/src/**","src/Controls/tests/Core.UnitTests/**","src/Controls/tests/DeviceTests/**","src/Core/src/**","src/Essentials/src/**","**/PublicAPI.Unshipped.txt"],"base_branch":"main","draft":true,"labels":["agentic-workflows","perf/memory-leak 💦"],"max":1,"max_patch_files":100,"max_patch_size":4096,"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"],"protected_files_policy":"request_review"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"push_to_pull_request_branch":{"allowed_files":["src/Controls/src/**","src/Controls/tests/Core.UnitTests/**","src/Controls/tests/DeviceTests/**","src/Core/src/**","src/Essentials/src/**","**/PublicAPI.Unshipped.txt"],"if_no_changes":"ignore","max":1,"max_patch_size":4096,"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"],"target":"*","title_prefix":"[leak-fix]"},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_7f124cc8961e87c6_EOF + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_83d8203845b9009c_EOF' + {"add_comment":{"max":1,"required_title_prefix":"[leak-fix]","target":"*"},"close_issue":{"max":1,"required_labels":["agentic-workflows","perf/memory-leak 💦"],"required_title_prefix":"[leak-scan] ","target":"*"},"create_pull_request":{"allowed_base_branches":["main"],"allowed_branches":["leak-fix/**"],"allowed_files":["src/Controls/src/**","src/Controls/tests/Core.UnitTests/**","src/Controls/tests/DeviceTests/**","src/Core/src/**","src/Essentials/src/**","**/PublicAPI.Unshipped.txt"],"base_branch":"main","draft":true,"labels":["agentic-workflows","perf/memory-leak 💦"],"max":1,"max_patch_files":100,"max_patch_size":4096,"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"],"protected_files_policy":"request_review"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"push_to_pull_request_branch":{"allowed_files":["src/Controls/src/**","src/Controls/tests/Core.UnitTests/**","src/Controls/tests/DeviceTests/**","src/Core/src/**","src/Essentials/src/**","**/PublicAPI.Unshipped.txt"],"if_no_changes":"ignore","max":1,"max_patch_size":4096,"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"],"target":"*","title_prefix":"[leak-fix]"},"report_incomplete":{}} + GH_AW_SAFE_OUTPUTS_CONFIG_83d8203845b9009c_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -1814,7 +1814,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,dev.azure.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,patch-diff.githubusercontent.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: "{\"add_comment\":{\"max\":1,\"required_title_prefix\":\"[leak-fix]\",\"target\":\"*\"},\"close_issue\":{\"max\":1,\"required_labels\":[\"agentic-workflows\"],\"required_title_prefix\":\"[leak-scan] \",\"target\":\"*\"},\"create_pull_request\":{\"allowed_base_branches\":[\"main\"],\"allowed_branches\":[\"leak-fix/**\"],\"allowed_files\":[\"src/Controls/src/**\",\"src/Controls/tests/Core.UnitTests/**\",\"src/Controls/tests/DeviceTests/**\",\"src/Core/src/**\",\"src/Essentials/src/**\",\"**/PublicAPI.Unshipped.txt\"],\"base_branch\":\"main\",\"draft\":true,\"labels\":[\"agentic-workflows\",\"perf/memory-leak 💦\"],\"max\":1,\"max_patch_files\":100,\"max_patch_size\":4096,\"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\"],\"protected_files_policy\":\"request_review\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_to_pull_request_branch\":{\"allowed_files\":[\"src/Controls/src/**\",\"src/Controls/tests/Core.UnitTests/**\",\"src/Controls/tests/DeviceTests/**\",\"src/Core/src/**\",\"src/Essentials/src/**\",\"**/PublicAPI.Unshipped.txt\"],\"if_no_changes\":\"ignore\",\"max\":1,\"max_patch_size\":4096,\"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\"],\"target\":\"*\",\"title_prefix\":\"[leak-fix]\"},\"report_incomplete\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1,\"required_title_prefix\":\"[leak-fix]\",\"target\":\"*\"},\"close_issue\":{\"max\":1,\"required_labels\":[\"agentic-workflows\",\"perf/memory-leak 💦\"],\"required_title_prefix\":\"[leak-scan] \",\"target\":\"*\"},\"create_pull_request\":{\"allowed_base_branches\":[\"main\"],\"allowed_branches\":[\"leak-fix/**\"],\"allowed_files\":[\"src/Controls/src/**\",\"src/Controls/tests/Core.UnitTests/**\",\"src/Controls/tests/DeviceTests/**\",\"src/Core/src/**\",\"src/Essentials/src/**\",\"**/PublicAPI.Unshipped.txt\"],\"base_branch\":\"main\",\"draft\":true,\"labels\":[\"agentic-workflows\",\"perf/memory-leak 💦\"],\"max\":1,\"max_patch_files\":100,\"max_patch_size\":4096,\"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\"],\"protected_files_policy\":\"request_review\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_to_pull_request_branch\":{\"allowed_files\":[\"src/Controls/src/**\",\"src/Controls/tests/Core.UnitTests/**\",\"src/Controls/tests/DeviceTests/**\",\"src/Core/src/**\",\"src/Essentials/src/**\",\"**/PublicAPI.Unshipped.txt\"],\"if_no_changes\":\"ignore\",\"max\":1,\"max_patch_size\":4096,\"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\"],\"target\":\"*\",\"title_prefix\":\"[leak-fix]\"},\"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/leak-fixer.md b/.github/workflows/leak-fixer.md index 704d76c538f6..8aae0bbce587 100644 --- a/.github/workflows/leak-fixer.md +++ b/.github/workflows/leak-fixer.md @@ -158,13 +158,16 @@ safe-outputs: # The agent supplies the issue number + a closing comment linking the merged fix. Write # access lives ONLY in the isolated safe-outputs/conclusion jobs — the agent itself stays # read-only. Deterministic guards: the safe-output validator will ONLY close an issue whose - # title starts with "[leak-scan] " AND that carries this workflow's own `agentic-workflows` - # label, so a hallucinated or injected number pointing at an unrelated issue is rejected - # outright (not merely bounded by max:1). + # title starts with "[leak-scan] " AND that carries BOTH of this workflow's labels + # (`agentic-workflows` and `perf/memory-leak 💦` — the exact pair the hunter stamps, and locks + # via allowed-labels, on every [leak-scan] issue it creates). `agentic-workflows` alone is + # shared by other agentic workflows, so requiring the memory-leak label too prevents a + # hallucinated/injected number pointing at an unrelated agentic issue from being closed + # (rejected outright, not merely bounded by max:1). close-issue: target: "*" required-title-prefix: "[leak-scan] " - required-labels: [agentic-workflows] + required-labels: [agentic-workflows, "perf/memory-leak 💦"] max: 1 # Most 6h runs are idle (every open leak already has a [leak-fix] PR). Without this, # gh-aw's auto-injected default (noop: report-as-issue: true) files a "no action taken" From e01a5cb47cc5ea6b9dbf375815a7795fb46ad51b Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 21:34:39 +0200 Subject: [PATCH 17/20] Normalize PR titles with symmetric last-pair extraction in leak-fix gates (b)/(d) Addresses the improved-reviewer [moderate] finding on gate (d): the Type.Member title-match anchored the API immediately after "Fix ", so a merged [leak-fix] PR titled with a qualifier ("Fix Shell BackButtonBehavior.Command ...") or a fully-qualified name ("Fix Microsoft.Maui.Controls.Picker.ItemsSource ...") did NOT match target BackButtonBehavior.Command / Picker.ItemsSource -- the orphaned [leak-scan] issue stayed open and was rebuilt from source every run. The API derivation already normalizes the SCAN ISSUE title via a last-dotted-pair awk extraction, with a comment promising both sides key "identically" -- but the PR side never did that extraction. Replace the anchored Fix-prefix regex in BOTH gate (b) (open, informational) and gate (d) (merged, destructive close) with the same last-dotted-pair extraction in jq, comparing exactly to the plain API string. Empirically verified (jq programs extracted from the file, run on a case matrix): - Now matches qualifier + fully-qualified titles (the two missed cases). - Also ELIMINATES a latent false-positive: the old anchored regex matched "Fix Picker.ItemsSource.Something" (a DIFFERENT member) for target Picker.ItemsSource, a wrongful-close risk on the destructive gate. - Preserves the cross-repo data-loss guard: "Refs: dotnet/runtime#999" still does NOT satisfy a maui #999 close; bare "dotnet/maui#N" does. - Target-sensitive; off-contract titles with no dotted API yield no match. Removed the now-unused API_RE variable (was only consumed by the replaced regexes). Recompiled with gh-aw v0.80.9 (0/0); only leak-fixer.lock.yml body_hash changed (frontmatter, actions-lock.json, hunter all unchanged); gate region passes bash -n. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 767d2d21-494b-41fb-8e8b-4410f9cc1b4b --- .github/workflows/leak-fixer.lock.yml | 2 +- .github/workflows/leak-fixer.md | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/.github/workflows/leak-fixer.lock.yml b/.github/workflows/leak-fixer.lock.yml index 3a9430ded4df..f33c753037ed 100644 --- a/.github/workflows/leak-fixer.lock.yml +++ b/.github/workflows/leak-fixer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"929c73bd3fc49199da15dd587cb2467d37e1f4e9bf7620a7f36baec35e64a85a","body_hash":"54c114d608e939e1acb1a4191f680d9b24792d8c2d8b7b558bfa964aafc3be3c","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"929c73bd3fc49199da15dd587cb2467d37e1f4e9bf7620a7f36baec35e64a85a","body_hash":"e31c9a36d368c34687466fa00845483e6f23456afdcd6c60ba97cfea481f67c5","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/leak-fixer.md b/.github/workflows/leak-fixer.md index 8aae0bbce587..cc383c535200 100644 --- a/.github/workflows/leak-fixer.md +++ b/.github/workflows/leak-fixer.md @@ -409,9 +409,6 @@ API=$(gh issue view "$N" --repo "$GITHUB_REPOSITORY" --json title -q '.title' \ | sed -E 's/^\[leak-scan\] *//' \ | awk '{ if (match($0, /[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)+/)) { chain=substr($0,RSTART,RLENGTH); n=split(chain,seg,"."); print seg[n-1]"."seg[n] } else print }') echo "target rooting API: $API" -# Escape regex metacharacters (notably the '.' in Type.Member) so the jq test() calls below match -# a LITERAL "Type.Member" — otherwise "BackButtonBehavior.Command" would also match "BackButtonBehaviorXCommand". -API_RE=$(printf '%s' "$API" | sed -E 's/[][(){}.^$*+?|\\]/\\&/g') # Escape the owner/repo so it embeds literally in the jq test() calls below. The issue-ref match # requires "#" to be a BARE same-repo reference OR an explicit "/#" qualifier — # NEVER an arbitrary cross-repo "other/repo#". Otherwise an unrelated upstream ref such as @@ -425,10 +422,15 @@ gh pr list --repo "$GITHUB_REPOSITORY" --state open --search '"[leak-fix]" in:ti > /tmp/gh-aw/agent/open-fix-prs.json jq 'length' /tmp/gh-aw/agent/open-fix-prs.json # (b) Open [leak-fix] PR already fixing the SAME rooting Type.Member (any issue number)? -# [leak-fix] PR titles are "Fix . memory leak". +# [leak-fix] PR titles lead with "Fix . ...". Normalize each PR title with the +# SAME last-dotted-pair extraction used for $API above (issue side) instead of anchoring the +# API right after "Fix " — that keys both sides identically, so a qualifier ("Fix Shell +# BackButtonBehavior.Command ...") or a fully-qualified title ("Fix +# Microsoft.Maui.Controls.Picker.ItemsSource ...") still matches the target Type.Member, and a +# deeper member ("Fix Picker.ItemsSource.Something") no longer false-matches Picker.ItemsSource. gh pr list --repo "$GITHUB_REPOSITORY" --state open --search '"[leak-fix]" in:title label:agentic-workflows' --limit 200 \ --json number,title \ - | jq --arg api "$API_RE" '[.[] | select(.title | test("Fix +"+$api+"([. ]|$)"))]' \ + | jq --arg apiplain "$API" 'def titleapi: [scan("[A-Za-z_][A-Za-z0-9_]*(?:\\.[A-Za-z_][A-Za-z0-9_]*)+")] | if length>0 then (.[0]|split(".")|.[-2:]|join(".")) else null end; [.[] | select(((.title // "") | titleapi) == $apiplain)]' \ > /tmp/gh-aw/agent/same-api-prs.json jq -r '.[] | "same-API open fix PR: #\(.number) \(.title)"' /tmp/gh-aw/agent/same-api-prs.json # (c) Closed-unmerged attempts for this issue (attempt cap = 3). @@ -449,8 +451,10 @@ jq 'length' /tmp/gh-aw/agent/closed-fix-prs.json # (d) MERGED [leak-fix] PR already fixing this issue number OR the SAME rooting Type.Member? # Merged fixes often reference an UPSTREAM issue (e.g. "Fixes #35775") instead of this # [leak-scan] number, so GitHub never auto-closed this scan issue — leaving it to be -# re-picked and rebuilt from source every run. Detect the merged fix by BOTH the issue-ref -# AND the rooting Type.Member so we can close this issue instead of rebuilding. The search is +# re-picked and rebuilt from source every run. Detect the merged fix by the issue-ref OR the +# rooting Type.Member (each merged title normalized with the SAME last-dotted-pair extraction +# as $API, so qualified / fully-qualified titles key identically) so we can close this issue +# instead of rebuilding. The search is # scoped to label:agentic-workflows so ONLY workflow-owned merged fixes can close a scan issue. # --limit 1000 = the GitHub SEARCH API's hard result ceiling (pagination cannot exceed it). If # the merged [leak-fix] set ever hits 1000, older fixes are TRUNCATED and this gate could miss a @@ -461,8 +465,8 @@ gh pr list --repo "$GITHUB_REPOSITORY" --state merged --search '"[leak-fix]" in: if [ "$(jq 'length' /tmp/gh-aw/agent/merged-leakfix-all.json)" -ge 1000 ]; then echo "WARNING: merged [leak-fix] provenance hit the 1000 search-API ceiling; older merged fixes may be TRUNCATED. Gate (d) stays fail-safe (under-closes) but close-coverage is incomplete — switch to date-windowed enumeration." fi -jq --arg n "$N" --arg api "$API_RE" --arg repo "$REPO_RE" \ - '[.[] | select(((.body // "") | test("(?i)\\b(Fixes|Refs)\\b:?[ \t]*("+$repo+"#|[^0-9A-Za-z_/]#)"+$n+"\\b")) or (.title | test("Fix +"+$api+"([. ]|$)")))]' \ +jq --arg n "$N" --arg apiplain "$API" --arg repo "$REPO_RE" \ + 'def titleapi: [scan("[A-Za-z_][A-Za-z0-9_]*(?:\\.[A-Za-z_][A-Za-z0-9_]*)+")] | if length>0 then (.[0]|split(".")|.[-2:]|join(".")) else null end; [.[] | select(((.body // "") | test("(?i)\\b(Fixes|Refs)\\b:?[ \t]*("+$repo+"#|[^0-9A-Za-z_/]#)"+$n+"\\b")) or (((.title // "") | titleapi) == $apiplain))]' \ /tmp/gh-aw/agent/merged-leakfix-all.json \ > /tmp/gh-aw/agent/merged-fix-prs.json jq -r '.[] | "merged fix for this leak: #\(.number) \(.title)"' /tmp/gh-aw/agent/merged-fix-prs.json From 7aeffeefa8a64b41c79dba7af54942bde82ff9b5 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Wed, 15 Jul 2026 21:56:01 +0200 Subject: [PATCH 18/20] Respect maintainer re-open in leak-fixer gate (d) Gate (d) closes a [leak-scan] issue whenever a MERGED [leak-fix] PR references the issue number or the same rooting Type.Member. If a maintainer re-opens the scan issue after that PR merged (incomplete fix or another retention edge remains), gate (d) re-closed it every 6h and posted a false "already fixed" comment, reversing the human decision. Add a maintainer re-open guard: fetch mergedAt for matched merged fixes, read the issue timeline, and if the newest 'reopened' event is newer than the newest matching mergedAt, emit `skipped: scan issue re-opened after merged fix (maintainer override)` instead of closing or rebuilding. The workflow never re-opens issues, so any reopened event is an external human action. The extra timeline call only runs when a merged fix already matched, so the common no-match path is unaffected. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 767d2d21-494b-41fb-8e8b-4410f9cc1b4b --- .github/workflows/leak-fixer.lock.yml | 2 +- .github/workflows/leak-fixer.md | 29 +++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/.github/workflows/leak-fixer.lock.yml b/.github/workflows/leak-fixer.lock.yml index f33c753037ed..82a0c7b22497 100644 --- a/.github/workflows/leak-fixer.lock.yml +++ b/.github/workflows/leak-fixer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"929c73bd3fc49199da15dd587cb2467d37e1f4e9bf7620a7f36baec35e64a85a","body_hash":"e31c9a36d368c34687466fa00845483e6f23456afdcd6c60ba97cfea481f67c5","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"929c73bd3fc49199da15dd587cb2467d37e1f4e9bf7620a7f36baec35e64a85a","body_hash":"1e3346ac5b71cb4bca1584935935f223f8638b28a36aff5931ef8cb03aaa7493","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/leak-fixer.md b/.github/workflows/leak-fixer.md index cc383c535200..cb9e3faa729f 100644 --- a/.github/workflows/leak-fixer.md +++ b/.github/workflows/leak-fixer.md @@ -461,7 +461,7 @@ jq 'length' /tmp/gh-aw/agent/closed-fix-prs.json # valid merged fix. Gate (d) stays fail-safe (it only CLOSES on a positive match, so a miss # under-closes rather than wrongly closing), but close-coverage would be incomplete, so warn. gh pr list --repo "$GITHUB_REPOSITORY" --state merged --search '"[leak-fix]" in:title label:agentic-workflows' --limit 1000 \ - --json number,title,body > /tmp/gh-aw/agent/merged-leakfix-all.json + --json number,title,body,mergedAt > /tmp/gh-aw/agent/merged-leakfix-all.json if [ "$(jq 'length' /tmp/gh-aw/agent/merged-leakfix-all.json)" -ge 1000 ]; then echo "WARNING: merged [leak-fix] provenance hit the 1000 search-API ceiling; older merged fixes may be TRUNCATED. Gate (d) stays fail-safe (under-closes) but close-coverage is incomplete — switch to date-windowed enumeration." fi @@ -470,10 +470,35 @@ jq --arg n "$N" --arg apiplain "$API" --arg repo "$REPO_RE" \ /tmp/gh-aw/agent/merged-leakfix-all.json \ > /tmp/gh-aw/agent/merged-fix-prs.json jq -r '.[] | "merged fix for this leak: #\(.number) \(.title)"' /tmp/gh-aw/agent/merged-fix-prs.json +# (d-override) MAINTAINER RE-OPEN GUARD: if this [leak-scan] issue was RE-OPENED *after* the +# newest matched merged [leak-fix] PR merged, a maintainer deliberately overrode the auto-close +# (the merged fix was incomplete / another retention edge remains). This workflow NEVER re-opens +# issues, so any 'reopened' event is an external human action. Respect it: never re-close (which +# would reverse the human decision every 6h and post a false "already fixed" comment) and never +# rebuild. Emit `skipped: scan issue re-opened after merged fix (maintainer override)` and stop. +if [ "$(jq 'length' /tmp/gh-aw/agent/merged-fix-prs.json)" -ge 1 ]; then + FIX_MERGED_AT=$(jq -r '[.[].mergedAt] | map(select(. != null)) | max // empty' /tmp/gh-aw/agent/merged-fix-prs.json) + gh api "repos/$GITHUB_REPOSITORY/issues/$N/timeline" --paginate -H "Accept: application/vnd.github+json" \ + > /tmp/gh-aw/agent/issue-timeline.json 2>/dev/null || echo '[]' > /tmp/gh-aw/agent/issue-timeline.json + REOPENED_AT=$(jq -r '[.[] | select(.event=="reopened") | .created_at] | map(select(. != null)) | max // empty' /tmp/gh-aw/agent/issue-timeline.json) + if [ "$(jq -n --arg r "$REOPENED_AT" --arg m "$FIX_MERGED_AT" '($r != "") and ($m != "") and ($r > $m)')" = "true" ]; then + echo "REOPEN OVERRIDE: issue #$N re-opened at $REOPENED_AT AFTER merged fix at $FIX_MERGED_AT — maintainer override; will NOT close or rebuild." \ + > /tmp/gh-aw/agent/reopen-override.txt + cat /tmp/gh-aw/agent/reopen-override.txt + fi +fi ``` +- **Re-open override (takes precedence over gate (d) and any rebuild):** if + `/tmp/gh-aw/agent/reopen-override.txt` exists — i.e. THIS `[leak-scan]` issue was **re-opened + after** the newest matched merged `[leak-fix]` PR merged — a maintainer has deliberately + overridden the auto-close. Do NOT emit `close-issue` and do NOT rebuild; emit + `skipped: scan issue re-opened after merged fix (maintainer override)` and stop. Never reverse a + deliberate human re-open (doing so would re-close it every 6h and post a false "already fixed" + comment). - If a **merged** `[leak-fix]` PR already fixes this issue number OR the same rooting - `Type.Member` (d) → the leak is **already on `main`**. Do NOT create a branch or rebuild. + `Type.Member` (d) **and the re-open override above did NOT fire** → the leak is **already on + `main`**. Do NOT create a branch or rebuild. Emit exactly one `close-issue` safe-output on THIS `[leak-scan]` issue `#` with a closing comment (AI-attributed, linking the merged PR), e.g.: > 🔍 **AI-generated action** (Memory Leak Fixer) — this leak is already fixed on `main` by From ecda5f43d5b133add7094cd75eb82d96b2bf08b2 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Wed, 15 Jul 2026 23:15:11 +0200 Subject: [PATCH 19/20] Fix fail-open re-open guard in leak-fixer (fail closed on unverified timeline) The maintainer re-open guard fetched the issue timeline with a `|| echo '[]'` fallback. On ANY timeline fetch/parse failure it wrote an empty timeline, so REOPENED_AT became empty, the re-open override did NOT fire, and the destructive close-issue/rebuild path ran -- re-closing a maintainer-reopened [leak-scan] issue every 6h and posting a false "already fixed" comment. Fix (fail CLOSED on this destructive path): only compute the re-open time when the timeline is BOTH fetched successfully AND parses as a JSON array. Otherwise write the override sentinel so close-issue/rebuild is skipped and the issue is left open. The agent emits a distinct skip reason ("re-open guard unverified (timeline lookup failed)") for the fail-closed case vs. a genuine maintainer override. Empirically verified all four timeline scenarios: (1) API failure and (2) non-array garbage now fail closed (skip close); (3) genuine re-open and (4) normal-no-reopen behave exactly as before. Recompiled leak-fixer.lock.yml with gh aw v0.80.9 (body_hash only; frontmatter and actions-lock.json unchanged). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 767d2d21-494b-41fb-8e8b-4410f9cc1b4b --- .github/workflows/leak-fixer.lock.yml | 2 +- .github/workflows/leak-fixer.md | 34 ++++++++++++++++++--------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/.github/workflows/leak-fixer.lock.yml b/.github/workflows/leak-fixer.lock.yml index 82a0c7b22497..a9affa88d6cb 100644 --- a/.github/workflows/leak-fixer.lock.yml +++ b/.github/workflows/leak-fixer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"929c73bd3fc49199da15dd587cb2467d37e1f4e9bf7620a7f36baec35e64a85a","body_hash":"1e3346ac5b71cb4bca1584935935f223f8638b28a36aff5931ef8cb03aaa7493","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"929c73bd3fc49199da15dd587cb2467d37e1f4e9bf7620a7f36baec35e64a85a","body_hash":"522f0eb37b0ef1d3c96b1c9a0893ac22ec866f94da5d92960d222963a5f9c768","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/leak-fixer.md b/.github/workflows/leak-fixer.md index cb9e3faa729f..bf25205cb1e5 100644 --- a/.github/workflows/leak-fixer.md +++ b/.github/workflows/leak-fixer.md @@ -478,11 +478,21 @@ jq -r '.[] | "merged fix for this leak: #\(.number) \(.title)"' /tmp/gh-aw/agent # rebuild. Emit `skipped: scan issue re-opened after merged fix (maintainer override)` and stop. if [ "$(jq 'length' /tmp/gh-aw/agent/merged-fix-prs.json)" -ge 1 ]; then FIX_MERGED_AT=$(jq -r '[.[].mergedAt] | map(select(. != null)) | max // empty' /tmp/gh-aw/agent/merged-fix-prs.json) - gh api "repos/$GITHUB_REPOSITORY/issues/$N/timeline" --paginate -H "Accept: application/vnd.github+json" \ - > /tmp/gh-aw/agent/issue-timeline.json 2>/dev/null || echo '[]' > /tmp/gh-aw/agent/issue-timeline.json - REOPENED_AT=$(jq -r '[.[] | select(.event=="reopened") | .created_at] | map(select(. != null)) | max // empty' /tmp/gh-aw/agent/issue-timeline.json) - if [ "$(jq -n --arg r "$REOPENED_AT" --arg m "$FIX_MERGED_AT" '($r != "") and ($m != "") and ($r > $m)')" = "true" ]; then - echo "REOPEN OVERRIDE: issue #$N re-opened at $REOPENED_AT AFTER merged fix at $FIX_MERGED_AT — maintainer override; will NOT close or rebuild." \ + # Fail CLOSED on this DESTRUCTIVE path: auto-closing a possibly maintainer-reopened issue must + # never happen on an unverified timeline. If the timeline cannot be fetched AND parsed as a JSON + # array, we cannot rule out a maintainer re-open, so write the override sentinel (skip close/ + # rebuild, leave the issue open) instead of treating a failed lookup as an empty timeline. + if gh api "repos/$GITHUB_REPOSITORY/issues/$N/timeline" --paginate -H "Accept: application/vnd.github+json" \ + > /tmp/gh-aw/agent/issue-timeline.json 2>/dev/null \ + && jq -e 'type == "array"' /tmp/gh-aw/agent/issue-timeline.json >/dev/null 2>&1; then + REOPENED_AT=$(jq -r '[.[] | select(.event=="reopened") | .created_at] | map(select(. != null)) | max // empty' /tmp/gh-aw/agent/issue-timeline.json) + if [ "$(jq -n --arg r "$REOPENED_AT" --arg m "$FIX_MERGED_AT" '($r != "") and ($m != "") and ($r > $m)')" = "true" ]; then + echo "REOPEN OVERRIDE: issue #$N re-opened at $REOPENED_AT AFTER merged fix at $FIX_MERGED_AT — maintainer override; will NOT close or rebuild." \ + > /tmp/gh-aw/agent/reopen-override.txt + cat /tmp/gh-aw/agent/reopen-override.txt + fi + else + echo "REOPEN GUARD UNVERIFIED: timeline lookup for #$N failed or returned non-array JSON — failing closed; will NOT close or rebuild (cannot rule out a maintainer re-open)." \ > /tmp/gh-aw/agent/reopen-override.txt cat /tmp/gh-aw/agent/reopen-override.txt fi @@ -490,12 +500,14 @@ fi ``` - **Re-open override (takes precedence over gate (d) and any rebuild):** if - `/tmp/gh-aw/agent/reopen-override.txt` exists — i.e. THIS `[leak-scan]` issue was **re-opened - after** the newest matched merged `[leak-fix]` PR merged — a maintainer has deliberately - overridden the auto-close. Do NOT emit `close-issue` and do NOT rebuild; emit - `skipped: scan issue re-opened after merged fix (maintainer override)` and stop. Never reverse a - deliberate human re-open (doing so would re-close it every 6h and post a false "already fixed" - comment). + `/tmp/gh-aw/agent/reopen-override.txt` exists — either THIS `[leak-scan]` issue was **re-opened + after** the newest matched merged `[leak-fix]` PR merged (a maintainer deliberately overrode the + auto-close), **or** the issue timeline could **not be fetched/parsed** so the re-open guard + failed closed — do NOT emit `close-issue` and do NOT rebuild. Read the sentinel and emit the + matching skip: `skipped: scan issue re-opened after merged fix (maintainer override)` for a real + re-open, or `skipped: re-open guard unverified (timeline lookup failed)` when the lookup failed; + then stop. Never reverse a deliberate human re-open, and never close on an unverified timeline + (either would re-close the issue every 6h and post a false "already fixed" comment). - If a **merged** `[leak-fix]` PR already fixes this issue number OR the same rooting `Type.Member` (d) **and the re-open override above did NOT fire** → the leak is **already on `main`**. Do NOT create a branch or rebuild. From 56abf8da25dfc0b9b61da5e122e34a71ee5b8033 Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Tue, 21 Jul 2026 16:43:08 +0200 Subject: [PATCH 20/20] leak-fixer: slurp paginated timeline + reset shared reopen-override sentinel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two review fixes for the maintainer re-open guard: - Merge paginated timeline pages before testing the override. 'gh api --paginate' writes each page as a SEPARATE JSON array, so a multi-page timeline emitted concatenated arrays and the jq reads ran once per page — REOPENED_AT could become multi-valued and the override comparison misfire. Fetch with --slurp and flatten the array-of-arrays with 'jq add // []' into a single event stream (also makes the 'type == array' guard robust). - Reset the shared reopen-override.txt sentinel at the start of each candidate. It is a single fixed path (not keyed by $N) written only inside the merged-fix block, while Step 3 may advance to the next-oldest candidate in the same run. Without the reset a sentinel written for an earlier candidate leaked forward and falsely gated a later one. rm -f it per candidate. Recompiled leak-fixer.lock.yml (body_hash only). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d00747b7-96f3-4e7a-8dfb-e3a48db04b2d --- .github/workflows/leak-fixer.lock.yml | 2 +- .github/workflows/leak-fixer.md | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/.github/workflows/leak-fixer.lock.yml b/.github/workflows/leak-fixer.lock.yml index a9affa88d6cb..af167cb18b21 100644 --- a/.github/workflows/leak-fixer.lock.yml +++ b/.github/workflows/leak-fixer.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"929c73bd3fc49199da15dd587cb2467d37e1f4e9bf7620a7f36baec35e64a85a","body_hash":"522f0eb37b0ef1d3c96b1c9a0893ac22ec866f94da5d92960d222963a5f9c768","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"929c73bd3fc49199da15dd587cb2467d37e1f4e9bf7620a7f36baec35e64a85a","body_hash":"1a6b8f3eb2a52459da0ac2af63b4de84e283455a2b9469fd9898f5e6ef9b2186","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.8","engine_versions":{"copilot":"1.0.63"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"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":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/leak-fixer.md b/.github/workflows/leak-fixer.md index bf25205cb1e5..120ed05216a5 100644 --- a/.github/workflows/leak-fixer.md +++ b/.github/workflows/leak-fixer.md @@ -470,6 +470,12 @@ jq --arg n "$N" --arg apiplain "$API" --arg repo "$REPO_RE" \ /tmp/gh-aw/agent/merged-leakfix-all.json \ > /tmp/gh-aw/agent/merged-fix-prs.json jq -r '.[] | "merged fix for this leak: #\(.number) \(.title)"' /tmp/gh-aw/agent/merged-fix-prs.json +# Reset the SHARED re-open override sentinel at the start of every candidate. It lives at a single +# fixed path (NOT keyed by $N) and is written only inside the merged-fix block below, while Step 3 +# may advance to the next-oldest candidate within the SAME run. Without this reset a sentinel +# written for an earlier candidate would leak forward and falsely gate a later one (phantom +# "maintainer override" / "unverified timeline"). Clear it so it reflects ONLY the current $N. +rm -f /tmp/gh-aw/agent/reopen-override.txt # (d-override) MAINTAINER RE-OPEN GUARD: if this [leak-scan] issue was RE-OPENED *after* the # newest matched merged [leak-fix] PR merged, a maintainer deliberately overrode the auto-close # (the merged fix was incomplete / another retention edge remains). This workflow NEVER re-opens @@ -482,9 +488,14 @@ if [ "$(jq 'length' /tmp/gh-aw/agent/merged-fix-prs.json)" -ge 1 ]; then # never happen on an unverified timeline. If the timeline cannot be fetched AND parsed as a JSON # array, we cannot rule out a maintainer re-open, so write the override sentinel (skip close/ # rebuild, leave the issue open) instead of treating a failed lookup as an empty timeline. - if gh api "repos/$GITHUB_REPOSITORY/issues/$N/timeline" --paginate -H "Accept: application/vnd.github+json" \ - > /tmp/gh-aw/agent/issue-timeline.json 2>/dev/null \ - && jq -e 'type == "array"' /tmp/gh-aw/agent/issue-timeline.json >/dev/null 2>&1; then + if gh api "repos/$GITHUB_REPOSITORY/issues/$N/timeline" --paginate --slurp -H "Accept: application/vnd.github+json" \ + > /tmp/gh-aw/agent/issue-timeline-pages.json 2>/dev/null \ + && jq -e 'type == "array"' /tmp/gh-aw/agent/issue-timeline-pages.json >/dev/null 2>&1; then + # `gh api --paginate` writes each page as a SEPARATE JSON array; without --slurp a multi-page + # timeline emits concatenated arrays ([..][..]) and every jq read below would run once PER page, + # so REOPENED_AT could become multi-valued and the string comparison would misfire. --slurp + # collects the pages into one array-of-arrays; `add` flattens them into a single event stream. + jq 'add // []' /tmp/gh-aw/agent/issue-timeline-pages.json > /tmp/gh-aw/agent/issue-timeline.json REOPENED_AT=$(jq -r '[.[] | select(.event=="reopened") | .created_at] | map(select(. != null)) | max // empty' /tmp/gh-aw/agent/issue-timeline.json) if [ "$(jq -n --arg r "$REOPENED_AT" --arg m "$FIX_MERGED_AT" '($r != "") and ($m != "") and ($r > $m)')" = "true" ]; then echo "REOPEN OVERRIDE: issue #$N re-opened at $REOPENED_AT AFTER merged fix at $FIX_MERGED_AT — maintainer override; will NOT close or rebuild." \