Install any skill in seconds. Free to start, no credit card required.
Get Started Free →The method for profiling and optimizing anything whose success is a measured cost — frame the phase and metric, choose an instrument that can actually see the cost, label the waste, falsify the hypothesis with a cheap control before building a fix, and prove the result without over-claiming. Covers app/device and server/database work. Use when work is about slowness, jank, startup/open time, blocked JS, hangs, memory, render churn, a slow query, or a claimed speedup. It does not carry the write-
.claude/skills/happier-dev-happier-profile-and-optimize/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-05 | ✗→✓ | ▲ Improved | 35% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 64% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 25% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 5% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 39% | 0% |
Use this skill for any work whose success is a measured cost, not a behavior: slow open/foreground/navigation, jank, hangs, startup, memory, render churn, a query that got slow, or verifying somebody's speedup claim. It is the investigation method — reach for it once a cost exists.
It is deliberately not the list of things to watch for while writing code. Those must apply unprompted, at authoring time, long before anyone suspects a problem, so they live in package instructions that are read on every task: apps/ui/AGENTS.md → Performance and continuity owns the UI write-time invariants (referential stability, narrow subscriptions, component-type stability, high-frequency state placement, dependency-array identity, loop stop conditions, one perf change lands on all platforms). If you are about to restate one of those here, stop and strengthen it there instead.
The rules this skill operates under also live elsewhere and are not restated: root AGENTS.md → Product priorities (name the phase and metric, instrument must be able to see the cost, no ratio without both sides on the same workload and machine state, no blanket memoization) and Risk-weighted execution. Read those; do not re-derive them.
Route out, do not absorb:
.agents/skills/happier-diagnose — the incident is a failure (error, hang-to-crash, broken session), not a cost. Diagnose first, then return here only if the outcome is a cost..agents/skills/happier-testing — lanes, RED/GREEN, mutation proof, live gates. A perf fix with a behavior change is still test-first there..agents/skills/happier-implement — the actual change, canonical-owner discovery, split-brain sweep..agents/skills/verify-claims — before relying on any delegated or reported number..agents/skills/attack-conclusion — before handing off a perf verdict.State, in one line each: the phase (cold open, warm foreground, navigation, steady state, per-commit), the metric (blocked ms, count, bytes, dropped frames), the workload (session size, row count, account), and the user-visible symptom. A metric without a phase cannot be reproduced or compared.
Measure the moment that hurts. Some costs are steady-state, not open-time; some are per-commit rather than a resting loop. Profiling the wrong moment produces a real number about the wrong thing.
Get a total first — total blocked/elapsed time for the phase — then reconcile every instrument's attributed total against it, per the instrument-coverage rule in root AGENTS.md. What that rule costs when skipped, measured here: a React profiler reported ~1.4 s while the JS thread was blocked ~12 s; 88% of the cost was outside React, and a full round of work went into render churn that was not the bottleneck.
Route to the instrument by what it can observe:
| You need | Instrument | | --- | --- | | Entry point for RN perf work, sweep order, fix patterns | argent-react-native-optimization | | React render/commit counts, slow components, before/after render deltas | argent-react-native-profiler | | CPU hotspots with call paths, UI hangs, memory — anything outside React | argent-native-profiler (xctrace / Perfetto) | | Component tree, props/state identity, why something re-rendered | react-devtools | | CDP evaluate, arming an in-app probe, reading the log registry | argent-metro-debugger | | Server/database cost: which index a query actually seeks, what falls into Filter:, whether a predicate forces a scan | the planner — EXPLAIN QUERY PLAN (SQLite) and EXPLAIN (PostgreSQL), on a seeded table, post-ANALYZE |
Do not restate their contents; open the one you need.
Server and database work uses this same method — the instrument just changes. Read a plan for its shape, not its cost number: which index was chosen, which columns the Index Cond actually binds, and what residual predicate landed in Filter:. Run every engine that ships, because they disagree in ways that change the diagnosis: here the same attention query was a bare SCAN main.Session on SQLite but a Bitmap Index Scan on Postgres whose index condition bound only meaningfulActivityAt IS NOT NULL, leaving accountId in the Filter: — an account-wide scan wearing a bitmap. Isolate the cause with counterfactual query shapes, not by reading the query: dropping one OR arm at a time proved the intended index existed and was correct all along, and that a visibility OR alone was sufficient to destroy the seek. Costs on a small dev table are planner estimates, not production timings; the shape is the load-bearing part, and it must be engine-consistent before you act on it.
Locate in time, then in code. Block timing (e.g. a 16 ms drift sampler armed over the phase) tells you when the thread was stolen; a CPU profile with call paths tells you what stole it. Never fix something located only in time — a time window plus a plausible suspect is a hypothesis, not an attribution.
Force one of these labels before proposing anything. The label constrains the fix:
TOO EARLY (work done before it is needed) · TOO OFTEN (repeated per event/commit/row) · TOO MUCH (correct work, oversized input) · TOO SERIAL (awaited in sequence, parallelizable) · WRONG SHAPE (data structure forces a scan) · N+1 (per-item round trip) · RENDER CHURN (re-render without changed output) · CACHE HAZARD (missing key input, no in-flight sharing, or a stale/poisoned entry).
Then, before any edit:
> Hypothesis: <cost> is caused by <work> because <evidence>. > Verification: measure with <tool>, inspect <files>.
If <evidence> is a subtraction ("the rest must be X"), root AGENTS.md already rules that a hypothesis, not a measurement — go observe X before fixing it.
Design the cheapest observation that would prove the hypothesis wrong, and run it first. A control that costs minutes routinely retires days of queued work: here, one second-open-with-modules-already-resolved run showed the cost was not module loading and retired an entire lazy-loading workstream before it was built.
Good controls: same flow twice (cold vs warm), the flow with one input emptied, the suspect path short-circuited behind a temporary local branch, the same phase on a second account/session size. Keep the control disposable; it is evidence, not a deliverable.
.agents/skills/happier-implement; a perf fix that adds a second path for the same concept is a defect, not an optimization.AGENTS.md's paired-measurement gate. Concretely here, work-avoided proofs are call counts, commit counts, bytes parsed, and blocked ms.AGENTS.md: the same waste label usually has siblings.No device, saturated machine, flapping tooling, unreproducible phase: say so plainly and claim nothing. Report the phase, what was attempted, and the missing prerequisite as [blocked]. An inferred number is worse than no number — it survives into later decisions with false authority.
curl -s localhost:18829/json/list and use reactNative.logicalDeviceId — never the raw simulator UDID. Wrong id = a profile of a different device.react-profiler-analyze requires project_root..agents/skills/happier-testing device QA rules.Do not wrap Metro's global.__r to count module evaluations. It drops the CDP connection: the for-in over the registry loses its non-enumerable state. Use the first-vs-second-open control from step 4 instead.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 83,676 | 7,648 | -91% | 1 | 1 | 0% | 4,876 | 2,449 | -50% | 0 | 0 | — |
case-02 | fail→fail | 51,816 | 9,622 | -81% | 1 | 1 | 0% | 8,248 | 2,437 | -70% | 0 | 0 | — |
case-03 | fail→fail | 37,990 | 6,860 | -82% | 1 | 1 | 0% | 5,637 | 2,435 | -57% | 0 | 0 | — |
case-04 | pass→pass | 17,712 | 19,989 | +13% | 1 | 1 | 0% | 2,593 | 4,364 | +68% | 0 | 0 | — |
case-05 | fail→pass | 15,497 | 5,708 | -63% | 1 | 1 | 0% | 2,041 | 2,747 | +35% | 0 | 0 | — |
case-06 | fail→pass | 18,879 | 13,485 | -29% | 1 | 1 | 0% | 2,527 | 4,138 | +64% | 0 | 0 | — |
case-07 | pass→pass | 10,718 | 4,795 | -55% | 1 | 1 | 0% | 1,442 | 2,846 | +97% | 0 | 0 | — |
case-08 | fail→pass | 17,544 | 10,700 | -39% | 1 | 1 | 0% | 2,833 | 3,530 | +25% | 0 | 0 | — |
case-09 | fail→pass | 14,948 | 3,087 | -79% | 1 | 1 | 0% | 2,332 | 2,459 | +5% | 0 | 0 | — |
case-10 | fail→pass | 18,340 | 9,466 | -48% | 1 | 1 | 0% | 2,600 | 3,609 | +39% | 0 | 0 | — |
case-11 | pass→pass | 14,737 | 5,193 | -65% | 1 | 1 | 0% | 2,671 | 2,903 | +9% | 0 | 0 | — |
case-12 | pass→pass | 11,463 | 8,236 | -28% | 1 | 1 | 0% | 1,536 | 3,116 | +103% | 0 | 0 | — |
case-13 | pass→pass | 7,397 | 8,244 | +11% | 1 | 1 | 0% | 880 | 3,141 | +257% | 0 | 0 | — |
case-14 | pass→pass | 16,698 | 5,038 | -70% | 1 | 1 | 0% | 2,101 | 2,830 | +35% | 0 | 0 | — |
case-15 | pass→pass | 16,675 | 6,524 | -61% | 1 | 1 | 0% | 2,148 | 3,058 | +42% | 0 | 0 | — |
case-16 | pass→pass | 9,337 | 19,379 | +108% | 1 | 1 | 0% | 1,338 | 3,436 | +157% | 0 | 0 | — |
case-17 | fail→pass | 9,978 | 3,977 | -60% | 1 | 1 | 0% | 1,365 | 2,591 | +90% | 0 | 0 | — |
case-18 | fail→pass | 6,215 | 3,819 | -39% | 1 | 1 | 0% | 800 | 2,586 | +223% | 0 | 0 | — |
case-19 | fail→pass | 10,084 | 5,356 | -47% | 1 | 1 | 0% | 1,109 | 2,972 | +168% | 0 | 0 | — |
case-20 | fail→pass | 10,367 | 4,154 | -60% | 1 | 1 | 0% | 1,280 | 2,688 | +110% | 0 | 0 | — |
case-21 | fail→fail | 15,497 | 4,747 | -69% | 1 | 1 | 0% | 856 | 2,757 | +222% | 0 | 0 | — |
case-22 | pass→pass | 12,889 | 6,172 | -52% | 1 | 1 | 0% | 1,774 | 2,879 | +62% | 0 | 0 | — |
case-23 | fail→pass | 10,764 | 3,105 | -71% | 1 | 1 | 0% | 1,320 | 2,676 | +103% | 0 | 0 | — |
case-24 | fail→pass | 16,472 | 7,497 | -54% | 1 | 1 | 0% | 2,153 | 3,142 | +46% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 24 cases were attempted, and 21 counted toward the lift figure. The other 3 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +46 percentage points is the difference between those two pass rates over the 21 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.