diff --git a/.gitignore b/.gitignore index ffa6182..6f59050 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,6 @@ node_modules/ bun.lock playwright-report/ test-results/ + +# Agent worktrees +.claude/worktrees/ diff --git a/MAP.md b/MAP.md index 4a98d78..335f354 100644 --- a/MAP.md +++ b/MAP.md @@ -40,7 +40,7 @@ MEMLNaut-NISPS — Neural Interactive Shaping of Parameter Spaces. One C++20 cod - `playground/src/input/pipeline.ts`, `playground/src/output/pipeline.ts`, `playground/src/output/curves.ts` — pure-fn pipelines (deadzone→zoom→curve→smoothing→momentum, then global curve→smoothing→slew→freeze). - `playground/src/features/` — additional feature modules (heatmap sampling, snapshot stack, A/B compare, region pin, param pin, trail, weight health, etc.). - `playground/src/debug/probe.ts` — synchronous `window.__nisps` debug probe for Playwright. -- `playground/public/nisps.{wasm,js}`, `c15.wasm`, `c15-glue.js` — compiled WASM artifacts (built by `scripts/build-wasm.sh`). +- `playground/public/nisps.{wasm,js}`, `c15.wasm`, `c15-glue.js` — WASM artifacts. `nisps.*` are a transitional copy from `scripts/build-wasm.sh` (canonical output is `manifold/public/`; copy dies with playground at P1). - `playground/tests/e2e/` — Playwright specs (`ml-engine`, `modes`, `persistence`, `ui-interactions`) + `helpers.ts`. - `playground/playwright.config.ts` — Vite preview server setup. @@ -113,7 +113,7 @@ the "BUILD DELTAS" block at the top of `docs/specs/vcv-module.md`). `src/MEMLNau ### `scripts/` — build + verify entry points - `build-firmware.sh`, `flash-firmware.sh`, `build-and-flash-firmware.sh`, `firmware-common.sh` — Arduino-CLI wrapper for RP2350 target with C++20 flag. -- `build-wasm.sh` — Emscripten compile producing `playground/public/nisps.{wasm,js}`. +- `build-wasm.sh` — Emscripten compile producing `manifold/public/nisps.{wasm,js}` (+ transitional copy to `playground/public/` until P1). - `build-cpp-tests.sh` — CMake configure + build + ctest (Ninja). - `parity-check.sh` — runs native + WASM and diffs binary outputs. - `lint-cpp.sh` — `.f` literal warn + heap/`Arduino.h` violation fail. diff --git a/docs/AGENT-REFERENCE.md b/docs/AGENT-REFERENCE.md index 9d99966..bb5acd7 100644 --- a/docs/AGENT-REFERENCE.md +++ b/docs/AGENT-REFERENCE.md @@ -124,9 +124,9 @@ Two WASM instances at runtime: 1. **Main thread** (`playground/src/ml/wasm-iml.ts`): ML inference + sync training + RL primitives. Update store after each call. Async training via disposable Web Worker (`wasm-worker.ts`). 2. **AudioWorklet** (`playground/src/audio/worklet/nisps-processor.ts`): runs engine `process_block` per audio block. Loads `nisps.wasm` directly via `WebAssembly.compile` (no Emscripten glue in worklet). Bytes posted from main thread. -C API is in `nisps/wasm/bindings.cpp`. Build: `bash scripts/build-wasm.sh` (~94KB output to `playground/public/`). +C API is in `nisps/wasm/bindings.cpp`. Build: `bash scripts/build-wasm.sh` (~94KB output to `manifold/public/`, transitional copy to `playground/public/` until P1). -The WASM target is fixed at `MLP<2, 10, 14, 18, 126>`. Modes with smaller `output_size` use the first N outputs only. +The WASM target is fixed at `MLP<32u, 10u, 14u, 18u, 126u>` (`nisps_ml_create` ignores requested dims — see `docs/specs/plans/one-core-engine-refactor.md` P2). Modes with smaller `input_size`/`output_size` use a slice. ### Known limitations diff --git a/docs/specs/plans/one-core-engine-refactor.md b/docs/specs/plans/one-core-engine-refactor.md index 7547f16..8bbbd4e 100644 --- a/docs/specs/plans/one-core-engine-refactor.md +++ b/docs/specs/plans/one-core-engine-refactor.md @@ -74,13 +74,13 @@ skin) still applies inside manifold; this plan adds the vertical one. Each phase ends green on its test gate and is independently landable. File phases as **ergo** tasks (one per phase, sub-tasks per bullet); do not start a phase before its predecessor's gate is green. -### P0 — Plumbing hygiene (hours, no behaviour change) +### P0 — Plumbing hygiene (hours, no behaviour change) — ✅ landed 2026-07-13 -- `scripts/build-wasm.sh` emits to `manifold/public/` (keep `playground/public/` copy only until P1 lands). -- `scripts/parity-check.sh` reads the manifold artifact. -- Fix stale doc: `MAP.md` §WASM still says `MLP<2,…>`; code is `MLP<32u,10u,14u,18u,126u>`. -- Delete `ml-debug.log`, `graphify-out/` from tree root; gitignore `.claude/worktrees/`. -- **Gate:** `run-all-tests.sh` green; manifold builds against a freshly-built (not copied) `nisps.wasm`. +- ✅ `scripts/build-wasm.sh` emits to `manifold/public/` (keep `playground/public/` copy only until P1 lands). +- ✅ `scripts/parity-check.sh` (and `tests/cpp/parity_wasm.mjs`) read the manifold artifact. +- ✅ Fix stale doc: the `MLP<2,…>` line was in `docs/AGENT-REFERENCE.md` + `nisps/wasm/README.md` (MAP.md was already correct); both now say `MLP<32u,10u,14u,18u,126u>`. +- ✅ `ml-debug.log`/`graphify-out/` were already absent from the tree; `.claude/worktrees/` gitignored. +- **Gate met:** `run-all-tests.sh` green (Playwright leg via the BUILD-PLAN non-snap-node VPS runner); parity PASS reading `manifold/public/` (max delta 2.4e-7); manifold typecheck+build green against the freshly-built artifact. ### P1 — Retire playground, single TS home (≈1 day) diff --git a/manifold/public/nisps.wasm b/manifold/public/nisps.wasm index 9ee47b2..cf9dbdb 100755 Binary files a/manifold/public/nisps.wasm and b/manifold/public/nisps.wasm differ diff --git a/nisps/wasm/README.md b/nisps/wasm/README.md index 0ce7871..07f0d68 100644 --- a/nisps/wasm/README.md +++ b/nisps/wasm/README.md @@ -1,11 +1,13 @@ # nisps/wasm Emscripten target that exposes `nisps/ml` (MLP) and `nisps/engines` (audio -engines) to the SolidJS playground via a flat C ABI. +engines) to the browser apps via a flat C ABI. This directory is a leaf — it does not export headers for inclusion by other C++ code. The only artifact is `bindings.cpp` plus the build script -that turns it into `playground/public/nisps.{wasm,js}`. +that turns it into `manifold/public/nisps.{wasm,js}` (with a transitional +copy to `playground/public/` until P1 of +`docs/specs/plans/one-core-engine-refactor.md` retires the playground). ## Building @@ -18,11 +20,11 @@ Requires `emcc` (Emscripten). The script defaults to Output: -- `playground/public/nisps.wasm` — the compiled module. -- `playground/public/nisps.js` — Emscripten glue (factory function +- `manifold/public/nisps.wasm` — the compiled module. +- `manifold/public/nisps.js` — Emscripten glue (factory function `createNispsModule`, MODULARIZE=1). -Both files are committed (so the playground works from a fresh clone +Both files are committed (so the browser apps work from a fresh clone without a C++ toolchain). Re-run `build-wasm.sh` after changes to `nisps/{core,ml,engines,wasm}`. @@ -32,9 +34,9 @@ The MLP class template is parametrised on `(input_size, hidden1, hidden2, hidden3, output_size)`. WASM cannot recompile templates at runtime, so this build instantiates exactly ONE configuration: - nisps::ml::MLP<2, 10, 14, 18, 126> + nisps::ml::MLP<32, 10, 14, 18, 126> -That serves the playground use case (2-D joystick → up to 126 synth +That serves the browser use case (up to 32 input axes → up to 126 synth parameters). `nisps_ml_create()` accepts caller-supplied dimensions for forward compatibility but currently ignores them — see comment at the top of `bindings.cpp`. The schemas in `schemas/modes/*.json` use up to diff --git a/playground/public/nisps.wasm b/playground/public/nisps.wasm index 9ee47b2..cf9dbdb 100755 Binary files a/playground/public/nisps.wasm and b/playground/public/nisps.wasm differ diff --git a/scripts/build-wasm.sh b/scripts/build-wasm.sh index 28db680..aac7dde 100755 --- a/scripts/build-wasm.sh +++ b/scripts/build-wasm.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # scripts/build-wasm.sh — compile nisps/wasm/bindings.cpp via Emscripten and -# write the result to playground/public/. +# write the result to manifold/public/. # # Requires emcc. Defaults to /usr/lib/emscripten/emcc; override via the EMCC # env var. Sample invocation: @@ -8,14 +8,17 @@ # EMCC=$(which emcc) scripts/build-wasm.sh # # Output: -# playground/public/nisps.js — Emscripten glue, MODULARIZE factory -# playground/public/nisps.wasm — the compiled module +# manifold/public/nisps.js — Emscripten glue, MODULARIZE factory +# manifold/public/nisps.wasm — the compiled module +# +# A copy is also written to playground/public/ until P1 of +# docs/specs/plans/one-core-engine-refactor.md retires the playground. set -euo pipefail EMCC="${EMCC:-$(command -v emcc || echo /usr/lib/emscripten/emcc)}" ROOT="$(cd "$(dirname "$0")/.." && pwd)" -OUT="$ROOT/playground/public" +OUT="$ROOT/manifold/public" SRC="$ROOT/nisps/wasm/bindings.cpp" if [[ ! -x "$EMCC" && ! -f "$EMCC" ]]; then @@ -82,3 +85,10 @@ set -x echo "[build-wasm] wrote $OUT/nisps.js + $OUT/nisps.wasm" ls -lh "$OUT/nisps.js" "$OUT/nisps.wasm" + +# Transitional copy for the playground; removed when P1 deletes playground/. +PLAYGROUND_OUT="$ROOT/playground/public" +if [[ -d "$PLAYGROUND_OUT" ]]; then + command cp -f "$OUT/nisps.js" "$OUT/nisps.wasm" "$PLAYGROUND_OUT/" + echo "[build-wasm] copied artifacts to $PLAYGROUND_OUT (transitional, until P1)" +fi diff --git a/scripts/parity-check.sh b/scripts/parity-check.sh index 12f594c..4b543e5 100755 --- a/scripts/parity-check.sh +++ b/scripts/parity-check.sh @@ -4,7 +4,7 @@ # # Prerequisites: # 1. scripts/build-cpp-tests.sh has run (need nisps_parity_check binary). -# 2. scripts/build-wasm.sh has run (need playground/public/nisps.{js,wasm}). +# 2. scripts/build-wasm.sh has run (need manifold/public/nisps.{js,wasm}). # # This script can run either step on demand if the artifacts are missing. # Skip auto-build with NISPS_PARITY_NO_BUILD=1. @@ -21,8 +21,8 @@ ROOT="$(cd "$(dirname "$0")/.." && pwd)" TESTS_DIR="$ROOT/tests/cpp" BUILD_DIR="${NISPS_BUILD_DIR:-$ROOT/nisps/build}" NATIVE_BIN="$BUILD_DIR/nisps_parity_check" -WASM_GLUE="$ROOT/playground/public/nisps.js" -WASM_MOD="$ROOT/playground/public/nisps.wasm" +WASM_GLUE="$ROOT/manifold/public/nisps.js" +WASM_MOD="$ROOT/manifold/public/nisps.wasm" NATIVE_OUT="$TESTS_DIR/parity_native.bin" WASM_OUT="$TESTS_DIR/parity_wasm.bin" TOL="${NISPS_PARITY_TOL:-1e-5}" diff --git a/tests/cpp/parity_wasm.mjs b/tests/cpp/parity_wasm.mjs index afa3f7b..b8e75da 100644 --- a/tests/cpp/parity_wasm.mjs +++ b/tests/cpp/parity_wasm.mjs @@ -4,7 +4,7 @@ * parity_check.cpp against the WASM build of nisps and writes a binary blob * with identical layout. The shell wrapper compares the two blobs. * - * The WASM module is loaded from playground/public/nisps.{js,wasm} — + * The WASM module is loaded from manifold/public/nisps.{js,wasm} — * scripts/build-wasm.sh must have run first. * * Output blob format matches parity_check.cpp: @@ -48,7 +48,7 @@ const SYNTH_FRAMES = 128; const PROBE_IDX = [0, 5, 19, 31, 73, 137, 251, 491, 999, 1583, 2401, 3289]; async function loadWasm() { - const wasmGluePath = resolve(repoRoot, 'playground', 'public', 'nisps.js'); + const wasmGluePath = resolve(repoRoot, 'manifold', 'public', 'nisps.js'); try { await access(wasmGluePath, fsConstants.R_OK); } catch { @@ -59,7 +59,7 @@ async function loadWasm() { // The Emscripten glue is generated with MODULARIZE=1, which writes // var createNispsModule = (() => ...)(); // if (typeof exports==='object' && typeof module==='object') module.exports = ...; - // It lives in playground/public/, which is a sub-package with + // It lives in manifold/public/, which is a sub-package with // "type":"module" in its parent package.json — so neither `require()` nor // `import()` can extract the factory cleanly. We work around this by // reading the file as text and evaluating it inside a thin shim that @@ -77,7 +77,7 @@ async function loadWasm() { console.error('[parity_wasm] could not locate createNispsModule in glue'); process.exit(2); } - const wasmBinaryPath = resolve(repoRoot, 'playground', 'public', 'nisps.wasm'); + const wasmBinaryPath = resolve(repoRoot, 'manifold', 'public', 'nisps.wasm'); const wasmBinary = await readFile(wasmBinaryPath); const Module = await factory({ wasmBinary }); return Module;