memlnaut-nisps/manifold/tests/fixtures/README.md
monkey-w1n5t0n fb0228e6c3 test(manifold): capture golden parity fixtures for TS pipelines/curves before P4
Records canonical gesture trace, curve catalog samples, and input/output
pipeline outputs from the current TS implementations, plus a bun-test drift
guard that re-runs them against the fixtures within 1e-9. Serves the P4
one-core-engine gate: same pointer trace -> same routed output pre/post the
C++/WASM migration.
2026-07-13 23:25:31 +02:00

4.4 KiB
Raw Blame History

Pipeline golden fixtures

Captured 2026-07-13 from the current TypeScript engine implementations, before the P4 "one core engine" migration (docs/specs/plans/one-core-engine-refactor.md §P4) replaces the TS curve/input/output code with calls into the C++/WASM core.

P4's own gate reads: "recorded-gesture regression: same pointer trace → same routed output pre/post migration (capture fixture before starting)." These files are that capture.

What is here

File What it pins Source under test
gesture-trace.json One canonical synthetic pointer trace (288 events, fixed 120 Hz dt) over the input pipeline's native [0,1]² domain: h/v sweeps, diagonal, spiral, figure-eight, dwell + abrupt corner jumps. Pure formula — no Math.random, no Date.now. (input to the input pipeline)
curves-golden.json applyCurve(name, x) for every curve id in curves.ts, 129 samples of x ∈ [0,1] inclusive, at each curve's default param. src/engine/curves.ts
input-pipeline-golden.json The gesture trace run through processInput under 14 representative configs (default, deadzone, zoom, sticky anchor, per-axis, curves, smoothing, invert, momentum gentle/strong, frozen axis, fully frozen, combined). Records {x, y, frozen} per event. Configs embedded. src/engine/input-pipeline.ts
output-pipeline-golden.json A deterministic raw-output sequence (120 vectors × 8 channels of offset sines, quantised to f32) run through processOutput under 8 configs (default, curves, smoothing, slew limiting, global-freeze toggled mid-sequence, per-output freeze mask, combined). Records the processed vector per step. Configs embedded. src/engine/output-pipeline.ts

The drift guard

../pipeline-golden.test.ts (bun test) re-runs the current TS implementations against these fixtures and asserts equality within 1e-9. It reads the trace, raw sequence, and configs from the JSON — the fixtures are authoritative, so editing pipeline-golden-lib.ts config lists cannot mask a regression. Any change to curves.ts / input-pipeline.ts / output-pipeline.ts that alters numeric behaviour breaks this test until the goldens are deliberately re-captured.

To re-capture (only when intended): cd manifold && bun tests/fixtures/_generate.ts.

Contracts you must reproduce to consume these

State contract (both pipelines are stateful)

  • Input: EMA-smoothed x/y, a velocity ring, and a momentum-zoom multiplier.
  • Output: prev + smoothed buffers driving slew/freeze.

Each config run resets state (defaultInputState() / defaultOutputState()) at step 0. Runs are independent; do not carry state between them.

Clock contract (input pipeline only)

input-pipeline.ts's momentum-zoom path reads performance.now() (wall clock) for its 150 ms velocity window. To make the momentum configs reproducible, the capture pins performance.now() to each event's t_ms before processing it, so the velocity window slides over the gesture's own timescale. dt passed to processInput is the per-event t_ms delta in seconds (fixed 1000/120 ms). A future consumer that ports this to C++ must feed the same per-event timestamps (the trace's t_ms) into whatever owns the velocity ring, or the momentum runs will not match. The output pipeline uses no wall clock; its dtMs is a fixed 1000/60.

JSON encodings

  • slewRate: null in an output spec means Infinity (JSON has no Infinity).
  • Output raw values are pre-quantised with Math.fround so they equal exactly what a Float32Array holds.

How P4 should consume these

After the input/output/curve logic moves into the C++/WASM core, flip pipeline-golden.test.ts to drive the WASM implementations (via the main-thread nisps instance) instead of the TS run* helpers, keeping the same fixtures as the expected values. That proves same pointer trace → same routed output across the migration.

Tolerance: these goldens were produced in TS f64. The WASM core computes in f32 for many paths, so exact 1e-9 equality will not hold post-migration — relax the comparison to about 1e-5 (and expect the sigmoid/exp/log curve tails and long smoothing/slew accumulations to be the widest-drifting points). If any value drifts materially beyond that, it is a real behavioural divergence, not float noise, and must be reconciled in the core rather than by widening tolerance.