Skip to content
Draft
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
65 changes: 54 additions & 11 deletions .agents/skills/triage/comment.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ Generate a GitHub issue comment from triage findings.

**SCOPE: Your job is comment generation only. Finish your work once you've completed this workflow. Do NOT go further than this. It is no longer time to attempt reproduction, diagnosis, or fixing of the issue. Do not spawn tasks/sub-agents.**

**HARD SCOPE RULES — do not violate these even if `report.md` is missing or sparse:**

- Do NOT read, grep, or explore any file in `packages/` or the wider codebase to fill in missing analysis.
- Do NOT run reproductions, builds, tests, or git/gh commands to fetch additional context beyond what's already in `issueDetails` and `report.md`.
- Do NOT invent investigation that was not actually performed by an earlier skill. If `report.md` is missing do not do the investigation yourself to compensate.
- You may only read: `report.md`, `issueDetails`, and the args passed in. Nothing else. If the material isn't there, fall back (see below).

## Prerequisites

These variables are referenced throughout this skill. They may be passed as args by an orchestrator, or inferred from the conversation when run standalone.
Expand All @@ -25,20 +32,48 @@ These variables are referenced throughout this skill. They may be passed as args

Read `report.md` from the `triageDir` directory. This file is the shared context log — each previous skill (reproduce, diagnose, fix) appends its findings to it.

If `report.md` is missing or empty, generate a minimal comment (see "Fallback" section below).
If `report.md` is missing or empty, generate a minimal comment using the Fallback template (see below). **Do not investigate the issue yourself to compensate.** A missing report means the pipeline exited early (e.g. reproduction was skipped). Report that honestly — do not substitute your own exploration.

### Fallback (missing or empty `report.md`)

If `report.md` is missing or empty, return only this comment — no extended analysis, no "Full Triage Report" section:

```markdown
- **Reproduced:** Skipped
- **Exploration:** Skipped
- **Priority:** **[select from priorityLabels, default to lowest reasonable].** [short phrase]

Automated triage could not be completed for this issue. No reproduction or diagnosis data is available.

_This report was made by an LLM. The analysis may be wrong, and the potential fix might not work, but is intended as a starting point for exploring the issue._
```

## Step 2: Generate Comment

Generate and return a GitHub comment following the template below.

### Writing Style — Be Concise

Maintainers scan this comment in seconds. Every word has to earn its place.

- **Prefer short phrases over full sentences** in the bullets. A bullet is a label, not a paragraph.
- **Do not use em dashes (`—`) to extend a sentence** with extra clauses. If you're tempted to add " — and also ..." or " — which means ...", stop and cut it.
- **One idea per sentence.** If a sentence has two clauses joined by a dash, comma, or "which", split or delete one.
- **No hedging filler.** Drop phrases like "it appears that", "it seems like", "in some cases", "under certain conditions" unless they carry information.
- **Name things directly.** `astro build` crashes on page with `getStaticPaths` > `the build process encounters an issue when a page uses getStaticPaths`.

A good bullet reads like a log line. A good explanation sentence reads like a commit message.

### "Fix" Instructions

The **Fix** line in the template has three possible forms. Choose the one that matches the triage outcome:
Choose the form that matches the triage outcome. Keep the follow-up sentence to **one short sentence, two max** — no em-dash clauses tacked on.

