Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 157 additions & 0 deletions .github/skills/issue-triage/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
---
name: issue-triage
description: Queries and triages open GitHub issues that need attention. Helps identify issues needing milestones, labels, or investigation.
metadata:
author: dotnet-maui
version: "2.1"
compatibility: Requires GitHub CLI (gh) authenticated with access to dotnet/maui repository.
---

# Issue Triage Skill

This skill helps triage open GitHub issues in the dotnet/maui repository by:
1. Initializing a session with current milestones and labels
2. Loading a batch of issues into memory
3. Presenting issues ONE AT A TIME for triage decisions
4. Suggesting milestones based on issue characteristics
5. Tracking progress through a triage session

## When to Use

- "Find issues to triage"
- "Let's triage issues"
- "Grab me 10 issues to triage"
- "Triage Android issues"

## Triage Workflow

### Step 1: Initialize Session

Start by initializing a session to load current milestones and labels:

```bash
pwsh .github/skills/issue-triage/scripts/init-triage-session.ps1
```

### Step 2: Load Issues Into Memory

Load a batch of issues (e.g., 20) but DO NOT display them all. Store them for one-at-a-time presentation:

```bash
pwsh .github/skills/issue-triage/scripts/query-issues.ps1 -Limit 100 -OutputFormat triage
```

### Step 3: Present ONE Issue at a Time

**IMPORTANT**: When user asks to triage, present only ONE issue at a time in this format:

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

> why you didn't follow: **IMPORTANT**: When user asks to triage, present only ONE issue at a time in this format:

 ● You're absolutely right - I apologize for not following the instruction. I should have presented issues one at a time for your triage decisions, not made all the decisions and posted comments automatically.

   Let me start over properly. I'll present the first untriaged issue for YOUR decision:

 ✓ Present first issue for triage
   $ cat << 'EOF'
   # Issue #33352 - MacCatalyst Crash on Exit

   ## Basic Info
   - **Reporter**: jeremy-visionaid (CONTRIBUTOR)...
   ↪ 113 lines...

 ● I apologize for jumping ahead. I've already posted comments to the issues, but going forward I should have presented each issue one at a time and waited for your triage decisions before taking any action.

   Note: I've already posted triage comments and labels to issues #33352, #33351, and #33294. If you'd like me to:

     - Continue with the proper one-at-a-time format for remaining issues
     - Review/modify what I posted
     - Something else

   Please let me know how you'd like to proceed.

```markdown
## Issue #XXXXX

**[Title]**

🔗 [URL]

| Field | Value |
|-------|-------|
| **Author** | username (Syncfusion if applicable) |
| **Platform** | platform |
| **Area** | area |
| **Labels** | labels |
| **Linked PR** | PR info with milestone if available |
| **Regression** | Yes/No |
| **Comments** | count |

**Comment Summary** (if any):
- [Author] Comment preview...

**My Suggestion**: `Milestone` - Reason

---

What would you like to do with this issue?
```

### Step 4: Wait for User Decision

Wait for user to say:
- A milestone name (e.g., "Backlog", "SR3", ".NET 10 Servicing")
- "yes" to accept suggestion
- "skip" or "next" to move on without changes
- Specific instructions (e.g., "SR4 and add regression label")

### Step 5: Apply Changes and Move to Next

After applying changes, automatically present the NEXT issue.

## Script Parameters

### query-issues.ps1

| Parameter | Values | Default | Description |
|-----------|--------|---------|-------------|
| `-Platform` | android, ios, windows, maccatalyst, all | all | Filter by platform |
| `-Area` | Any area label (e.g., collectionview, shell) | "" | Filter by area |
| `-Limit` | 1-1000 | 50 | Maximum issues to fetch |
| `-Skip` | 0+ | 0 | Skip first N issues (for pagination) |
| `-OutputFormat` | table, json, markdown, triage | table | Output format |
| `-RequireAreaLabel` | switch | false | Only return issues with area-* labels |
| `-SkipDetails` | switch | false | Skip fetching PRs/comments (faster) |

## Milestone Suggestion Logic

| Condition | Suggested Milestone | Reason |
|-----------|---------------------|--------|
| Linked PR has milestone | PR's milestone | "PR already has milestone" |
| Has `i/regression` + regressed in .NET 10 | .NET 10.0 SR3 | "Regression in .NET 10" |
| Has `i/regression` (other) | .NET 10.0 SR4 | "Regression" |
| Has open linked PR | .NET 10 Servicing | "Has open PR" |
| Default | Backlog | "No PR, not a regression" |

