Gunnar

mikronova/workspace_znippy

release/SPEC.md

# 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.