72 lines
4.4 KiB
Markdown
72 lines
4.4 KiB
Markdown
|
|
# 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.
|