Measured change
Build the measuring instrument before changing anything whose effect you cannot see by reading the diff — quality scoreboards (pinned aggregate metrics) and performance benchmarks (pnpm bench, interleaved runs). Use for optimisation, layout/routing quality, heuristic tuning, or any change where "did that help?" is a real question.From its SKILL.md
npx -y skills add kamiazya/whiteboard --skill measured-changeAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 5 stars5 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
5.4 KB, as published. Nobody here has run it
Measure before you change it
Some changes announce themselves in the diff. A heuristic, a cost model, a search, an optimisation does not: it is correct-looking code whose worth is entirely in numbers nobody has taken. For those, the instrument comes first, in its own commit, and the change is judged by it.
This is not a preference. Across one routing/performance batch the instrument rejected three separate changes that were obviously right:
- per-blocker detours — fixed 10 more defects, added 16 crossings; crossings are a higher tier, so it was a bad trade in the project's own currency
- a per-group placement cache — measured slower in all three rounds; the cached groups held two or three items and the key cost more than the work
- an aligned re-score post-pass — targeted 9 layouts in 400 and would have charged every layout an extra pass
Every one of them would have shipped on argument alone.
Quality: a scoreboard
For "does this look better", one canvas cannot answer — it says the fix worked, never whether the failure moved somewhere nobody looked.
Build a corpus plus an aggregate. packages/canvas-render/src/layout/edges/edge-routing-quality.test.ts
is the worked example.
- Metrics live in an independent oracle.
test-utils/routing-metrics.tscomputes geometry directly and never calls the production scorer, so a broken rule cannot satisfy a test by agreeing with itself. Same contract astest-utils/reversal-count.ts. - Corpus = every reported defect, plus generated cases. Generate DENSE: the defects only appear when things crowd, and a generator that spreads them out reports a clean score while drawing the same broken pictures.
- Pin the numbers EXACTLY, not as a ceiling. An improvement must be as loud as a regression — the point is that the number moves and someone says why. It is not a golden to regenerate.
- Separate DEBT from PRICE. Debt metrics (violations, ink through a body) target zero. Price metrics (bends, length, crossings) have no target and exist so a change that buys less of one harm with more of another cannot do it silently.
For a behaviour-preserving change, the scoreboard not moving is the proof: an optimisation that leaves every pinned count identical, with no expectation edited, has demonstrated it changed nothing but speed.
Performance: pnpm bench
vitest bench (packages/canvas-render/src/layout/edges/spatial-edges.bench.ts),
never a hand-rolled performance.now() loop — you need the variance and the
sample count to know whether you measured anything.
Interleave, or you are measuring the machine
Between-run drift on a loaded dev machine routinely exceeds the effect. Run the versions alternately and compare paired rounds:
run() { pnpm bench 2>&1 | grep -aE "<the bench name>" | awk '{printf "%s ", $(NF-8)}'; } # min
for i in 1 2 3; do
echo -n "AFTER r$i: "; run; echo
git stash push -q <changed file>
echo -n "BEFORE r$i: "; run; echo
git stash pop -q
done
Report "faster in every paired round, median X → Y". A single before/after pair is not evidence.
A second bench in the same process is not a control
Benching a primitive alongside the thing you changed feels like a control. It
is not: they share a process, so JIT warm-up couples them. A change that
made the search call routeEdge 854 → 558 times left it colder when the
primitives bench ran, and it measured ~40% slower with its own code
untouched. Treat any movement in an untouched bench as a warning that the
comparison is contaminated, not as a result.
Profile before hypothesising
The prediction was that 104 full-edge-set anchor recomputations were the bulk. Measured: 16ms of 290ms. The real cost was elsewhere entirely, and a large refactor was nearly built on the wrong target. Time the phases first, even crudely.
Counting is often better than timing. routeEdge calls: 854, of which 296
were byte-identical repeats — that number said "memoize" far more clearly
than any flame graph.
Deciding a trade
When a change buys one metric with another, the project's declared ordering
decides it, not the size of the win. PENALTY_RULES' tiers are that
ordering: paying tier-2 debt for a tier-3 gain is a bad trade however good
the ratio looks.
When the ordering does not settle it — the currencies are genuinely different, or the declared order is itself in question — that is a human decision, and it is worth interrupting for. State both columns and the concrete counts, and recommend one. ("42% less ink through a box, 8% more crossings" was decided by a human in ten seconds; guessing it would have been a coin flip.)
Before a big rewrite, ask whether the target is reachable
Cheapest experiment in the batch: for every route still cutting through a node, does a clean path exist at all? A throwaway BFS answered 67 of 68, which said the remaining debt was a candidate-generation problem and not a cost-model one — redirecting the work and retiring a planned task outright.
Ask "is this even fixable, and by what class of fix" before building the fix.