From f29c2a4200e2df68f8133523fbea517861029bf6 Mon Sep 17 00:00:00 2001 From: Celia Amador Date: Wed, 19 Aug 2026 09:37:28 +0200 Subject: [PATCH] triage: treat UI/API/CLI sibling tickets as non-duplicates When the same symptom is filed separately per fix surface, duplicate detection should cluster related tickets instead of marking them DUPLICATE. Adds fixSurface inference from summary prefixes, components, and description cues. Made-with: Cursor --- triage/SKILL.md | 2 +- triage/guidelines.md | 1 + triage/skills/analyze.md | 52 ++++++++++++++++++++++++++++++++-------- triage/skills/assess.md | 2 ++ triage/skills/scan.md | 1 + 5 files changed, 47 insertions(+), 11 deletions(-) diff --git a/triage/SKILL.md b/triage/SKILL.md index 8524c30d..ca27e25a 100644 --- a/triage/SKILL.md +++ b/triage/SKILL.md @@ -1,6 +1,6 @@ --- name: triage -version: 0.3.0 +version: 0.4.0 description: >- Bulk-triage unresolved Jira bugs with AI-driven recommendations and an interactive HTML report. Scan also loads recently resolved bugs for regression diff --git a/triage/guidelines.md b/triage/guidelines.md index 4bad58b1..cc9e105c 100644 --- a/triage/guidelines.md +++ b/triage/guidelines.md @@ -54,6 +54,7 @@ Any tool not listed above is **prohibited** in that phase. If a phase needs data - Recommendations must be consistent — similar bugs should receive similar treatment - Duplicate detection should reference the specific target issue key, not just say "duplicate" +- **UI vs API vs CLI (and other fix surfaces) are not duplicates** — when the same symptom is tracked in separate tickets per layer (different summary prefix, component, or repro surface), treat them as related siblings; cluster with `"relates to"`, do not mark DUPLICATE - The HTML report must ship as one file with inline CSS/JS and embedded JSON; optional Google Fonts load when online (system fonts offline) - Artifact JSON files must be valid, parseable JSON diff --git a/triage/skills/analyze.md b/triage/skills/analyze.md index d9c1f294..6093195f 100644 --- a/triage/skills/analyze.md +++ b/triage/skills/analyze.md @@ -52,7 +52,7 @@ Steps 3–7 progressively populate the following fields for each issue. Refer to ```json { "key": "EDM-1234", - "summary": "...", + "summary": "[Backend] Checkout throws 500 on submit", "status": "Open", "priority": "High", "suggestedPriority": null, @@ -61,7 +61,8 @@ Steps 3–7 progressively populate the following fields for each issue. Refer to "created": "2025-06-15T10:30:00Z", "updated": "2026-01-20T14:00:00Z", "labels": ["backend"], - "components": ["API"], + "components": ["Backend"], + "fixSurface": "API", "errorType": "NullPointerException", "errorCode": null, "errorMessageExcerpt": "at com.example.OrderTotals.apply", @@ -90,6 +91,7 @@ Steps 3–7 progressively populate the following fields for each issue. Refer to | `priorityMismatch` | Step 3 | Object `{assigned, suggested, reason}` when assigned priority ≠ description severity; otherwise null | | `autoFixLikelihood` | Step 3 | Integer 0–100, only when recommendation is AUTO_FIX | | `errorType`, `errorCode`, `errorMessageExcerpt`, `affectedComponent`, `symptoms`, `environmentHint` | Step 4 | Nullable strings — error signature fields | +| `fixSurface` | Step 5 | `CLI`, `UI`, or `API` when inferable; otherwise null (see "Determine fix surface") | | `duplicateOf` | Step 5 | Jira key of the duplicate target, or null | | `duplicateConfidence` | Step 5 | Integer 0–100 when `duplicateOf` is set; null otherwise | | `regressionOf` | Step 6 | Object `{key, summary, resolved, reason}` or null | @@ -107,7 +109,7 @@ For every issue, evaluate the following signals and assign a recommendation. - **Description quality** — length, presence of reproduction steps, error details, expected vs actual behavior - **Components** — which area of the system is affected - **Labels** — any existing categorization -- **Similar titles** — scan for issues with near-identical summaries (potential duplicates) +- **Similar titles** — scan for issues with near-identical summaries (potential duplicates, but see Step 5 — different fix surfaces such as UI vs API are not duplicates) - **Assignee** — assigned or unassigned #### Recommendation Types @@ -185,17 +187,47 @@ Populate `duplicateOf` and `duplicateConfidence` from the schema above. Before f 1. **Error / signature angle** — same or highly similar `errorType`, `errorCode`, or overlapping `errorMessageExcerpt` / stack location 2. **Component + symptom angle** — same Jira component(s) and matching `symptoms` or summary phrases -3. **Description similarity** — same root cause described (not merely similar titles) +3. **Description similarity** — same root cause described **on the same fix surface** (not merely similar titles) -For each issue, pick the strongest non-self candidate. If two issues describe the **same** underlying bug, mark the **newer** (by `created` or `key`) as **DUPLICATE** with `duplicateOf` pointing to the older. +For each issue, pick the strongest non-self candidate. If two issues describe the **same** underlying bug **on the same fix surface**, mark the **newer** (by `created` or `key`) as **DUPLICATE** with `duplicateOf` pointing to the older. + +#### UI / API / CLI fix surfaces are not duplicates + +Many projects file **separate bugs per fix surface** when the same user-visible problem must be fixed in more than one layer (e.g. UI and API). Two tickets with **near-identical descriptions** but **different fix surfaces** are **intentional siblings**, not duplicates. + +**Do not mark DUPLICATE** when the only strong overlap is shared symptom or description but the **fix surface differs**. Treat as distinct issues; **cluster** them (Step 7) with `"relates to"` when they clearly track the same underlying problem across layers. + +#### Determine fix surface + +There is no Jira Target custom field. Infer `fixSurface` as `CLI`, `UI`, or `API` using the first match below: + +1. **Summary prefix** — title tags at the start of the summary: + - `[CLI]` → `CLI` + - `[UI]` → `UI` + - `[Backend]` → `API` (backend/API work) +2. **Jira component** — component names that indicate the layer: + - `*-UI` suffix (e.g. `MyApp-UI`) → `UI` + - paired backend/API component without a `-UI` suffix (e.g. `MyApp`, `Backend`) → `API` +3. **Description cues** — when title and component are ambiguous: + - **UI**: browser, page, button, modal, screenshot, frontend repro + - **API**: endpoint, HTTP status, request/response, backend service, server-side stack trace + - **CLI**: command-line invocation, terminal output, CLI subcommand + +Set `fixSurface` to `null` only when none of the above apply. Re-infer during Step 5 even if `/scan` left it null. + +| Pair | Verdict | +|------|---------| +| Same description, `fixSurface` UI vs API (e.g. `[UI]` vs `[Backend]`, or `MyApp-UI` vs `Backend`) | **Not duplicate** — cluster as related | +| Same description, same `fixSurface` and same component | Candidate duplicate — apply confidence bands below | +| Same symptom, one ticket UI-only repro and one API-only repro | **Not duplicate** even if summaries match | **`duplicateConfidence`** — integer **0–100** when there is a named duplicate target, reflecting how strong the match is: | Band | When to use | |------|-------------| -| **85–100** | Same error signature and same repro path; or explicit duplicate reference in text | -| **70–84** | Strong component + symptom overlap and very similar description | -| **50–69** | Plausible duplicate; needs human confirmation | +| **85–100** | Same fix surface, same error signature and same repro path; or explicit duplicate reference in text | +| **70–84** | Same fix surface, strong component + symptom overlap and very similar description | +| **50–69** | Same fix surface, plausible duplicate; needs human confirmation | | **Below 50** | Do not mark DUPLICATE — prefer BACKLOG or cluster with a note in `reason` | Set `duplicateOf` to **null** and `duplicateConfidence` to **null** when there is no duplicate target. If you keep DUPLICATE recommendation, both `duplicateOf` and `duplicateConfidence` must be set consistently. @@ -274,8 +306,8 @@ Field details: #### Cluster vs Duplicate -- **Duplicate**: the issues describe the exact same bug — mark the newer as DUPLICATE -- **Cluster**: the issues are related but distinct — they share a theme, root cause area, or feature, but each describes a different manifestation. Cluster members keep their own recommendation (FIX_NOW, AUTO_FIX, BACKLOG, etc.); clustering does not change individual recommendations +- **Duplicate**: the issues describe the exact same bug **on the same fix surface** — mark the newer as DUPLICATE +- **Cluster**: the issues are related but distinct — they share a theme, root cause area, or feature, but each describes a different manifestation or **different fix surface** (e.g. UI vs API). Cluster members keep their own recommendation (FIX_NOW, AUTO_FIX, BACKLOG, etc.); clustering does not change individual recommendations ### Step 8: Generate Key Recommendations diff --git a/triage/skills/assess.md b/triage/skills/assess.md index 7b65d0be..3df354ee 100644 --- a/triage/skills/assess.md +++ b/triage/skills/assess.md @@ -81,6 +81,8 @@ Use **project key** `PROJECT`. Assign **duplicateConfidence** (0–100) per strong candidate using bulk bands. +**UI / API / CLI sibling tickets:** One bug per fix surface is intentional. Candidates with near-identical text but **different fix surfaces** (e.g. `[UI]` vs `[Backend]`, `MyApp-UI` vs `Backend`, or UI vs API repro in the description) are **not duplicates** — note them as related siblings and recommend linking (`relates to`), not DUPLICATE. See "Determine fix surface" in [`analyze.md`](analyze.md) Step 5. + ### Step 5: Integrate and finalize - If a **duplicate** is conclusive, set recommendation to **DUPLICATE** with `duplicateOf` (target key) and **duplicateConfidence**; align reason. diff --git a/triage/skills/scan.md b/triage/skills/scan.md index 9bf7f1b9..14559b38 100644 --- a/triage/skills/scan.md +++ b/triage/skills/scan.md @@ -94,6 +94,7 @@ For each **unresolved** issue, extract and normalize: - `updated` — last update date (ISO 8601) - `labels` — array of labels - `components` — array of component names +- `fixSurface` — inferred fix surface (`CLI`, `UI`, or `API`) when determinable; use `null` when unknown. Infer using the same rules as `/analyze` Step 5 (title prefix, component, then description) - `description` — full description text (may be long; preserve it for analysis) For each **resolved** issue, normalize the same fields plus when available: