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

71 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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