Imported from jantimon/web-performance-debugger (
AGENTS.md). Install upstream withnpx skills add jantimon/web-performance-debugger. Copyright stays with the author.
Driving wpd from an agent
For an agent consuming wpd. wpd finds and investigates; it does not grade, rank, or recommend. It
hands you typed, measurement-tagged numbers and a clean exit code; the judgment stays with you.
The loop
- Record.
wpd record <module> [flags]writes a recording plus siblings. Onerecordis one capture pass. - Query. Read the recording through the
queryverbs. Never read the recording file directly -- it can be many MB. Drill instead:
query spans <file> # overview: one row per span, one shape across chrome/firefox/node
-> query span <file> <kind:label> # one span's full anatomy
-> query get <file> <id> # one raw event (stack + args); needs --deep or firefox
- Every verb takes
latestin place of<file>(a cwd-keyed pointer, never resolved by mtime). - Span identity is kind + label. A bare label that matches more than one kind is a collision;
qualify it (
run,step:first increment,measure:hydrate). query spans --label Xwith no match is a filter: it reports the empty result and exits 0.query span Xwith no match is a lookup: it exits 1. Pick the verb for the branch you want.- Other verbs:
query cpu(hot functions + rollup),query frame <id>(one function's callers and callees),query blame --forced(forced-layout read sites),query events(the raw log).
Output
- Always pass
--format jsonor--format toon. Both are plain: no ANSI, whatever the TTY. TOON is a compact, JSON-shaped text format that spends fewer tokens than JSON. - The view shapes are typed and exported from the package root (
SpansResult,SpanAnatomy,CpuOverview,CpuDiffResult, ...). Import them; do not hand-roll a shape.
Reading the numbers: three traps
nullis not0. A count or slice the capture could not observe is an explicitnull(not-measured), never0(measured clean). Do not coerce it. A gate on such a metric is a loudn/aFAIL, never a silent pass:assert --max-forced 0on a--breakdownrecording FAILs, because forced counts need--deep.jsSelfMsis the JS headline. Do not denominate a package share onactiveMs-- the larger non-idle total (js + gc + engine).- A function's display name is
fn, notname(its self time isselfMs, its sourcesource). - The full field reference: README, Consuming the JSON.
Exit codes and refusals: treat the refusal as the answer
- A failed gate exits non-zero.
assertover a blown budget,diff/cpu-diffwith--fail-on-regressionon a real regression: exit 1. A clean run exits 0. Gate on the exit code. - wpd refuses rather than fabricate.
diff/cpu-diffacross an incompatible pair (a different browser, runtime, capture mode, workload, iterations, warmup, headless flavour, or throttle) names the mismatch and declines to gate. The refusal text is the answer -- do not retry to force a number. The workload is the whole flow (lane + host page + module): the same host with a different module refuses too. - Bot-wall. On
record --urlonto a bot-challenge page (Cloudflare, DataDome), wpd refuses and writes a screenshot rather than measure the challenge as the site.--allow-bot-wallmeasures it on purpose, with a loud note that the numbers describe the challenge page.
Where to read more
- Capture modes and what each yields: README, Choose a capture.
- The full JSON type table: README, Consuming the JSON.
- The query verbs: README, The query verbs.
- Trust tiers (which numbers are exact, which directional): README, The numbers and docs/verification.md.
- The scope boundary (what wpd leaves to you): docs/dev/orchestrator-boundary.md.