Answers one question with data: when a coding agent is asked to debug a performance problem with a trace, does a given change (to the skill, to what trace_processor prints, to the docs) make it do a better job, and at what cost? Reading a skill tells you whether it looks right; only running an agent against it tells you whether it helps. What the first rounds found, and how it shaped the skill, is summarised in ai/skills/README.md.
A trial is one agent, one prompt, one condition:
test/data, a script) are copied in. Nothing else: no user settings, plugins or memory, so the only Perfetto knowledge available is the model’s own plus what the condition adds.trace_processor build on PATH or nothing at all, extra environment. Conditions are named in conditions.json; the things they point at (bundled skills, binaries) live outside the tree and are assembled by setup_assets.py.trace_processor calls, how many SQL errors, cost, duration, and whether it cheated by finding a local build.compare puts several result directories side by side.trace_processor itself. Every grader's body records the query that produced the expected value, and where two readings of a trace are defensible both are accepted. The first round showed why: agents reasonably excluded a container track the reference script counted, and reported a different but correct top retainer.The control condition is only meaningful if the agent cannot find help lying around, and in the first round it did: agents walked up from the workspace into the checkout and used its build, then looked in the wrapper's prebuilt cache by name. So the runner:
~/.local/share/perfetto from the agent: sandbox-exec on macOS (the cache is unlistable but files stay reachable by exact path, so wrappers keep working), bubblewrap on Linux (an empty tmpfs over each hidden path, so downloads vanish with the sandbox), a loud warning elsewhere;TMPDIR (warm-session sockets live there) and refuses pip install outside a virtualenv;contaminated if it still used a hidden binary, so the run can be discarded.ai/evals/
run_evals.py runner, harnesses, graders, aggregation, comparison
setup_assets.py builds the out-of-tree assets conditions refer to
conditions.json named conditions (plugin dirs, PATH entries, env)
cases/<id>/prompt.md frontmatter (files to stage, runs, tags) + prompt
(tags are free-form; see below for the ones in use)
cases/<id>/graders/ one grader per file: regex | bash | tool_used |
file_exists | llm (scored: false = indicator only)
results/<name>/ transcripts, answers, grading, reports (gitignored)
Tags exist only so --tag / --exclude-tag can select a subset of cases; they carry no meaning for grading. The ones in use:
android, gpu, cpu, memory, startup, jank, frames, binder, sched, anr, profiling, stacks. What the question is about.workflow (a shipped runbook under workflows/ should answer it), stdlib (a standard-library module does most of the work), adhoc (plain SQL over the core tables).hard for multi-step questions where a wrong method gives a plausible wrong number. The set without hard is the cheap smoke run.trigger (does the skill fire, or stay quiet, when it should?), no-mention (the prompt never says “Perfetto”; every other prompt does), negative (the skill must not fire), trap / hallucination (the trace does not contain what was asked).Setup once: build trace_processor_shell, make sure the test traces are present (tools/install-test-deps), then assemble the assets outside the checkout and point the runner at them:
git fetch origin ai-agents ai/evals/setup_assets.py --out ~/perfetto-eval-assets \ --tp-binary out/mac_release/trace_processor_shell export EVAL_ASSETS=~/perfetto-eval-assets
Re-run setup_assets.py after editing ai/skills/ or rebuilding. The runner‘s one dependency (PyYAML) is declared as inline script metadata, so uv run ai/evals/run_evals.py (or the file’s shebang) needs no venv.
# With vs without the local skill, three runs per case, five in parallel. ai/evals/run_evals.py run --conditions baseline-tp,skill-local --runs 3 --jobs 5 # Same thing driven by Codex; only the harder cases, on a cheaper model. ai/evals/run_evals.py run --agent codex --tag hard --conditions baseline-tp,skill-local ai/evals/run_evals.py run --model sonnet --budget 2 --conditions baseline-tp,skill-local # Re-grade after editing graders (keeps existing LLM verdicts). ai/evals/run_evals.py grade ai/evals/results/<name> --skip-llm # One table across result directories; later dirs win for a cell. ai/evals/run_evals.py compare ai/evals/results/a ai/evals/results/b \ --conditions baseline-tp,skill-local
Harnesses: claude (default, reports cost) and codex are tested; gemini is written against its documented interface but untested. Budget: with Opus a trial costs $0.1 to $3 depending on the case; a full 11-case, 3-run, single-condition matrix is about $30 and 40 minutes at five parallel jobs. Sonnet is about a fifth of that.
test/data that answers it. Compute the answer with trace_processor_shell and put the query in the grader body.llm grader for “grounded, not fabricated”; process checks (bash, tool_used) marked scored: false unless the process is the point."\\d+"), or single-quote them literally ('\d+').