Skip to content

Commit 09bf318

Browse files
steveruizokclaude
andauthored
feat(skills): add simplify-trace skill (tldraw#9353)
In order to reason about large Chrome DevTools performance traces without loading tens to hundreds of MB of JSON into context, this PR adds a `simplify-trace` agent skill that summarizes a trace into a compact, few-KB markdown report. The skill ships a Node script (`scripts/simplify-trace.mjs`) plus `SKILL.md` guidance on when and how to use it. The report surfaces the work taking too long or happening too often: long tasks (main-thread jank, with offsets), hottest event types by self time, most frequent event types, hottest JS functions (with `file:line` from the embedded V8 CPU profile), and a self-time-by-category breakdown. Opt-in sections cover a per-second main-thread timeline, a network waterfall, and WebSocket lifecycle. `--window START-END` scopes the whole report to a time slice, which is how you isolate a single action in an otherwise idle "what happens when I do X" recording. The script is read-only — it summarizes the trace and never modifies it. ### Change type - [x] `other` (internal agent tooling) ### Test plan 1. Run the script on a Chrome DevTools trace: `node skills/simplify-trace/scripts/simplify-trace.mjs <trace.json> --out /tmp/report.md` 2. Confirm the markdown report includes the summary header, long tasks, hottest events, frequent events, hottest JS functions, and categories. 3. Re-run with `--window START-END`, `--only timeline`, and `--list` and confirm scoping and section selection work. ### Code changes | Section | LOC change | | -------------- | ---------- | | Config/tooling | +549 / -0 | Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 330056f commit 09bf318

2 files changed

Lines changed: 549 additions & 0 deletions

File tree

skills/simplify-trace/SKILL.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
---
2+
name: simplify-trace
3+
description: Summarize a large Chrome DevTools performance trace into a compact markdown report so it can be reasoned about without loading the whole file. Use when given a Chrome/DevTools/Performance-panel trace (a multi-MB `Trace-*.json` or `*.json` with `traceEvents`) and asked to find what is slow, what runs too often, long tasks, jank, or hot JS functions.
4+
---
5+
6+
# Simplify trace
7+
8+
Chrome DevTools performance traces are tens to hundreds of MB of JSON — far too large to read directly. This skill turns one into a few-KB markdown report that surfaces the work taking too long or happening too often.
9+
10+
## Usage
11+
12+
Run the script on the trace file. It prints markdown to stdout (or `--out FILE`):
13+
14+
```bash
15+
node skills/simplify-trace/scripts/simplify-trace.mjs <trace.json> [--top N] [--long-task-ms MS] [--window START-END] [--out report.md]
16+
```
17+
18+
- `--top N` — rows per table (default 25).
19+
- `--long-task-ms MS` — long-task threshold (default 50).
20+
- `--window START-END` — scope the whole report to a time slice (offsets in ms from trace start).
21+
- `--only a,b` / `--except a,b` / `--all` — pick which sections to emit.
22+
- `--match TEXT` — case-insensitive filter for rows (event names, function names, URLs).
23+
- `--list` — print the available section keys and exit.
24+
- `--out FILE` — write to a file instead of stdout.
25+
- Traces over ~500 MB: prefix `node --max-old-space-size=8192`.
26+
27+
Default to running with `--out` to a temp file for big traces, then read the report. Don't read the raw trace.
28+
29+
## Sections
30+
31+
Default sections: `summary`, `longtasks`, `events`, `frequent`, `functions`, `categories`. Opt-in: `timeline` (per-second main-thread busy time — use to locate activity), `network` (resource/fetch waterfall with TTFB/duration/size), `websocket` (WebSocket lifecycle — `/app/file` doc sync). Run `--list` for the full set. Interrogate narrowly, e.g.:
32+
33+
```bash
34+
# where is the activity? then window to it
35+
node …/simplify-trace.mjs trace.json --only timeline
36+
# what was the network/socket doing during the action?
37+
node …/simplify-trace.mjs trace.json --only network,websocket --window 62000-69000 --match tldraw
38+
```
39+
40+
The trace's `metadata.startTime` (ISO/UTC) anchors offset 0 to wall-clock, so trace offsets can be lined up against server logs (zero-cache, sync-worker) by timestamp. For an idle, network-quiet gap, the trace shows *when* but not *why* — add `performance.mark()`/`console.timeStamp()` in the client path and they appear on the trace timeline.
41+
42+
## "What happens when I do X" traces
43+
44+
A recording of a single action (switch file, open menu) is mostly idle setup time, which dilutes the action across the whole trace. Window to the action instead:
45+
46+
1. Run once with no window. Note where activity is — the long tasks' offsets, or bucket main-thread busy-time per second with a quick inline script to find the active span.
47+
2. Re-run with `--window START-END` around that span. All tables then describe only the action.
48+
49+
The recording artifact `CpuProfiler::StartProfiling` (the profiler turning on, ~50–60ms) is excluded from the long-task table, including when it's nested inside a `RunTask`. If long tasks shows "None", the action genuinely has no single blocking task — look at aggregate self time, GC, and animation-loop events instead.
50+
51+
## What the report contains
52+
53+
- **Header** — event count, wall-clock span, sampled JS CPU time, and idle %.
54+
- **Long tasks** — top-level tasks over the threshold (main-thread jank), with the time offset where each occurred.
55+
- **Hottest event types (self time)** — where engine/browser time actually goes (layout, GC, paint, function calls), excluding time spent in nested children.
56+
- **Most frequent event types (count)** — work happening too often.
57+
- **Hottest JS functions** — bottom-up self time from the embedded V8 CPU profile, with `file:line`. Synthetic `(idle)`/`(program)` frames are excluded here (idle is in the header).
58+
- **Self time by category** — high-level breakdown across trace categories.
59+
60+
## How to read it
61+
62+
- A function high in **self time** is the actual cost; high **total** but low self means the cost is in its callees — follow the call tree.
63+
- High **count** with low avg = death by a thousand cuts (often a reactive/render loop firing too often); investigate why it fires, not its per-call cost.
64+
- Long tasks point at *when* jank happened; cross-reference the offset against what the user was doing.
65+
- Minified names (`r`, `Tg`) come with a `file:line` — use it to locate the source.
66+
67+
The script only summarizes; it does not modify the trace.

0 commit comments

Comments
 (0)