# Release SPEC — declarative contract (znippy constellation)
This file is the source of truth from which release/bench Rust drivers are
generated/driven. A human or LLM reads this plus the per-repo entry in
`release/nornir.toml` and drives the `nornir` APIs. Generated files carry the
header `// GENERATED FROM workspace_znippy/release/SPEC.md — DO NOT EDIT`.
## Principles
1. **Rust only.** No shell, no Make, no Just. Each step is one call into
`nornir`.
2. **Append-only history.** `bench_history.jsonl` lines are never edited or
reordered. New runs append one JSON object per line.
3. **Same-machine comparison.** Regression checks compare only entries with the
same `machine` field. First run on a new machine establishes the baseline.
4. **Fail closed.** Any gate failure aborts the release. No "warn and continue".
5. **One bench suite.** znippy benches go through nornir's `Bencher` trait in
`znippy/xtask/examples/nornir-bench.rs` — never `cargo bench`/criterion for
the gate. See `.nornir/benching.md` in the znippy repo.
## Gates (executed in order)
| # | Name | nornir API | Aborts on |
|---|-------------------------|-------------------------------------------------------------|------------------------------------------|
| 1 | `no-path-patches` | `release::gate::no_path_patches(repo)` | any `[patch.crates-io]` znippy entry (znippy: off) |
| 2 | `bench-run` | `bench::run(repo_spec)` | any bench panics or exits non-zero |
| 3 | `no-regression` | `release::gate::no_regression(run, history, max_drop=0.10)` | any metric drops >10% vs last same-machine entry |
| 4 | `history-appended` | `bench::history::append(path, run)` | write/lock failure |
| 5 | `docs-updated` | `docs::assemble_and_check(repo)` | README/CLAUDE.md out of date |
Only after all gates pass: `release::publish::tag_and_publish(repo_spec)` walks
`publish_order` from the repo TOML (bottom-up, per crate).
## Bench history schema
One JSON object per line. Required keys:
```json
{
"date": "2026-06-09", // ISO date
"version": "0.9.1", // semver of the repo being released
"machine": "oden", // stable identifier, kebab-case
"cores": 32,
"results": [ /* repo-specific result objects */ ]
}
```
Result object shape (znippy):
- **archive throughput:** `{ "name": "archive_small_10k", "compress_mbs": F,
"decompress_mbs": F, "ratio_x": F, "files": N, "in_mb": F, "cores": N }`.
- **decompressor vs legacy:** `{ "name": "ljar_guava", "<ours>_mbs": F,
"<legacy>_mbs": F, "speedup_x": F, "bytes": N }`.
`nornir::bench::history` exposes typed structs; `append` serialises one line
and `fsync`s.
## Maven asset fixtures
Bench inputs that download real JARs/wheels live in `nornir::bench::assets`
(`BENCH_JARS`). Cached under the default cache dir; downloaded via pure-Rust
HTTPS. The `archive_real_{jars,crates,wheels}` benches additionally want a
staged corpus under `~/work/holger_tests/{jars,crates,wheels}/` and skip
gracefully (recorded red, not fatal) when absent.
**Bench fixtures live on local disk only — never on T9.** T9 is an external USB
drive and its throughput would skew every measurement; copy to
`/home/rickard/work/` first.
## Documentation assembly (gate #5)
`nornir::docs` owns documentation generation. The znippy README has
`<!-- nornir:gen:start:* -->` / `<!-- nornir:gen:end:* -->` markers for the
benches/tests tables and the dependency graph; those regions are rewritten from
the warehouse, never by hand. See `.nornir/docs-generation.md` in the nornir
repo for the marker grammar.