/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.
$ npx -y skills add charleswiltgen/axiom --skill axiom-analyze-triage --agent claude-codeHow 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.mdname: 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
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
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.
Repo: charleswiltgen/axiom
Other skills on axiom.
- /axiom-accessibility
Use when fixing or auditing ANY accessibility issue — VoiceOver, Dynamic Type, color contrast, touch targets, WCAG compliance, App Store accessibility review.
Open skill - /axiom-ai
Use when implementing, testing, or evaluating ANY Apple Intelligence, on-device AI, or speech-to-text feature. Covers Foundation Models, @Generable, LanguageModelSession, Tool protocol, eval suites, model-as-judge scoring, SpeechTranscriber, CoreML.
Open skill - /axiom-analyze-crash
Use when the user has a crash log (.
Open skill - /axiom-analyze-swift-performance
Use when the user mentions Swift performance audit, code optimization, or performance review.
Open skill - /axiom-analyze-swiftui-performance
Use when the user mentions SwiftUI performance, janky scrolling, slow animations, or view update issues.
Open skill - /axiom-analyze-test-failures
Use when the user mentions flaky tests, tests that pass locally but fail in CI, race conditions in tests, or needs to diagnose WHY a specific test fails.
Open skill