1. **You created a fix:** Use `I found a potential fix for this issue.` and include the suggested fix link. Avoid claiming certainty, even if the fix passes tests, frame it as a suggestion that needs human review.
1. **You created a fix:** Use `I found a potential fix for this issue.` and include the suggested fix link. Frame it as a suggestion needing human review, even if it passes tests.
2. **The issue is already fixed on main** (e.g. the user is on an older major version and the bug doesn't reproduce on current main): Use `This issue has already been fixed.` and tell the user how to get the fix (e.g. upgrade). Link the relevant upgrade guide if applicable: [v6](https://docs.astro.build/en/guides/upgrade-to/v6/), [v5](https://docs.astro.build/en/guides/upgrade-to/v5/).
3. **Low-confidence or no fix:** Use `I wasn't able to find a fix, but I identified some areas that may be relevant.` and list the files/code paths that seem related. Frame this as a jumping-off point for a human, not a diagnosis. If a failing test was added, mention it.
4. **No leads at all:** Use `I was unable to determine the cause of this issue.` This should be rare, only use it when you genuinely have nothing useful to point to.
3. **Low-confidence or no fix:** Use `I wasn't able to find a fix, but I identified some areas that may be relevant.` and list the files/code paths that seem related. Frame this as a jumping-off point, not a diagnosis. If a failing test was added, mention it.
4. **No leads at all:** Use `I was unable to determine the cause of this issue.` Rare — only use it when you genuinely have nothing to point to.

**Do not suggest workflow-breaking workarounds.** If the user explicitly configured something (e.g. `prerenderEnvironment: 'node'`, a specific adapter, an opt-in feature, an integration), do NOT tell them to remove the option, use the default, disable the feature, or switch away from their stack. They chose that configuration deliberately. "Omit the option" or "use the default instead" is not a fix — it abandons the user's use case. The only acceptable guidance is toward a real fix, an upgrade, or an honest "no fix identified yet."

### "Priority" Instructions

Expand All @@ -57,16 +92,24 @@ Select exactly ONE priority label from the `priorityLabels` arg. Use the label d

### Template

The comment must start with an at-a-glance summary, followed by short explanations, then the full report in a collapsible section. Keep the top section scannable, a maintainer should understand the status in under 5 seconds and be able to quickly jump into fixing the issue.
The comment must start with an at-a-glance summary, followed by short explanations, then the full report in a collapsible section. Keep the top section scannable — a maintainer should understand the status in under 5 seconds.

**Bullet rules:** each bullet after its label must be a short phrase, not a sentence. No em dashes stretching the phrase into a second clause. If you can't say it in ~10 words, cut it.

**Exploration bullet is driven by `branchName`, not by narrative:**

- If `branchName` is **null**: `Exploration` MUST be `Skipped`. Do not write "Partial" or "Yes" when there is no branch — the push stage found nothing worth committing, which means no real exploration output exists. A written analysis in `report.md` without any code changes is NOT exploration for the purposes of this bullet.
- If `branchName` is **non-null**: use `Yes`, `Partial`, or `Already fixed on main` as appropriate, and append the `— [View branch](...)` link.
- Never claim exploration that a maintainer cannot click through to verify.

```markdown
- **Reproduced:** [Yes / No / Skipped reason]
- **Exploration:** [Yes / No / Partial / Already fixed on main] [If `branchName` is non-null: — [View branch](https://github.com/withastro/astro/compare/{branchName}?expand=1)]
- **Priority:** [See "Priority" Instructions above. Keep to one line explaining why this priority was chosen, who is likely to be affected, and under what conditions (this section should answer the question: "how bad is it?")]
- **Reproduced:** [Yes / No / Skipped: <short reason>]
- **Exploration:** [Yes / Partial / Already fixed on main — [View branch](https://github.com/withastro/astro/compare/{branchName}?expand=1) OR Skipped (if branchName is null)]
- **Priority:** **[label name].** [one short phrase on who's affected — max ~15 words, no em-dash tail clause]

[2-3 sentences describing the root cause or key observations, or where/when it was already fixed. Be specific about what's happening and where in the codebase.]
[2-3 short sentences on the root cause or where it's already fixed. Name the file/function. No em-dash subclauses. Cut anything that isn't load-bearing.]

**[See "Fix" Instructions above.]** [1-2 sentences describing the fix in more detail: what was changed, guidance on where a fix might be, or relevant code areas.]
**[See "Fix" Instructions above.]** [One short sentence, two max, on the fix or the relevant code area.]

<details>
<summary><em>Full Triage Report</em></summary>
Expand Down
1 change: 1 addition & 0 deletions .agents/skills/triage/diagnose.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ Consider:
- Does this affect other similar use cases?
- Are there edge cases to consider?
- Never suggest removing a user's dependency (adapters, framework integrations, features like MDX or DB) as a fix, those are things the user needs. The fix must work within the user's existing stack and expected feature-set.
- Never suggest that the user change a configuration value they explicitly set (e.g. "use the default", "omit this option", "switch to X instead"). For example, if they opted into `prerenderEnvironment: 'node'` or a specific adapter option, they want that value. The fix must make their chosen configuration work, not route around it.

**Tone calibration:** Describe the root cause factually, not dramatically. Avoid language that overstates impact ("critical flaw", "fundamentally broken", "severe vulnerability") unless the evidence genuinely supports it. A missing null check is a missing null check, not a "critical oversight in the rendering pipeline." The diagnosis should help a maintainer understand what's wrong, guiding them towards a fix, not alarm them.

Expand Down
2 changes: 2 additions & 0 deletions .agents/skills/triage/fix.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ Make changes in `packages/` source files. Follow these principles:
- Don't refactor unrelated code
- Don't add new features
- **Never "fix" an issue by removing a user's dependency.** Removing an adapter (Cloudflare, Netlify, Vercel, etc.), framework integration (Svelte, React, Vue, etc.), or feature (MDX, DB, etc.) is not a fix, these are things the user needs. The fix must work within the user's existing stack or expected feature set.
- **Never "fix" an issue by telling the user to change their configuration.** If the user explicitly set a config option (e.g. `prerenderEnvironment: 'node'`, a specific adapter option, an opt-in flag), they chose that value deliberately. "Use the default", "omit this option", "switch to X instead" is NOT a fix — it abandons their use case. The fix must make their chosen configuration work. If you cannot, prefer the low-confidence / breadcrumb path over suggesting a workflow-breaking workaround.

**Consider edge cases:**

Expand Down Expand Up @@ -145,6 +146,7 @@ The report must include all information needed for a final GitHub comment to be
- Debug code, `console.log`s, or temporary test files
- Changes outside `packages/` that were only needed for diagnosis/reproduction
- Build artifacts that shouldn't be committed
- **Lockfile churn: if `pnpm-lock.yaml` appears in `git status` and you did NOT intentionally modify it as part of the fix (a dependency pin, bump, or resolution change), revert it with `git checkout -- pnpm-lock.yaml`.** The install step in the reproduce skill can rewrite the lockfile as a side effect; that churn must not end up on the fix branch. Only keep the lockfile diff if the fix itself includes a lockfile change.
3. Use `git checkout -- <file>` to discard unwanted changes
4. Confirm with a final `git status` that only the intended fix files remain
5. DO NOT commit or push anything yet! The user will handle that at a later step.
Expand Down
10 changes: 10 additions & 0 deletions .agents/skills/triage/reproduce.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,13 @@ The report must include all information needed for a final GitHub comment to be
- Expected vs actual result
- Error messages and stack traces
- Whether the issue was reproduced, not reproduced, or skipped (and why)

### Structured return value

In addition to writing `report.md`, your return value MUST include the skip reason when applicable:

- `reproducible: boolean` — true only if you successfully triggered the bug
- `skipped: boolean` — true if you hit any early-exit condition from Step 2
- `skipReason: string | null` — when `skipped` is true, return the exact reason code from Step 2 (`not-actionable`, `missing-details`, `unsupported-version`, `host-specific`, `unsupported-runtime`, `maintainer-override`). Otherwise null.

The orchestrator branches on `skipReason`, so returning the correct code matters — don't collapse `missing-details` into a generic skip.
44 changes: 44 additions & 0 deletions .flue/workflows/issue-triage/WORKFLOW.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,17 @@ ${comment}
return [labelResult.priority, ...labelResult.packages];
}

const skipReasonCodes = [
'not-actionable',
'missing-details',
'unsupported-version',
'host-specific',
'unsupported-runtime',
'maintainer-override',
] as const;

type SkipReason = (typeof skipReasonCodes)[number];

async function runTriagePipeline(
flue: FlueClient,
issueNumber: number,
Expand All @@ -122,6 +133,7 @@ async function runTriagePipeline(
completedStage: 'reproduce' | 'verify' | 'fix';
reproducible: boolean;
skipped: boolean;
skipReason: SkipReason | null;
verdict: 'bug' | 'intended-behavior' | 'unclear' | null;
diagnosisConfidence: 'high' | 'medium' | 'low' | null;
fixed: boolean;
Expand All @@ -140,14 +152,29 @@ async function runTriagePipeline(
'true if reproduction was intentionally skipped (host-specific, unsupported version, etc.)',
),
),
skipReason: v.pipe(
v.nullable(v.picklist(skipReasonCodes)),
v.description(
'The specific early-exit reason code when skipped=true, null otherwise. Must match one of the documented codes in reproduce.md.',
),
),
}),
});

// Reproduce runs `pnpm install --no-frozen-lockfile` at the monorepo root,
// which rewrites `pnpm-lock.yaml` as a side effect of wiring up the triage
// project. That churn is not triage output and must not end up on the fix
// branch. Revert it here so the post-pipeline working tree only contains
// intentional changes made by diagnose/verify/fix. `node_modules` is already
// installed, so downstream skills still work.
await flue.shell('git checkout -- pnpm-lock.yaml');

if (reproduceResult.skipped || !reproduceResult.reproducible) {
return {
completedStage: 'reproduce',
reproducible: reproduceResult.reproducible,
skipped: reproduceResult.skipped,
skipReason: reproduceResult.skipReason,
verdict: null,
diagnosisConfidence: null,
fixed: false,
Expand Down Expand Up @@ -183,6 +210,7 @@ async function runTriagePipeline(
completedStage: 'verify',
reproducible: true,
skipped: false,
skipReason: null,
verdict: verifyResult.verdict,
diagnosisConfidence: diagnoseResult.confidence,
fixed: false,
Expand All @@ -209,13 +237,18 @@ async function runTriagePipeline(
completedStage: 'fix',
reproducible: true,
skipped: false,
skipReason: null,
verdict: verifyResult.verdict,
diagnosisConfidence: diagnoseResult.confidence,
fixed: fixResult.fixed,
commitMessage: fixResult.commitMessage,
};
}

const MISSING_REPRO_COMMENT = `Automated triage could not run: this issue is missing a reproduction. Applied the \`needs repro\` label.

_This comment was generated by an automated triage bot._`;

export const args = v.object({
issueNumber: v.number(),
});
Expand All @@ -239,6 +272,17 @@ export default async function triage(

// Run the triage pipeline: reproduce → diagnose → verify → fix
const triageResult = await runTriagePipeline(flue, issueNumber, issueDetails);

// Short-circuit: issue is missing a reproduction. Skip LLM comment generation
// entirely — post a fixed canned message, swap `needs triage` for `needs repro`,
// and exit. Other skip reasons fall through to the normal comment path below.
if (triageResult.skipReason === 'missing-details') {
await postGitHubComment(issueNumber, MISSING_REPRO_COMMENT);
await removeGitHubLabel(issueNumber, 'needs triage');
await addGitHubLabels(issueNumber, ['needs repro']);
return { ...triageResult, isPushed: false };
}

let isPushed = false;

// Push the fix branch if there are meaningful changes (fix, failing test, etc.).
Expand Down
Loading