NISPS is a controller, not a synth: the object of study is the mapping
f: control-space -> parameter-space and how a musician's gestures deform
it. Loss measures fit to points the user dictated, which is the one thing
they never experience. So this measures geometry and gesture-response.
tests/cpp/ml_bench.cpp 61 scenarios, REPORTS never asserts (same
discipline as engine_bench.cpp). Shape-
agnostic via MLPCore<DynamicStorage>
(--shape, default 2,16,16,16,8), seeded
RNG throughout, branch points replayed
from scratch rather than snapshotted.
tests/cpp/test_ml_behaviour.cpp 20 asserting invariants, wired into
nisps_core_tests.
scripts/bench-ml.sh native + WASM from one source; --compare,
--sweep-shape, --smoke, --scenario, --seed.
Documents two contracts that fail SILENTLY (both now pinned by tests):
a thumbs-up must call BOTH mlp.add_example() and fb.store_positive(),
since dislike_geometric k-NNs the replay buffer and not the MLP dataset;
and placed_output() is valid only while state == Placing, after which an
empty span whose l2() is 0 scores a broken lifecycle as a perfect place.
ALIGNMENT defect 6 re-ranked (SGD-vs-RMSProp is not a research axis - it
silently invalidated every ported hyperparameter) and split into 6b (the
geometric dislike was ported from a superseded upstream design) and 6c
(InterfaceRL, the reference impl, is not in the tree).
Gates: build-cpp-tests (138 tests, ctest 4/4), parity-check PASS,
lint-cpp clean, bench-ml.sh --smoke runs end to end.