The corpus is a recording of what agents typed, and it goes stale as models and workloads change. This is the path from a fork to a spectrum tuned to your programs — capture, harvest, gate, step — gated at every step because invariant 1 (a silent disagreement is worse than a refusal) is your fork’s too.
CAPTURE hooks + the python3 shim append every program to $LYPNING_LOG
HARVEST `harvest --export` (the Stop hook runs it) → tests/corpus/sightings/
<session>.jsonl; `harvest` derives the corpus: redacted, deduped,
content-addressed — it grows only with what was really typed
GATE the gates decide whether a step stands
STEP one measured change: a fix, a table row, a refusal — then repeat
The gates, in the order that catches the most:
lypning build --rust # the exit-90 contract is asserted on the binary
lypning conformance # MISMATCH must be 0
lypning doctor # 0 FAIL
git status # the corpus runs behind a net; check it anyway
lypning gate; lypning gate ~/.lypning/bin/lypning-l # PASS twice — bare gates the core only
Run it as a standing loop (/loop, or a cron): harvest, run the gates, take
the top item of lypning conformance --plan, commit one measured step with the
numbers the tools printed. The point is that gathering is continuous and cheap
while stepping is deliberate and gated. It has caught model drift once
(CHANGELOG.md, 2026-08-30). Two traps it has paid for: lypning bench is
spawn-bound and cannot see a compute win — use valgrind --tool=callgrind
(.claude/skills/hillclimb/SKILL.md §3); and a remembered number is already
wrong (invariant 3).
lypning build --rust -v # → `ok` per variant, `unsupported contract: held` in the log (§C2)
lypning install # merges hooks into .claude/settings.json; re-run → `all 3 hook entries already present` (§C10)
lypning shim install # python3 on $PATH records what runs
lypning status # wired: the shim's PATH line; shadowed: `… is NOT on PATH — the shim will never run`
lypning harvest --dry-run # → `harvest: N sighting(s) collected; nothing written (--dry-run).`
lypning harvest --export && lypning harvest # sightings, then the corpus — prints the count it loaded
lypning conformance --mixture both # MISMATCH 0 · dispatchers agree N/N (§C3, §C4)
lypning conformance --plan # the build order, ranked by ->cpy cost with blocks beside it
lypning routes --plan # run-time refusals a static route cannot see (§C11)
Batteries run only in a worktree with its own LYPNING_HOME; git status
--porcelain is empty afterwards, and after a crash mid-way you check it
yourself — the net cannot undo a write outside the repository (§C8).
A clean slate. Truncate src/lypning/assets/corpus/corpus.jsonl
(paths.CORPUS_FILE), keep seed-corpus.jsonl (paths.SEED_CORPUS_FILE), read
the source split from lypning corpus --stats.
Adding a rung. Every Rust variant is the one crate under a variant-*
cargo feature (assets/rust/Cargo.toml); the common step is a cap-* feature
on lypning-l, not a new rung. A rung is one row in each of
route.rs::SPECTRUM and CAPS, engines.SPECTRUM, VARIANT_CAPS,
env_var_for and parse_binary_name, pinned by
tests/test_engines.py::test_parse_binary_name_grows_with_the_spectrum,
tests/test_engines.py::test_env_var_for_spells_every_pin_by_rule and
tests/test_routing.py::test_the_spectrum_copy_in_engines_is_the_rust_table;
qualify it with lypning gate <bin> (§C6) and lypning conformance --mixture
both, which must print dispatchers agree N/N (§C4). Importable is not the
same as complete — the tables are earned with lypning conformance, never
with import x returning 0. Test the wheel shape on purpose (§C13).
Universal — keep byte for byte. Facts about Python or about running captured programs; tests object if you touch them.
| what | where |
|---|---|
| The exit-90 refusal contract, spelled once and asserted on the built binary | engines.refusal_line, main.rs::finish, build.check_refusal_contract |
| The commit barrier: stdout/writes staged, discarded on refusal, so a retry is safe | io.rs::COMMIT_THRESHOLD (8 MiB) |
| Fall-through only on a genuine refusal — never on exit 90 alone, never on the program’s own errors | engines.dispatch, main.rs::dispatch |
CPython-exact semantics: identity-first element comparison (elem_eq), floor-division rounding, arity/keyword rejection, refuse-don’t-guess (NaN identity, set order, interning) |
value.rs::elem_eq, ops.rs, builtins.rs |
The comparison environment: PYTHONHASHSEED=0, LC_ALL=C.UTF-8, identical decode rules on both sides, capture disabled inside the battery |
conformance._env_for |
the net — a temp cwd per entry per engine and a git status snapshot; not a sandbox |
conformance.py (§C8) |
| The battery’s verdict asymmetry: MISMATCH is always a bug, UNSUPPORTED never is | conformance.classify |
| Recursion guards expressed as refusals, so deep data can never SIGSEGV a host | err.rs::MAX_NEST (500), eval.rs::MAX_DEPTH (180), parse.rs::MAX_PARSE_DEPTH (64) |
Workload-general — keep the mechanism, re-derive the contents.
| mechanism | contents | re-derived by |
|—|—|—|
| what each variant serves | route.rs::CAPS, modules.rs::MODULES (ONE array: the core’s rows, then each capability’s module as one element behind its own #[cfg(feature = "cap-…")], appended in build order and never reordered), engines.VARIANT_CAPS | conformance --plan per variant; tests/test_routing.py::test_the_spectrum_copy_in_engines_is_the_rust_table |
| kinds no Rust variant may answer, at any size | route.rs::ONLY_CPYTHON_KINDS = engines.ONLY_CPYTHON_REFUSALS, read by both dispatchers, held equal by test | routes --plan lists them as NOT IMPLEMENTABLE; a kind leaves the set only by being implemented exactly |
| the spectrum itself | engines.SPECTRUM, route.rs::SPECTRUM (ENGINE_ORDER and conformance.DEFAULT_ARMS derive from it) | the tests in §2 |
| the nondeterminism screens | conformance._RUN_SPECIFIC, _IMPLEMENTATION_DEFINED | extend as your corpus surfaces shapes; never prune |
| constants sized to one-liners | io.rs::COMMIT_THRESHOLD, conformance.LIBRARY_STEP_LIMIT | re-tune only if your programs are heavier |
Corpus-specific — re-measure or discard. Every number (corpus counts,
coverage, routing grades, docs/BENCH-LEDGER.md, docs/HILLCLIMB.md); the
build order (--plan is a pure function of the loaded corpus); the catalogue’s
entries (§4); the block budgets, a CheerpX fact (docs/SANDBOX-PERFORMANCE.md
§1).
CPython is the reference for every Rust variant. Require zero mismatches from
lypning conformance; a known-mismatch allowance is not part of the stack.
Preserve dated historical reports when retiring a runtime, but do not treat
their engine names as current build targets or routing destinations.
Engine strings, LYPNING_* variables and $LYPNING_HOME are load-bearing
(invariant 9); rename the project, not the plumbing. Check the redaction before
committing a sightings file (docs/CAPTURE.md, Privacy). A fork whose README
says “3× faster” without a date and a corpus count is claiming someone else’s
measurement.
lypning build --rust && lypning conformance --mixture both && lypning doctor && lypning gate; echo $?
# → ok per variant · MISMATCH 0, dispatchers agree N/N · 0 FAIL · PASS · 0 — docs/VERIFICATION.md §C1–C8