Bootstrap hotpath from engineering spec v0.1

Commit the spec and scaffold the system it describes:

- SPEC.md: the full v0.1 engineering spec
- crates/: trust-layer Rust workspace, unit-tested
  - bench-runner: interleaved ABAB scheduling with discarded warm-ups,
    deterministic bootstrap CIs over the ratio of medians,
    CI-lower-bound accept rule, env fingerprinting, and a CLI with
    compare + A/A self-test modes (A/A verified passing end-to-end)
  - diff-test: gate outcome model, sha256 corpus pin verifier that
    refuses on mismatch, per-target equivalence policy parser
    (byte-identical default, explicit fp-tolerance opt-in)
  - ledger: append-only SQLite attempt schema with UPDATE/DELETE
    refused by triggers, anti-double-attempt query
  - report: cores-saved and dollars/yr ROI math over speedup CIs,
    pricing.toml inputs
- playbook/: classes 1-7 with preconditions, procedure, verification
  notes, and known failure modes per class
- agent/: Python package skeleton — six-tool contract and prompting
  spine, harness IPC deferred to Phase 1
- config/: accept.toml (2% threshold, 30 runs/side), pricing.toml
- targets/, corpora/, results/calibration/: layout and policy docs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EcKPWQ3MQD4NdEHwar96u2
This commit is contained in:
Claude
2026-07-06 18:34:51 +00:00
parent 836d4c9208
commit 328aafae77
40 changed files with 2468 additions and 0 deletions
+8
View File
@@ -216,3 +216,11 @@ __marimo__/
# Streamlit
.streamlit/secrets.toml
# Rust (trust-layer workspace)
target/
# hotpath run outputs
results/**/*.json
results/**/*.sqlite
targets/*/workspace/
Generated
+614
View File
@@ -0,0 +1,614 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "ahash"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "anstream"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
[[package]]
name = "anstyle-parse"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys",
]
[[package]]
name = "anyhow"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
[[package]]
name = "bench-runner"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"ledger",
"serde",
"serde_json",
"toml",
]
[[package]]
name = "bitflags"
version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "cc"
version = "1.2.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "clap"
version = "4.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
[[package]]
name = "colorchoice"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "diff-test"
version = "0.1.0"
dependencies = [
"anyhow",
"serde",
"sha2",
"thiserror",
"toml",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "fallible-iterator"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
[[package]]
name = "fallible-streaming-iterator"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
]
[[package]]
name = "hashbrown"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "hashlink"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
dependencies = [
"hashbrown 0.14.5",
]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "indexmap"
version = "2.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [
"equivalent",
"hashbrown 0.17.1",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "ledger"
version = "0.1.0"
dependencies = [
"anyhow",
"rusqlite",
"serde",
"serde_json",
"thiserror",
]
[[package]]
name = "libc"
version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "libsqlite3-sys"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
dependencies = [
"cc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "memchr"
version = "2.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "pkg-config"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
dependencies = [
"proc-macro2",
]
[[package]]
name = "report"
version = "0.1.0"
dependencies = [
"anyhow",
"ledger",
"serde",
"serde_json",
"toml",
]
[[package]]
name = "rusqlite"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e"
dependencies = [
"bitflags",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
"libsqlite3-sys",
"smallvec",
]
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "serde_spanned"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
dependencies = [
"serde",
]
[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "shlex"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "smallvec"
version = "1.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "toml"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.22.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"toml_write",
"winnow",
]
[[package]]
name = "toml_write"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
[[package]]
name = "typenum"
version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "winnow"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
dependencies = [
"memchr",
]
[[package]]
name = "zerocopy"
version = "0.8.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75726053136156d419e285b9b7eddaaea9e3fea6ce32eed44a89901f0bd98de1"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4714fd92cf900833d49538023a9b3915155210801d1c1169eba513b2addefd71"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+24
View File
@@ -0,0 +1,24 @@
[workspace]
resolver = "2"
members = [
"crates/bench-runner",
"crates/diff-test",
"crates/ledger",
"crates/report",
]
[workspace.package]
version = "0.1.0"
edition = "2021"
license = "GPL-3.0-or-later"
repository = "https://github.com/bdbrown4/peltier.io"
[workspace.dependencies]
anyhow = "1"
clap = { version = "4", features = ["derive"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sha2 = "0.10"
thiserror = "2"
toml = "0.8"
rusqlite = { version = "0.32", features = ["bundled"] }
+58
View File
@@ -1,2 +1,60 @@
# peltier.io
Spot cooling for hot paths. A profile-guided optimization agent that only ships wins it can prove.
hotpath finds the 10–90% left on the floor between "the compiler" and
"hire a perf consultant," with two properties no consultant offers:
**verified equivalence** (a change provably preserves behavior or is
discarded) and **self-verifying ROI** (the value claim is a stopwatch on
trusted infrastructure, not a survey).
Full design: **[SPEC.md](SPEC.md)**.
## Layout
```
crates/ Trust layer (agent has NO write access — SPEC §10)
bench-runner/ Interleaved A/B timing, bootstrap CIs, A/A calibration
diff-test/ Equivalence gates, corpus hash-pinning, per-target policy
ledger/ Append-only SQLite attempt ledger (enforced by triggers)
report/ ROI math: speedup CI → cores → dollars, caveats included
agent/ Untrusted proposer (Claude Agent SDK, Python)
playbook/ Optimization classes 1–7, tried strictly cheapest-first
config/ accept.toml (thresholds), pricing.toml (ROI inputs)
targets/ Per-target checkouts — the only agent-writable path
corpora/ Hash-pinned golden-replay inputs (read-only to agent)
results/ Calibration evidence, per-engagement outputs
```
## Quick start
```sh
cargo test --workspace # trust-layer unit tests
just aa # A/A self-test: must yield a null verdict
just compare "cmd-a" "cmd-b" # interleaved A/B with bootstrap CI
```
## Status
Phase 0–1 scaffold (see SPEC §5 for the phase plan and exit criteria).
Done:
- Workspace with the four trust-layer crates; core statistics
(interleaved scheduling, bootstrap ratio CIs, CI-lower-bound accept
rule), append-only ledger schema with mutation-refusing triggers,
corpus hash-pin verifier, equivalence-policy parser, ROI formulas —
all unit-tested.
- bench-runner CLI: whole-program `compare` and `aa` modes with env
fingerprinting.
- Playbook classes 1–7; agent tool contract and prompting spine.
Next (Phase 1 exit criteria):
- `perf stat` counters + max RSS + RAPL capture per run.
- Automated A/A calibration sessions and regression-injection self-test
recorded to `results/calibration/` (<5% FP, ≥95% detection).
- diff-test orchestration end-to-end on two real targets; harness IPC
for the agent tool surface.
## License
GPL-3.0-or-later — see [LICENSE](LICENSE).
+163
View File
@@ -0,0 +1,163 @@
# hotpath — Engineering Spec v0.1
## 1. Mission & thesis
Performance engineering is scarce artisan labor. Compilers closed the gap at general codegen; nothing closed it at the application and infrastructure layer, where profile-guided rewrites, build configuration, data-layout changes, and kernel-level work still leave 10–90% on the floor. hotpath is an agent that harvests that gap with two properties no consultant offers: **verified equivalence** (a change provably preserves behavior or is discarded) and **self-verifying ROI** (the value claim is a stopwatch on trusted infrastructure, not a survey).
Why this wins:
- **Empty niche.** Between "the compiler" and "hire a perf consultant" there is no product. LLM codegen is crowded; verified optimization is not.
- **The metric defends itself.** Same inputs, same outputs, fewer cycles, with confidence intervals. No correlation-causation problem is possible by construction.
- **The ledger compounds.** Every accepted/rejected attempt with full evidence is future RL training data and, eventually, the substrate for learned optimization policies (see §13, research fork).
## 2. System overview
```
┌────────────────────────────────────────────┐
│ TRUST LAYER │
│ (agent has NO write access to this box) │
┌─────────┐ patch │ ┌────────────┐ ┌───────────┐ ┌───────┐ │
│ agent/ │──────────▶│ │ diff-test │─▶│bench-runner│─▶│ledger │ │
│ (Agent │ via git │ │ gates │ │ interleaved│ │SQLite │ │
│ SDK) │ apply + │ └────────────┘ │ A/B + CIs │ └───┬───┘ │
└────┬────┘ allowlist│ └───────────┘ │ │
│ └──────────────────────────────────────┼──────┘
│ reads profiles, ledger, playbook ▼
▼ ┌─────────┐
┌──────────┐ │ report/ │
│ profiler │ perf / flamegraph / coz │ ROI │
│ adapter │ └─────────┘
└──────────┘
```
Loop: profile → rank hotspots → select playbook class → hypothesize → patch → gates → bench → verdict → ledger → repeat.
## 3. Component specs
### 3.1 `bench-runner` (the product is this crate)
Requirements:
- Runs baseline and candidate **interleaved** (ABABAB…), never sequential blocks, to control thermal and background drift.
- Baseline is rebuilt from a **pristine checkout** each session — never from agent workspace.
- Environment control: CPU pinning (`taskset`/cgroup cpuset), performance governor, turbo/SMT state recorded (disabled where root permits), ASLR handled explicitly (see §7), no-network container, warm-up runs discarded.
- Statistics: ≥30 measured runs per side (configurable); report median and bootstrap 95% CI of the ratio; effect accepted only if CI lower bound ≥ threshold (`config/accept.toml`, default 2%).
- **A/A self-test mode**: run the same binary as both sides; must produce a null verdict. Scheduled automatically before any measurement session.
- Regression-injection self-test: a synthetic 5% slowdown must be detected ≥95% of the time.
- Metrics captured per run: wall time, cycles, instructions, cache misses, branch misses (via `perf stat`), max RSS, and RAPL energy where available.
- Modes: (a) whole-program via hyperfine-style CLI timing; (b) microbench via criterion harness hooks; (c) service mode (Phase 4): wrk2 / vegeta load replay with **coordinated-omission-aware** latency recording (HdrHistogram).
Acceptance: passes A/A calibration at <5% false-positive rate and injected-regression detection at ≥95%, documented in `results/calibration/`.
### 3.2 `diff-test` (equivalence gates)
Layered, all must pass:
1. **Upstream test suite** of the target, unmodified, green.
2. **Golden replay**: recorded input corpus → byte-identical outputs (stdout, files, exit codes). FP-producing targets declare an explicit tolerance policy in `targets/<name>/equivalence.toml`; absent a policy, byte-identical is required.
3. **Differential fuzzing** of changed functions: old vs. new implementation compiled side by side, driven by proptest/cargo-fuzz (Rust) or libFuzzer (C/C++), outputs compared. Minimum 60s or 10k iterations per changed function.
4. **Sanitizers** on the candidate: ASan + UBSan always; TSan when the diff touches threads, atomics, or locks; MIRI for Rust targets where feasible.
Corpus and test files live outside agent-writable paths and are hash-pinned; `diff-test` refuses to run if hashes mismatch.
### 3.3 `ledger`
Append-only SQLite. One row per attempt:
`run_id, timestamp, target, target_commit, phase, hotspot (symbol + % of profile), playbook_class, hypothesis, patch (diff), gates {tests, golden, fuzz_iters, sanitizers}, bench {baseline_ci, candidate_ci, delta_ci, env_fingerprint}, verdict {accepted | rejected-gate | rejected-bench | needs-human-review}, tokens_spent, wall_time`.
Nothing is ever deleted. The ledger is the audit trail, the anti-double-attempt memory, and the future training set.
### 3.4 `profiler adapter`
Phase 0–2: `perf record` + flamegraphs (inferno) + `perf stat`; hotspot ranking = exclusive samples by symbol with source mapping. Phase 3: add **coz** causal profiling — "what does speeding this line up buy end-to-end" — which is the literal bridge from cycles to ROI and the correct prioritizer for multi-threaded targets.
### 3.5 `agent/`
Claude Agent SDK (Python). Tools exposed to the model: `read_profile`, `read_ledger`, `read_playbook`, `propose_patch(diff, hypothesis)`, `run_verdict`, `read_target_source` (read-only). Deliberately **not** exposed: shell on the host, writes outside `targets/<name>/`, any access to `crates/`, `config/`, `corpora/`.
Prompting spine: (1) state hypothesis before patching; (2) prefer the cheapest untried playbook class; (3) max two iterations per rejected hypothesis; (4) a rejection with a clean ledger row is a successful outcome.
### 3.6 `report/`
Consumes ledger rows, emits per-engagement ROI report:
- Throughput jobs: cores saved = fleet_cores × (1 − 1/speedup); $ = cores × $/core-hr × hours/yr (rates in `config/pricing.toml`).
- Services: p50/p95/p99 deltas under replayed traffic, with the business-value mapping left as a customer-supplied parameter and one worked example.
- Optional: joules saved (RAPL) for the sustainability line.
- Every figure carries CI + workload description + environment fingerprint. Caveats are printed on the report, not in an appendix.
## 4. Optimization playbook v0 (strict order)
1. **Build configuration**: opt level, `-march`/`target-cpu`, LTO (thin → fat), PGO, BOLT where applicable.
2. **Allocator swap**: mimalloc/jemalloc trial. (Classes 1–2 are the "your 30% was a linker flag" tier — always exhausted first.)
3. **Allocation churn**: hoisting, reuse, arenas, small-vector.
4. **Data layout**: struct packing/reordering, AoS→SoA, false-sharing fixes, cache blocking.
5. **Algorithmic**: complexity class, better std/library primitive, memoization, precomputation.
6. **SIMD**: autovectorization enablement first (aliasing, bounds, FP flags — FP flags trigger human review), intrinsics last.
7. **Concurrency**: contention reduction, sharding, lock-free only with `needs-human-review`.
Each class ships as `playbook/NN-name.md`: preconditions (profile signature that suggests it), procedure, verification notes, known failure modes.
## 5. Phase plan & exit criteria
| Phase | Scope | Exit criteria |
|---|---|---|
| **0** Manual dry run (1–2 wknd) | Two CPU-bound OSS targets; loop run by hand with Claude Code as copilot | ≥1 verified win ≥10% on one target; written case study with methodology |
| **1** Trust layer (3–4 wknd) | bench-runner + diff-test + ledger | A/A false-positive <5%; injected 5% regression caught ≥95%; gates run on 2 targets end-to-end |
| **2** Agent (3–4 wknd) | Agent SDK loop, Rust targets first | Unattended profile→verdict loop on 1 target; ≥1 auto-accepted win; **zero false accepts** across ≥20 audited attempts |
| **3** Playbook + proof | Classes 3–7, coz, C/C++ targets | 3–5 public case studies; zero regressions shipped; playbook ≥6 classes |
| **4** Services & scale | Load replay, latency mode, ROI reports | One real service workload optimized under replayed traffic; report generated mechanically |
Language order: Rust first (criterion/proptest/MIRI make Phases 1–2 cheaper), C/C++ second (the richer legacy hunting ground), services third.
## 6. Target selection criteria (Phase 0–3)
CPU-bound (>70% user time on profile); meaningful test suite; existing benchmark or easily scripted workload; permissive license; active but not hyper-optimized (skip anything with a dedicated perf team); builds cleanly in container. Good hunting: codecs, parsers, serializers, image processing, compression, static-site generators, linters.
## 7. Statistical methodology notes
- Interleaved A/B with pristine-checkout baselines is the core defense against drift.
- **Layout bias** (Mytkowicz et al., ASPLOS'09: link order and env size alone swing results past "real" effects): mitigate by randomizing link order / ASLR *across* runs and aggregating, Stabilizer-style, rather than fixing one lucky layout. Document the chosen mode in the env fingerprint.
- Report ratios with bootstrap CIs; never means without spread; never a single run, ever.
- Service latency: wrk2 constant-throughput mode + HdrHistogram to avoid coordinated omission; never trust closed-loop load generators for p99 claims.
## 8. Equivalence policy tiers
- **Auto-acceptable**: bit-identical outputs across all gates, no FP-ordering changes, no concurrency-primitive changes, sanitizers clean.
- **needs-human-review** (never auto-accepted): FP reassociation/contraction or `-ffast-math`-class flags; any diff touching atomics, locks, memory ordering, or thread counts; unsafe blocks / raw pointer arithmetic introduced; anything the sanitizers flag even as warnings.
- **Auto-rejected**: any gate failure, any divergence under differential fuzz, patch outside allowlist.
## 9. ROI methodology (the pitch artifact)
The report answers one question: *what did the stopwatch say, and what does that cost or buy?* Throughput → cores → dollars via public cloud pricing; latency → percentile deltas under recorded production-shaped load, priced by the customer's own latency-value number; batch windows → wall-clock hours returned to the schedule. All three come with CIs and a workload statement. The methodology section ships *inside* every report so the number survives hostile review — that is the differentiation.
## 10. Security & anti-reward-hacking (load-bearing)
The agent's incentive is "make the number go up"; the design must make cheating structurally impossible, not merely discouraged:
- Trust layer (`crates/`, `config/`, `corpora/`, upstream tests) is **read-only to the agent** — enforced by filesystem permissions and a separate-process harness, not by prompt.
- Patches are applied by the harness via `git apply` after a path-allowlist check; the agent never holds a general shell on the host.
- Baselines rebuilt from pristine checkouts; corpora hash-pinned; `diff-test` refuses on mismatch.
- Target code executes only in no-network containers (bench containers additionally seccomp-restricted).
- Periodic human audit of accepted wins (100% in Phase 2, sampled thereafter); any false accept is a stop-the-line event.
## 11. Risk register
| Risk | Mitigation |
|---|---|
| Measurement bias / noise | §7 discipline; A/A tests before every session; env fingerprints |
| Overfitting to the benchmark | Holdout workloads; Phase 4 uses recorded real traffic only |
| FP equivalence gaps | Explicit per-target tolerance policy; default byte-identical; human review tier |
| Concurrency bugs introduced | TSan gate; human-review tier for all concurrency diffs |
| UB introduced | UBSan always; MIRI for Rust; human-review for unsafe |
| Agent games the harness | §10 structural controls; audits |
| License/IP hygiene | Permissive-license targets only for public case studies |
| Wins too small to matter | Cheap-wins-first ordering keeps cost-per-attempt low; ledger prevents re-grinding dead ends |
## 12. Tech stack
Rust workspace (`bench-runner`, `diff-test`, `ledger`, `report`) · `just` · hyperfine-style timing + criterion hooks · perf, inferno flamegraphs, coz · proptest, cargo-fuzz, libFuzzer · ASan/UBSan/TSan, MIRI · podman/docker (no-net) · wrk2 + HdrHistogram, vegeta · SQLite · Python 3.11+ with Claude Agent SDK (`agent/`) — confirm current SDK package name and auth at https://docs.claude.com/en/docs/claude-code/overview.
## 13. Later / research fork (explicitly out of scope now)
- GPU kernel lane (where 2–10x gaps live): Triton/CUDA targets, correctness via reference-kernel differential testing, reward = measured kernel time.
- Learned optimization policies: ledger → RL; DELTA-over-program-graphs (ProGraML-style IR graphs) as the policy/ranking model. Prior art anchors: MLGO (learned inlining/regalloc in LLVM), AlphaDev (Nature 2023), Meta LLM Compiler (2024).
## 14. References
Mytkowicz et al., *Producing Wrong Data Without Doing Anything Obviously Wrong* (ASPLOS 2009) — read before building bench-runner · Curtsinger & Berger, *Stabilizer* and *coz* · Gil Tene on coordinated omission · MLGO (Trofin et al.) · ProGraML (Cummins et al.) · AlphaDev (Mankowitz et al., Nature 2023) · Meta LLM Compiler (Cummins et al., 2024).
+10
View File
@@ -0,0 +1,10 @@
"""hotpath agent (SPEC §3.5).
The agent is the *untrusted* half of the system. It reads profiles, the
ledger, and the playbook; it proposes patches and requests verdicts. It
never holds a shell on the host, never writes outside targets/<name>/,
and never touches crates/, config/, or corpora/ — enforced by the
harness and filesystem permissions, not by this code or any prompt.
"""
__version__ = "0.1.0"
+31
View File
@@ -0,0 +1,31 @@
"""Prompting spine (SPEC §3.5). The system prompt is versioned code, not
a config knob: changes here change what the ledger's attempts mean."""
SYSTEM_PROMPT = """\
You are hotpath, a performance-optimization agent. You operate in a loop:
profile -> rank hotspots -> select playbook class -> hypothesize -> patch
-> gates -> bench -> verdict -> ledger.
Rules, in priority order:
1. State your hypothesis BEFORE proposing a patch: which hotspot, which
playbook class, what mechanism, and what speedup you expect. The
hypothesis is recorded verbatim in the ledger.
2. Prefer the cheapest untried playbook class for the current hotspot.
Check the ledger first; never re-attempt a (hotspot, class,
hypothesis) combination that already has a verdict.
3. At most two iterations per rejected hypothesis. After the second
rejection, move to the next class or the next hotspot.
4. A rejection with a clean ledger row is a successful outcome. You are
measured on the integrity of the ledger, not on acceptance rate.
Never restructure a patch to influence measurement rather than to
express the hypothesis.
5. Equivalence is not yours to relax. Floating-point flag changes,
concurrency-primitive changes, and new unsafe code are routed to
human review automatically; do not attempt to phrase a patch to
avoid that routing.
"""
+59
View File
@@ -0,0 +1,59 @@
"""Tool surface exposed to the model (SPEC §3.5).
Exactly six tools, all mediated by the trust-layer harness over IPC —
the agent process has no direct filesystem or shell capability beyond
what these return. Deliberately NOT exposed: shell on the host, writes
outside targets/<name>/, any access to crates/, config/, corpora/.
Phase 2 wiring: each function below becomes a Claude Agent SDK tool
definition whose implementation calls the harness. The signatures and
docstrings are the contract; the bodies are placeholders until the
harness IPC exists (Phase 1 exit criteria).
"""
from __future__ import annotations
from typing import Any
class HarnessUnavailable(RuntimeError):
"""Raised until the Phase 1 trust-layer harness IPC is wired up."""
def read_profile(target: str) -> dict[str, Any]:
"""Latest profile for a target: ranked hotspots (symbol, exclusive %,
source mapping) plus perf stat counters. Read-only."""
raise HarnessUnavailable("profiler adapter not wired (Phase 1)")
def read_ledger(target: str) -> list[dict[str, Any]]:
"""All prior attempts against this target — the anti-double-attempt
memory. The agent must not re-grind a (hotspot, playbook_class,
hypothesis) combination that already has a verdict."""
raise HarnessUnavailable("ledger IPC not wired (Phase 1)")
def read_playbook(class_number: int) -> str:
"""Playbook class markdown (1-7): preconditions, procedure,
verification notes, known failure modes."""
raise HarnessUnavailable("playbook IPC not wired (Phase 1)")
def read_target_source(target: str, path: str) -> str:
"""Read-only view of the target's source tree at the pinned commit."""
raise HarnessUnavailable("source IPC not wired (Phase 1)")
def propose_patch(target: str, diff: str, hypothesis: str) -> str:
"""Submit a unified diff plus the hypothesis it tests. The harness
applies it via `git apply` after a path-allowlist check (SPEC §10);
a diff touching anything outside targets/<target>/ is auto-rejected
before any gate runs. Returns a patch id."""
raise HarnessUnavailable("patch IPC not wired (Phase 1)")
def run_verdict(patch_id: str) -> dict[str, Any]:
"""Run the full gate + bench sequence on a proposed patch and return
the ledger row (gates, bench CIs, verdict). This is the only way the
agent ever learns whether a change 'worked' — there is no other
number to optimize."""
raise HarnessUnavailable("verdict IPC not wired (Phase 1)")
+18
View File
@@ -0,0 +1,18 @@
[project]
name = "hotpath-agent"
version = "0.1.0"
description = "hotpath optimization agent: the untrusted proposer in the profile→patch→verdict loop"
requires-python = ">=3.11"
dependencies = [
"claude-agent-sdk>=0.1",
]
[project.optional-dependencies]
dev = ["pytest>=8"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["hotpath_agent"]
+20
View File
@@ -0,0 +1,20 @@
# Accept thresholds for bench-runner (SPEC §3.1).
# This file is trust-layer: read-only to the agent (SPEC §10).
# CI lower bound of the speedup ratio must clear 1 + threshold.
threshold = 0.02
# Measured runs per side (SPEC minimum: 30). Interleaved ABAB.
runs_per_side = 30
# Warm-up runs per side, discarded.
warmup_runs = 3
# Bootstrap resamples for the ratio CI.
bootstrap_iters = 10000
# Confidence level for the ratio CI.
confidence = 0.95
# Fixed PRNG seed so calibration runs are reproducible.
bootstrap_seed = 1887343589
+12
View File
@@ -0,0 +1,12 @@
# ROI pricing inputs (SPEC §3.6, §9). Defaults are public-cloud list
# prices; replace with the customer's own numbers per engagement.
# This file is trust-layer: read-only to the agent (SPEC §10).
# On-demand vCPU rate, dollars per core-hour.
dollars_per_core_hour = 0.04
# Hours per year the fleet actually runs the workload (8760 = 24/7).
hours_per_year = 8760.0
# Printed on the report so the number survives hostile review.
rate_source = "example: AWS m7i on-demand us-east-1, per-vCPU approximation — replace per engagement"
+15
View File
@@ -0,0 +1,15 @@
# corpora/
Golden-replay input corpora and expected outputs, one directory per
target. **Trust layer: read-only to the agent** (SPEC §10).
Every corpus directory carries a `MANIFEST.sha256` (`sha256sum` format:
`<digest> <relative-path>` per line). `diff-test` verifies the manifest
before every run and refuses to run on any mismatch — a tampered corpus
is a stop-the-line event.
Regenerate a manifest only as a deliberate human action:
```sh
cd corpora/<target> && find . -type f ! -name MANIFEST.sha256 -exec sha256sum {} + > MANIFEST.sha256
```
+15
View File
@@ -0,0 +1,15 @@
[package]
name = "bench-runner"
description = "Interleaved A/B benchmark harness with bootstrap CIs, A/A calibration, and environment fingerprinting"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
[dependencies]
anyhow.workspace = true
clap.workspace = true
ledger = { path = "../ledger" }
serde.workspace = true
serde_json.workspace = true
toml.workspace = true
+48
View File
@@ -0,0 +1,48 @@
//! Accept-threshold configuration, loaded from `config/accept.toml`.
use serde::Deserialize;
use std::path::Path;
#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct AcceptConfig {
/// CI lower bound must clear 1 + threshold (default 2%).
pub threshold: f64,
/// Measured runs per side (SPEC minimum: 30).
pub runs_per_side: usize,
/// Unmeasured warm-up runs per side, discarded.
pub warmup_runs: usize,
/// Bootstrap resample count.
pub bootstrap_iters: usize,
/// Confidence level for the ratio CI, e.g. 0.95.
pub confidence: f64,
/// PRNG seed for the bootstrap (reproducible calibration).
pub bootstrap_seed: u64,
}
impl Default for AcceptConfig {
fn default() -> Self {
Self {
threshold: 0.02,
runs_per_side: 30,
warmup_runs: 3,
bootstrap_iters: 10_000,
confidence: 0.95,
bootstrap_seed: 0x707E_17E5,
}
}
}
impl AcceptConfig {
pub fn load(path: &Path) -> anyhow::Result<Self> {
let raw = std::fs::read_to_string(path)?;
let cfg: Self = toml::from_str(&raw)?;
anyhow::ensure!(cfg.runs_per_side >= 2, "runs_per_side must be >= 2");
anyhow::ensure!(cfg.threshold > 0.0, "threshold must be positive");
anyhow::ensure!(
(0.0..1.0).contains(&cfg.confidence),
"confidence must be in (0, 1)"
);
Ok(cfg)
}
}
+59
View File
@@ -0,0 +1,59 @@
//! Command execution and timing. Whole-program mode (hyperfine-style):
//! spawn the command, measure wall time, discard warm-ups per the
//! schedule. `perf stat` counters and RAPL energy are Phase 1 follow-ups
//! (tracked in README roadmap); wall time alone is enough to bring up
//! A/A calibration.
use crate::schedule::{interleaved, Side};
use anyhow::Context;
use std::process::{Command, Stdio};
use std::time::Instant;
#[derive(Debug, Clone)]
pub struct MeasuredSamples {
pub baseline_s: Vec<f64>,
pub candidate_s: Vec<f64>,
}
fn time_once(shell_cmd: &str) -> anyhow::Result<f64> {
let start = Instant::now();
let status = Command::new("sh")
.arg("-c")
.arg(shell_cmd)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.with_context(|| format!("failed to spawn: {shell_cmd}"))?;
let elapsed = start.elapsed().as_secs_f64();
anyhow::ensure!(status.success(), "command exited nonzero: {shell_cmd}");
Ok(elapsed)
}
/// Run baseline and candidate commands on the interleaved schedule and
/// return measured wall times per side. For A/A mode, pass the same
/// command as both sides.
pub fn run_interleaved(
baseline_cmd: &str,
candidate_cmd: &str,
runs_per_side: usize,
warmup: usize,
) -> anyhow::Result<MeasuredSamples> {
let mut out = MeasuredSamples {
baseline_s: Vec::with_capacity(runs_per_side),
candidate_s: Vec::with_capacity(runs_per_side),
};
for (side, measured) in interleaved(runs_per_side, warmup) {
let cmd = match side {
Side::Baseline => baseline_cmd,
Side::Candidate => candidate_cmd,
};
let elapsed = time_once(cmd)?;
if measured {
match side {
Side::Baseline => out.baseline_s.push(elapsed),
Side::Candidate => out.candidate_s.push(elapsed),
}
}
}
Ok(out)
}
+53
View File
@@ -0,0 +1,53 @@
//! Environment fingerprint (SPEC §3.1, §7): every reported number carries
//! the machine state it was measured under. Collection is best-effort —
//! a field we cannot read is recorded as "unknown", never omitted.
use serde::Serialize;
use std::fs;
#[derive(Debug, Clone, Serialize)]
pub struct EnvFingerprint {
pub kernel: String,
pub cpu_model: String,
pub governor: String,
pub smt: String,
pub turbo: String,
/// ASLR handling mode actually in effect for this session (SPEC §7:
/// randomize-and-aggregate vs. fixed; document, don't assume).
pub aslr: String,
pub cpu_pinning: String,
}
fn read_trimmed(path: &str) -> String {
fs::read_to_string(path)
.map(|s| s.trim().to_string())
.unwrap_or_else(|_| "unknown".to_string())
}
impl EnvFingerprint {
pub fn collect(pinning: &str, aslr_mode: &str) -> Self {
let cpu_model = fs::read_to_string("/proc/cpuinfo")
.ok()
.and_then(|s| {
s.lines()
.find(|l| l.starts_with("model name"))
.and_then(|l| l.split(':').nth(1))
.map(|v| v.trim().to_string())
})
.unwrap_or_else(|| "unknown".to_string());
Self {
kernel: read_trimmed("/proc/sys/kernel/osrelease"),
cpu_model,
governor: read_trimmed(
"/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor",
),
smt: read_trimmed("/sys/devices/system/cpu/smt/active"),
turbo: read_trimmed("/sys/devices/system/cpu/intel_pstate/no_turbo"),
aslr: format!(
"mode={aslr_mode} randomize_va_space={}",
read_trimmed("/proc/sys/kernel/randomize_va_space")
),
cpu_pinning: pinning.to_string(),
}
}
}
+43
View File
@@ -0,0 +1,43 @@
//! bench-runner core (SPEC §3.1, §7).
//!
//! The product is this crate: interleaved A/B measurement, bootstrap CIs
//! over the ratio of medians, threshold-gated accept decisions, and A/A
//! self-calibration. Pure statistics live here and are unit-tested;
//! process execution and `perf stat` integration live in `exec`.
pub mod config;
pub mod exec;
pub mod fingerprint;
pub mod schedule;
pub mod stats;
use ledger::Verdict;
/// Decide a verdict from a speedup CI and the accept threshold.
///
/// Speedup is baseline/candidate (>1 means the candidate is faster).
/// Accepted only if the CI *lower bound* clears `1 + threshold` — the
/// point estimate is never enough (SPEC §3.1).
pub fn decide(speedup_ci: stats::RatioCi, threshold: f64) -> Verdict {
if speedup_ci.lo >= 1.0 + threshold {
Verdict::Accepted
} else {
Verdict::RejectedBench
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::stats::RatioCi;
#[test]
fn accept_requires_ci_lower_bound_not_point_estimate() {
// 10% median speedup but the CI dips below threshold: rejected.
let noisy = RatioCi { median: 1.10, lo: 1.005, hi: 1.21 };
assert_eq!(decide(noisy, 0.02), Verdict::RejectedBench);
// 4% speedup with a tight CI: accepted.
let tight = RatioCi { median: 1.04, lo: 1.025, hi: 1.055 };
assert_eq!(decide(tight, 0.02), Verdict::Accepted);
}
}
+81
View File
@@ -0,0 +1,81 @@
use anyhow::Result;
use bench_runner::{config::AcceptConfig, decide, exec, fingerprint::EnvFingerprint, stats};
use clap::{Parser, Subcommand};
use ledger::Verdict;
use std::path::PathBuf;
#[derive(Parser)]
#[command(name = "bench-runner", about = "Interleaved A/B benchmark harness (hotpath trust layer)")]
struct Cli {
/// Path to accept.toml; built-in defaults if omitted.
#[arg(long)]
config: Option<PathBuf>,
#[command(subcommand)]
command: Cmd,
}
#[derive(Subcommand)]
enum Cmd {
/// Interleaved A/B comparison of two shell commands.
Compare {
#[arg(long)]
baseline: String,
#[arg(long)]
candidate: String,
},
/// A/A self-test: same command both sides; must yield a null verdict.
Aa {
#[arg(long)]
cmd: String,
},
}
fn main() -> Result<()> {
let cli = Cli::parse();
let cfg = match &cli.config {
Some(p) => AcceptConfig::load(p)?,
None => AcceptConfig::default(),
};
let (baseline, candidate, aa_mode) = match &cli.command {
Cmd::Compare { baseline, candidate } => (baseline.clone(), candidate.clone(), false),
Cmd::Aa { cmd } => (cmd.clone(), cmd.clone(), true),
};
let fp = EnvFingerprint::collect("none", "system-default");
eprintln!("env fingerprint: {}", serde_json::to_string(&fp)?);
eprintln!(
"plan: {} measured + {} warm-up runs per side, interleaved",
cfg.runs_per_side, cfg.warmup_runs
);
let samples =
exec::run_interleaved(&baseline, &candidate, cfg.runs_per_side, cfg.warmup_runs)?;
let ci = stats::bootstrap_ratio_ci(
&samples.baseline_s,
&samples.candidate_s,
cfg.bootstrap_iters,
cfg.confidence,
cfg.bootstrap_seed,
);
println!(
"speedup (baseline/candidate): median {:.4}, {:.0}% CI [{:.4}, {:.4}]",
ci.median,
cfg.confidence * 100.0,
ci.lo,
ci.hi
);
if aa_mode {
// Null verdict required: an "accept" here is a calibration failure.
let verdict = decide(ci, cfg.threshold);
if verdict == Verdict::Accepted {
anyhow::bail!("A/A self-test FAILED: harness claims a speedup from identical binaries");
}
println!("A/A self-test passed (null verdict)");
} else {
println!("verdict: {}", decide(ci, cfg.threshold).as_str());
}
Ok(())
}
+44
View File
@@ -0,0 +1,44 @@
//! Interleaved run scheduling (SPEC §3.1): baseline and candidate run
//! ABABAB…, never sequential blocks, so thermal and background drift
//! land on both sides equally.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Side {
Baseline,
Candidate,
}
/// ABAB… schedule with `runs_per_side` measured runs on each side,
/// preceded by `warmup` unmeasured warm-up entries (alternating too, so
/// both binaries reach steady state).
pub fn interleaved(runs_per_side: usize, warmup: usize) -> Vec<(Side, bool)> {
let mut plan = Vec::with_capacity(2 * (runs_per_side + warmup));
for i in 0..(runs_per_side + warmup) {
let measured = i >= warmup;
plan.push((Side::Baseline, measured));
plan.push((Side::Candidate, measured));
}
plan
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn strictly_alternating_and_counts_match() {
let plan = interleaved(30, 3);
assert_eq!(plan.len(), 66);
for pair in plan.chunks(2) {
assert_eq!(pair[0].0, Side::Baseline);
assert_eq!(pair[1].0, Side::Candidate);
}
let measured_baseline = plan
.iter()
.filter(|(s, m)| *s == Side::Baseline && *m)
.count();
assert_eq!(measured_baseline, 30);
assert!(!plan[0].1 && !plan[5].1, "warm-ups are unmeasured");
assert!(plan[6].1, "runs after warm-up are measured");
}
}
+118
View File
@@ -0,0 +1,118 @@
//! Ratios with bootstrap CIs; never means without spread; never a single
//! run, ever (SPEC §7).
/// Bootstrap confidence interval over the ratio of medians
/// (baseline / candidate).
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct RatioCi {
pub median: f64,
pub lo: f64,
pub hi: f64,
}
pub fn median(xs: &[f64]) -> f64 {
assert!(!xs.is_empty(), "median of empty sample");
let mut v = xs.to_vec();
v.sort_by(|a, b| a.partial_cmp(b).expect("NaN in sample"));
let n = v.len();
if n % 2 == 1 {
v[n / 2]
} else {
(v[n / 2 - 1] + v[n / 2]) / 2.0
}
}
/// Deterministic xorshift64* PRNG. Seeded explicitly so calibration runs
/// are reproducible; no wall-clock entropy anywhere in the stats path.
struct XorShift64(u64);
impl XorShift64 {
fn new(seed: u64) -> Self {
Self(seed.max(1))
}
fn next_u64(&mut self) -> u64 {
let mut x = self.0;
x ^= x >> 12;
x ^= x << 25;
x ^= x >> 27;
self.0 = x;
x.wrapping_mul(0x2545F4914F6CDD1D)
}
fn index(&mut self, n: usize) -> usize {
(self.next_u64() % n as u64) as usize
}
}
/// Percentile-method bootstrap CI of median(baseline)/median(candidate).
///
/// `confidence` is e.g. 0.95. Both samples are resampled independently
/// with replacement `iters` times.
pub fn bootstrap_ratio_ci(
baseline: &[f64],
candidate: &[f64],
iters: usize,
confidence: f64,
seed: u64,
) -> RatioCi {
assert!(baseline.len() >= 2 && candidate.len() >= 2, "need >=2 runs per side");
assert!((0.0..1.0).contains(&confidence));
let mut rng = XorShift64::new(seed);
let mut ratios = Vec::with_capacity(iters);
let mut b_resample = vec![0.0; baseline.len()];
let mut c_resample = vec![0.0; candidate.len()];
for _ in 0..iters {
for slot in b_resample.iter_mut() {
*slot = baseline[rng.index(baseline.len())];
}
for slot in c_resample.iter_mut() {
*slot = candidate[rng.index(candidate.len())];
}
ratios.push(median(&b_resample) / median(&c_resample));
}
ratios.sort_by(|a, b| a.partial_cmp(b).expect("NaN ratio"));
let alpha = (1.0 - confidence) / 2.0;
let lo_idx = ((iters as f64) * alpha).floor() as usize;
let hi_idx = (((iters as f64) * (1.0 - alpha)).ceil() as usize).min(iters - 1);
RatioCi {
median: median(baseline) / median(candidate),
lo: ratios[lo_idx],
hi: ratios[hi_idx],
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn median_odd_and_even() {
assert_eq!(median(&[3.0, 1.0, 2.0]), 2.0);
assert_eq!(median(&[4.0, 1.0, 2.0, 3.0]), 2.5);
}
#[test]
fn aa_ci_straddles_one() {
// Same distribution both sides: the CI must contain 1.0.
let sample: Vec<f64> = (0..40).map(|i| 1.0 + 0.01 * ((i * 7) % 11) as f64).collect();
let ci = bootstrap_ratio_ci(&sample, &sample, 2000, 0.95, 42);
assert!(ci.lo <= 1.0 && ci.hi >= 1.0, "A/A CI must straddle 1.0, got {ci:?}");
}
#[test]
fn detects_injected_regression() {
// Candidate 5% slower with mild noise: speedup CI entirely < 1.
let base: Vec<f64> = (0..40).map(|i| 1.0 + 0.002 * ((i * 3) % 7) as f64).collect();
let cand: Vec<f64> = base.iter().map(|t| t * 1.05).collect();
let ci = bootstrap_ratio_ci(&base, &cand, 2000, 0.95, 42);
assert!(ci.hi < 1.0, "5% regression must be detected, got {ci:?}");
}
#[test]
fn deterministic_for_fixed_seed() {
let a = vec![1.0, 1.1, 1.05, 0.98, 1.02, 1.07];
let b = vec![0.9, 0.95, 0.93, 0.91, 0.97, 0.92];
let x = bootstrap_ratio_ci(&a, &b, 500, 0.95, 7);
let y = bootstrap_ratio_ci(&a, &b, 500, 0.95, 7);
assert_eq!(x, y);
}
}
+14
View File
@@ -0,0 +1,14 @@
[package]
name = "diff-test"
description = "Layered equivalence gates: upstream tests, golden replay, differential fuzzing, sanitizers; hash-pinned corpora"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
[dependencies]
anyhow.workspace = true
serde.workspace = true
sha2.workspace = true
thiserror.workspace = true
toml.workspace = true
+67
View File
@@ -0,0 +1,67 @@
//! Equivalence gates (SPEC §3.2) and corpus hash-pinning.
//!
//! The gate *sequencing* (upstream tests → golden replay → differential
//! fuzz → sanitizers) is orchestrated per-target; this crate owns the
//! pieces that must be trustworthy regardless of target: the pin
//! verifier that refuses to run on a tampered corpus, and the
//! per-target equivalence policy parser (byte-identical unless a target
//! declares an explicit FP tolerance).
pub mod pin;
pub mod policy;
use serde::{Deserialize, Serialize};
/// Outcome of one gate layer. `Skipped` is only legal where the SPEC
/// allows it (e.g. TSan when no concurrency primitives changed) and the
/// reason is always recorded.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum GateOutcome {
Passed,
Failed { detail: String },
Skipped { reason: String },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum GateLayer {
UpstreamTests,
GoldenReplay,
DifferentialFuzz,
Sanitizers,
}
/// All layers must pass; any failure is an auto-reject (SPEC §8).
pub fn all_passed(results: &[(GateLayer, GateOutcome)]) -> bool {
results.iter().all(|(_, o)| {
matches!(o, GateOutcome::Passed | GateOutcome::Skipped { .. })
}) && results
.iter()
.any(|(_, o)| matches!(o, GateOutcome::Passed))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn any_failure_rejects() {
let results = vec![
(GateLayer::UpstreamTests, GateOutcome::Passed),
(
GateLayer::GoldenReplay,
GateOutcome::Failed { detail: "stdout diverged".into() },
),
];
assert!(!all_passed(&results));
}
#[test]
fn all_skipped_is_not_a_pass() {
let results = vec![(
GateLayer::Sanitizers,
GateOutcome::Skipped { reason: "n/a".into() },
)];
assert!(!all_passed(&results));
}
}
+90
View File
@@ -0,0 +1,90 @@
//! Corpus hash-pinning (SPEC §3.2, §10): corpora live outside
//! agent-writable paths and are pinned by a SHA-256 manifest. diff-test
//! refuses to run if any hash mismatches — a tampered corpus is a
//! stop-the-line event, not a warning.
use sha2::{Digest, Sha256};
use std::path::Path;
#[derive(Debug, thiserror::Error)]
pub enum PinError {
#[error("io error reading {path}: {source}")]
Io {
path: String,
source: std::io::Error,
},
#[error("malformed manifest line {line}: {content:?}")]
MalformedManifest { line: usize, content: String },
#[error("HASH MISMATCH for {path}: manifest {expected}, actual {actual} — refusing to run")]
Mismatch {
path: String,
expected: String,
actual: String,
},
}
pub fn sha256_hex(path: &Path) -> Result<String, PinError> {
let bytes = std::fs::read(path).map_err(|source| PinError::Io {
path: path.display().to_string(),
source,
})?;
Ok(format!("{:x}", Sha256::digest(&bytes)))
}
/// Verify every entry of a `sha256sum`-format manifest
/// (`<hex-digest> <relative-path>` per line, paths relative to `root`).
/// Returns the number of files verified; errors on the first mismatch.
pub fn verify_manifest(manifest: &Path, root: &Path) -> Result<usize, PinError> {
let raw = std::fs::read_to_string(manifest).map_err(|source| PinError::Io {
path: manifest.display().to_string(),
source,
})?;
let mut verified = 0;
for (i, line) in raw.lines().enumerate() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
let (expected, rel) = line
.split_once(" ")
.ok_or_else(|| PinError::MalformedManifest {
line: i + 1,
content: line.to_string(),
})?;
let path = root.join(rel);
let actual = sha256_hex(&path)?;
if !actual.eq_ignore_ascii_case(expected) {
return Err(PinError::Mismatch {
path: path.display().to_string(),
expected: expected.to_string(),
actual,
});
}
verified += 1;
}
Ok(verified)
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
#[test]
fn verifies_and_refuses() {
let dir = std::env::temp_dir().join("hotpath-pin-test");
fs::create_dir_all(&dir).unwrap();
let corpus = dir.join("input.bin");
fs::write(&corpus, b"golden input").unwrap();
let digest = sha256_hex(&corpus).unwrap();
let manifest = dir.join("MANIFEST.sha256");
fs::write(&manifest, format!("{digest} input.bin\n")).unwrap();
assert_eq!(verify_manifest(&manifest, &dir).unwrap(), 1);
// Tamper: verification must refuse.
fs::write(&corpus, b"tampered input").unwrap();
let err = verify_manifest(&manifest, &dir).unwrap_err();
assert!(matches!(err, PinError::Mismatch { .. }));
}
}
+73
View File
@@ -0,0 +1,73 @@
//! Per-target equivalence policy (`targets/<name>/equivalence.toml`).
//! Absent a policy file, byte-identical is required (SPEC §3.2).
use serde::Deserialize;
use std::path::Path;
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(tag = "mode", rename_all = "kebab-case", deny_unknown_fields)]
pub enum EquivalencePolicy {
/// Default: stdout, output files, and exit codes must match exactly.
ByteIdentical,
/// FP-producing targets only: numeric fields may differ within the
/// declared tolerance; everything else stays byte-identical. Using
/// this tier at all routes the attempt to needs-human-review when
/// FP *flags* changed (SPEC §8) — tolerance covers output compare,
/// not compiler-flag laxity.
FpTolerance {
/// Maximum absolute difference per numeric value.
abs: f64,
/// Maximum relative difference per numeric value.
rel: f64,
},
}
impl EquivalencePolicy {
/// Load the target's policy, defaulting to byte-identical when no
/// equivalence.toml exists.
pub fn load(target_dir: &Path) -> anyhow::Result<Self> {
let path = target_dir.join("equivalence.toml");
if !path.exists() {
return Ok(Self::ByteIdentical);
}
let raw = std::fs::read_to_string(&path)?;
let policy: Self = toml::from_str(&raw)?;
if let Self::FpTolerance { abs, rel } = &policy {
anyhow::ensure!(
*abs >= 0.0 && *rel >= 0.0,
"tolerances must be non-negative"
);
}
Ok(policy)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn absent_policy_means_byte_identical() {
let dir = std::env::temp_dir().join("hotpath-policy-none");
std::fs::create_dir_all(&dir).unwrap();
assert_eq!(
EquivalencePolicy::load(&dir).unwrap(),
EquivalencePolicy::ByteIdentical
);
}
#[test]
fn parses_fp_tolerance() {
let dir = std::env::temp_dir().join("hotpath-policy-fp");
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(
dir.join("equivalence.toml"),
"mode = \"fp-tolerance\"\nabs = 1e-9\nrel = 1e-6\n",
)
.unwrap();
assert_eq!(
EquivalencePolicy::load(&dir).unwrap(),
EquivalencePolicy::FpTolerance { abs: 1e-9, rel: 1e-6 }
);
}
}
+14
View File
@@ -0,0 +1,14 @@
[package]
name = "ledger"
description = "Append-only SQLite attempt ledger: audit trail, anti-double-attempt memory, future training set"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
[dependencies]
anyhow.workspace = true
rusqlite.workspace = true
serde.workspace = true
serde_json.workspace = true
thiserror.workspace = true
+218
View File
@@ -0,0 +1,218 @@
//! Append-only attempt ledger (SPEC §3.3).
//!
//! One row per optimization attempt. Nothing is ever deleted or updated —
//! enforced with SQLite triggers, not convention. The ledger is the audit
//! trail, the anti-double-attempt memory, and the future training set.
use rusqlite::Connection;
use serde::{Deserialize, Serialize};
use std::path::Path;
#[derive(Debug, thiserror::Error)]
pub enum LedgerError {
#[error("sqlite error: {0}")]
Sqlite(#[from] rusqlite::Error),
#[error("serialization error: {0}")]
Serde(#[from] serde_json::Error),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum Verdict {
Accepted,
RejectedGate,
RejectedBench,
NeedsHumanReview,
}
impl Verdict {
pub fn as_str(&self) -> &'static str {
match self {
Verdict::Accepted => "accepted",
Verdict::RejectedGate => "rejected-gate",
Verdict::RejectedBench => "rejected-bench",
Verdict::NeedsHumanReview => "needs-human-review",
}
}
}
/// Gate outcomes for one attempt (SPEC §3.2).
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct GateResults {
pub upstream_tests: bool,
pub golden_replay: bool,
pub fuzz_iters: u64,
pub fuzz_divergence: bool,
pub sanitizers_clean: bool,
}
/// Benchmark evidence for one attempt. All times in seconds; CIs are
/// bootstrap 95% intervals over the ratio baseline/candidate.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct BenchEvidence {
pub baseline_median: f64,
pub baseline_ci: (f64, f64),
pub candidate_median: f64,
pub candidate_ci: (f64, f64),
pub speedup_median: f64,
pub speedup_ci: (f64, f64),
pub env_fingerprint: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Attempt {
pub run_id: String,
/// RFC 3339, supplied by the harness (the ledger records, it does not clock).
pub timestamp: String,
pub target: String,
pub target_commit: String,
pub phase: u8,
/// Symbol plus its share of the profile, e.g. "png_read_filter_row (31.4%)".
pub hotspot: String,
/// Playbook class 1–7 (SPEC §4).
pub playbook_class: u8,
pub hypothesis: String,
/// Unified diff as proposed by the agent, pre-application.
pub patch: String,
pub gates: GateResults,
pub bench: Option<BenchEvidence>,
pub verdict: Verdict,
pub tokens_spent: u64,
pub wall_time_s: f64,
}
pub struct Ledger {
conn: Connection,
}
const SCHEMA: &str = r#"
CREATE TABLE IF NOT EXISTS attempts (
run_id TEXT PRIMARY KEY,
timestamp TEXT NOT NULL,
target TEXT NOT NULL,
target_commit TEXT NOT NULL,
phase INTEGER NOT NULL,
hotspot TEXT NOT NULL,
playbook_class INTEGER NOT NULL,
hypothesis TEXT NOT NULL,
patch TEXT NOT NULL,
gates TEXT NOT NULL, -- JSON GateResults
bench TEXT, -- JSON BenchEvidence, NULL if gates failed first
verdict TEXT NOT NULL CHECK (verdict IN
('accepted','rejected-gate','rejected-bench','needs-human-review')),
tokens_spent INTEGER NOT NULL,
wall_time_s REAL NOT NULL
);
-- Append-only, structurally: mutation raises instead of succeeding.
CREATE TRIGGER IF NOT EXISTS attempts_no_update
BEFORE UPDATE ON attempts
BEGIN SELECT RAISE(ABORT, 'ledger is append-only'); END;
CREATE TRIGGER IF NOT EXISTS attempts_no_delete
BEFORE DELETE ON attempts
BEGIN SELECT RAISE(ABORT, 'ledger is append-only'); END;
CREATE INDEX IF NOT EXISTS attempts_by_target ON attempts (target, playbook_class);
"#;
impl Ledger {
pub fn open(path: &Path) -> Result<Self, LedgerError> {
let conn = Connection::open(path)?;
conn.execute_batch(SCHEMA)?;
Ok(Self { conn })
}
pub fn open_in_memory() -> Result<Self, LedgerError> {
let conn = Connection::open_in_memory()?;
conn.execute_batch(SCHEMA)?;
Ok(Self { conn })
}
pub fn record(&self, a: &Attempt) -> Result<(), LedgerError> {
let bench_json = a.bench.as_ref().map(serde_json::to_string).transpose()?;
self.conn.execute(
"INSERT INTO attempts (run_id, timestamp, target, target_commit, phase, hotspot,
playbook_class, hypothesis, patch, gates, bench, verdict, tokens_spent, wall_time_s)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14)",
rusqlite::params![
a.run_id,
a.timestamp,
a.target,
a.target_commit,
a.phase,
a.hotspot,
a.playbook_class,
a.hypothesis,
a.patch,
serde_json::to_string(&a.gates)?,
bench_json,
a.verdict.as_str(),
a.tokens_spent,
a.wall_time_s,
],
)?;
Ok(())
}
/// Playbook classes already attempted against a target — the agent's
/// anti-double-attempt memory (SPEC §3.5 prompting spine, rule 2).
pub fn attempted_classes(&self, target: &str) -> Result<Vec<u8>, LedgerError> {
let mut stmt = self.conn.prepare(
"SELECT DISTINCT playbook_class FROM attempts WHERE target = ?1 ORDER BY playbook_class",
)?;
let rows = stmt.query_map([target], |r| r.get::<_, u8>(0))?;
Ok(rows.collect::<Result<Vec<_>, _>>()?)
}
pub fn count(&self) -> Result<u64, LedgerError> {
Ok(self
.conn
.query_row("SELECT COUNT(*) FROM attempts", [], |r| r.get(0))?)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn sample_attempt(run_id: &str) -> Attempt {
Attempt {
run_id: run_id.into(),
timestamp: "2026-07-06T00:00:00Z".into(),
target: "example".into(),
target_commit: "deadbeef".into(),
phase: 1,
hotspot: "hot_fn (42.0%)".into(),
playbook_class: 2,
hypothesis: "mimalloc reduces allocator contention".into(),
patch: "--- a/Cargo.toml\n+++ b/Cargo.toml\n".into(),
gates: GateResults::default(),
bench: None,
verdict: Verdict::RejectedGate,
tokens_spent: 1000,
wall_time_s: 12.5,
}
}
#[test]
fn record_and_query() {
let ledger = Ledger::open_in_memory().unwrap();
ledger.record(&sample_attempt("r1")).unwrap();
assert_eq!(ledger.count().unwrap(), 1);
assert_eq!(ledger.attempted_classes("example").unwrap(), vec![2]);
}
#[test]
fn append_only_is_enforced() {
let ledger = Ledger::open_in_memory().unwrap();
ledger.record(&sample_attempt("r1")).unwrap();
let upd = ledger
.conn
.execute("UPDATE attempts SET verdict = 'accepted'", []);
assert!(upd.is_err(), "UPDATE must be rejected by trigger");
let del = ledger.conn.execute("DELETE FROM attempts", []);
assert!(del.is_err(), "DELETE must be rejected by trigger");
assert_eq!(ledger.count().unwrap(), 1);
}
}
+14
View File
@@ -0,0 +1,14 @@
[package]
name = "report"
description = "ROI reports from ledger rows: throughput→cores→dollars with CIs, workload statement, and env fingerprint on every figure"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
[dependencies]
anyhow.workspace = true
ledger = { path = "../ledger" }
serde.workspace = true
serde_json.workspace = true
toml.workspace = true
+74
View File
@@ -0,0 +1,74 @@
//! ROI methodology (SPEC §3.6, §9): what did the stopwatch say, and what
//! does that cost or buy? Every figure carries its CI; the caveats print
//! on the report, not in an appendix.
pub mod pricing;
/// Cores returned to the fleet by a throughput speedup:
/// `fleet_cores × (1 − 1/speedup)`.
pub fn cores_saved(fleet_cores: f64, speedup: f64) -> f64 {
assert!(speedup > 0.0, "speedup must be positive");
fleet_cores * (1.0 - 1.0 / speedup)
}
/// Annual dollars from saved cores at a $/core-hour rate.
pub fn dollars_per_year(cores: f64, dollars_per_core_hour: f64, hours_per_year: f64) -> f64 {
cores * dollars_per_core_hour * hours_per_year
}
/// ROI range induced by a speedup CI — the report quotes the interval,
/// never just the point estimate.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct RoiRange {
pub cores_lo: f64,
pub cores_median: f64,
pub cores_hi: f64,
pub dollars_lo: f64,
pub dollars_median: f64,
pub dollars_hi: f64,
}
pub fn roi_from_speedup_ci(
fleet_cores: f64,
speedup_median: f64,
speedup_ci: (f64, f64),
dollars_per_core_hour: f64,
hours_per_year: f64,
) -> RoiRange {
let (lo, hi) = speedup_ci;
let (c_lo, c_med, c_hi) = (
cores_saved(fleet_cores, lo),
cores_saved(fleet_cores, speedup_median),
cores_saved(fleet_cores, hi),
);
RoiRange {
cores_lo: c_lo,
cores_median: c_med,
cores_hi: c_hi,
dollars_lo: dollars_per_year(c_lo, dollars_per_core_hour, hours_per_year),
dollars_median: dollars_per_year(c_med, dollars_per_core_hour, hours_per_year),
dollars_hi: dollars_per_year(c_hi, dollars_per_core_hour, hours_per_year),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cores_saved_formula() {
// 2x speedup halves the fleet: 100 cores → 50 saved.
assert!((cores_saved(100.0, 2.0) - 50.0).abs() < 1e-12);
// 1.25x on 1000 cores: 1000 × (1 − 0.8) = 200.
assert!((cores_saved(1000.0, 1.25) - 200.0).abs() < 1e-9);
// No speedup, no savings.
assert_eq!(cores_saved(100.0, 1.0), 0.0);
}
#[test]
fn roi_range_is_ordered() {
let r = roi_from_speedup_ci(1000.0, 1.10, (1.05, 1.16), 0.04, 8760.0);
assert!(r.cores_lo < r.cores_median && r.cores_median < r.cores_hi);
assert!(r.dollars_lo < r.dollars_median && r.dollars_median < r.dollars_hi);
}
}
+30
View File
@@ -0,0 +1,30 @@
//! Pricing inputs (`config/pricing.toml`). Rates are engagement inputs,
//! not constants — public cloud list prices by default, customer's own
//! numbers when supplied.
use serde::Deserialize;
use std::path::Path;
#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Pricing {
/// Dollars per core-hour (e.g. public-cloud on-demand vCPU rate).
pub dollars_per_core_hour: f64,
/// Fleet hours per year the workload actually runs (8760 for 24/7).
pub hours_per_year: f64,
/// Where the rate came from — printed on the report.
pub rate_source: String,
}
impl Pricing {
pub fn load(path: &Path) -> anyhow::Result<Self> {
let raw = std::fs::read_to_string(path)?;
let p: Self = toml::from_str(&raw)?;
anyhow::ensure!(p.dollars_per_core_hour > 0.0, "rate must be positive");
anyhow::ensure!(
p.hours_per_year > 0.0 && p.hours_per_year <= 8784.0,
"hours_per_year must be in (0, 8784]"
);
Ok(p)
}
}
+29
View File
@@ -0,0 +1,29 @@
# hotpath task runner. `just --list` for a summary.
# Build the trust-layer workspace
build:
cargo build --workspace
# Run all trust-layer unit tests
test:
cargo test --workspace
# Lint: warnings are errors in the trust layer
lint:
cargo clippy --workspace --all-targets -- -D warnings
cargo fmt --all --check
# A/A self-test of the bench harness against a sample command.
# Must print a null verdict; an "accept" is a calibration failure.
aa cmd="sleep 0.05":
cargo run -p bench-runner -- --config config/accept.toml aa --cmd "{{cmd}}"
# Interleaved A/B comparison of two shell commands
compare baseline candidate:
cargo run -p bench-runner -- --config config/accept.toml compare \
--baseline "{{baseline}}" --candidate "{{candidate}}"
# Verify a target corpus against its pinned manifest
# (wired through diff-test CLI once it exists; manual check for now)
pin-check target:
cd corpora/{{target}} && sha256sum -c MANIFEST.sha256
+41
View File
@@ -0,0 +1,41 @@
# Class 1 — Build configuration
Zero source changes. Always first: a large fraction of real-world CPU is
lost to shipping defaults.
## Preconditions
- Any CPU-bound target not already using an optimized release profile.
- Profile signature: flat-ish flamegraph with time spread across many
small frames (inlining starved); heavy call overhead into tiny leaf
functions; or a build that visibly lacks `-O2`/`--release`.
- Check before anything else: `opt-level`, `lto`, `codegen-units`,
`panic`, `target-cpu` (Rust); `-O`, `-march`, `-flto` (C/C++).
## Procedure (cheapest first)
1. Confirm release profile: `opt-level=3`, `panic=abort` where the target
permits, `codegen-units=1`.
2. `target-cpu=native` / `-march=native` — only when the deployment
fleet's ISA baseline is known; record the chosen baseline in the
hypothesis.
3. LTO: thin, then fat. Watch build-time cost; fat LTO on huge targets
can be a net loss for the engagement.
4. PGO: instrument with the golden-replay corpus as the training run,
rebuild with the profile. The corpus is representative by
construction.
5. BOLT (post-link layout) where the binary and toolchain permit.
## Verification notes
- Flags must not include any `-ffast-math`-class option — that's an
equivalence change, auto-routed to `needs-human-review` (SPEC §8).
- Byte-identical golden replay still applies: build flags occasionally
change FP contraction on some ISAs (`-march` enabling FMA); if golden
replay diverges on numeric output, stop — this class is not
equivalence-preserving for that target without a tolerance policy.
## Known failure modes
- `target-cpu=native` on the bench machine but not the fleet: a real win
that the customer cannot deploy. Pin the ISA baseline to the fleet.
- PGO trained on a non-representative workload overfits the benchmark —
hold out workloads (SPEC §11).
- Layout changes from LTO/BOLT interact with layout bias (§7): measured
wins here *especially* need the randomize-and-aggregate protocol.
+36
View File
@@ -0,0 +1,36 @@
# Class 2 — Allocator swap
Link-line only. With class 1, forms the "your 30% was a linker flag"
tier that is always exhausted before touching source.
## Preconditions
- Profile signature: `malloc`/`free`/`operator new` or allocator-internal
symbols (`_int_malloc`, `tcache_*`, page-fault handling) ≥5% of
exclusive samples; or high syscall time in `mmap`/`brk`; or
multithreaded target with allocator lock contention visible.
## Procedure
1. Rust: add `mimalloc` (then `jemallocator`) as `#[global_allocator]` —
two-line diff, still effectively link-level.
2. C/C++: `LD_PRELOAD` trial first (free experiment, no rebuild), then
proper linking of the winner.
3. Bench each candidate allocator separately; do not stack with other
changes.
## Verification notes
- Behavior-preserving by contract, so gates should pass trivially — but
run them all anyway: allocator swaps change address-space layout and
can unmask latent UB (use-after-free that "worked" under glibc). A
sanitizer failure here is a pre-existing target bug: record it,
route to `needs-human-review`, do not ship.
- Max RSS is captured per run: an allocator that wins time but doubles
memory is a trade-off the report must state, not hide.
## Known failure modes
- Wins on the bench box's core count that vanish (or invert) at the
fleet's concurrency level.
- `LD_PRELOAD` trial passing but static-link integration subtly failing
(symbol interposition differences) — re-run full gates on the final
link, not the trial.
- Fragmentation pathologies on long-running services that a short bench
window never sees; service-mode targets need soak evidence (Phase 4).
+36
View File
@@ -0,0 +1,36 @@
# Class 3 — Allocation churn
First source-touching class: reduce the *number* of allocations, not the
allocator servicing them.
## Preconditions
- Class 2 helped but allocator symbols still hot, or allocation shows up
as cache misses from pointer-chasing freshly allocated objects.
- Profile signature: allocation inside hot loops; `Vec`/`String`
reallocation churn; many short-lived objects of the same type.
## Procedure (cheapest first)
1. Hoist allocations out of loops; reuse buffers via `clear()` instead
of re-creation.
2. `Vec::with_capacity` / `reserve` where the size is known or bounded.
3. Small-vector types (`smallvec`, fixed inline buffers) for collections
that are almost always tiny — confirm the size distribution from the
profile or a counting instrument first.
4. Arena allocation (`bumpalo`, typed arenas) for phase-scoped object
graphs with a clear reset point.
## Verification notes
- Buffer reuse is where stale-data bugs come from: golden replay must
cover inputs of *decreasing* size across a session so a dirty buffer
would surface. Add such a sequence to the corpus if missing.
- Arenas change drop order; targets with observable `Drop` side effects
(logging, flushing) can diverge on golden replay — that divergence is
a real reject, not noise.
## Known failure modes
- `with_capacity` guesses that overshoot: RSS regression the wall-clock
number won't show — check the captured max RSS.
- smallvec inline sizes chosen by vibes: an inline buffer bigger than
the cache line it saves. Measure, don't guess.
- Reuse introducing borrow-checker contortions that force `unsafe` —
`unsafe` introduction routes to `needs-human-review` (SPEC §8).
+37
View File
@@ -0,0 +1,37 @@
# Class 4 — Data layout
Make the memory hierarchy work for the access pattern.
## Preconditions
- Profile signature: high cache-miss rate in `perf stat` (LLC misses per
instruction), hot loops whose flamegraph frames are memory-bound
(`perf` cycles concentrated on loads), low IPC (<1) on wide cores.
- For false sharing: multithreaded target where per-thread throughput
degrades as threads are added; `perf c2c` confirms.
## Procedure (cheapest first)
1. Struct field reordering / packing: kill padding, group hot fields on
one cache line, split hot/cold (`#[repr(C)]` review, cold-field
box-out).
2. AoS→SoA for loops that touch one field across many elements —
restructure the container, not the algorithm.
3. False-sharing fixes: pad or align per-thread state to 64 bytes
(`#[repr(align(64))]`, `crossbeam_utils::CachePadded`).
4. Cache blocking / loop tiling for nested loops over large matrices or
images; tile to L1/L2, verified empirically, not by datasheet.
## Verification notes
- Layout changes to any type that crosses FFI, serialization, or hashing
boundaries can change observable behavior (struct hash, serialized
bytes, memcmp-based equality) — golden replay catches this; check the
target for `#[repr]`-sensitive traits before patching.
- Differential fuzz the functions whose types changed, not just the
functions whose code changed.
## Known failure modes
- Reordering fields of a type that derives `Hash`/`Ord` used in ordered
iteration → output order changes → replay divergence (correct reject).
- AoS→SoA wins the target loop but loses every other access site; the
end-to-end number is the verdict, not the microbench.
- Alignment padding blowing up per-element size until the working set
falls out of cache — the cure worse than the disease.
+43
View File
@@ -0,0 +1,43 @@
# Class 5 — Algorithmic
Complexity class and primitive selection. Highest variance: the biggest
wins live here, and so do the most seductive wrong ideas.
## Preconditions
- Profile signature: one dominant frame that is *algorithm*, not memory
(high IPC, low miss rate, still slow); superlinear scaling of runtime
with input size; hot `contains`/`find` over linear structures; repeated
recomputation of pure functions on repeated arguments.
## Procedure (cheapest first)
1. Better std/library primitive: `HashMap` for linear scans, `sort_unstable`
over `sort`, `entry()` API over get-then-insert, buffered I/O,
`memchr`-class scanning.
2. Precomputation: hoist invariant work out of loops; build lookup tables
at startup when the domain is small.
3. Memoization for pure hot functions with high argument repeat rates —
confirm the repeat rate from a counting instrument before adding a
cache.
4. Complexity-class replacement (the real prize): O(n²) → O(n log n)
with a known-correct algorithm. Prefer a well-tested library
implementation over a hand-rolled one, always.
## Verification notes
- HashMap iteration order is not deterministic order: if results flow
from iteration order into output, replay diverges — either sort at the
boundary or reject the change.
- Memoization + interior mutability in multithreaded targets touches the
concurrency review tier (SPEC §8).
- Differential fuzz is load-bearing here: algorithmic replacements have
edge-case surfaces (empty input, duplicates, already-sorted, unicode)
where "equivalent" algorithms differ. 10k iterations minimum, seeded
with corpus-derived cases.
## Known failure modes
- Asymptotic win, constant-factor loss at real input sizes — the ledger
records the workload; the verdict is measured at that workload.
- Caches without bounds: memoization that trades a 10% speedup for
unbounded RSS growth. Check max RSS; bound the cache.
- "Equivalent" floating-point reformulations that aren't (different
rounding paths) — FP output changes route to the tolerance policy or
human review, never silently absorbed.
+44
View File
@@ -0,0 +1,44 @@
# Class 6 — SIMD
Autovectorization enablement first, intrinsics last (SPEC §4).
## Preconditions
- Profile signature: hot, trip-count-heavy inner loops over contiguous
numeric/byte data; scalar math or byte-wise processing in the top
frames; disassembly of the hot loop shows scalar ops where vector ops
are plausible.
## Procedure (cheapest first)
1. Read the optimizer's mind: check the hot loop's assembly (or
`-Cremark`/`-Rpass=loop-vectorize`) for *why* vectorization failed.
2. Remove the blocker, in order of cheapness:
- Bounds checks: restructure with slices/iterators or hoist a single
length check (`&x[..n]` pattern) — not `unsafe` indexing.
- Aliasing: split borrows, `split_at_mut`, pass slices instead of
overlapping raw pointers.
- Loop-carried dependencies: reassociate integer reductions, unroll
accumulators.
3. Widen the ISA baseline if class 1 hasn't already (`target-cpu`), since
SSE2-only baselines strangle the vectorizer.
4. `std::simd` / portable-SIMD explicit vectorization.
5. Raw intrinsics only when all of the above fail and the win is large;
isolate behind a scalar-fallback path selected at runtime.
## Verification notes
- **FP flags are a hard line**: any `-ffast-math`-class flag, FP
reassociation, or FMA-contraction change routes to
`needs-human-review` — no exceptions, even for a 2x win (SPEC §8).
Integer/byte SIMD has no such taint and can auto-accept.
- Intrinsics imply `unsafe` → automatic `needs-human-review` + MIRI
where feasible + differential fuzz against the scalar path with
adversarial lengths (0, 1, lane-1, lane, lane+1, unaligned offsets).
## Known failure modes
- Tail handling: the classic SIMD bug class. Fuzz lengths around lane
boundaries explicitly.
- Alignment assumptions that hold on the bench box and fault (or crawl)
elsewhere.
- Runtime feature detection done per-call in the hot loop, eating the
win; hoist detection to init.
- Integer reduction reassociation is safe; float reduction reassociation
changes results — do not confuse the two.
+43
View File
@@ -0,0 +1,43 @@
# Class 7 — Concurrency
Last in the order, highest risk. **Every diff in this class routes to
`needs-human-review`** (SPEC §8); the agent proposes, a human disposes.
## Preconditions
- Profile signature: multithreaded target where wall time ≫ ideal
(cpu-time / cores); lock symbols (`futex`, `pthread_mutex`,
`parking_lot`) hot; run-queue latency; per-thread throughput that
*drops* as threads are added. coz (Phase 3) is the right prioritizer
here — cycle profiles mislead on blocked time.
## Procedure (cheapest first)
1. Shrink critical sections: move allocation, I/O, and computation out
from under locks; drop guards early.
2. Reduce sharing: per-thread accumulation + merge at the end beats a
shared counter; sharding a hot map by key hash beats a fancier lock.
3. Right-size the primitive: `RwLock` for read-mostly, `OnceLock` for
init-once, atomics for simple counters *with explicitly justified
orderings* (start from `SeqCst`; relax only with a written argument).
4. Lock-free structures: last resort, library-only (crossbeam), never
hand-rolled, and only when the profile proves the lock is the
bottleneck after 1–3.
## Verification notes
- TSan gate is mandatory for this class, on both the upstream suite and
the golden replay run (SPEC §3.2).
- Thread-count sweep: gates and bench at 1, 2, N, and 2N threads —
concurrency bugs and wins are both thread-count-dependent.
- Nondeterministic output ordering (e.g. results gathered from threads)
must be canonicalized at the boundary or the change rejected; replay
equivalence is not negotiable to make a parallel win look clean.
## Known failure modes
- Benchmarks too short to hit the contention window: a race that fires
once per million ops passes 60s of fuzz. TSan + long soak, and still
route to a human.
- Relaxed atomics that are correct on x86's strong model and wrong on
ARM — orderings need an argument, not a test pass.
- Sharding that wins the benchmark's uniform key distribution and loses
production's skewed one — holdout workloads (SPEC §11).
- Deadlocks introduced by lock-order changes that no gate exercises;
the human reviewer checks lock ordering explicitly.
+25
View File
@@ -0,0 +1,25 @@
# Optimization playbook v0
Classes are tried in **strict order** (SPEC §4) — cheapest first. Classes
1–2 are the "your 30% was a linker flag" tier and are always exhausted
before any source change. Each class documents:
- **Preconditions** — the profile signature that suggests it applies
- **Procedure** — what to actually try, cheapest variant first
- **Verification notes** — gate interactions specific to this class
- **Known failure modes** — where this class produces fake wins or real bugs
| # | Class | Touches source? | Human review? |
|---|-------|-----------------|---------------|
| 1 | [Build configuration](01-build-config.md) | no | only PGO/BOLT pipeline changes |
| 2 | [Allocator swap](02-allocator.md) | link line only | no |
| 3 | [Allocation churn](03-allocation-churn.md) | yes | no |
| 4 | [Data layout](04-data-layout.md) | yes | no |
| 5 | [Algorithmic](05-algorithmic.md) | yes | no |
| 6 | [SIMD](06-simd.md) | yes | FP-flag changes: always |
| 7 | [Concurrency](07-concurrency.md) | yes | **always** |
The agent's prompting spine (SPEC §3.5): state the hypothesis before
patching; prefer the cheapest untried class; max two iterations per
rejected hypothesis; a rejection with a clean ledger row is a successful
outcome.
+13
View File
@@ -0,0 +1,13 @@
# results/calibration/
Evidence for the bench-runner acceptance criteria (SPEC §3.1):
- **A/A self-tests**: same binary as both sides must yield a null
verdict; false-positive rate < 5% across sessions.
- **Regression injection**: a synthetic 5% slowdown must be detected
≥ 95% of the time.
One JSON file per calibration session, including the full env
fingerprint. These files are the license to trust every number the
system reports afterward; they are referenced from case studies and ROI
reports.
+34
View File
@@ -0,0 +1,34 @@
# targets/
One directory per optimization target. This is the **only**
agent-writable path in the repository (SPEC §10); everything else is
trust layer.
## Layout
```
targets/<name>/
target.toml # upstream repo URL, pinned commit, build & bench commands
equivalence.toml # optional; absent = byte-identical required (SPEC §3.2)
workspace/ # agent's checkout — patches land here via harness git-apply
```
Selection criteria (SPEC §6): CPU-bound (>70% user time), meaningful
test suite, scriptable workload, permissive license, active but not
hyper-optimized, builds cleanly in a container. Good hunting: codecs,
parsers, serializers, image processing, compression, static-site
generators, linters.
## equivalence.toml
Default (no file) is byte-identical outputs. FP-producing targets may
declare an explicit tolerance policy:
```toml
mode = "fp-tolerance"
abs = 1e-9 # max absolute difference per numeric value
rel = 1e-6 # max relative difference per numeric value
```
Declaring a tolerance covers output comparison only; FP *flag* changes
still route to needs-human-review (SPEC §8).