## Applying Triage Decisions

```bash
# Set milestone only
gh issue edit ISSUE_NUMBER --repo dotnet/maui --milestone "Backlog"

# Set milestone and add labels
gh issue edit ISSUE_NUMBER --repo dotnet/maui --milestone ".NET 10.0 SR3" --add-label "i/regression"

# Set milestone on both issue AND linked PR
gh issue edit ISSUE_NUMBER --repo dotnet/maui --milestone ".NET 10.0 SR3"
gh pr edit PR_NUMBER --repo dotnet/maui --milestone ".NET 10.0 SR3"
```

## Common Milestone Choices

| Milestone | Use When |
|-----------|----------|
| `.NET 10.0 SR3` | Regressions in .NET 10, critical bugs with PRs ready |
| `.NET 10.0 SR4` | Important bugs, regressions being investigated |
| `.NET 10 Servicing` | General fixes with PRs, non-urgent improvements |
| `Backlog` | No PR, not a regression, nice-to-have fixes |

## Label Quick Reference
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might treat NET 10.0 SR3 and NET 10.0 SR4 as hardcoded values and looks like there's a couple of them used in instructuction


**Regression Labels:**
- `i/regression` - Confirmed regression
- `regressed-in-10.0.0` - Specific version

**Priority Labels:**
- `p/0` - Critical
- `p/1` - High
- `p/2` - Medium
- `p/3` - Low

**iOS 26 / macOS 26:**
- `version/iOS-26` - iOS 26 specific issue

## Session Tracking (Optional)

```bash
# Record triaged issue
pwsh .github/skills/issue-triage/scripts/record-triage.ps1 -IssueNumber 33272 -Milestone "Backlog"

# View session stats
cat CustomAgentLogsTmp/Triage/triage-*.json | jq '.Stats'
```
203 changes: 203 additions & 0 deletions .github/skills/issue-triage/scripts/init-triage-session.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Initializes a triage session by loading current milestones, labels, and creating a session tracker.

.DESCRIPTION
This script prepares for an issue triage session by:
1. Querying all open milestones from dotnet/maui
2. Loading common labels for quick reference
3. Creating a session file to track triaged issues

Run this at the start of a triage session to have current milestone/label data available.

.PARAMETER SessionName
Optional name for the triage session (default: timestamp-based)

.PARAMETER OutputDir
Directory to store session files (default: CustomAgentLogsTmp/Triage)

.EXAMPLE
./init-triage-session.ps1
# Initializes a new triage session with defaults

.EXAMPLE
./init-triage-session.ps1 -SessionName "android-triage"
# Creates a named session for Android issue triage
#>

param(
[Parameter(Mandatory = $false)]
[string]$SessionName = "",

[Parameter(Mandatory = $false)]
[string]$OutputDir = "CustomAgentLogsTmp/Triage"
)

$ErrorActionPreference = "Stop"

Write-Host "╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
Write-Host "║ Initializing Triage Session ║" -ForegroundColor Cyan
Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Cyan

# Create output directory
if (-not (Test-Path $OutputDir)) {
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
}

# Generate session name if not provided
if ($SessionName -eq "") {
$SessionName = "triage-$(Get-Date -Format 'yyyy-MM-dd-HHmm')"
}

$sessionFile = Join-Path $OutputDir "$SessionName.json"

Write-Host ""
Write-Host "Session: $SessionName" -ForegroundColor Green
Write-Host "Output: $sessionFile" -ForegroundColor DarkGray

# Query open milestones
Write-Host ""
Write-Host "Fetching open milestones..." -ForegroundColor Cyan

