One-core-engine P4.3/P4.4: the input/output pipeline processing and the curve
catalog now live in the C++/WASM core (nisps/pipeline/*, nisps/core/math.hpp).
The TS ports are deleted and the browser drives the WASM chains.
Engine:
- WasmIML owns a nisps_pipeline_create handle + bridge buffers and exposes
setInputConfig (TS InputConfig → 15-float wire), processInput, resetInput,
setOutputConfig (Infinity slew → 0), setOutputFreezeMask, processOutput
(in place), resetOutput, curveApply, curveApplyBatch (chunked). Handle +
buffers created in init_, freed in dispose, output-sized buffers realloc'd
on reshape.
- Spine routes setInputs through iml.processInput/processOutput (state lives
C++-side); config source-of-truth stays TS-side and is pushed on attach /
setInputConfig / setOutputConfig. Preserves ?debug=1 fixed-dt determinism
(same dt fed to the WASM calls). EngineApi gains setInputConfig/
setOutputConfig/curveApply/curveApplyBatch.
- New types-only modules: pipeline-types.ts (InputConfig/OutputConfig +
defaults + wire int mappers) and curve-catalog.ts (CurveName + name→id).
types.ts declares the pipeline/curve C ABI. engine barrel updated.
- DELETED src/engine/{input-pipeline,output-pipeline,curves}.ts.
Tests (P4.4 gate — recorded-gesture regression):
- pipeline-golden.test.ts now loads the built WASM (indirect-eval shim,
tests/wasm-load.ts) and drives the frozen gesture/output fixtures through the
C++ chains, honouring the per-event dt clock contract. Tolerance 1e-5
(non-momentum drift <5e-7). The 3 momentum configs carry 1e-2: proven-inherent
f32 drift (a byte-faithful f32 port of the exact original algorithm matches
the WASM to <6e-8 while both diverge from the f64 capture by ~7-9e-3), NOT a
core bug.
- curves-golden.json: linear/square/sqrt/centered_power kept as the original
f64 captures (C++ matches within <3e-8); exp/log/sigmoid/cubic RE-BASELINED
from the WASM (deliberate switch to firmware-exact k=1 exp/log, slope-6
sigmoid, true cubic x^3). Provenance recorded in-file.
- _generate.ts rebuilt as the WASM curve re-baseline tool; pipeline-golden-lib
trimmed to pure data builders.
Docs: fixtures/README.md + manifold/ONBOARDING.md updated.
Gates: typecheck, bun test (9), vite build, playwright e2e (27) all green.
TS half of P3.1/P3.2/P3.3: wire the new WASM exports and delete the TS
approximations now that geometric-dislike, jolt, OU, and the seeded RNG live
in the C++/WASM core.
- types.ts/wasm-iml.ts: bind + wrap dislike_geometric, store_positive,
positive/negative_count, set_avoid_style, jolt_press/step/release/active/
lr_scale/tick_lr_ramp, explore_intensity/get/apply. Weight-mutating wrappers
republish weights (version bump); explore_apply reuses feedbackBuf.
- engine-api: extend .feedback (dislikeGeometric/storePositive/counts/
setAvoidStyle) + new .explore facade. thumbsDown now passes the HEARD
(routed) vector, not the raw MLP output (raw == net output => inert cold-start).
- feedback/controller.ts: delete dislikes[] + applyDislikeBias + both C++ GAP
blocks + the SeededRng field; dislike() -> core dislikeGeometric (returns
action, 15 => cold-start prompt); like() feeds the centroid via the core
thumbsUp; getState() exposes positive/negative counts. Delete feedback/rng.ts.
- engine/exploration.ts: execute the P3 SWAP POINT -> drive engine.explore.*;
delete engine/jolt.ts + ou-explore.ts. UI surface unchanged.
- ConsoleApp: one-time cold-start banner (British spelling), routed heard vector
at the dislike call site.
- App.tsx/spine.ts: under ?debug=1 pin a fixed seed + fixed per-tick dt so the
probe/e2e are deterministic (production keeps time-seeded, real-time dt).
- tests: new geo-dislike.spec.ts; probe gains dislikeGeometric/storePositive/
feedbackCounts/setAvoidStyle; the thumbsDown probe test now drives a real
distinct-heard-vector dislike (bare thumbsDown on the net's own output is
correctly inert under the geometric core). 27 e2e + 9 unit green.
Docs: manifold/ONBOARDING.md engine+feedback sections synced.
Wire the runtime-shaped WASM MLP (one-core-engine P2) through the manifold:
- WasmIML.reshape(dims, spread): calls nisps_ml_reshape, re-describes the
instance, reallocates every dim-dependent heap buffer, refreshes weightCount,
clears the TS Dataset mirror (C-side resets), drops the stale training worker,
and pushes the new shape through the sink.
- EngineApi.reshape exposes it and re-ticks the spine so outputs/audio reflect
the new net. Spine already tolerates the arity change (buffers resize, version
bumps); documented.
- Training worker protocol carries the current hidden dims; the worker
ensureNet()s its mirror net to match after a reshape.
- ConsoleApp offers the reshape behind ReshapeModal on an active-layout CHANGE
(never on load; default 32-input over-provisioned head + zero-padding
preserved when declined). British copy, reset-on-reshape.
- Drawers: delete the stale even/odd blending note; honest dedicated-dimensions
line + net-arity chip.
- Probe: __nisps.reshape(nIn) / .describe(); e2e reshape.spec (default 32/126,
reshape to 4, describe reports 4, bounded outputs, weight count 3148→2868,
spine still propagates). All 25 e2e pass (20 existing + 5 new).
- ONBOARDING: refresh the reshape status + stale hardwired-arity gotcha.
Wire the modular input layer into the Console and reshape the browser
engine so input axes are genuine independent dimensions.
Inputs (manifold/src/inputs/):
- gamepad-source: emit press+release edges with standard-mapping labels
(enables hold-and-move); single/double-stick already present.
- midi-input-source: single-device selection + batch "MIDI Learn"
(every CC swept while armed becomes an axis); notes stay discrete.
- input-layer: compose() forwards each axis 1:1 (no mean-blend);
add onReducedInput so the manifold tracks gamepad/MIDI position.
- types: InputAction.phase, InputMode.
Console (manifold/src/console/):
- ConsoleApp: bind gamepad buttons to verdicts (RB up / LB down /
X randomise / Y nudge / B undo / A-hold reposition); mirror composed
position onto the manifold.
- Drawers: rebuilt Inputs drawer (source picker, gamepad legend, MIDI
device picker + batch-learn flow, learned-control meters).
Engine (nisps/wasm, manifold/src/engine):
- DefaultMLP widened MLP<2,..> -> MLP<32,..> (32 = MAX_AXES); each
active axis gets a dedicated slot, unused slots held at 0 (inert).
Rebuilt nisps.wasm (playground + manifold).
- spine/engine-api: setInputs writes the full N-D vector (was dropping
arr[2+]); primary pair keeps the 2-D pipeline; process() re-ticks the
whole vector via spine.reprocess().
Tests:
- parity_check/parity_wasm: ParityMLP -> 32 inputs, widen example bufs.
- CMakeLists: build parity binary with -ffp-contract=off so native
matches FMA-free WASM (training amplified the gap past 1e-5).
Inputs dock is still an exclusive picker; mixing toggles, reshape modal,
and the >2-D slider view (inputs-spec.md) are groundwork-laid but not
yet wired. See docs/redesign/midi-gamepad-inputs-worklog.md.
Adds the Manifold app (manifold/) — the convertible-mode React front-end on
the real NISPS ML+audio engine, served live at meml.lnfinitemonkeys.org/next/.
Console chrome trimmed per UI pass:
- drop mode label + subtitle from the top-left overlay (keep MEMLNaut wordmark)
- remove the composite split preset/ratio readout (top-centre)
- remove the OUTPUT corner tag above the bars
- remove the A/B compare toggle from the verdict cluster
- remove the follow button + input/noise readout (bottom-left)
- remove AltitudeNav focus switcher (bottom-right)