lanekeeper-performance
How to meet and prove Lanekeeper's performance budgets (P1–P15), such as drift in 15 s, the grid across 40 swimlanes in 150 ms, text search in 400 ms, and live updates in 1 s. Covers writing criterion
Files2
View on GitHubSKILL.md.claude/skills/lanekeeper-performance/SKILL.md
GitHubLanekeeper performance
Performance is the second priority (D61). Budgets are checked by machines, not by opinion: a P# without a registered benchmark or load test counts as unmet. Budgets live in docs/performance.md, and the numbers enforced are in perf/budgets.toml.
Workflow for any P# in your prompt
Locate the budget. Note the P#, the threshold, and the scale it's measured at. Design scale is 40 swimlanes × 2,000 files, 100 web users and 30 MCP sessions.
Generate realistic data. Run
cargo xtask gen-fixtures --scale target. It's deterministic, and it includes CRLF files, 6,000-line YAML, images, channel folders and duplicate names. Never benchmark on toy data; budgets fail at scale for different reasons.Write the benchmark with criterion, in your crate's
benches/, measuring the real hot path end to end within your crate. A benchmark of a helper that skips the database or the parse doesn't count.Register it in
perf/budgets.toml:[[budget]] id = "P7.grid" bench = "hub-registry/grid_40_swimlanes" statistic = "p95" threshold_ms = 150 scale = "target"This file is a contract, so new entries go through a
contract-changePR (thelanekeeper-contract-changeskill).Check it:
just bench && cargo xtask bench-check. Paste the table into the PR.If it misses, profile before optimising:
- CPU:
cargo flamegraph --bench <name>. - Allocations: dhat or heaptrack.
- SQL:
EXPLAIN (ANALYZE, BUFFERS). - Async stalls: tokio-console.
- CPU:
Mechanisms to reach for, in order
- Don't do the work twice.
- Content-address everything (SHA-256).
- Parse each unique blob once into
settings_index. - Index each Git commit once into
git_tree_index. - Cache derived results in moka, keyed by input hashes. They never go stale, so they never need invalidating.
- Don't do work that didn't change.
- Recompute incrementally, from events.
- Agents send Merkle deltas.
- Whole-swimlane compare skips subtrees whose effective tree hashes are equal.
- Push filtering into indexed SQL.
- pg_trgm for setting paths.
- Keyset pagination.
- No N+1 queries; batch with
= ANY($1).
- Use the cores. rayon over deduplicated blobs, inside
spawn_blocking. - Move fewer bytes.
- zstd on agent streams.
Bytesinstead of copies.- Immutable blob URLs with ETags.
- Server-computed diff hunks.
- MCP output capped at 16 KB.
- Render less.
- Virtualised lists with fixed row heights.
- Lazy-loaded Monaco.
- Route-level code splitting.
- Query keys that include content hashes, so cached data is safe to reuse.
Anti-patterns that have sunk budgets elsewhere
- Parsing YAML per request instead of reading
settings_index. - Building the grid by loading whole files for 40 swimlanes.
OFFSETpagination on large tables.- Holding a database transaction across an agent round trip.
- Unbounded fan-out: one SSE event triggering a refetch of everything on every client. Invalidate the precise query keys.
- Cloning large
Vec<u8>buffers between layers. - Synchronous filesystem access inside async code.
Escalating a miss
If you can't meet a budget with your prompt's approach, stop and write in the PR:
- the P#;
- the measured p50, p95 and p99;
- a flamegraph or
EXPLAINexcerpt; - what you tried;
- the options, each with its trade-off: an index change, a contract change, or a scale assumption.
A human decides. Never loosen perf/budgets.toml yourself.