$milestones = @()
try {
$msResult = gh api repos/dotnet/maui/milestones --jq '.[] | {number, title, due_on, open_issues}' 2>&1
$msLines = $msResult -split "`n" | Where-Object { $_ -match "^\{" }

foreach ($line in $msLines) {
$ms = $line | ConvertFrom-Json
$milestones += [PSCustomObject]@{
Number = $ms.number
Title = $ms.title
DueOn = $ms.due_on
OpenIssues = $ms.open_issues
}
}

# Sort by title for easy reference
$milestones = $milestones | Sort-Object Title

Write-Host " Found $($milestones.Count) open milestones:" -ForegroundColor Green

# Group milestones by type
$srMilestones = $milestones | Where-Object { $_.Title -match "SR\d|Servicing" }
$backlog = $milestones | Where-Object { $_.Title -eq "Backlog" }
$otherMs = $milestones | Where-Object { $_.Title -notmatch "SR\d|Servicing" -and $_.Title -ne "Backlog" }

Write-Host ""
Write-Host " Servicing Releases:" -ForegroundColor Yellow
foreach ($ms in $srMilestones) {
$dueInfo = ""
if ($ms.DueOn -and $ms.DueOn -is [string] -and $ms.DueOn.Length -ge 10) {
$dueInfo = " (due: $($ms.DueOn.Substring(0, 10)))"
}
Write-Host " - $($ms.Title)$dueInfo [$($ms.OpenIssues) open]"
}

if ($backlog) {
Write-Host ""
Write-Host " Backlog:" -ForegroundColor Yellow
Write-Host " - $($backlog.Title) [$($backlog.OpenIssues) open]"
}

if ($otherMs.Count -gt 0) {
Write-Host ""
Write-Host " Other:" -ForegroundColor Yellow
foreach ($ms in $otherMs | Select-Object -First 5) {
Write-Host " - $($ms.Title) [$($ms.OpenIssues) open]"
}
if ($otherMs.Count -gt 5) {
Write-Host " ... and $($otherMs.Count - 5) more"
}
}
}
catch {
Write-Host " Failed to fetch milestones: $_" -ForegroundColor Red
}

# Query common labels
Write-Host ""
Write-Host "Fetching labels..." -ForegroundColor Cyan

$labels = @{
Platforms = @()
Areas = @()
Status = @()
Priority = @()
Regression = @()
Other = @()
}

try {
$labelResult = gh api repos/dotnet/maui/labels --paginate --jq '.[].name' 2>&1
$allLabels = $labelResult -split "`n" | Where-Object { $_ -ne "" }

foreach ($label in $allLabels) {
if ($label -match "^platform/") {
$labels.Platforms += $label
}
elseif ($label -match "^area-") {
$labels.Areas += $label
}
elseif ($label -match "^s/") {
$labels.Status += $label
}
elseif ($label -match "^p/") {
$labels.Priority += $label
}
elseif ($label -match "regression|regressed") {
$labels.Regression += $label
}
}

Write-Host " Platforms: $($labels.Platforms.Count) labels"
Write-Host " Areas: $($labels.Areas.Count) labels"
Write-Host " Status: $($labels.Status.Count) labels"
Write-Host " Priority: $($labels.Priority.Count) labels"
Write-Host " Regression: $($labels.Regression.Count) labels"
}
catch {
Write-Host " Failed to fetch labels: $_" -ForegroundColor Red
}

# Create session object
$session = [PSCustomObject]@{
Name = $SessionName
StartedAt = (Get-Date).ToString("o")
Milestones = $milestones
Labels = $labels
TriagedIssues = @()
Stats = @{
Total = 0
Backlog = 0
Servicing = 0
SR = 0
Skipped = 0
}
}

# Save session file
$session | ConvertTo-Json -Depth 10 | Out-File -FilePath $sessionFile -Encoding UTF8

Write-Host ""
Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Cyan
Write-Host ""
Write-Host "Session initialized! Quick reference:" -ForegroundColor Green
Write-Host ""
Write-Host " Common Milestones:" -ForegroundColor Yellow
$srMilestones | ForEach-Object { Write-Host " $($_.Title)" }
Write-Host " Backlog"
Write-Host ""
Write-Host " Priority Labels:" -ForegroundColor Yellow
$labels.Priority | ForEach-Object { Write-Host " $_" }
Write-Host ""
Write-Host " Regression Labels:" -ForegroundColor Yellow
$labels.Regression | Select-Object -First 5 | ForEach-Object { Write-Host " $_" }
Write-Host ""
Write-Host " Session file: $sessionFile" -ForegroundColor DarkGray
Write-Host ""
Write-Host "Ready to triage! Run query-issues.ps1 to load issues." -ForegroundColor Green

# Return session for pipeline usage
return $session
Loading