From eec492eb49e1a1a175b4eed3860cc36fa0f39ccc Mon Sep 17 00:00:00 2001 From: kotlarmilos Date: Fri, 3 Jul 2026 13:29:06 +0200 Subject: [PATCH 1/4] [ci-scan-feedback] Harden fixer dedup and mobile trimming-root guardrails Consolidates two ci-failure-scan-feedback iterations whose auto-push failed. Fixer dedup (ci-failure-fix.md Step 3): the per-KBE `#` token search is unreliable because GitHub's tokenizer drops the leading `#`, letting duplicate `[ci-fix]` PRs through (KBE #129045 got 3 PRs incl. one 8 days after a merge; #128890 had 2 open). Enumerate all `[ci-fix]` PRs once, parse each `Linked KBE:` marker into a ` -> [PRs]` map, and dedup against the map. Scanner (create-kbe.instructions.md): add a chronic-recurrence guard so a signature with >=2 closed `[ci-scan]` predecessors sharing a test-name stem is not re-filed as a fresh duplicate; widen the recently-closed look-back to 90d for such stems. Mobile fix policy (ci-failure-fix.md): do not propose an ILLink/TrimmerRoot that roots a type in an already-rooted test assembly (a no-op, rejected on #130106); only root a separate owning assembly confirmed from source, else loop-in comment. Fixes #130121 Fixes #130171 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/ci-failure-fix.md | 4 +++- .github/workflows/shared/create-kbe.instructions.md | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-failure-fix.md b/.github/workflows/ci-failure-fix.md index 28271b3472b499..a51854497d4f50 100644 --- a/.github/workflows/ci-failure-fix.md +++ b/.github/workflows/ci-failure-fix.md @@ -151,6 +151,8 @@ Before doing any analysis work, confirm nothing already handles this KBE. Use th 5. **Author already engaged on the KBE.** From the KBE comments you read in Step 2, if any `MEMBER`/`OWNER` comment expresses active investigation or fix-forward intent (case-insensitive any of: `i'm fixing`, `i am fixing`, `investigating`, `will investigate`, `looking into`, `root cause`, `fix forward`, `fix-forward`, `landing in #`, `wait for #`, `pr is up`, `working on`), do NOT duplicate their work -> `-> skipped: author already engaged on #`. 6. **Prior hand-off comment** — if the KBE already carries a comment containing the marker ``, a hand-off was already posted; you may still emit a fix PR this run if you have one, but you may NOT post a second comment (Hard rule 5). +**Search reliability — the `#` token is unreliable, so verify against a parsed map.** GitHub's search tokenizer drops the leading `#`, so a bare `"#"` or `"Linked KBE: #"` phrase query frequently fails to surface an existing artifact — that is how one KBE ends up with two open `[ci-fix]` PRs, or a fresh PR gets opened after a prior one already merged. Do **not** rely on the per-KBE `#`-token searches in 1–3 alone. Once per run, enumerate every `[ci-fix]` PR with `repo:dotnet/runtime is:pr in:title "[ci-fix]"` (walking `is:open`, `is:merged`, and `is:closed closed:>=`), read each result's `Linked KBE: #` marker, and build a ` -> [PRs]` map under `/tmp/gh-aw/agent/`. Checks 1–3 MUST consult this map — confirming a candidate by its parsed marker, not by trusting the phrase match — before opening a PR. If the map shows any open, merged, or within-30d-closed `[ci-fix]` PR for ``, apply the matching skip (`open fix PR # already exists` / `fix PR # already merged; KBE may be stale` / `prior fix PR # closed without merge within 30d`) instead of opening a duplicate. + Persist each KBE's dedup verdict to `/tmp/gh-aw/agent/dedup/.txt` so later steps don't re-query. ### Step 4 — Root-cause analysis (read-only) @@ -178,7 +180,7 @@ Apply these fixer-specific bounds on top of the skill's guidance: | KBE pipeline / area | Fix policy | |---|---| -| Mobile (ios/tvos/maccatalyst/android/wasm/wasi) | Small test/csproj/condition fixes in bounds are fair game. | +| Mobile (ios/tvos/maccatalyst/android/wasm/wasi) | Small test/csproj/condition fixes in bounds are fair game. Exception for trimming/AOT reflection failures (`Type.GetType`/reflection name returns `null` under ILLink): do **not** propose an `ILLink.Descriptors.xml` / `TrimmerRootDescriptor` that roots a type **in the test assembly itself** as the fix — library test assemblies are rooted whole by default, so rooting their own (e.g. nested enum) types is a no-op that does not address the failure. Only rooting a *separate* product/helper assembly the test reflects over is potentially valid; confirm from in-repo source which assembly owns the stripped member first, and if you cannot, downgrade to a loop-in comment rather than open a help-wanted PR proposing a likely no-op root. | | JIT / GC / PGO stress (codegen) | JIT/GC product fixes are OUT of bounds for any PR — no safe diff is producible, so loop in JIT/GC owners with a comment. Workarounds in unrelated code (e.g. changing library buffer sizes or API call patterns to sidestep a codegen bug) are equally OUT of bounds — go straight to the loop-in comment instead of opening a workaround PR. | | `System.Net.*` | In bounds only if it satisfies Step 5.2. | | `Microsoft.Extensions.*` | In bounds only if it satisfies Step 5.2. | diff --git a/.github/workflows/shared/create-kbe.instructions.md b/.github/workflows/shared/create-kbe.instructions.md index d5c9f9fd99e9f9..946d008045e187 100644 --- a/.github/workflows/shared/create-kbe.instructions.md +++ b/.github/workflows/shared/create-kbe.instructions.md @@ -118,6 +118,19 @@ it from the build metadata, not the queue time) against the issue's `closed_at`: predecessor `#` in the body so the regression is explicit, mirroring the merged-fix-PR recurrence rule below. +**Chronic recurrence — stop re-filing a signature humans keep closing.** If the +searches above surface **two or more** closed `[ci-scan]` predecessors sharing +the same test-name stem, the signature is a chronic recurrence (a flaky +crossgen2/R2R test, a stress-only assert, a long-cycle timeout), not a fresh +regression. Filing an Nth KBE for it just produces another issue a maintainer +closes as `duplicate`. Do **not** file: record `existing-kbe #` against the +most recently closed predecessor and stop (reopening or escalating is a human / +`ci-failure-fix` decision). For a stem already known to recur this way, widen the +predecessor searches above from `closed:>=<30-days-ago>` to +`closed:>=<90-days-ago>` — chronic signatures recur on cycles longer than the +30-day look-back, and the shorter bound is exactly what lets the duplicate +through. + ## Search for an area-team tracker From 26e01c668ce736d408d1056a8779aa9c9d85404b Mon Sep 17 00:00:00 2001 From: kotlarmilos Date: Fri, 3 Jul 2026 13:31:50 +0200 Subject: [PATCH 2/4] Tighten guardrail wording: fold into existing rules, drop bloat Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/ci-failure-fix.md | 6 ++---- .../workflows/shared/create-kbe.instructions.md | 17 +++++------------ 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci-failure-fix.md b/.github/workflows/ci-failure-fix.md index a51854497d4f50..21694c7daa752d 100644 --- a/.github/workflows/ci-failure-fix.md +++ b/.github/workflows/ci-failure-fix.md @@ -142,7 +142,7 @@ For each result, read the body + latest comments through the `github` MCP (NOT ` ### Step 3 — Existing-artifact dedup (search live GitHub, every KBE) -Before doing any analysis work, confirm nothing already handles this KBE. Use the `github` MCP search tools: +Before doing any analysis work, confirm nothing already handles this KBE. GitHub's search tokenizer drops the leading `#`, so a bare `"#"` phrase match is unreliable: build a ` -> [PRs]` map once per run by enumerating every `[ci-fix]` PR (`repo:dotnet/runtime is:pr in:title "[ci-fix]"` across `is:open`, `is:merged`, `is:closed closed:>=`) and parsing each `Linked KBE:` marker, then resolve checks 1–3 against that map. Use the `github` MCP search tools: 1. **Open fix PR already exists** — `repo:dotnet/runtime is:pr is:open in:title "[ci-fix]" "#"` OR body contains `Linked KBE: #`. If found -> `-> skipped: open fix PR # already exists`. 2. **Merged fix PR exists** — `repo:dotnet/runtime is:pr is:merged "Linked KBE: #"`. If found, the KBE is likely already fixed -> `-> skipped: fix PR # already merged; KBE may be stale`. @@ -151,8 +151,6 @@ Before doing any analysis work, confirm nothing already handles this KBE. Use th 5. **Author already engaged on the KBE.** From the KBE comments you read in Step 2, if any `MEMBER`/`OWNER` comment expresses active investigation or fix-forward intent (case-insensitive any of: `i'm fixing`, `i am fixing`, `investigating`, `will investigate`, `looking into`, `root cause`, `fix forward`, `fix-forward`, `landing in #`, `wait for #`, `pr is up`, `working on`), do NOT duplicate their work -> `-> skipped: author already engaged on #`. 6. **Prior hand-off comment** — if the KBE already carries a comment containing the marker ``, a hand-off was already posted; you may still emit a fix PR this run if you have one, but you may NOT post a second comment (Hard rule 5). -**Search reliability — the `#` token is unreliable, so verify against a parsed map.** GitHub's search tokenizer drops the leading `#`, so a bare `"#"` or `"Linked KBE: #"` phrase query frequently fails to surface an existing artifact — that is how one KBE ends up with two open `[ci-fix]` PRs, or a fresh PR gets opened after a prior one already merged. Do **not** rely on the per-KBE `#`-token searches in 1–3 alone. Once per run, enumerate every `[ci-fix]` PR with `repo:dotnet/runtime is:pr in:title "[ci-fix]"` (walking `is:open`, `is:merged`, and `is:closed closed:>=`), read each result's `Linked KBE: #` marker, and build a ` -> [PRs]` map under `/tmp/gh-aw/agent/`. Checks 1–3 MUST consult this map — confirming a candidate by its parsed marker, not by trusting the phrase match — before opening a PR. If the map shows any open, merged, or within-30d-closed `[ci-fix]` PR for ``, apply the matching skip (`open fix PR # already exists` / `fix PR # already merged; KBE may be stale` / `prior fix PR # closed without merge within 30d`) instead of opening a duplicate. - Persist each KBE's dedup verdict to `/tmp/gh-aw/agent/dedup/.txt` so later steps don't re-query. ### Step 4 — Root-cause analysis (read-only) @@ -180,7 +178,7 @@ Apply these fixer-specific bounds on top of the skill's guidance: | KBE pipeline / area | Fix policy | |---|---| -| Mobile (ios/tvos/maccatalyst/android/wasm/wasi) | Small test/csproj/condition fixes in bounds are fair game. Exception for trimming/AOT reflection failures (`Type.GetType`/reflection name returns `null` under ILLink): do **not** propose an `ILLink.Descriptors.xml` / `TrimmerRootDescriptor` that roots a type **in the test assembly itself** as the fix — library test assemblies are rooted whole by default, so rooting their own (e.g. nested enum) types is a no-op that does not address the failure. Only rooting a *separate* product/helper assembly the test reflects over is potentially valid; confirm from in-repo source which assembly owns the stripped member first, and if you cannot, downgrade to a loop-in comment rather than open a help-wanted PR proposing a likely no-op root. | +| Mobile (ios/tvos/maccatalyst/android/wasm/wasi) | Small test/csproj/condition fixes in bounds are fair game. For trimming/AOT reflection failures, do **not** root a type in the test assembly itself via `ILLink.Descriptors.xml`/`TrimmerRootDescriptor` — test assemblies are rooted whole, so this is a no-op; only root a separate owning assembly confirmed from source, else loop in with a comment. | | JIT / GC / PGO stress (codegen) | JIT/GC product fixes are OUT of bounds for any PR — no safe diff is producible, so loop in JIT/GC owners with a comment. Workarounds in unrelated code (e.g. changing library buffer sizes or API call patterns to sidestep a codegen bug) are equally OUT of bounds — go straight to the loop-in comment instead of opening a workaround PR. | | `System.Net.*` | In bounds only if it satisfies Step 5.2. | | `Microsoft.Extensions.*` | In bounds only if it satisfies Step 5.2. | diff --git a/.github/workflows/shared/create-kbe.instructions.md b/.github/workflows/shared/create-kbe.instructions.md index 946d008045e187..3f5daf2426a499 100644 --- a/.github/workflows/shared/create-kbe.instructions.md +++ b/.github/workflows/shared/create-kbe.instructions.md @@ -118,18 +118,11 @@ it from the build metadata, not the queue time) against the issue's `closed_at`: predecessor `#` in the body so the regression is explicit, mirroring the merged-fix-PR recurrence rule below. -**Chronic recurrence — stop re-filing a signature humans keep closing.** If the -searches above surface **two or more** closed `[ci-scan]` predecessors sharing -the same test-name stem, the signature is a chronic recurrence (a flaky -crossgen2/R2R test, a stress-only assert, a long-cycle timeout), not a fresh -regression. Filing an Nth KBE for it just produces another issue a maintainer -closes as `duplicate`. Do **not** file: record `existing-kbe #` against the -most recently closed predecessor and stop (reopening or escalating is a human / -`ci-failure-fix` decision). For a stem already known to recur this way, widen the -predecessor searches above from `closed:>=<30-days-ago>` to -`closed:>=<90-days-ago>` — chronic signatures recur on cycles longer than the -30-day look-back, and the shorter bound is exactly what lets the duplicate -through. +**Chronic recurrence.** If the searches above surface **two or more** closed +`[ci-scan]` predecessors sharing the same test-name stem, treat it as a chronic +recurrence, not a fresh regression: do **not** file — record `existing-kbe #` +against the most recently closed predecessor. For such stems, widen the +predecessor look-back from `closed:>=<30-days-ago>` to `closed:>=<90-days-ago>`. From 5b8895f07d904c688afd66000966cded1bf5eb38 Mon Sep 17 00:00:00 2001 From: kotlarmilos Date: Fri, 3 Jul 2026 13:33:35 +0200 Subject: [PATCH 3/4] Reword "chronic recurrence" to "recurring signature" Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/shared/create-kbe.instructions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/shared/create-kbe.instructions.md b/.github/workflows/shared/create-kbe.instructions.md index 3f5daf2426a499..bfc5ccac5f11b0 100644 --- a/.github/workflows/shared/create-kbe.instructions.md +++ b/.github/workflows/shared/create-kbe.instructions.md @@ -118,9 +118,9 @@ it from the build metadata, not the queue time) against the issue's `closed_at`: predecessor `#` in the body so the regression is explicit, mirroring the merged-fix-PR recurrence rule below. -**Chronic recurrence.** If the searches above surface **two or more** closed -`[ci-scan]` predecessors sharing the same test-name stem, treat it as a chronic -recurrence, not a fresh regression: do **not** file — record `existing-kbe #` +**Recurring signature.** If the searches above surface **two or more** closed +`[ci-scan]` predecessors sharing the same test-name stem, treat it as a recurring +signature, not a fresh regression: do **not** file — record `existing-kbe #` against the most recently closed predecessor. For such stems, widen the predecessor look-back from `closed:>=<30-days-ago>` to `closed:>=<90-days-ago>`. From 6c41edbe9c89fba42358e466a1d1c3a61a436b98 Mon Sep 17 00:00:00 2001 From: Milos Kotlar Date: Fri, 3 Jul 2026 13:54:43 +0200 Subject: [PATCH 4/4] Clarify recurring-signature rule sequencing and precedence Make the 90-day widened re-scan an explicit first step so the two-or-more-predecessors determination is no longer gated on a result only that wider scan can produce, and state that the recurring-signature rule is evaluated before and takes precedence over the post-close recurrence case. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/shared/create-kbe.instructions.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/shared/create-kbe.instructions.md b/.github/workflows/shared/create-kbe.instructions.md index bfc5ccac5f11b0..86c895dca71862 100644 --- a/.github/workflows/shared/create-kbe.instructions.md +++ b/.github/workflows/shared/create-kbe.instructions.md @@ -118,11 +118,16 @@ it from the build metadata, not the queue time) against the issue's `closed_at`: predecessor `#` in the body so the regression is explicit, mirroring the merged-fix-PR recurrence rule below. -**Recurring signature.** If the searches above surface **two or more** closed -`[ci-scan]` predecessors sharing the same test-name stem, treat it as a recurring +**Recurring signature.** Evaluate this check *before* acting on the post-close +recurrence case above; when it applies, it takes precedence. First re-run the +closed-candidate searches for the test-name stem with the look-back widened from +`closed:>=<30-days-ago>` to `closed:>=<90-days-ago>` — the wider window is what +lets a recurring signature surface at all. If that widened scan returns **two or +more** closed `[ci-scan]` predecessors sharing the stem, treat it as a recurring signature, not a fresh regression: do **not** file — record `existing-kbe #` -against the most recently closed predecessor. For such stems, widen the -predecessor look-back from `closed:>=<30-days-ago>` to `closed:>=<90-days-ago>`. +against the most recently closed predecessor, even if the current build finished +after that predecessor's `closed_at`. Fewer than two hits is not a recurring +signature; fall back to the post-close recurrence rule above.