Skip to content

New plugin: performance — measurement-first optimization workflow (baseline/post snapshots, realistic vs ideal goal tiers, drift-immune metrics) #3530

Description

@kyle-sexton

Summary

Create a performance plugin whose skill drives a measurement-first optimization workflow end to end: pick targets, construct a goal with realistic and ideal tiers, take a baseline snapshot, change something, take a post snapshot, and refuse to claim a win the data does not support.

This issue is written from a real end-to-end run of that workflow, done by hand in one session against the disk-hygiene destructive-guard hook (PR #3523, issues #3527 / #3528). Everything below is a generalization of something that actually happened, including four places where the naive version of the workflow produced a confidently wrong answer.

This issue REQUIRES a /planning:interview session with the user before implementation. The workflow has real design forks (see "Open questions") and the transcript this is drawn from is one domain — a Windows hook launcher. Do not build from this issue alone.

Why a skill, not a checklist

The session it came from had a competent operator and a strong initial prompt, and it still produced these, each caught only by an explicit verification step:

  1. A benchmark harness that silently measured itself. Its shim directory was mktemp -d, and the thing under test cached on PATH — so every run was a forced cache miss and the "after" looked identical to the "before". The harness was measuring its own randomization.
  2. A probe that proved nothing while reporting success. It tested a hard-link case; os.link failed cross-volume on Windows, the code fell back to shutil.copyfile, and a copy is a different file — so the probe reported "0 gaps" for a case it never exercised. A green result from a check that did not run.
  3. A "before/after" comparison invalidated by drift. Bare bash -c true measured 1825 ms and 283 ms in the same hour at ~10% CPU. Any two-pass measurement attributes that 6x to the change. Only interleaving old/new within one run survives it.
  4. A root cause asserted from a plausible mechanism instead of from data. A parallel session diagnosed WDAC code-integrity enforcement (real, CodeIntegrityPolicyEnforcementStatus = 2) as the cause of slow spawns, then had to retract it: a 7-sample spread of min 180.5ms / median 1107.7ms / max 2841.3ms, spread ratio 15.74, at 501 concurrent processes, is a contention signature. The bimodality was the diagnosis; the plausible mechanism was a distraction.

None of these are knowledge gaps. They are all "the measurement was wrong in a way that looked right", which is exactly what a skill with enforced gates prevents and a checklist does not.

Proposed workflow

Phase 0 — target identification

Accept targets from any of: the current session's own pain, a named path/component, a telemetry store, or a broad "what is slow here". Rank candidates by evidence quality, not by suspicion. If nothing is measured yet, the top recommendation is "instrument this first" rather than a guess.

Phase 1 — goal construction (interview-gated, user in the loop)

This is where the user is REQUIRED. Produce, together:

  • the metric and the exact command that produces it;
  • a realistic target and an ideal target, held separately;
  • the floor: the irreducible cost the target cannot go below, computed before any work. In the source session this was one process spawn; the goal asked for p50 ≤ 250 ms on a host charging 0.3–0.9 s per spawn, so the goal was unreachable by any code change and knowing that up front would have reframed the whole task;
  • what counts as done, including whether merge is in scope.

The skill must be able to say "your target is below the measured floor" and make the user decide, rather than silently failing the goal at the end.

Phase 2 — baseline snapshot

Refuse to proceed without: sample count, p50 AND p95 (never a single sample or a bare mean), the environment's own noise characterization, and a drift-immune counter alongside the wall clock wherever one exists (spawn count, syscall count, query count, allocation count). In the source run the spawn census 4 → 1 was the number that survived review; the milliseconds were not reproducible even by an independent verifier on the same machine an hour later.

Phase 3 — change

Ordinary implementation, with one rule carried over: re-measure the drift-immune counter after every change, because it catches harness bugs immediately. Both harness bugs above surfaced as a counter that did not move when it should have.

Phase 4 — post snapshot

Interleaved A/B by default: alternate arms within one run, flip the order each iteration, report the median of per-pair ratios alongside per-arm percentiles. Under concurrency, report per-arm percentiles ONLY and suppress the paired ratio — the arms are no longer load-matched, and pairing by index compares samples that never shared conditions.

Phase 5 — verification (fresh context)

Dispatch a verifier that does not inherit the implementer's numbers and re-derives them from the trees. In the source session this caught two blocking correctness defects that the implementer, the implementer's own 143-test suite, and a full green CI run had all missed.

Phase 6 — report

State the target as met or not met, with the measurement that explains why. Never round a miss into a win. Separately state any behavior change the optimization introduced.

Non-negotiable gates the skill should enforce

These are the specific things that went wrong, as rules:

  • A harness must prove it is not measuring itself. Anything the harness injects into the environment under test (PATH entries, temp dirs, env vars) must be stable across runs, or keyed out of the subject's caching.
  • A probe must assert its own precondition. If a test depends on a hard link, a symlink, a specific filesystem, or a permission, it must FAIL when that precondition is unmet, never silently degrade into a weaker test that passes.
  • Never compare two separate passes on a drifting host. Characterize drift first (repeat the no-op baseline); if the spread ratio exceeds a threshold, force interleaving.
  • A drift-immune counter outranks wall clock in the report whenever one exists.
  • p50 and p95 over ≥20 samples, never a single sample, never a bare mean.
  • Optimizing a guard requires a differential, not a passing test suite. A suite proves nothing asserted broke; it does not prove behavior is unchanged. And the differential must cover every MODE the subject runs in — the source session's differential ran one of two modes and missed a real deny→ask downgrade in the other.
  • Correctness regressions outrank any speedup. If the change alters behavior, that is a first-class finding stated separately, never folded into the performance claim.

Composition with existing plugins

Should reuse rather than reinvent: /planning:interview for Phase 1 (this is the required user-in-the-loop gate), /verification:measure and /verification:confirm if they already cover snapshot mechanics, /discovery:explore for Phase 0 on an unfamiliar codebase, /implementation:implement for Phase 3, review:fanout or a dedicated verifier agent for Phase 5, and /claude-ops:observability / /claude-ops:audit-performance as telemetry sources for Phase 0. The audit-performance engine already reaches bimodal-spawn-latency, which is exactly the Phase 2 noise characterization this skill needs — worth wiring rather than duplicating.

Scope boundary to settle in the interview: /claude-ops:audit-performance diagnoses a slow Claude Code installation. This skill optimizes an arbitrary target and owns the before/after discipline. They overlap at noise characterization and should share it.

Open questions for the interview

  1. Scope. Any code, or start narrow (hooks and CLI tools, where spawn-count metrics are well-defined) and widen?
  2. Where do baselines live? Committed artifacts, a plugin data dir, or ephemeral? Committed baselines enable regression detection over time but go stale across machines — and given the drift measured here, a baseline is arguably only valid on the machine and hour that produced it.
  3. How hard are the gates? Hard-block on a missing baseline, or warn? The source session's value came from gates that actually blocked.
  4. Cross-machine claims. Is the skill allowed to report a number from a host whose noise it has characterized as pathological, or must it refuse and say "measure elsewhere"?
  5. Metric catalog. Which drift-immune counters ship built-in (process spawns, syscalls, queries, allocations, bytes) and how are per-domain ones declared?
  6. Does it own the fix, or only the measurement? Cleanest split is probably measurement + goal + verification, delegating the change to the implementation lane.
  7. Autonomy. The user-in-the-loop requirement is firm for Phase 1. Are Phases 2–6 allowed to run unattended?

Source material

The transcript this is drawn from covers the whole arc and is worth reading before the interview: target identification from live session pain, goal construction (including a target that turned out to be below the floor), baseline, root-cause confirmation before any edit, implementation, two self-inflicted harness bugs and how each was caught, interleaved A/B, concurrent measurement, a differential correctness proof, independent verification by two fresh-context agents, and two blocking defects found only at that last step.

Concrete artifacts from that run, usable as reference implementations:

  • spawn-census via a PATH shim directory (the drift-immune counter);
  • interleaved A/B harness with order flipping and a paired-ratio summarizer that suppresses itself under concurrency;
  • a differential harness comparing a pre-change and post-change binary over a harvested corpus, requiring byte-identical output;
  • the two verifier agent prompts, which are written to be adversarial and to distrust the implementer's numbers.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority: highSignificant impact, or blocks an imminent release; staff this cycle.work-class: structuralRefactors, migrations, contract changes; cross-cutting and hard to reverse.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions