Adding to the parity suite
The procedure for a new golden fixture, a new second-oracle gate or a new property test — where each lives, what it may assert, and the never-widen rule as a review checklist.
The parity suite is the set of tests that pin arithmetic exactly rather than approximately. This page is how to add to it. What the suite is — frozen bytes, the retired oracle, why bit-exactness is still the criterion — is The parity gate.
Where a new test goes
crates/vike-backtest/tests/parity.rs is one binary over five member files included as plain
modules; the tests, their names and their fixtures are byte-unchanged from when each was its own
binary, so only the --test slot moved. Filtering still works by name:
cargo test -p vike-backtest --test parity -- broker_sim_bit_parityThe eligibility rule for joining a grouped binary, stated in that header: the file must be
plain — no #![cfg] feature gate, no #[ignore]d live or heavy test, no proptest regression
sidecar, no process-global mutation (env::set_var, set_current_dir). A plain test run executes
one binary's tests as threads in a single process, so a file mutating process state would turn
independent binaries into a race.
Everything else — store-backed, feature-gated, ignored, and every property suite — stays its own
binary on purpose.
A new golden fixture
The frozen rounds cannot be regenerated
fixtures/r0 through r6 were exported from the Python app while it was still the source of truth,
and every exporter was deleted from this tree. The committed bytes are the oracle: a changed
fixture byte is a changed contract, not a refresh. r1_parity's loader asserts only that the
manifest records the exporting oracle's SHA as provenance. A missing file is a deleted or moved
fixture, never a stale one — and the dead exporter path stays named in the panic message on purpose,
because it is the evidence for where the bytes came from. A citation whose target was genuinely
deleted goes in the citation gate's exception table; it is never simply removed.
New golden expectations are still authored — just not by re-running an oracle. fixtures/recon/ is
the sanctioned shape: hand-written scenarios with no manifest, auto-discovered at run time by
crates/vike-exec/tests/recon/recon_fixtures.rs's recon_golden_fixtures, where adding a directory
is authoring a new expectation rather than regenerating an oracle.
Whatever you add must be replayed. crates/vike-ops/tests/fixture_consumption_gate.rs fails CI on
any committed file under fixtures/ that no test under crates/ consumes, unless it carries a
written exception row saying why. The check is text-only and recognises three shapes: the full
relative path spelled verbatim in one file; the directory and the bare file name in the same
file; or a fixture under a DISCOVERY_ROOTS entry whose consumer enumerates directories and names
no file. Two files each holding one half do not count. The gate exists because a retired engine took
its parity suite with it and left four fixtures behind, reading as coverage and pinning nothing.
Floats go in as IEEE-754 hex bit patterns — vike_model::f64_from_hex_bits decodes them, and its
doc gives the reason: fixtures carry f64::to_bits hex so a last-ulp divergence is unambiguous.
What a new assertion may claim
Two tiers, and neither is ever widened. Pure + − × ÷ sqrt paths assert exact equality, because
IEEE 754 requires those operations to be correctly rounded. Outputs derived from erf, exp or
log gate at ≤ 1e-12 relative — crates/vike-options/tests/oracle_parity.rs's assert_rel is
the shape, and that file's header states the tier and the never-widen rule together.
Portability is the trap underneath. crates/vike-backtest/tests/libm_platform_probe.rs spells it
out: IEEE 754 requires nothing of ln/exp/pow/cos, so an f64 method call reaches whichever
libm the platform ships, and two platforms disagree in the last bit. Transcendentals therefore come
from the libm crate, pinned per crate by a probe file. The frozen r1/r3/r4 rounds are
portable by construction only because every module they drive reaches no transcendental and no
powi at any arity — the parity.rs header lists those modules and tells you to grep them before
trusting the paragraph.
One porting rule outlives the oracle: vike_model::py_sum is the CPython builtin sum() twin,
Neumaier-compensated since CPython 3.12 rather than a naive left fold. A ported site the original
wrote as builtin sum(...) over floats must use it; an explicit accumulate loop stays a naive fold.
A second oracle instead of a fixture
Where no frozen bytes exist, reconcile two implementations. engine_kernel_parity runs the event
engine and the vectorized kernel over identical synthetic bars and requires the same trade count and
a bit-identical final equity — its header says plainly: never widen this to a tolerance.
riskgate_simbroker_parity is the risk-model twin, and its discipline is the one to copy: for each
row the two sides must agree, or the row must pin the difference with a named reason.
Row::divergence is None exactly when they agree, so an unpinned divergence fails the test and a
newly pinned one is a loud, reviewable diff rather than a silent behaviour change.
A new property test
Property suites randomize the pure cores where golden fixtures only pin examples; the roster is on Property tests, and the decimal wire sites on Wire quantizers. Three authoring rules travel with them:
- Bound the domain to the real one, then assert exactly.
scalar_propskeeps values and steps inside the price/tick range sovalue/stepstays under f64's exact-integer ceiling, then asserts idempotence with no tolerance at all;format_propsdraws only terminating-decimal steps, so the internal decimal division is exact and every assertion is an exact comparison. A bounded domain buys the exactness — it is not a fudge factor. - Commit the regression seed. A counterexample lands as a
proptest-regressions/sidecar, and the workspace convention is to commit it so a CI-found case reproduces. That sidecar is also what disqualifies the file from a grouped binary, above. - A counterexample is a finding, not a spec bug.
order_fsm_props's failure policy is the model: stop and report with the printed seed, and never adjust the subject to satisfy a property without an explicit decision. It also ships a generator-coverage witness, because a property over an arm the generator never reaches is vacuously green — worth copying whenever a generator enumerates a variant set.
Relaxing an existing assertion
Retiring the Python oracle licensed nothing here, and the record that retired it is what a relaxation is argued against. Four guards, all four required: the comparison stays exact — bits against bits of a stated function of the reference's own values, never a tolerance band; the reference's raw value is still bounded as the pre-image of that function; a control fails if the transformation is a no-op on the fixture; and the fixture bytes are untouched. A widened tolerance, a deleted assertion, or an "expected" value quietly recomputed from the port is not covered by that record and never was. How to read one is on Decision records.