Skip to content

Commit 71c2e1d

Browse files
Address PR feedback: remove 'other' label, add comment for unclassifiable issues, clean up
- Remove 'other' from classification labels; agent now leaves a comment when an issue doesn't fit established categories - Remove unused 'mode' and 'snapshot_text' workflow_dispatch inputs - Add 'add-comment' safe-output for unclassifiable issue comments - Reduce max labels from 3 to 2 (one classification + ai-triaged) - Add scripts/corrections/README.md - Clarify test timestamps relative to frozen clock - Remove 'other' from CLASSIFICATION_LABELS constant Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent cbe9ba9 commit 71c2e1d

6 files changed

Lines changed: 107 additions & 49 deletions

File tree

.github/workflows/issue-classification.lock.yml

Lines changed: 26 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.github/workflows/issue-classification.md

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,6 @@ on:
99
description: "Issue number to triage"
1010
required: true
1111
type: string
12-
mode:
13-
description: "Execution mode: live (default) or eval (future use)"
14-
required: false
15-
type: string
16-
default: "live"
17-
snapshot_text:
18-
description: "Issue snapshot JSON for eval mode (future use): {title, body, author}"
19-
required: false
20-
type: string
2112
roles: all
2213
permissions:
2314
contents: read
@@ -30,8 +21,11 @@ tools:
3021
safe-outputs:
3122
staged: true
3223
add-labels:
33-
allowed: [bug, enhancement, question, documentation, other, ai-triaged]
34-
max: 3
24+
allowed: [bug, enhancement, question, documentation, ai-triaged]
25+
max: 2
26+
target: triggering
27+
add-comment:
28+
max: 1
3529
target: triggering
3630
timeout-minutes: 10
3731
---
@@ -40,18 +34,18 @@ timeout-minutes: 10
4034

4135
You are an AI agent that classifies newly opened issues in the copilot-sdk repository.
4236

43-
Your **only** job is to apply labels. You do not post comments, close issues, or modify issues in any other way.
37+
Your **only** job is to apply labels and, when necessary, leave a brief comment. You do not close issues or modify them in any other way.
4438

4539
## Your Task
4640

4741
1. Fetch the full issue content using GitHub tools
4842
2. Read the issue title, body, and author information
49-
3. Follow the classification instructions below to determine the correct labels
50-
4. Apply the labels
43+
3. Follow the classification instructions below to determine the correct classification
44+
4. Take action:
45+
- If the issue fits one of the established categories (`bug`, `enhancement`, `question`, `documentation`): apply that label **and** the `ai-triaged` label
46+
- If the issue does **not** clearly fit any category: do **not** apply a classification label. Instead, leave a brief comment explaining why the issue couldn't be classified and that a human will review it. Still apply the `ai-triaged` label.
5147

52-
You must apply:
53-
- **Exactly one** classification label (`bug`, `enhancement`, `question`, `documentation`, or `other`)
54-
- **The `ai-triaged` label** (always, alongside the classification label)
48+
You must always apply the `ai-triaged` label.
5549

