/crash-report — weekly Roku crash CSV → tracked issues + epic evidence
SkillFiles & storageProcess a Roku weekly crash-report CSV (or a zip containing one or more CSVs) into tracked GitHub issues + architectural-epic evidence, using an enrich-before-file flow (stage → enrich → file). STAGE parses/filters the CSV, groups by pkg-path signature, builds each cited app version in a temporary git worktree, source-maps the transpiled `.brs:line` back to source `.bs:line`, runs the GH dedup search, and writes a local worksheet where every crash starts pending — a mechanism hint (task-launch/network/other) orders which to pull first but never authorizes a file. ENRICH folds in the per-crash backtraces you paste from Roku's dashboard (7-day window, one click per crash — no bulk export), extracts each `&hNN` exception code, and routes every crash to a disposition (nothing files or aggregates without a backtrace): `file` (a scoped bug → a new per-signature [crash] issue, born enriched), `aggregate` (a known architectural class — `&h29` big-library too-many-task-threads, `&h23` server timeouts, `&hec`+init `m.global.constants` race — upserted as one flat record comment per `file·function·line·version` onto the class epic, never a standalone issue), or `watch` (accepted noise, spike-comment only). FILE performs the GitHub writes (create/comment/reopen for scoped bugs; epic record upsert for architectural classes) and writes a run-summary handoff. Dedup-aware and idempotent. Use when a fresh Roku crash CSV arrives (weekly). Per-crash deep dive is offloaded to `/issue-triage <N>` after filing.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the /crash-report — weekly Roku crash CSV → tracked issues + epic evidence skill
What this skill tells your AI
The instructions your AI receives, as published by jellyrock/jellyrock in .claude/skills/crash-report/SKILL.md and read by ahel’s review.
Enrich-before-file flow: stage → enrich → file. The data that decides whether a crash is a scoped bug, a big-library too-many-tasks crash, or a server timeout is the exception code — and that lives only behind Roku's per-crash dashboard backtrace, not in the CSV. So this skill stages a local worksheet first, folds in the backtraces you paste, and only then files. Issues are born complete, and architectural-class noise never becomes a standalone issue.
The mechanical work — CSV parse, ZIP extraction + filtering, version→tag resolution, isolated build, source-map lookup, backtrace parsing + exception-code routing, epic-comment upsert, GH dedup search, body drafting, GH writes, run-summary handoff — lives in scripts/crash-report.js (subcommands stage / enrich / file). This skill orchestrates it across the three phases with human gates before any GitHub write.
The three dispositions
Every above-threshold crash routes to exactly one disposition. The routing table is .crash-report/known-noise.yml:
file— a real, small-scoped bug → a new per-signature[crash]issue, born enriched (backtrace + exception + source frames).aggregate— a known architectural class you intend to fix but that has no small-scoped fix (&h29big libraries / too-many-task-threads;&h23server timeouts;&hec+init()+m.global.constantsthemed-init race). Each unique crash record (file·function·line·version) is upserted as one flat comment onto the class epic. Distinct lines are never auto-merged; occurrence stats accumulate. Nothing is ignored — the evidence lands on the epic.watch— accepted noise we've decided not to fix. Counted, silent unless a spike crossesbaseline × multiplier, then one comment on the tracker.
Routing is by exception code + context: &h29/&h23 are the class regardless of site (code alone routes them); &hec is the init-race epic only when it's init() + m.global.constants — bare &hec elsewhere is an ordinary scoped null-deref (that asymmetry is why the config gates &hec on function + snippet too).
⚠️ The 7-day dashboard window
Roku's analytics dashboard retains only 7 days of crash backtraces, one click per crash line-item, no bulk export. Enrichment must happen within the report's window. Whenever you ask the user to pull a backtrace, give the exact <basename>.brs:<line> and date, and only for crashes inside the current report window — anything older can never be enriched (file it unenriched, or hold it).
Inputs
$ARGUMENTS: a path to the CSV, a path to a Roku zip (multiple CSVs + unrelated files tolerated via header filtering), or pasted CSV text. Threshold overrides: --min-devices N (default 2), --min-dates N (default 2). Either threshold met files the crash; both must fail to skip it.
Step 0 — Preflight
- Labels exist:
gh label list --search crash,--search known-issue,--search epic. Create any missing:gh label create crash --color e11d48 --description "Filed by /crash-report from Roku's weekly crash report"gh label create known-issue --color cccccc --description "Long-running known bug — tracked but deprioritized"gh label create epic --color 5319e7 --description "Architectural class tracker — /crash-report aggregates crashlog evidence here"
- Deps:
ls node_modules/source-map/source-map.js && ls node_modules/adm-zip/package.json;npm installif missing. - Epics seeded: the
aggregatepatterns inknown-noise.ymlmust reference real epic issues. If the two architectural-class patterns are still commented out (unseeded), seed them first (seedocs/dev/crash-reports.md→ "Seeding the epics"), then uncomment them and fill eachtracker_issue. - Working tree: dirty is fine (the build uses
git worktree); mention uncommitted changes as a courtesy — don't block.
Step 1 — Stage (no GitHub writes)
WS=$(mktemp /tmp/crash-report-worksheet.XXXXXX.json) # NOTE: no --suffix — mktemp rejects --suffix unless the template ends in X
node scripts/crash-report.js stage --input "$ARGUMENTS" --plan-out "$WS"
The helper parses/filters the CSV, groups by signature, applies the threshold, builds each cited version in a temporary worktree, source-maps pkg:/…brs:N back to .bs:N, runs the GH dedup search, and annotates each above-threshold crash with a mechanismHint (task-launch / network / other). Every crash starts pending / needs-backtrace — nothing files or aggregates without a pasted backtrace (the exception code decides the disposition and lives only in the backtrace). The mechanismHint does not authorize a file; it only orders which crashes to pull first. Expect ~30–90s per unique cited version.
Step 2 — Render the worksheet
Read $WS. Render: (1) an input summary (kind, source, CSVs found, ignored files, window dates, total rows, unique signatures, above/below threshold); (2) an action table, one row per above-threshold crash with signature → source, function, mechanism, crashes, devices, dates, dedup match — ordered by mechanismHint (task-launch / network first, then other); (3) the filtered-out rows (below threshold) with the reason; (4) any build errors, surfaced prominently (affected rows resolve to —).
Every above-threshold crash needs a pasted backtrace before it can be filed or aggregated — you can't know from the CSV + code alone whether a crash is a scoped bug or an architectural class (even an ordinary-looking line can be a compute-bound &h23 timeout). The mechanismHint only tells you what to pull first: task-launch / network sites are the likeliest architectural classes (&h29 / &h23); other sites are likeliest scoped bugs — but all of them get confirmed by a backtrace, never a guess.
Step 3 — Enrich (no GitHub writes)
For every needs-backtrace row, tell the user the exact <basename>.brs:<line> on <date> to open in Roku's dashboard ("View report → Backtrace") and paste back — remind them of the 7-day window. Batch is fine: collect the pastes (as @file refs or inline), save each to a temp file, then:
node scripts/crash-report.js enrich --worksheet "$WS" <backtrace1.txt> [<backtrace2.txt> ...]
enrich extracts each &hNN, source-maps the frames, and routes each crash: &h29/&h23/&hec-init → aggregate (marked for its epic, will NOT be filed standalone); anything else → file (its issue body gets the backtrace appended so it's born complete). One representative backtrace per signature is enough — the code is stable per site; you don't need one per date. Re-read $WS and re-render the table with the now-known disposition + exception code.
Step 4 — Confirm dispositions (human gate)
Present the final plan: which enriched crashes will file (new issues), which will aggregate (→ which epic), which are still un-enriched and will therefore be held (never filed without a backtrace — the file phase skips them), and any below-threshold skips. Use AskUserQuestion: proceed / adjust thresholds & re-stage / hold specific rows. Nothing has hit GitHub yet.
Step 5 — File (GitHub writes)
node scripts/crash-report.js file --worksheet "$WS"
file creates/comments/reopens the file crashes, and for each aggregate crash upserts one flat record comment onto its epic — creating a new comment, or editing the existing record when its file·function·line·version key is already present (accumulating occurrences, never duplicating; a no-op edit is skipped). It writes a run-summary handoff to .claude/handoffs/crash-report-<timestamp>.md.
Step 6 — Surface the summary
Read the handoff and recap: N filed (#…), N commented, N reopened, N aggregated to epics (#…), N suppressed, N skipped. Call out any per-action errors for manual retry. Next: per-crash deep dive on a filed issue → /issue-triage <N>. Clean up the worksheet: rm "$WS".
When NOT to use
- A single developer-device
.textcrashlog →/runtime-triage. - A single manually-filed GH crash issue →
/issue-triage <N>. - A CI failure log →
/ci-triage <run-id>.
Cron / weekly cadence
Idempotent: re-running the whole flow on the same report re-files nothing (dedup short-circuits) and re-upserts epic records to the same comments (no-op edits skipped). Prefer one run per fresh weekly report — and run it promptly, because the 7-day backtrace window closes.
Sub-agent invocation
To invoke from a parent sub-agent: parent passes Read .claude/skills/crash-report/SKILL.md and run the stage → enrich → file flow for input <path>; render the worksheet, gather pasted backtraces for the needs-backtrace rows, confirm dispositions, file, and surface the summary path in the Task prompt.
Signals
- GitHub stars
- 41
- Forks
- 2
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
crash-report- Source
- github.com/jellyrock/jellyrock