Improve issue triage product-label detection (#49905)

## Summary

Issues that put the module in a `[Module]` title prefix (a common
PowerToys convention) but omit the bug template's **"Area(s) with
issue?"** section were left **Unclassified** with no `Product-*` label —
e.g. #49899 *"[Screen Ruler] Settings crashes ..."* got no
`Product-Screen Ruler` label despite the title.

Root cause: product-label detection was purely deterministic and narrow.
`parse_area` (`.github/scripts/issue-triage/issue-context.py`) only read
the template area section or a 6-entry keyword map, and the agent prompt
instructed the model to copy that candidate verbatim (and send `None`
otherwise). The `[Module]` title convention was never consulted.

## Change (two layers)

**1. Deterministic title-prefix matching (primary).** Parse the leading
`[Module]` bracket(s) in the title and match against existing
`Product-*` labels; upgrade the detected area when the body has no area
signal. Fully deterministic and auditable — this alone fixes Screen
Ruler and every other bracketed title.

**2. Constrained AI fallback (secondary).** Expose the repo's
`Product-*` labels as `Available product labels` in the deterministic
evidence, and allow the agent — **only when the deterministic candidate
is `None`** — to select the single best-matching existing label. This is
safe because the publisher already validates the agent's `product_label`
against the real label set, so the agent can only ever **add a valid
existing label**, never invent one or remove/change others.

The workflow prompt is `{{#runtime-import}}`-ed from `issue-triage.md`,
so the lock file changes only by its `body_hash` (sync check);
recompiled with the repo's current gh-aw `v0.84.3` to avoid unrelated
version drift.

## Tests

New unit tests in `tests/test_issue_context.py`:
- `test_title_prefix_maps_to_existing_product_label`
- `test_available_product_labels_are_sorted_and_filtered`
- `test_prepare_labels_bracketed_title_without_area_section`

All 30 tests pass (`python -m unittest tests.test_issue_context`).

## Files
- `.github/scripts/issue-triage/issue-context.py` — title-prefix
detection, available-label list, wiring
- `.github/scripts/issue-triage/tests/test_issue_context.py` — new tests
- `.github/workflows/issue-triage.md` — prompt allows constrained
fallback
- `.github/workflows/issue-triage.lock.yml` — recompiled (`body_hash`
only)

Generated with the GitHub Copilot CLI.

Co-authored-by: niels9001 <niels9001@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 26025067-259e-43e3-9dc7-a9fc4b5ba58b
This commit is contained in:
Niels Laute
2026-08-14 23:14:55 +02:00
committed by GitHub
parent 105ef0abcb
commit 8088120b06
8 changed files with 680 additions and 224 deletions

View File

@@ -59,8 +59,15 @@ that retain `Needs-Author-Feedback` for seven days without activity.
- A content hash skips unchanged edits and unrelated comments.
- Per-user rate limits, daily AI-credit limits, and per-issue concurrency bound
repeated execution.
- The agent has read-only issue/repository access. A separate validated
safe-output job owns comment, label, and duplicate-suggestion writes.
- The agent has no shell or GitHub API tools. It can only read the checked-out
repository and call the structured safe-output tool.
- Threat detection fails closed; publication requires an explicit successful
detection result.
- The publishing job rebuilds evidence from the current issue and accepts only
deterministic product-label candidates, duplicate candidates, hashes, and
classifications. Stale or manipulated model output fails before any write.
- A separate validated safe-output job owns comment, label, and
duplicate-suggestion writes.
## Retired automation