memlnaut-nisps/manifold/tests/fixtures/README.md

72 lines
4.4 KiB
Markdown
Raw Normal View 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.