with one click
sentry-triage
// Run Fern CLI Sentry false-positive triage for buildwithfern/cli. Use when investigating Sentry CLI issues, classifying false positives, updating the triage ledger, or creating PRs for Sentry error fixes.
// Run Fern CLI Sentry false-positive triage for buildwithfern/cli. Use when investigating Sentry CLI issues, classifying false positives, updating the triage ledger, or creating PRs for Sentry error fixes.
| name | sentry-triage |
| description | Run Fern CLI Sentry false-positive triage for buildwithfern/cli. Use when investigating Sentry CLI issues, classifying false positives, updating the triage ledger, or creating PRs for Sentry error fixes. |
buildwithfernclipnpm exec sentry-cli.Read these before proposing code changes:
.devin/automation/sentry-triage/DESIGN_CHOICES.md — general principles, not pre-written solutions..devin/automation/sentry-triage/ledger/ — one JSON file per shortId (e.g. ledger/CLI-2W.json); plus ledger/_meta.json for repo-wide constants. Read only the rows you need..devin/automation/sentry-triage/SUBAGENT_CONTRACT.md — passed to every spawned subagent.The ledger is one file per shortId. Parallel subagent branches touch disjoint files, so merge conflicts are eliminated by construction. Do not load or rewrite the whole directory unless a task explicitly requires broad record maintenance.
Classification authority lives in the subagent, not the parent. The parent does cheap, structural decisions (ledger filter, dedupe, grouping hypothesis). The subagent has the code in front of it and owns the actual disposition call — including keep_sentry, ignored, pending_review, and "the parent's grouping was wrong." The parent's Group Plan is a working hypothesis, not a binding classification.
pnpm exec sentry-cli issues list \
--org buildwithfern \
--project cli \
--status unresolved
pnpm exec sentry-cli issues list \
--org buildwithfern \
--project cli \
--id <ISSUE_ID>
If the baked CLI cannot show enough detail for a shortId, use the Sentry API through pnpm exec sentry-cli or an authenticated project-approved Sentry command. Keep payloads small.
shortIds from Sentry.for id in CLI-2W CLI-2V; do
path=".devin/automation/sentry-triage/ledger/$id.json"
if [[ -f "$path" ]]; then
jq --arg shortId "$id" '{shortId: $shortId, disposition, duplicateOf, problemSignature, prOrIssue}' "$path"
else
jq -n --arg shortId "$id" '{shortId: $shortId, status: "unknown"}'
fi
done
shortId files with terminal disposition: shipped, ignored, duplicate, or keep_sentry.pending_review.problemSignature text is only a hint. Never treat a similar past issue as proof the current shortId is fixed.ls .devin/automation/sentry-triage/ledger/CLI-*.json. _meta.json holds repo-wide constants and rarely changes.After the ledger filter, run the dedupe script for every remaining shortId. Do not run ad-hoc gh searches for this purpose.
gh auth status
.devin/automation/sentry-triage/scripts/find-existing-pr.sh CLI-44 CLI-4T
Steps the script performs, per shortId:
prOrIssue and disposition from ledger/<SHORT_ID>.json; if prOrIssue is a PR URL, fetch PR state (any branch).gh pr list call per run for open PRs with head branch prefix fix/cli-sentry-triage/; per shortId, match when the branch name, title, or body contains the id (case-insensitive).files, title, body, state, headRefName.PR discovery does not search all open PRs in the repo — only triage-shaped branches (plus ledger URLs).
Stdout is a JSON array (one object per shortId with ledger and candidates). For each shortId, judge against the boundary you identified:
none — no candidate covers the boundary; eligible for a subagent.covered — a candidate PR's diff/title/body matches the group's boundary and fix; record the prOrIssue update for the parent reconciliation PR (do not spawn).partial — some shortIds covered, others not; split the group and only spawn for uncovered shortIds.If the script exits non-zero (auth failure, rate limit, network), halt the run and ask a human to fix gh auth or rate before proceeding. The --limit 100 triage-branch cap is unlikely to be hit; if exceeded, narrow the filter and re-run.
Env vars: FERN_REPO (default fern-api/fern), LEDGER_DIR (default .devin/automation/sentry-triage/ledger).
Group by call site, not by title alone.
shortIds may share one PR only when the stack proves the same function or boundary produces the same false-positive classification.problemSignature.Parent investigation scope. Parent reads Sentry stacks and may optionally grep the codebase for the top frame. Do not open source files — the subagent does the source-code reading. The Group Plan is built from Sentry data and ledger context, nothing deeper.
Before spawning, write one Group Plan block per group. The plan is the parent's working hypothesis; the subagent verifies it against code and may return any disposition.
Group: <call site or function — hypothesis from top stack frame>
ShortIds: <CLI-XX, CLI-YY>
Evidence:
- top frames (>=3, with file:line where available)
- exception type and message
- breadcrumb tail if relevant
- event count and lastSeen
ExistingPR: <none | PR URL and why it covers/does not cover this group>
Branch: fix/cli-sentry-triage/<YYYY-MM-DD>-<up-to-3-short-ids-kebab>
Evidence is the subagent's only window into Sentry — the subagent is forbidden from refetching, so include everything it needs to locate and verify the boundary. Paste the data as text; no need for structured JSON.
The parent does not propose a fix or pre-match DESIGN_CHOICES.md principles — the subagent owns both once it reads the code. The parent contributes only what it can reliably produce from Sentry data: the call-site hypothesis, the ShortIds set, the raw evidence, the dedupe result, and the branch name. The subagent surfaces any new principle worth recording via newPrincipleProposal.
Any group with no ExistingPR coverage and no terminal ledger row is eligible for a subagent. The parent must not pre-classify groups as keep_sentry, ignored, or pending_review to avoid spawning; those decisions belong to the subagent. The only parent-side shortcuts are:
shipped, duplicate, keep_sentry, ignored).prOrIssue for the parent reconciliation PR).Anything else gets a subagent.
One subagent per eligible group. Strict 1:1, no exceptions.
For every eligible group from Grouping Rule, the parent spawns exactly one subagent. The mapping is:
| 1 Group Plan | 1 subagent | 1 branch | 1 PR | 1 disjoint set of ShortIds |
Task tool with run_in_background: true, called once per group within a single message.SUBAGENT_CONTRACT.md. Pass only that file's path plus one Group Plan block. Never paste multiple Group Plans into one spawn prompt.main. Branches must not be stacked across groups.ShortIds only.Anti-patterns (do not do these):
ShortIds from different call sites into one "miscellaneous" group to spawn fewer subagents.If the parent is uncertain a group is well-formed, leave it unspawned and roll it into the next run — skipping is better than merging groups.
Parent spawn prompt (uniform for every group, one spawn per group):
Read .devin/automation/sentry-triage/SUBAGENT_CONTRACT.md and follow it.
Group Plan:
<paste the single Group Plan block here>
shortId scheme (each shortId belongs to exactly one group).shortId across separate files (ledger/<SHORT_ID>.json); parallel subagents never touch the same file. This is the primary reason for the per-file layout.DESIGN_CHOICES.md is parent-only. Subagents return newPrincipleProposal instead of editing it.ledger/_meta.json is parent-only and rarely edited.After all subagents finish:
Collect each subagent's prUrl (or skipped: <reason>), shortIdsTouched, principlesApplied, newPrincipleProposal.
Ledger collision check (per-file layout makes this trivial): no two open triage PRs should include the same ledger/CLI-XX.json file. Verify with gh pr view <num> --json files against each open triage PR.
Print a per-group summary: group label, ShortIds, PR URL or skip reason.
Optional parent reconciliation PR (only when there is something to write):
prOrIssue updates for groups the parent marked covered during dedupe.DESIGN_CHOICES.md bullets surfaced via newPrincipleProposal, after human review.The reconciliation PR contains no code changes, only ledger pointer updates and/or DESIGN_CHOICES.md edits. Branch: chore/cli-sentry-triage/<YYYY-MM-DD>-reconcile.
ShortIds still unresolved and an open triage PR already exists at the right boundary, push additional commits to that branch rather than creating a new PR (host-agent convention). Most cases never reach here because terminal ledger rows are skipped at the ledger-filter step.fix/cli-sentry-triage/{date}-<short-ids> (e.g. fix/cli-sentry-triage/2026-11-03-cli-44-cli-4t-cli-4v).YYYY-MM-DD) and at most three shortIds in the branch name, lowercased and kebab-cased.shortIds. If more than three, list the first three and summarize the family.shortId and a short fix note.shortId files under .devin/automation/sentry-triage/ledger/ for that group, in the same PR as the code. Never touch other rows' files or _meta.json.packages/cli/cli/changes/unreleased/ when CLI behavior or reporting changes.One file per issue at .devin/automation/sentry-triage/ledger/<SHORT_ID>.json. Required fields:
titleproblemSignaturedispositionrationalefixSummaryprOrIssuelastAnalyzedduplicateOf when disposition is duplicateThe shortId is the filename (no shortId field inside the file). ledger/_meta.json holds repo-wide constants (schemaVersion, project) and is rarely edited.
Disposition values: shipped, duplicate, keep_sentry, ignored, pending_review. pending_review is the only non-terminal value (re-processed next run); the rest are terminal. Full decision rules live in SUBAGENT_CONTRACT.md (Choosing a disposition).
Do not mark Sentry issues resolved unless the exact shipped CLI release is known and explicitly provided. Prefer explicit release resolution over "next release".