Conformance¶
The conformance suite is Punix's executable spec. Every claim made elsewhere on this site ("rollback is constant-time", "permuted source yields byte-identical builds", "secret values never reach the store") has a dedicated test in tests/c_e2e/test_conformance_stage*.py that runs end-to-end (no mocks above the eval/realise seam, no subprocess mocks below) and is a CI release-blocker.
A regression in any of these properties blocks the release.
The twelve properties¶
| Property | What it asserts | Stage | Test file |
|---|---|---|---|
| Empty program | punix check "" is ok; nothing fetched or built. |
0 | (implicit) |
| End-to-end build | A real autotools package builds via RealiseLocal, content-addressed, with built + derivation.json; rebuild is a store hit. |
3 | test_conformance_stage3.py |
| Order-independence | A program and a fully-permuted variant ⇒ byte-identical evaluated values and store paths; only provenance line numbers differ. | 2 | test_conformance_stage2.py |
| CVE-pin propagation + isolation | force on a shared transitive dep changes exactly its dependents' store paths; unrelated packages are byte-identical. |
2 | test_conformance_stage2.py |
| Atomic updates | Five sub-properties — see below. | 4, 5 | test_conformance_stage4.py, test_conformance_stage5_ssh.py |
| Secret hash-exclusion | Two deploys differing only in from_env values ⇒ identical store path; unset secret ⇒ [E11] naming it; no secret value in derivation.json / provenance / store / manifest. |
2, 6 | test_conformance_stage2.py, test_conformance_stage6.py |
| Multi-version | Ncurses5 / Ncurses6 coexist; a dependent pins one; distinct store paths. |
3 | test_conformance_stage3.py |
| Cache correctness | Changing a recipe hook body changes the store path. | 3 | test_conformance_stage3.py |
| Type-system surface | Each [E1]–[E6] has a minimal program producing exactly that code, located. |
1 | test_conformance_stage1.py |
| Multi-arch | Same inputs on x86_64-linux vs aarch64-linux ⇒ different store paths; a type=url source missing an arch ⇒ [E14]. |
6 | test_conformance_stage6_multi_arch.py |
| Migration | Migrator on real .rb brew formulae produces type-checking PCL; every shape pattern (cargo / go / npm / autotools / cmake / meson / swift / pip / git-tag source) has a dedicated test. |
7 | test_migrate_brew.py (95 tests) |
| Demo deploy | The monitoring stack deploys on systemd + docker-compose and (via SSH) to a remote; Grafana on :3000 returns 200. |
8 (release evidence) | TBD |
The atomic-updates sub-properties¶
The atomic-updates property has five sub-clauses, each with its own dedicated test:
| Sub-property | What it asserts |
|---|---|
| Kill mid-deploy | The previous generation stays wholly live; current is unchanged. |
| Rollback exact | Rollback after a failed deploy restores the previous gen exactly; the forward gen is preserved on disk. |
| GC respects pins | A GC pass never collects a path pinned by any generation. |
| Rollback after GC | A normal GC pass leaves rollback's target intact. |
| Pinned-path missing | A path that should be there but isn't surfaces a clear [E12] instead of leaving current pointing at gone bytes. |
The Stage 5 suite re-runs the first two clauses against SshTransport (different atomic primitive — mv over ssh vs os.replace locally) to prove they're transport-agnostic.
How to run¶
# Just the conformance suite
uv run pytest tests/c_e2e/test_conformance_stage*.py -v
# A single property
uv run pytest tests/c_e2e/test_conformance_stage4.py::test_20_5_1_kill_mid_deploy_leaves_previous_generation_wholly_live -v
# Everything (the green gate)
make test
Coverage¶
| Stage | Tests | Properties covered |
|---|---|---|
| 1 | 13 | type-system surface ([E1]–[E6]) |
| 2 | 4 | order-independence, CVE-pin propagation, hash-exclusion at the evaluator |
| 3 | 6 | end-to-end build, multi-version, cache correctness |
| 4 | 6 | atomic-updates (all five clauses) + manifest-schema round-trip |
| 5 | 3 | atomic-updates over SSH + manifest round-trip over SSH |
| 6 | 9 | hash-exclusion at deploy (3) + fixed-output [E13] (3) + multi-arch [E14] (3) |
41 conformance tests across the eight stage files, plus 95 migration tests under tests/a_unit/migrate/test_migrate_brew.py driving the brew→PCL translator against every install-block shape. Each is a property statement; the test body is the proof.
The conformance promise¶
If a conformance test regresses:
- The PR is rejected. No "we'll fix this in a follow-up."
- The fix lands in the same PR as whatever broke the property.
- If the property is genuinely wrong (the requirement is incorrect, not the code), the test update lands in the same PR — and the PR description calls out the change in the property.
This is what makes the test suite load-bearing: it is the contract for what Punix does.
Related¶
- Status: what works — per-stage rollup.
- Decisions — the design pins the conformance tests enforce.