# Sentry Triage

> This skill should be used when the user asks to "triage Sentry", "pull top unresolved Sentry issues", "what's blowing up in Sentry", "which Sentry errors qualify for a GitHub issue", "cross-reference Sentry with GitHub", "escalate Sentry hits to the backlog", or any task that applies a qualifying gate (unresolved, recent, user/event thresholds) to Sentry issues and optionally files GitHub issues from the survivors. Also triggers when a project workflow skill (a generated survey-work) invokes sassy-dog:sentry-triage by name. For Sentry SDK setup or open-ended natural-language Sentry exploration, defer to the sentry plugin's own skills.

- Skill: `sassy-dog/sentry-triage` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add sassy-dog/sentry-triage`
- Raw SKILL.md: https://api.skillmd.com/api/skills/sassy-dog/sentry-triage/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Product & Planning
- Author: Sassy-Dog (https://skillmd.com/u/sassy-dog)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/sassy-dog/sentry-triage

---


# Sentry Triage

Gate-and-escalate triage: pull not-resolved Sentry issues for a project, apply a qualifying gate, cross-reference against GitHub, and (only with explicit approval) escalate survivors to GitHub issues. Reads only — this skill never mutates Sentry state.

## Inputs to establish first

1. **Org + project slug(s)** — from the caller or by listing projects. Don't hunt for phantom projects: platforms sharing a DSN don't appear separately.
2. **Gate policy** — the caller's thresholds, or the defaults in `references/qualifying-gate.md`.
3. **Escalation target** — repo (and optional board IDs) if filing is on the table; otherwise this is a read-only report.

## Workflow

### 1. Pull (discovery only)

**MCP-first**: use the connected Sentry MCP server's project-listing and issue-search tools (resolve the project slug first, then search). Do not hardcode `mcp__...` tool IDs — resolve by capability. No MCP connected → `references/api-fallback.md` (REST with `SENTRY_AUTH_TOKEN`).

Query `!is:resolved` — one call covers unresolved + ignored (status is exclusive; boolean `OR` returns HTTP 400 — full syntax notes in `references/query-syntax.md`). Pull per issue: `shortId, title, count, userCount, lastSeen, firstSeen, level, culprit, permalink, status`.

**Issue search is a DISCOVERY tool, never a SCORING tool.** It tells you *which* fingerprints exist; its counts must never reach a gate or a plate. The MCP's `search_issues` renders Events and Users **scoped to its `period` argument**, labelled identically to lifetime totals and with nothing in the output marking them as windowed — so a long-running issue reads as a one-off. Measured on `Sassy-Dog/velovate`, 2026-08-20 and re-verified live 2026-08-21: `VELOVATE-WORKERS-2` returned 1 event / 1 user against a true 30 occurrences / 3 users, a **30× undercount**, and the 30d pull returned 1 event too — widening `period` is not a workaround (issue #218). **`firstSeen` is rescaled the same way**: search reported it as *2 days ago* against a true first-seen of 2026-06-25, 57 days earlier, so a long-running fingerprint reads as a fresh regression from the latest deploy. On the true numbers that issue qualifies (production environment, 3 users ≥ 2); on the windowed pair it fails every clause of the severity rule. The gate outcome inverts, silently. The same names mean different things per transport, and REST is the one that is already lifetime; the table in `references/api-fallback.md` is the authority for both.

**Then resolve environments before gating** (gate rule 6). Issue search does not return an environment breakdown, and an issue can span several, so ask the events dataset once per project rather than per issue: an aggregate over the `errors` dataset with fields `["environment", "count()"]` grouped by environment, scoped to the same window. An issue whose events are *entirely* in a non-production environment is `skip-nonprod`. Skipping this step is not a smaller version of the gate — it is the version that ranks CI noise as a customer-facing outage, because a CI environment manufactures `userCount` (see the rule 6 rationale).

### 2. Confirm counts (gate rule 0)

Apply the **count-independent** gate rules first — status, staleness, parked, environment (rules 1, 2, 5, 6) — then fetch true lifetime `Occurrences` and `Users Impacted` for **every survivor**, via the connected server's resource-fetch capability (`get_sentry_resource` with `resourceType='issue'` and the SHORT-ID; resolve by capability, never a literal `mcp__...` id). Only then apply the count rule.

**Never use a windowed count to decide which issues are worth confirming.** That is the defect, not a shortcut around it: the issues that most need confirming are precisely the ones a window under-reports, so any pre-filter computed from the search counts re-creates the bug one step earlier. The ordering above exists so the cheap rules — none of which reads a count — do the narrowing instead.

An issue whose counts could not be confirmed is `skip-unconfirmed`: it never qualifies, and it is **reported, never dropped**. Unconfirmed is not clean and it is not noise; it is a number this skill declined to trust.

### 3. Gate

Read `references/qualifying-gate.md`; apply each rule and tag non-qualifiers (`skip-noise` / `skip-stale` / `skip-parked` / `skip-nonprod` / `skip-unconfirmed` / `already-linked`) so the report shows *why* something didn't escalate, not just that it didn't.

Cross-reference against GitHub by marker: `gh issue list --search '"sentry-source: <SHORT_ID>" in:body' --state all`. **That search is a hint, not a verdict** — it matches a token subsequence, so it also hits `sentry-source: <SHORT_ID>-<something>`; a hit may annotate the preview but must never drop a candidate. `file-or-link-issue.sh` owns the `already-linked` decision (see rule 4 in `references/qualifying-gate.md`). If the caller also pulled TestFlight crash feedback, fuzzy-merge on `culprit`/stack signature before reporting — the same crash in two surfaces is one item, not two.

### 4. Report

One line per issue: `SHORT_ID · title · events/users · lastSeen · level · gate verdict · GH peer (#N or —)`. Qualifiers first, then skips grouped by reason. No section for empty buckets.

**Every counts figure renders its provenance** — `30/3 (lifetime)` or `1/1 (windowed, unconfirmed)`. A confirmed and an unconfirmed count are otherwise indistinguishable on the page, which is how a windowed number gets quoted onward into a plate as though it had been verified.

### 5. Escalate (only if asked, never silently)

Filing goes through `sassy-dog:github-issues` — its `file-or-link-issue.sh` with `--marker "sentry-source: <SHORT_ID>"`, following that skill's preview-then-confirm contract and burst rail (> 5 candidates → stop and summarize). Never raw `gh issue create`.

## Hard prohibitions

- Never resolve, ignore, assign, or otherwise mutate Sentry issues.
- Never escalate `ignored` issues — ignoring was a human decision.
- Never file without a preview the user approved in this run.
- **Never score, rank, escalate, or dismiss an issue on counts this skill has not confirmed as lifetime.**