5650
{{#import shared/triage-classification.md}}
5751

.github/workflows/shared/triage-classification.md

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ You are classifying issues for the **copilot-sdk** repository — a multi-langua
44

55
## Classification Labels
66

7-
Apply **exactly one** of these routing labels to each issue:
7+
Apply **exactly one** of these routing labels to each issue. If none fit, see "Unclassifiable Issues" below.
88

99
### `bug`
1010
Something isn't working correctly. The issue describes unexpected behavior, errors, crashes, or regressions in existing functionality.
@@ -38,13 +38,9 @@ Examples:
3838
- "API reference for session.ui is outdated"
3939
- "Add migration guide from v1 to v2"
4040

41-
### `other`
42-
The issue doesn't clearly fit any of the above categories. Use this for meta discussions, process questions, infrastructure issues, or anything that doesn't map to a specific routing category.
41+
## Unclassifiable Issues
4342

44-
Examples:
45-
- "Proposal to restructure the monorepo"
46-
- "CI is failing on the main branch"
47-
- "License question about commercial use"
43+
If the issue doesn't clearly fit any of the above categories (e.g., meta discussions, process questions, infrastructure issues, license questions), do **not** apply a classification label. Instead, leave a brief comment explaining why the issue couldn't be automatically classified and that a human will review it.
4844

4945
## Classification Guidelines
5046

scripts/corrections/README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Correction Tracking Scripts
2+
3+
TypeScript scripts that detect and record human corrections to the AI triage agent's issue classifications.
4+
5+
## How it works
6+
7+
When the AI triage agent classifies an issue, it applies a classification label (`bug`, `enhancement`, `question`, `documentation`) plus `ai-triaged`. If a maintainer disagrees, they remove the agent's label and apply the correct one. This system detects that change and records it as a **correction**.
8+
9+
### Detection logic
10+
11+
- **Correction**: A classification label is added while `ai-triaged` is present, and the timeline shows the same actor removed a different classification label within the last 2 minutes.
12+
- **Confirmation**: `ai-triaged` is removed without changing the classification label — the maintainer agrees with the agent.
13+
- **Late comments**: If the corrector adds a follow-up comment, it's appended to the correction record as context.
14+
15+
### Where corrections go
16+
17+
Corrections are stored as JSON files (`evals/corrections/issue-{N}.json`) on a `triage-corrections` branch. A single PR accumulates all corrections for human review before merging.
18+
19+
### Guards
20+
21+
- Only users with `write`, `maintain`, or `admin` permission can record corrections
22+
- Bot-triggered label events are ignored
23+
- Non-classification labels (e.g., `priority/high`) are ignored
24+
25+
## Development
26+
27+
```bash
28+
npm ci # install dependencies
29+
npm run build # compile TypeScript → dist/
30+
npm test # run tests (vitest)
31+
npm run typecheck # type-check without emitting
32+
```
33+
34+
Or from the repo root:
35+
36+
```bash
37+
just install-corrections
38+
just test-corrections
39+
just lint-corrections
40+
```
41+
42+
## Files
43+
44+
| File | Purpose |
45+
|------|---------|
46+
| `src/track-correction.ts` | Main detection logic (correction vs confirmation) |
47+
| `src/write-correction.ts` | Branch management, file writing, PR creation |
48+
| `src/update-context-comments.ts` | Appends late justification comments |
49+
| `src/types.ts` | Shared interfaces and constants |
50+
| `src/github-types.ts` | Type aliases for `actions/github-script` params |
51+
| `src/test-helpers.ts` | Mock factories for unit tests |
52+
| `src/integration.test.ts` | End-to-end scenarios with recording mock client |
53+
54+
## Workflow
55+
56+
These scripts are called by `.github/workflows/track-correction.yml`, which triggers on `issues.labeled`, `issues.unlabeled`, and `issue_comment.created` events. The workflow builds the TypeScript, then runs the compiled JS via `actions/github-script`.

scripts/corrections/src/track-correction.test.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,15 @@ describe('trackCorrection', () => {
5353
currentLabels: ['bug', 'ai-triaged'],
5454
});
5555

56-
// Set up as a correction scenario with timeline
57-
const twoSecondsAgo = new Date('2026-01-15T11:59:58Z').toISOString();
56+
// System time is frozen at 2026-01-15T12:00:00Z (see beforeEach)
57+
const withinWindow = new Date('2026-01-15T11:59:58Z').toISOString(); // 2s before frozen clock
5858
github.paginate
5959
.mockResolvedValueOnce([
6060
{
6161
event: 'unlabeled',
6262
label: { name: 'enhancement' },
6363
actor: { login: 'maintainer' },
64-
created_at: twoSecondsAgo,
64+
created_at: withinWindow,
6565
},
6666
])
6767
.mockResolvedValueOnce([]); // comments
@@ -119,21 +119,21 @@ describe('trackCorrection', () => {
119119
currentLabels: ['bug', 'ai-triaged'],
120120
});
121121

122-
const twoSecondsAgo = new Date('2026-01-15T11:59:58Z').toISOString();
122+
const withinWindow = new Date('2026-01-15T11:59:58Z').toISOString(); // 2s before frozen clock
123123
github.paginate
124124
.mockResolvedValueOnce([
125125
{
126126
event: 'unlabeled',
127127
label: { name: 'enhancement' },
128128
actor: { login: 'maintainer' },
129-
created_at: twoSecondsAgo,
129+
created_at: withinWindow,
130130
},
131131
])
132132
.mockResolvedValueOnce([
133133
{
134134
user: { login: 'maintainer' },
135135
body: 'This is actually a bug, not an enhancement',
136-
created_at: twoSecondsAgo,
136+
created_at: withinWindow,
137137
},
138138
]);
139139

@@ -179,7 +179,7 @@ describe('trackCorrection', () => {
179179
event: 'unlabeled',
180180
label: { name: 'bug' }, // same label
181181
actor: { login: 'maintainer' },
182-
created_at: new Date('2026-01-15T11:59:58Z').toISOString(),
182+
created_at: new Date('2026-01-15T11:59:58Z').toISOString(), // within 2-min window
183183
},
184184
]);
185185

@@ -199,7 +199,7 @@ describe('trackCorrection', () => {
199199
currentLabels: ['bug', 'ai-triaged'],
200200
});
201201

202-
const recentTimestamp = new Date('2026-01-15T11:59:58Z').toISOString();
202+
const recentTimestamp = new Date('2026-01-15T11:59:58Z').toISOString(); // within 2-min window
203203
github.paginate
204204
.mockResolvedValueOnce([
205205
{
@@ -250,8 +250,8 @@ describe('trackCorrection', () => {
250250
);
251251
});
252252

253-
// --- Timeline window ---
254-
it('ignores old unlabel events outside 2-minute window', async () => {
253+
// --- Timeline window (events must be within 2 minutes of current time) ---
254+
it('ignores unlabel events outside 2-minute window', async () => {
255255
const context = mockLabelContext({
256256
action: 'labeled',
257257
labelName: 'bug',

scripts/corrections/src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ export interface ContextComment {
2020
created_at: string;
2121
}
2222

23-
export const CLASSIFICATION_LABELS = ['bug', 'enhancement', 'question', 'documentation', 'other'];
23+
export const CLASSIFICATION_LABELS = ['bug', 'enhancement', 'question', 'documentation'];
2424
export const REVIEW_LABEL = 'ai-triaged';
2525
export const CORRECTIONS_BRANCH = 'triage-corrections';

0 commit comments

Comments
 (0)