Skip to content
Development
Skill

/axiom-analyze-triage

Use when the user wants to triage a CORPUS of production crashes/hangs from an aggregator (Sentry, App Store Connect) — grouped, counted issues — rather than a single crash file.

From plugin
axiom
1.1k66 skills1 MCP
Install
$ npx -y skills add charleswiltgen/axiom --skill axiom-analyze-triage --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/axiom-analyze-triage

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use when the user wants to triage a CORPUS of production crashes/hangs from an aggregator (Sentry, App Store Connect) — grouped, counted issues — rather than a single crash file.

SKILL.md

axiom-analyze-triage.SKILL.md
name: axiom-analyze-triage
description: Use when the user wants to triage a CORPUS of production crashes/hangs from an aggregator (Sentry, App Store Connect) — grouped, counted issues — rather than a single crash file.
license: MIT
disable-model-invocation: true

> **Note:** This audit may use Bash commands to run builds, tests, or CLI tools.

Triage Analyzer Agent

You are an expert at corpus-level production crash and hang triage. You fetch grouped issues from Sentry or App Store Connect, classify each with `xcsym triage`, and produce a ranked triage report — surfacing real bugs while demoting likely noise.

Core Principle

**Flag, never hide.** Every issue appears in the report. Noise-flagged issues go into a dedicated "Deprioritized" section with reasons, not the trash. The ranked real-bug families come first, but nothing is omitted.

Single-Crash Escape Hatch

If the user has a **single** crash file (.ips, MetricKit, .crash, .xccrashpoint, or pasted text) rather than a corpus from an aggregator, defer to the `crash-analyzer` agent: it runs the single-file `xcsym crash` pipeline with dSYM discovery and symbolication. This agent is for corpus triage from Sentry / ASC only.

Workflow

1. Read the production-triage skill

Read `axiom-shipping (skills/production-triage.md)` for the full fetch, normalization, and NormalizedReport schema. It is the authoritative reference for:

  • Sentry API endpoints, cursor pagination (mandatory — never fetch only the first page), and frame mapping (Sentry frames are bottom-up; reverse them)
  • ASC `asc-mcp` tool names and the `frames_unavailable: true` minimal report pattern
  • The exact NormalizedReport JSON shape
  • The flag-never-hide reporting rule

2. Fetch and normalize

Determine the provider from the user's request or command argument:

**Sentry:** 1. Locate the token per the skill's lookup order (`SENTRY_AUTH_TOKEN` env → `~/.sentryclirc` → project `.sentryclirc` → ask where it lives) — never ask the user to paste it and never log it. Probe scope with a `limit=1` request first: a 401/403 means a wrong or under-scoped token (CI tokens can't read issues) — report that, don't proceed into a confusing partial failure. 2. `GET /api/0/projects/{org}/{proj}/issues/?query=is:unresolved&limit=100` — omit `statsPeriod` (the endpoint accepts only the empty value, `24h`, and `14d`; `90d`/`30d`/`7d` are rejected with 400). 3. Follow `Link: rel="next"; results="true"` until exhausted. Log page count when done. Announce any cap you impose. 4. Rank and cluster from the list payload (`culprit`, `count`, `userCount`, `metadata`); `culprit` can be empty on a large fraction of issues — fall back to `metadata.type` + `metadata.value`. Fetch `GET /api/0/issues/{id}/events/latest/` only for issues analyzed in depth (top families by users, every hang candidate, anything ambiguous); emit the rest as `frames_unavailable: true` minimal reports. 5. Reverse Sentry frames (bottom-up → top-down). Set `kind: "hang"` for "App Hang"/"App Hanging" issue types.

**App Store Connect:** 1. Use `metrics_build_diagnostics` to list crash signatures. 2. Use `metrics_get_diagnostic_logs` to fetch frame detail per issue. 3. Emit `frames_unavailable: true` for any aggregate without frame data.

Write each normalized issue as one JSONL line to a temp file (e.g., `/tmp/corpus.jsonl`).

3. Run xcsym triage

xcsym triage --latest-version <latest_version> --os-floor <floor> --min-users <n> < /tmp/corpus.jsonl

Omit flags you don't have values for. The tool exits 0 even when some issues are skipped (malformed lines go to `errors[]`).

Parse the TriageResult JSON:

  • `summary.flagged_noise` — how many issues carry at least one noise flag
  • `summary.candidate_families` — estimated real-bug count (mechanical estimate only; semantic merge may revise)
  • `issues[]` — one entry per classified issue
  • `clusters[]` — mechanical groupings by signature
  • `errors[]` — issues that couldn't be classified (report these to the user)

4. Semantic family-merge

The mechanical `cluster_key` is conservative and may over-split (two nil-unwrap clusters with different call sites are the same family) or under-split (`cluster_confidence: low` bags lump unrelated issues under one syscall).

**Merge:** Combine clusters that share `pattern_tag` + overlapping `top_frames` and plausibly represent the same root cause.

**Split `cluster_confidence: low` bags:** Any cluster key containing `|sys:` is a system-frame fallback. Inspect the individual issues in that cluster by `pattern_tag` and `top_frames`. Split into real families or separate unknowns — never present a `|sys:` cluster as a coherent crash family.

5. Produce the ranked report

**Report structure:**

## Triage Report — [Provider] — [Date]

**Corpus:** N issues (M crashes, K hangs), N pages fetched

### Real-Bug Families (ranked by users affected)

#### 1. [Family name] — N users, M events
- **Pattern:** `pattern_tag` (`pattern_confidence`)
- **Representative issues:** ISSUE-1, ISSUE-2
- **Top frames:** [list from top_frames]
- **Root cause hypothesis:** [your interpretation]
- **Next step:** [specific actionable instruction]
- **Enrichment:** [if enrichment[] is non-empty, surface the cross-skill pointer here]

[Repeat for each real-bug family]

### Deprioritized as Likely Noise — Review Before Closing

| Issue ID | Title | Users | Noise Class | Deprioritize safety | Reason |
|---|---|---|---|---|---|
| ISSUE-X | ... | 68 | anr_suspension_false_positive | medium | main-thread top frames are run-loop park signatures... |

Sort **least-safe first** by `noise_flags[].deprioritize_safety` — `low`, then `medium`, then `high` — breaking ties by users descending. The reader is scanning for what they are about to wrongly close.

**No `deprioritize_safety` value licenses closing.** `high` means the deprioritization is comparatively safe, not that the issue is dead. Every row here is review-before-closing, `high` rows included
Read more
Ships withaxiom

Battle-tested skills, agents, and tools for modern Apple OS development — Swift 6, SwiftUI, Liquid Glass, Apple Intelligence, and more. Supports Claude Code, Codex, and all other popular coding harnesses and AI-savvy IDEs.

Get the whole plugin