memlnaut-nisps/docs/specs/recon/simplification-audit-2026-07.md
monkey-w1n5t0n fd0aee2354 docs(audit): simplification audit 2026-07 — recon findings, phased plan, ALIGNMENT rewrite
66-agent adversarially-verified audit vs the five-bullet one-core vision.
113 findings: CI red since 2026-07-13 (memllib pin on no remote), ungated
deploys, unshared mode layer, no curated/advanced split, dead-mass inventory.
Recon: docs/specs/recon/simplification-audit-2026-07.md (immutable).
Plan: docs/specs/plans/simplification-plan.md (proposal; phases gated on
operator adoption, §7 decisions). ALIGNMENT rewritten; MAP flatly-false
lines fixed (phantom MEMLCelium-upstream entry, exploration.ts, daisysp
non-submodule, pre-P5 sentence, perf-attr claims); MAIN registry updated.
2026-07-21 01:24:35 +02:00

298 lines
No EOL
64 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
kind: finding
date: 2026-07-21
immutable: true
---
# Simplification Audit 2026-07 — full-repo smell/bloat/spec review vs the one-core vision
_Dated 2026-07-21. Method: a 66-agent workflow — 11 parallel subsystem reviewers (ml-core, dsp-engines-modes, wasm-bridge, manifold-ui, manifold-backends-inputs, firmware, schemas-codegen, specs-docs, tests-build, satellites-hygiene, architecture-gap) → adversarial verification (per-finding skeptics for architectural/structural claims, batch spot-checks for local/style) → a completeness critic. 113 findings survived, 0 were refuted outright, 22 were downgraded/corrected by verifiers. Yardstick: the operator's five-bullet target vision (2026-07-20): (1) one C++20 NISPS core serving RP2350 firmware and the browser, performance-sensitive on the MCU; (2) firmware modes runnable as modes in Manifold; (3) curated-presets default with a maximalist "advanced" dev mode; (4) PlatformIO, no more .ino; (5) Manifold as interface/editor for the hardware. Actionable mitigations are phased in `../plans/simplification-plan.md`; the strategic tier is promoted into `ALIGNMENT.md` (both updated in the same commit as this finding)._
## Headline
1. **CI has been 100% red on main since 2026-07-13** and nothing noticed. The memllib submodule pin (`b37fc53`, branch `feat/nisps-core-swap`) is reachable from **no remote** — GitHub's `submodules: recursive` checkout fails before any gate runs, so every P4P6 "gates green" claim rests on local runs only, and fresh clones cannot build firmware. Verified live via `gh run list` (all recent main runs `failure`) and `git branch -r --contains b37fc53` (empty).
2. **Those three memllib commits exist only on one disk.** A single disk failure loses firmware-critical code. (The `w1n5t0n` fork remote exists but does not contain them.)
3. **Production deploys are gated by nothing.** Push to main → VPS webhook → live at `meml.lnfinitemonkeys.org/next/`, with CI red and the committed `manifold/public/nisps.wasm` having no freshness gate — what users run is whatever was last committed, unverified.
4. **Real behaviour bugs surfaced**, not just smells: the TS example-store mirror caps at 100 while the C++ store caps at 128, so `train()` and `trainAsync()` silently train on different datasets past 100 examples (plus a latent OOB read on sample weights); VCV bridged mode truncates the input vector to 2-D against a 32-input head; the VCV plugin has an audio-thread data race and does full-state JSON on the audio thread.
5. **The five-bullet vision has zero footprint in ALIGNMENT.md or the spec corpus** — nothing records PlatformIO, curated/advanced, or the hardware-editor role as goals, so nothing was steering toward them.
6. **No performance measurement exists anywhere** — the headline "performance-sensitive" constraint is enforced entirely by static discipline (no heap/virtual, section attrs) and vibes; no benchmark, no size report, no CPU-load assertion on either target.
The codebase itself is in **better shape than its vibe-coded provenance suggests**: the `nisps/` core's storage-policy split is justified and parity-pinned, hot paths are clean, the CRTP mode scaffold genuinely centralises, the backend/input contracts are uniform. The dominant rot is *vestigial mass* (dead subsystems, decorative UI, executed-plan specs presented as live contracts, hand-maintained registries that codegen should own) — most fixes are deletions.
## Area verdicts
**ml-core** — The ml-core area (nisps/core, nisps/ml, nisps/pipeline) is genuinely one of the healthier parts of this codebase: the MLPCore<Storage> policy split is justified (two real consumers — zero-heap firmware FixedStorage, arena-allocated browser/VCV DynamicStorage — sharing one algorithm body, pinned by a bit-parity test), hot paths are heap-free, virtual-free, .f-consistent, and RNG is deterministic per instance. Jolt/OU/replay/geo_push are small, inert-by-default, and cleanly ported. The rot is concentrated in vestigial baggage rather than wrong abstractions: feedback.hpp carries two whole legacy […]
**dsp-engines-modes** — The dsp/engines/modes stack is in better shape than the vibe-coded provenance suggests: the DSP primitives are lean, faithful ports with no maximilian/daisysp vestiges beyond honest attribution comments; all 8 engines have real consumers on both targets; the CRTP ModeBase genuinely centralizes orchestration (inputs, jolt, OU, events) rather than hiding duplication. The biggest gap versus the vision is architectural: the modes/ layer is consumed ONLY by firmware — the WASM bindings include engines but zero mode headers and re-assemble mode behavior (jolt stepping, OU, routing) in bindings.cpp […]
**wasm-bridge** — The wasm-bridge is in better shape than its history suggests: the P3 'SWAP POINT' duplication is genuinely resolved (jolt.ts/ou-explore.ts are deleted; exploration.ts is now a thin timer-driver over the shared C++ core), the flat C API in bindings.cpp is stylistically coherent (consistent null-guards, grouped, documented wire layouts), and worklet messaging is RT-acceptable (postMessage between render quanta, fixed-size param buffer, no allocation inside process() beyond trivial subarray views). The real problems are accretion and dead mass: roughly a dozen API entries exist across a 5-file […]
**manifold-ui** — The manifold UI is functional and the engine wiring (FeedbackController → shared C++ core, backend manager, input layer) is genuinely live, but the console layer carries heavy sediment from its window-global JSX ancestry: a dead four-way "focus/altitude" system whose only reachable branch is 'composite' (SplitStage, ReadoutStrip, InputMini, AltitudeNav all unreachable), a large stratum of decorative state that renders real-looking controls doing nothing (learning-rate/decay/spread/tame sliders, master volume, bpm, A/B toggle, snapshots, health/gradient visuals), and a ConsoleCtx god-object […]
**manifold-backends-inputs** — This is one of the cleaner vibe-coded areas: the OutputBackend contract is genuinely uniform (start/send/teardown/status with in-backend throttle+dead-zone), mapping.ts is a single shared truth, presets.ts is real and consumed by OutputsBackendConfig (not a stub), and midi-devices/ is proper schema→codegen with one import path. The real debts are: (1) the VCV bridged-training path is pinned to an obsolete 2-D input assumption while the engine has a 32-input head and the spine already stores the full N-dim raw vector — so VCV mode silently truncates gamepad/MIDI inputs; (2) the OSC bridge […]
**firmware** — The firmware C++ glue itself is in decent shape — it is thin (~1.4k lines), template-based with no virtual dispatch in the audio path, the SPSC/dual-core discipline is respected, and peripherals.hpp/selftest.hpp are unusually well-commented and readable for vibe-coded work. The rot is concentrated in the build system, which is exactly what vision bullet 4 targets: the Arduino-CLI toolchain forces a symlink forest, a build script that mutates the committed .ino (variant flips pollute git history), a triple-bookkept mode list (the .ino comment block, mode_select.hpp aliases, and a […]
**schemas-codegen** — The schemas→codegen pipeline is one of the better-built parts of this vibe-coded repo: both generators validate via ajv, emit deterministic/idempotent output, and the generated C++ is genuinely sane (inline constexpr arrays, string_views, .f literals, no heap — spot-checked against the perf contract). But the "schemas are the single source of truth" story is only about half true in practice: net dimensions are hand-duplicated in every C++ mode's MLP template args with no compile-time tie back to the generated constants (browser reshapes from schema, firmware uses the hand-written dims — the […]
**specs-docs** — The corpus has an unusually good ontology (spec/plan/finding/ADR split, _archive dir, front-matter kinds) and MAP.md is ~90% accurate and current — but the July one-core refactor (P4P6, landed 2026-07-14/18) rotted the registry layer faster than the doc-sync discipline caught it. MAIN.md, the corpus entry point, now makes at least six false or self-contradictory claims; two 2026-06-era specs describe the RETIRED SolidJS playground world as present tense; both plans that are done still carry status:active in ways the corpus's own ontology forbids (BUILD-PLAN) or defensibly (one-core, gated on […]
**tests-build** — The test estate is in genuinely good shape at its core: 42 focused C++ test files with a documented hand-rolled harness, a 7-stage native-vs-WASM parity blob, committed golden baselines (impulse, ML vectors), fixed↔dynamic storage parity, VCV-adapter parity, and post-P4 pipeline-golden fixtures whose provenance and re-baselining are exemplary — the fixtures are meaningful, not frozen accidents. The problems cluster at the edges: an entirely dead root-level Playwright rig left over from the retired playground (config points at an HTML file that no longer exists anywhere in the tree, and […]
**satellites-hygiene** — The satellites are in better shape than their paperwork. Post-P6, vcv/src/iml.hpp genuinely is a thin adapter over MLPCore<DynamicStorage> (pinned bit-exact by tests/cpp/test_vcv_iml_parity.cpp, wired into CMake), and keeping the module in-repo is coherent since it builds against ../../nisps directly; the one defensible wart is the insertion-order example mirror (features_/labels_), which carries a named requirement (JSON persistence + cross-thread staging) and is acceptable. firmware/useq-celium is the cleanest corner of the repo: I byte-verified the protocol.h ↔ useq-protocol.ts mirror […]
**architecture-gap** — Scored against the 5-bullet vision: (1) "one C++ core" is ~80% real — the algorithm layer (ML, feedback, jolt/OU, pipelines, curves) genuinely lives once in nisps/ after the P0P6 refactor, but the MODE layer does not: nisps/modes/ never compiles to WASM, so the browser recomposes engine+ML in TS and every ModeBase behaviour (event ring, voice spaces, note_on/BPM, analysis feature-splicing, pinning) is firmware-only. (2) Consequently "firmware modes runnable in Manifold" is ~40%: 4 of the 9 catalogued browser modes are non-functional (sequencers emit events nobody drains; audio-in modes have […]
## Completeness-critic gaps
_A final agent asked "what did the audit miss?". Verdict: file-tree coverage was strong; the systematic blind spot was everywhere the repo touches the outside world._
**CI has been entirely red on main since 2026-07-13 and no auditor noticed.** Verified live: `gh run list` shows 8/8 recent runs on main FAILED, spanning every P3P6 push (2026-07-14 through 2026-07-18). Cause confirmed from run 29641902005: the cpp-tests job dies at the Checkout step — 'remote error: upload-pack: not our ref b37fc53…' — because ci.yml uses `submodules: recursive` and the memllib gitlink was re-pinned (commit 43ef6d5, 2026-07-13) to a commit that exists on no remote. The vcv-plugin workflow fails identically. The firmware area found the unreachable pin ('fresh clones cannot build firmware') and tests-build criticized CI *content* drift, but the compound fact is worse than either: the entire C++ gate […]
**The unpushed memllib fork is a data-loss single point of failure, and its ownership question is unasked.** The three commits on memllib's local `feat/nisps-core-swap` branch (bf9691c 'swap memlp includes for nisps/core, inline RL utilities', 32cc831 newlib RNG fix, b37fc53 seed-helper rename) are NISPS-load-bearing source that exists ONLY in this working copy — `git ls-remote` of MusicallyEmbodiedML/memllib shows no ref containing them, and the operator has no fork remote configured. One disk failure loses firmware-critical code. The audit framed this purely as a build blocker; the trivial mitigation (push the branch to a monkey-w1n5t0n/w1n5t0n Forgejo or GitHub fork and repoint .gitmodules) was never proposed. The deeper unasked question matters […]
**The production deploy pipeline was audited by nobody — live deploys are gated by nothing.** manifold/ONBOARDING.md:43 documents it: push to GitHub main → VPS webhook → builds manifold/ → rsyncs to the live meml.lnfinitemonkeys.org/next/. Three consequences no area examined: (a) deploys do not wait for CI — and CI is red anyway (gap 1), so every P4P6 push went straight to the live site with zero automated gate between a nisps/ change and production; (b) the webhook builds only manifold/, so vite copies the COMMITTED manifold/public/nisps.wasm into dist — the 'stale artifact, no freshness gate' finding that tests-build scoped to browser tests actually extends to what users run in production; (c) the webhook config itself (on the […]
**No performance measurement exists anywhere, though 'super performance-sensitive on the microcontroller' is the vision's first bullet.** Every area checked perf DISCIPLINE statically (no heap, no virtual, .f literals, section attrs) but nobody asked how the project would ever KNOW the audio path meets budget or regressed: grep across scripts/ and tests/cpp/ finds zero benchmark harness, no cycle/CPU-load assertions, no blocks-per-second smoke for engine_process_block (native or WASM), and no firmware flash/RAM budget report in any gate — the 16 KB dead loss-history buffer was found by reading, not by a size report that would have caught it mechanically. The only perf scaffolding is on-device (src/memllib/utils/perf.hpp, included by the .ino) with no automated consumer. A […]
**Training-health telemetry is one product decision fragmented into four uncoordinated findings.** Four areas each hold one limb of the same feature: ml-core flags the 16 KB loss-history buffer in every firmware MLP (nothing reads it); specs-docs verifies wasm-worker.ts:304 fakes a 1-element lossHistory; manifold-ui flags the health/gradient visuals as decorative; architecture-gap notes the real get_layer_stats API is plumbed end-to-end and unused. Nobody asked the single question that resolves all four: IS training-health telemetry a feature of this instrument? If yes (plausible — 'interactive control visualisation' is in vision bullet 5 and 'advanced mode as preset-authoring dev tool' in bullet 3 wants training introspection), the fix […]
**The osc-bridge release workflow already names the surviving bridge — and two of three workflows were reviewed by nobody.** tests-build audited only ci.yml, but .github/workflows/ contains three workflows. osc-bridge.yml (tag-triggered) deno-compiles ONLY manifold/osc-bridge/bridge.ts into cross-platform release binaries — that is the named external consumer that settles the manifold-backends 'two hand-maintained bridge implementations' finding: bridge.ts has a release pipeline, bridge.mjs has no consumer in any workflow, so the mjs twin is the deletion candidate. vcv-plugin.yml was also unreviewed: it fails on the same unreachable submodule pin as ci.yml (verified in the 2026-07-18 run), and still triggers on stale feat/manifold-mission / feat/vcv-dist branch […]
## Findings inventory
_`confirmed` = an adversarial verifier re-read the cited code and the claim held; `downgraded` = the claim held but severity or fix needed correction (fix shown is the verifier's). IDs are referenced from `../plans/simplification-plan.md`; evidence lives at the cited lines._
### Architectural (8)
**A1. Mode layer is not shared: nisps::modes never compiles to WASM; Manifold rebuilds modes in TS** (architecture-gap, downgraded, week+)
`nisps/wasm/bindings.cpp:54`, `nisps/modes/base.hpp:113`, `manifold/src/console/model.ts:199`, `manifold/src/engine/spine.ts:1`
bindings.cpp includes nisps/engines/* and nisps/ml/* but nothing from nisps/modes/ (includes at lines 54-77; engine dispatch at 267-275 instantiates bare engines). grep for events/voice_space/note_on/update_bpm in bindings.cpp returns nothing. So ModeBase behaviours — initial inference at setup […]
*Fix:* Reunify mode BEHAVIOUR in C++ without pretending the browser can host one monolithic mode object: (1) storage-policy ModeBase exactly as P2 did for MLPCore — ModeBase<Derived, EngineT, MLCore<Storage>, ...> with Fixed for firmware (zero-heap intact) and Dynamic for WASM (heap at construction only), so browser modes stay runtime-reshapeable; (2) split the mode C ABI along the […]
**A2. Manifold catalogues 4 modes that structurally cannot work in the browser** (architecture-gap, downgraded, days)
`manifold/src/console/model.ts:206`, `nisps/engines/breakor.hpp:123`, `manifold/src/engine/engine-host.ts:1`, `manifold/src/engine/worklet/nisps-processor.ts:287`
SCHEMA_MODES (model.ts:199-221) lists all 9 schema modes including breakor, elysiamorf (sequencers: engine audio is silence; their whole output is pop_events, breakor.hpp:123-128) and xiasri, sound_analysis_midi (audio-in). But: (a) no WASM binding drains engine events and no manifold code […]
*Fix:* Immediate (hours, manifold-only): add a manifold-side audio-topology map keyed by engineId in model.ts — 'generator' (paf_synth, memlcelium→also slp_workshop, elysiamorf? no: event-only) vs 'audio-in-fx' (channel_strip, verb_fx, xiasri) vs 'event-only' (breakor, elysiamorf) vs 'analysis' (sound_analysis_midi) — and surface it as a 'firmware-only'/'needs audio-in' badge on […]
**A3. No curated/advanced split exists; Manifold is 100% dev-maximalist with no preset data model to author against** (architecture-gap, downgraded, week+)
`manifold/src/settings/settings-store.ts:24`, `manifold/src/backends/presets.ts:30`, `manifold/src/console/ConsoleApp.tsx:105`
Grep for advanced/curated across manifold/src finds only BackendAdvanced.tsx (per-backend transport settings) — no global mode toggle. settings-store.ts:24-40 holds only cosmetic prefs (icon colour, input-map shape, corner radius). The only preset machinery is presets.ts: per-BACKEND output-routing […]
*Fix:* Split into three differently-sized items. (a) NOW, small bug: add `cv` to OutputPresetRow, rowsFromParams, and applyPreset in manifold/src/backends/presets.ts (three one-line additions). (b) IF/WHEN the curated-default direction is actually adopted, first record it in ALIGNMENT.md (it currently contradicts open question Q2) — then build the unified preset artifact by EXTENDING […]
**A4. Hardware-editor (vision 5) is a facade: serial stub in the UI, zero firmware command surface, zero on-device persistence** (architecture-gap, downgraded, week+)
`manifold/src/serial/memlnaut-serial.ts:102`, `manifold/src/console/Drawers.tsx:590`, `firmware/MEMLNaut-NISPS/MEMLNaut-NISPS.ino:126`
memlnaut-serial.ts saveModel/restoreModel/getSettings are explicit no-op stubs (lines 102-132: 'TODO: real protocol', returns false/null/{}), and git log shows manifold/src/serial untouched since the June initial commit. EditorPanel is shipped live in the Settings drawer (Drawers.tsx:590) with […]
*Fix:* Drop the short-term step — the 'not wired' badge already exists in EditorPanel.tsx:88-94; at most soften the dock description in output-mode.ts:79 ('configure / save / restore — protocol not yet implemented'). Real path stands as proposed: spec the USB-serial protocol the useq-celium way (shared versioned header under firmware/, mirrored TS + frame-size unit test), add a […]
**A5. modes/ layer has one consumer: WASM bindings bypass it and re-orchestrate modes from engines + ml primitives** (dsp-engines-modes, downgraded, days)
`nisps/wasm/bindings.cpp:54`, `nisps/wasm/bindings.cpp:844`, `nisps/modes/base.hpp:311`, `firmware/MEMLNaut-NISPS/glue/mode_select.hpp:41`
bindings.cpp includes all 8 engine headers (lines 54-62) and ml/jolt.hpp, ml/ou_noise.hpp directly, but not a single nisps/modes/*.hpp; only firmware glue (mode_select.hpp:41-50) consumes the mode classes. bindings.cpp:844-851 (nisps_ml_jolt_step) is a line-for-line duplicate of […]
*Fix:* Do NOT bind the mode classes into WASM and do NOT delete the per-primitive jolt/OU C API — both contradict the locked one-core-engine architecture (runtime-shaped browser MLP, P2; primitives-via-C-ABI, P3) and the two-WASM-instance RT split. Instead: (1) collapse the one true code duplication by moving the copy→morph→writeback sequence into the shared primitive — e.g. a […]
**A6. Arduino-CLI/.ino build machinery: build mutates committed source, triple-bookkept mode list, symlink forest — PlatformIO migration deletes ~400 lines of hackery** (firmware, confirmed, days)
`scripts/firmware-common.sh:192-208`, `scripts/firmware-common.sh:298-337`, `firmware/MEMLNaut-NISPS/MEMLNaut-NISPS.ino:37-59`, `firmware/MEMLNaut-NISPS/glue/mode_select.hpp:90-106`
Mode selection lives in THREE hand-synced places: the .ino comment block (lines 38-54) that firmware-common.sh greps (load_firmware_variants:192-208), the mode_select.hpp using-aliases (54-85), and the NISPS_ST_* macro table (90-104). set_firmware_variant (298-337) runs an inline Python script that […]
*Fix:* Migrate to PlatformIO in one cut, no dual path: platformio.ini with maxgerhardt/platform-raspberrypi + board_build.core=earlephilhower (the FQBN solderparty_rp2350_stamp_xl proves the variant exists in arduino-pico; verify the PIO board def or add a small custom board JSON). One [env] per firmware variant with build_flags = -std=gnu++20 -DMEMLNAUT_MODE_TYPE=<alias> (keep […]
**A7. No curated-default/advanced split and no in-UI instrument picker — UI structure fights the core vision** (manifold-ui, downgraded, days)
`manifold/src/console/ConsoleApp.tsx:355`, `manifold/src/console/ConsoleApp.tsx:61`, `manifold/src/settings/settings-store.ts:24`, `manifold/src/console/Drawers.tsx:1`
The vision requires curated presets/modes by default with maximalist settings behind an 'advanced' gate. Settings (settings-store.ts:24-38) contains only monochromeIcons/unfocusedIconColour/inputMap/cornerRadius — no advanced flag exists anywhere. The default surface is the full five-drawer console […]
*Fix:* Render the instrument-mode picker from the already-plumbed ctx.modes/setModeId (pure UI; engine reshape-on-mode-switch already works per P5; keep the window.__mf seam for e2e). Drop the global advancedMode boolean: progressive disclosure is already designed and partly built as the per-drawer depth ladder (dock-spec §0 peek/expand/FULL with FULL as the advanced page). If more […]
**A8. The operator's target vision (PlatformIO, curated-presets default vs advanced mode, Manifold as hardware editor) has zero footprint in ALIGNMENT or the spec corpus** (specs-docs, confirmed, hours)
`ALIGNMENT.md:48`, `docs/specs/MAIN.md:140`, `MAP.md:150`, `manifold/src/serial/memlnaut-serial.ts`
No spec, plan, ALIGNMENT defect, or open question mentions PlatformIO anywhere (grep -ri platformio docs/ MAP.md ALIGNMENT.md hits only firmware/useq-celium, which already uses it — proving the toolchain is in-house viable). MAP.md:150 codifies the .ino/Arduino-CLI flow as a Convention; ALIGNMENT […]
*Fix:* Add three dated ALIGNMENT entries (PlatformIO migration — killing the .ino sketch-tree symlink hacks MAP.md:161 documents; curated-presets/advanced-mode product model — promoting Q2 from question to defect and linking the pending manifold-parity presets plan; hardware-editor role for manifold/src/serial). Specs follow when each is picked up.
### Structural (35)
**S1. Mode identity is ~6 hand-maintained registries with demonstrated drift, not one source of truth** (architecture-gap, downgraded, day) — `manifold/src/console/model.ts:199`, `manifold/src/console/model.ts:324`
Adding a mode touches: schema JSON (canonical) + hand C++ mode (legit) PLUS (a) mode_select.hpp using-alias, (b) the NISPS_ST_* zero-table, […] *Fix:* Keep points (1), (4), (5) as proposed: codegen an ALL_MODE_SCHEMAS array (TS-side codegen template change; SCHEMA_MODES becomes a mode_id→overlay map, keeping hand label/glyph/cls/ordering, which are legitimately display truth); collapse […]
**S2. Vision 4 (PlatformIO) not started; the .ino build machinery is actively hostile (symlink farm, sed-mutates-source mode selection, macro undef dance)** (architecture-gap, downgraded, days) — `scripts/firmware-common.sh:311`, `firmware/MEMLNaut-NISPS/glue/mode_select.hpp:19`
Mode selection = build scripts regex-rewriting the .ino's #define line in place (firmware-common.sh:194-205 grep/sed discovery, :311 python […] *Fix:* Migrate firmware/MEMLNaut-NISPS to one platformio.ini with an env per variant passing -DMEMLNAUT_MODE_TYPE=<alias> (grep/sed/python rewrite machinery, the .ino comment registry, and the NISPS_ST_* SelfTest grep-discovery tag all deleted; […]
**S3. Two 'authoritative' specs still prescribe the retired SolidJS/playground2 world** (architecture-gap, downgraded, hours) — `docs/specs/engine-architecture.md:38`, `docs/specs/aimmersive-clone-spec.md:1`
engine-architecture.md (kind: spec — 'wins over code by intent' per MAIN.md §0) mandates building in a fresh `playground2/` tree with […] *Fix:* One commit, three coordinated edits. (1) engine-architecture.md: do not flatten to one page — excise the dead sections (§1 playground2 tree, §3 reuse-vs-rebuild, §4 two-entry Vite/nginx serving, §5 S0S7, stale §6 open questions) and […]
**S4. DriverConfig is a dead contract: required by the AudioEngine concept, implemented by all 8 engines, consumed by nobody** (dsp-engines-modes, confirmed, day) — `nisps/core/concepts.hpp:62`, `nisps/core/types.hpp:59`
Repo-wide grep for driver_config/DriverConfig finds only the struct (types.hpp:59), the concept requirement (concepts.hpp:62), and the 8 […] *Fix:* Decide once: either wire the firmware audio driver to read the active mode's engine().driver_config() at mode start (makes the mic/line settings real), or delete DriverConfig, the concept line, and all 8 implementations. Do not keep the […]
**S5. Per-mode kSchema aggregates are hand-written 12-field boilerplate that codegen should emit** (dsp-engines-modes, confirmed, hours) — `nisps/modes/breakor.hpp:76`, `nisps/modes/memlcelium.hpp:48`
Nine mode headers each contain a mechanically identical positional ParamSchema aggregate wiring 12 generated constants (kXModeId, […] *Fix:* Move struct ParamSchema into generated/schema_types.hpp and have codegen emit 'inline constexpr ParamSchema kXSchema{...}' per mode; each mode's param_schema() becomes a one-line return of the generated constant. Delete the 9 hand-written […]
**S6. MLP topology is a dual source of truth: hand-typed template args vs generated hidden_layers** (dsp-engines-modes, confirmed, hours) — `nisps/modes/memlcelium.hpp:28`, `nisps/modes/generated/memlcelium_schema.hpp:19`
Every mode hardcodes its net shape (e.g. ml::MLP<4u,10u,14u,18u,56u> at memlcelium.hpp:28) while the generated header carries the same dims […] *Fix:* Have codegen emit 'using MemlceliumMLP = ::nisps::ml::MLP<4u,10u,14u,18u,56u>;' (or a static_assert block comparing MLP dims to kXHiddenLayers) into each generated header, and make modes use the alias. One source of truth, compile-time […]
**S7. memllib submodule pin is unreachable from any remote — fresh clones cannot build firmware** (firmware, confirmed, hours) — `.gitmodules:1-3`, `scripts/build-firmware-arch.sh:74-79`
git submodule status → b37fc53 (heads/feat/nisps-core-swap). Inside src/memllib, `git branch -r --contains HEAD` returns EMPTY — the pinned […] *Fix:* Push feat/nisps-core-swap to the monkey-w1n5t0n/memllib fork and change the .gitmodules URL to the fork (or upstream the changes to MusicallyEmbodiedML and re-pin). Then delete the compensating error-message paragraph in […]
**S8. daisysp is fully dead weight: 41 .cpp files compiled into every firmware build with zero consumers, and docs mislabel it a live submodule** (firmware, confirmed, hours) — `firmware/MEMLNaut-NISPS/src/daisysp`, `nisps/dsp/pitch_shift.hpp:3`
grep for daisysp across firmware glue, the .ino, nisps/, and the symlinked memllib subdirs (audio/hardware/interface/synth/utils) finds […] *Fix:* Delete the firmware/MEMLNaut-NISPS/src/daisysp symlink and the vendored src/daisysp tree; correct MAP.md, firmware/README.md, and setup-firmware-toolchain.sh in the same commit. (Answers the focus question: yes, the granular pitch-shift […]
**S9. Firmware compilation is in no automated gate: CI, run-all-tests.sh, and lint all skip firmware/** (firmware, confirmed, day) — `scripts/run-all-tests.sh`, `.github/workflows/ci.yml:14-15`
grep 'firmware' scripts/run-all-tests.sh → no hits. ci.yml comments that 'firmware build is documented as a manual […] *Fix:* After the PlatformIO migration, add a CI job running `pio run -e slpworkshop -e selftest -e pafsynth` with a cached toolchain (arduino-pico core download is the only heavy step). Pre-migration stopgap: a weekly/manual-dispatch arduino-cli […]
**S10. VCV bridged mode truncates the input vector to 2-D against a 32-input engine head** (manifold-backends-inputs, downgraded, day) — `manifold/src/engine/engine-api.ts:264`, `manifold/src/backends/vcv-backend.ts:67`
engine-api.ts inputVector() returns `[this.spine.lastRawX, this.spine.lastRawY]` (a fresh 2-element array allocated on EVERY spine tick […] *Fix:* In manifold TS only: (1) change EngineApi.inputVector() to return spine.lastRawInputs (signature ArrayLike<number>; document "live reused buffer — copy, don't retain"; VcvBackend.setInputVector already copies). (2) Make VcvBackend […]
**S11. Two hand-maintained OSC bridge implementations (bridge.ts Deno + bridge.mjs Node)** (manifold-backends-inputs, confirmed, hours) — `manifold/osc-bridge/bridge.ts:1`, `manifold/osc-bridge/bridge.mjs:1`
bridge.mjs is not compiled output — it is a separate hand-written Node port (node:dgram + ws dependency vs Deno.listenDatagram; deno […] *Fix:* Delete bridge.mjs; keep bridge.ts as the single source and compile.sh binaries as the no-Deno distribution. Fix the spec line that calls .mjs 'compiled'.
**S12. Inputs: spec promises source composition, code enforces exclusive single-source; composition machinery unreachable** (manifold-backends-inputs, downgraded, day) — `docs/specs/inputs-spec.md:16`, `docs/specs/inputs-spec.md:230`
inputs-spec.md §0 says the operator picks 'one source *or a combination*' and §223-230 specifies multi-source composition ('XY pad + a MIDI […] *Fix:* Do not prune the composition machinery or rewrite the spec to exclusive — mix-and-match is a recorded, unreversed operator decision (2026-06-28 memory; commit 9e59eb0 and docs/specs/recon/midi-gamepad-inputs-worklog.md both mark the […]
**S13. Per-backend config side-channel + instanceof special-casing in the manager's hot path** (manifold-backends-inputs, downgraded, day) — `manifold/src/backends/manager.ts:69`, `manifold/src/backends/manager.ts:78`
The OutputBackend contract carries mappings/names via BackendContext, but each real backend ALSO needs an index-aligned parallel specs […] *Fix:* Two independent fixes, narrower than proposed. (a) Replace the per-tick instanceof (manager.ts:69) with an optional `setInputVector?(vec: ReadonlyArray<number>): void` on OutputBackend, implemented only by VcvBackend — this alone removes […]
**S14. MEMLNaut Editor (vision bullet 5) is a 237-line shell: sound connect lifecycle, zero protocol, and a third unshared Web Serial implementation** (manifold-backends-inputs, downgraded, week+) — `manifold/src/serial/memlnaut-serial.ts:102`, `manifold/src/serial/EditorPanel.tsx:72`
memlnaut-serial.ts is honestly labelled STUB: saveModel returns false, restoreModel returns null, getSettings returns {} (lines 102-133); […] *Fix:* When bullet 5 is scheduled: (1) define the wire FRAMING as a C header under firmware/ following the useq-celium discipline (header single source of truth → TS mirror → parity test asserting frame sizes/offsets) — this part of the original […]
**S15. Dead focus/altitude system: SplitStage, ReadoutStrip, InputMini, AltitudeNav, CompactAxis are unreachable** (manifold-ui, confirmed, hours) — `manifold/src/console/ConsoleApp.tsx:105`, `manifold/src/App.tsx:78`
ConsoleApp.tsx:105 `const [focus, setFocus] = useState<Focus>(initialFocus)` — setFocus is never called anywhere in the repo (grep […] *Fix:* As proposed (delete SplitStage.tsx, ReadoutStrip.tsx, InputMini.tsx, AltitudeNav, CompactAxis, the UI Focus type/prop, the focus branches + stripPinned + vacuous keyboard gates in ConsoleApp, and the barrel exports; keep MiniMeters), with […]
**S16. Decorative/fake controls: training-param sliders, master volume, bpm, A/B toggle, snapshots and health/gradient visuals drive nothing** (manifold-ui, downgraded, day) — `manifold/src/console/ConsoleApp.tsx:113`, `manifold/src/console/Drawers.tsx:242`
Verified by grep: learningRate/decay/tame/spreadLevel (ConsoleApp.tsx:126,149-151) are read ONLY by their own Drawers.tsx:242-255 sliders — […] *Fix:* Deletion-first, with three corrections. (1) Delete the A/B machinery (ab/aRef/setHoldingA/toggleAB, the VerdictCluster props, and the '+ A/B toggle' header comment), the fake seed, seededGradient + rev/weightsRevision, the unconsumed […]
**S17. VCV backend UI is unreachable: no OutputMode maps to BackendId 'vcv'** (manifold-ui, confirmed, hours) — `manifold/src/console/output-mode.ts:40`, `manifold/src/console/types.ts:27`
OutputMode type (types.ts:27) is 'particles'|'midi'|'osc'|'cv'|'synth'|'editor' and OUTPUT_MODES (output-mode.ts:40-83) maps these to […] *Fix:* Decide which: add `{ id: 'vcv', backend: 'vcv' }` to OUTPUT_MODES + OutputMode + MODE_ICON (three small edits) to make the path live, or delete VcvConfig/VcvChannelEditor/forwardVcvFeedback/vcv ctx state. The first is likely intended given […]
**S18. Per-backend config editors duplicated in two files and BOTH rendered in the same expanded drawer** (manifold-ui, confirmed, hours) — `manifold/src/console/Drawers.tsx:646`, `manifold/src/console/Drawers.tsx:677`
BackendAdvanced.tsx:8-9 self-describes as 'the full-depth duplicate of the inline config in OutputsBackendConfig; both write the same […] *Fix:* Delete BackendAdvanced.tsx and the Drawers.tsx:674-679 block (keep SynthGroupNote inline in Drawers if its one paragraph is wanted). OutputsBackendConfig already covers every backend.
**S19. ConsoleCtx god-object: ~25 of its fields have zero consumers outside ConsoleApp** (manifold-ui, downgraded, day) — `manifold/src/console/types.ts:56`, `manifold/src/console/ConsoleApp.tsx:789`
Grep across console/, dock/, serial/ (excluding ConsoleApp/types): ctx.modes, setModeId, axes, setAxis, preset, setPreset, offsetActive, […] *Fix:* Prune ConsoleCtx to the 57 fields Dock/Drawers/OutputsBackendConfig actually read (plus mode). Delete outright: the Axes type + axes/setAxis state, the preset/setPreset/offsetActive chain (permanently 'Sculpt'/false), and the dead ctx […]
**S20. Dead legacy feedback modes: RandomiseOutputs, RandomiseMlp, AvoidStyle::Diffuse, on_drag have no product consumer** (ml-core, downgraded, day) — `nisps/ml/feedback.hpp:59-73`, `nisps/ml/feedback.hpp:258-271`
The only product callers of the feedback controller select two behaviours: firmware sets only FeedbackMode::ExploreAndPlace […] *Fix:* Treat as a spec-governed decision, not dead-code removal. First surface to the operator: deleting AvoidStyle::Diffuse reverses the explicit "keep for A/B comparison" decision in docs/adr/rl-feedback-design.md, and deleting […]
**S21. perf.hpp SRAM-section macro regime is mostly dead; the one real NISPS_AUDIO_FUNC use is shape-mismatched; docs claim discipline that doesn't exist** (ml-core, confirmed, hours) — `nisps/core/perf.hpp:26-35`, `firmware/MEMLNaut-NISPS/glue/midi_io.hpp:72`
Repo-wide grep: NISPS_AUDIO_MEM and NISPS_APP_SRAM are used ZERO times outside perf.hpp and comments (the .ino uses memllib's own AUDIO_MEM […] *Fix:* Delete NISPS_AUDIO_MEM/NISPS_APP_SRAM/NISPS_AUDIO_FUNC from perf.hpp; change midi_io.hpp:72 to plain 'inline void ...' (or Pico's __not_in_flash_func(name) applied correctly); rewrite MAP.md:151/164 and drop ALIGNMENT #4. If SRAM placement […]
**S22. All vcv/ user-facing docs describe the pre-P6 nisps-core-era module (2×12, v1 3D weights, dead paths)** (satellites-hygiene, confirmed, hours) — `vcv/README.md:3`, `vcv/README.md:31`
README.md:3 says '2 inputs … 12 outputs' (module is 8×16 per MEMLNaut.cpp:16-17); README.md:31+265 and BUILDING.md:29+73 reference […] *Fix:* Rewrite README.md's numbers/paths (small diff), delete the playground-sharing sections, and either rewrite NISPS-FORMAT.md against the v3 flat layout or delete it and point at docs/specs/vcv-module.md (which the /specs corpus already owns) […]
**S23. Retired-playground build output and its root E2E harness still tracked on main** (satellites-hygiene, confirmed, hours) — `playground/dist/index.html`, `playground/dist/nisps.wasm`
git ls-files shows playground/dist/* (1.2 MB: bundled JS+sourcemaps, a second nisps.wasm and nisps.js) tracked, though the playground app […] *Fix:* git rm -r playground/ tests/e2e/ package.json package-lock.json playwright.config.js; delete root node_modules and the playground dir from disk. The archive branch already preserves the app; nothing else names these paths.
**S24. CI never runs codegen — the 'schema changes require regenerated outputs in the same change' gate is unenforced** (schemas-codegen, confirmed, hours) — `.github/workflows/ci.yml:24`, `scripts/run-all-tests.sh:51`
ci.yml's two jobs (cpp-tests, manifold-tests) contain no codegen step at all. The golden test runs only in local run-all-tests.sh stage 5, […] *Fix:* Add a CI step (in the manifold-tests job, where bun is already set up, before typecheck): `cd codegen && bun install --frozen-lockfile && bun run generate.ts && bun run generate-midi-devices.ts && cd .. && git add -N nisps/modes/generated […]
**S25. MLP net dimensions are a dual source of truth: hand-written template args in every C++ mode vs schema dims used by the browser** (schemas-codegen, confirmed, hours) — `nisps/modes/paf_synth.hpp:31`, `nisps/modes/memlcelium.hpp:28`
All 9 modes hand-write dims, e.g. paf_synth.hpp:31 `ml::MLP<4u, 10u, 10u, 14u, 33u>` duplicating schemas/modes/paf_synth.json ml {4, […] *Fix:* Use the generated constexpr constants as the template args directly — they are valid constant expressions today: ml::MLP<generated::kPafSynthMLConfig.input_size, generated::kPafSynthHiddenLayers[0], generated::kPafSynthHiddenLayers[1], […]
**S26. Large fraction of the schema surface has zero consumers — the single-source-of-truth claim is ~half freight** (schemas-codegen, downgraded, day) `schemas/schema.json:45`, `codegen/generate.ts:195`
Verified by repo-wide grep excluding generated dirs: (a) ml.input_channels names read nowhere (input_router.hpp:4 admits it may 'grow […] *Fix:* Keep the per-field wire-or-delete structure, with three corrections. (1) default_learning_rate/default_max_iterations: wire BOTH platforms in one change or delete from both if wiring, firmware glue passes […]
**S27. MAIN.md (corpus entry point) carries at least six claims the code or its own table contradicts** (specs-docs, confirmed, hours) `docs/specs/MAIN.md:42`, `docs/specs/MAIN.md:67`
(1) :67 'TS schema emission is dormant since P1; it returns at P5' P5 landed 2026-07-18; codegen/generate.ts:8-12 writes […] *Fix:* Keep the original editing pass over docs/specs/MAIN.md (fix §1.2 superset claim per ALIGNMENT #1 same eight nisps engines on both targets, C15 archived, mic input unwired; fix §2 to state TS emission is live at […]
**S28. manifold-parity-features-spec.md is part-executed, part-pending, but still labelled 'no implementation authorised' with ground-truth references deleted from main** (specs-docs, confirmed, day) `docs/specs/manifold-parity-features-spec.md:9`, `docs/specs/manifold-parity-features-spec.md:20`
Front matter: aspirational; body :9-11 'Status: draft awaiting review... no implementation is authorised'. But its §0 principle 1 (bind […] *Fix:* Reclassify as kind: plan; mark §on Jolt/OU as executed-by-other-means (one-core P3, core bindings its own preferred route); repoint playground references to the archive branch/tag; keep presets/pins/control-surface sections as the active […]
**S29. Root Playwright rig is dead: tests a page that no longer exists, invoked by nothing** (tests-build, confirmed, hours) `playwright.config.js:13-22`, `package.json:1-13`
playwright.config.js webServer serves cwd './playground' and testDir './tests/e2e'; tests/e2e/helpers.js:19 does […] *Fix:* Delete playwright.config.js, package.json, package-lock.json, and tests/e2e/ at the repo root; rm -rf root node_modules/, playwright-report/, test-results/ (already gitignored). No consumer exists: CI, run-all-tests.sh, MAP.md's entry […]
**S30. lint-cpp.sh heap audit has proven false-negative modes and never covers nisps/pipeline/** (tests-build, confirmed, hours) `scripts/lint-cpp.sh:121`, `scripts/lint-cpp.sh:122`
(1) Line 121 hardcodes subdirs dsp/engines/ml/modes nisps/pipeline/ (the P4 control-rate hot path) and nisps/core/ are never scanned for […] *Fix:* In lint-cpp.sh: add "$NISPS_DIR/pipeline" and "$NISPS_DIR/core" to the subdirs array; replace the `grep -v ' *//'` post-filter with comment-stripping before matching (same perl strip already used by audit_float_suffix at lines 78-98); […]
**S31. CI re-implements run-all-tests.sh and has drifted: codegen golden test not gated in CI** (tests-build, confirmed, hours) `.github/workflows/ci.yml:24-131`, `scripts/run-all-tests.sh:3`
run-all-tests.sh:3 says 'Designed to be the single command CI invokes', but ci.yml never calls it it re-lists the stages as discrete […] *Fix:* Add one step to the manifold-tests job (bun is already installed): `working-directory: codegen; run: bun install --frozen-lockfile && bun run tests/golden_test.ts`. Fix the false 'single command CI invokes' header comment in […]
**S32. Committed manifold/public/nisps.{js,wasm} has no freshness gate — all browser tests validate a potentially stale artifact** (tests-build, downgraded, day) `.github/workflows/ci.yml:88-122`, `manifold/tests/wasm-load.ts:48-49`
manifold/public/nisps.{js,wasm} are tracked build artifacts, last rebuilt/committed at 1672fe3 (P4). The manifold-tests CI job […] *Fix:* Replace the byte-diff with a toolchain-agnostic behavioral freshness gate: in the cpp-tests job, BEFORE the 'Build WASM' step (i.e. while manifold/public/ still holds the committed artifact), run the existing wasm parity harness against it […]
**S33. Dead API entries threaded through the full 5-layer registration chain** (wasm-bridge, downgraded, day) `nisps/wasm/bindings.cpp:643`, `nisps/wasm/bindings.cpp:868`
Grepped all of manifold/src, manifold/tests, e2e, and tests/cpp/parity_wasm.mjs (the WASM parity driver I extracted its full list of […] *Fix:* Delete the dead entries at every layer as proposed, with two corrections. (1) EXPORTED_RUNTIME must remain ["HEAP32","HEAPU8","HEAPF32","cwrap"] tests/cpp/parity_wasm.mjs (parity-check.sh gate) and manifold/tests/wasm-load.ts […]
**S34. publishWeights_ channel: full weight-vector copy at up to 200 Hz into a field nothing reads** (wasm-bridge, confirmed, hours) `manifold/src/engine/wasm-iml.ts:1093`, `manifold/src/engine/wasm-iml.ts:929`
publishWeights_() (wasm-iml.ts:1093-1096) runs getWeights() a Cheap copy plus a fresh Float32Array allocation then sink.setWeights, […] *Fix:* Delete EngineSink.setWeights, Spine.setWeights/weights()/liveWeights, and all publishWeights_ call sites (keep getWeights for persistence and the debug probe). If a weight-health view ever materialises, it should pull getLayerStats on […]
**S35. Dual example store with mismatched caps: train() and trainAsync() diverge past 100 examples; latent OOB read on sample weights** (wasm-bridge, downgraded, hours) `manifold/src/engine/wasm-iml.ts:167`, `manifold/src/engine/dataset.ts:81`
WasmIML keeps a TS Dataset mirror (default cap 100, wasm-iml.ts:167) AND pushes every example into the C++ FIFO ring (DynamicStorage […] *Fix:* Keep the cap-alignment core of the proposal: name the constant once in nisps (e.g. constexpr kDefaultMaxExamples = 128 shared by storage.hpp:55 and dynamic_storage.hpp:43 defaults), expose it through nisps_ml_describe (extend the dims […]
### Local (55)
| ID | Area | Finding | Where | Fix |
|----|------|---------|-------|-----|
| L1 | architecture-gap | Console health UI shows fabricated gradient data while the real layer-stats API sits plumbed […] | `manifold/src/console/model.ts:305` | Delete seededGradient; wire the health readout to engine.getLayerStats() (already reactive via the version counter). Separately land the half-day […] *(verifier: All cited lines verify: seededGradient (model.ts:305-319) fabricates norms/statuses from the rev counter, is […])* |
| L2 | architecture-gap | MAP.md drift: swap points and TS prototypes described as live are already swapped/deleted | `MAP.md:79` | Fix the two MAP.md entries (exploration = thin WASM wrappers, controller = thin driver, drop rng.ts and the phantom files). Delete the dead […] |
| L3 | dsp-engines-modes | nisps/modes/voice_space.hpp is entirely dead code | `nisps/modes/voice_space.hpp:27` | Delete the file. |
| L4 | dsp-engines-modes | Dead oscillators: SineOsc/SawOsc/SquareOsc have no consumers | `nisps/dsp/osc.hpp:28` | Delete the three classes; keep detail::kPi/kTwoPi, PAFOperator, FMOp. *(verifier: SawOsc and SquareOsc are genuinely dead, but SineOsc has a live consumer the reviewer's grep scope missed: […])* |
| L5 | dsp-engines-modes | ModeBase::input_dirty_ is write-only state | `nisps/modes/base.hpp:165` | Delete the flag, or actually gate tick_control's inference on it (measure first; deletion is the safe default). |
| L6 | dsp-engines-modes | VerbFXEngine dead fields and setters: delay_to_verb_ computed 12 times, never read; […] | `nisps/engines/verb_fx.hpp:242` | Delete delay_to_verb_, enable_reverb_, enable_delay_to_reverb_, and the unused set_enable_* setters (keep the ones a named consumer appears for). If […] |
| L7 | dsp-engines-modes | MEMLCeliumEngine feedback path is inert: feedback_ never written in process() | `nisps/engines/memlcelium.hpp:180` | Check the original MEMLCeliumAudioApp: if it writes feedback, port the missing write; if not, delete feedback_, fbzm1_, fb_smooth_alpha_ and the […] |
| L8 | dsp-engines-modes | Sequencer machinery duplicated across engines: ratio_seq, event queue, bar/MIDI-clock phasors, […] | `nisps/engines/breakor.hpp:178` | Extract dsp/ratio_seq.hpp (free function template), a small SeqClock struct (bar phasor + MIDI clock + counter + update_bpm), and EventQueue<E,64> […] |
| L9 | dsp-engines-modes | Dead no-op VoiceSpace boilerplate on five engines | `nisps/engines/breakor.hpp:37` | Delete the VoiceSpace enum/table/setter from the five engines with zero real voice spaces; keep them only on PAFSynth, VerbFX, ChannelStrip. ModeBase […] |
| L10 | dsp-engines-modes | VerbFXEngine recomputes the full 47-param voice-space mapping every audio sample (and its […] | `nisps/engines/verb_fx.hpp:77` | Run smoothing + voice-space mapping at a shared control rate (e.g. every 16-32 samples, same divisor on both targets so native↔WASM parity holds), […] |
| L11 | dsp-engines-modes | ExternalSynthMIDIMode hand-rolls its schema and ML config outside codegen | `nisps/modes/external_synth_midi.hpp:132` | Fold the ext-synth ML config into the schema/codegen pipeline (a shared 'ext_synth' base schema emitting the constants), or at minimum define the […] |
| L12 | firmware | NISPS_ST_ selftest token-paste table silently out of date: SLPWorkshop (the currently active […] | `firmware/MEMLNaut-NISPS/glue/mode_select.hpp:90-106` | Fold into the PlatformIO migration: delete the entire NISPS_ST_ table and the SelfTestRig tag type, and select the selftest build with […] |
| L13 | firmware | midi_io.hpp uses the misshapen NISPS_AUDIO_FUNC macro that MAP.md and ALIGNMENT.md both claim […] | `firmware/MEMLNaut-NISPS/glue/midi_io.hpp:72` | Apply ALIGNMENT #4 option A: make NISPS_AUDIO_FUNC(name) decorate a function NAME on both targets, update the handful of use sites (midi_io.hpp here; […] |
| L14 | firmware | peripherals.hpp: dead declarations, duplicated commit-and-train block, and the documented […] | `firmware/MEMLNaut-NISPS/glue/peripherals.hpp:67` | Delete kAnalogInputCount, PeripheralBindings, and the unused spread local; extract one commit_and_train(mode, feedback) helper used by both branches; […] |
| L15 | firmware | MAP.md documents a vendored firmware/MEMLCelium-upstream/ directory that does not exist on […] | `MAP.md:30-31` | Delete the MAP.md entry (or, if the snapshot lives untracked on another machine and is genuinely wanted, commit it — but name the consumer first; the […] |
| L16 | manifold-backends-inputs | Dead protocol legs from the retired playground: sendState/sendWeights, legacy array format, […] | `manifold/src/backends/osc-client.ts:116` | Delete sendState/sendWeights, the legacy-array branch in both bridges (or the one surviving bridge), […] |
| L17 | manifold-backends-inputs | Status/throttle boilerplate duplicated across all four real backends while inputs/ already has […] | `manifold/src/backends/midi-backend.ts:163` | Extract a small BaseBackend (status plumbing + throttle gate + lastSent management), mirroring BaseSource. Pure deletion-by-refactor, no behaviour […] |
| L18 | manifold-backends-inputs | MIDI input floods React with per-CC-message state updates and snapshot allocations | `manifold/src/inputs/midi-input-source.ts:224` | Only call notifyBindings on binding-LIST changes (learn capture, clearBinding, clearAllBindings); if the drawer wants live values, read them on the […] |
| L19 | manifold-backends-inputs | BackendManager.setActive silently drops a switch requested while another is in flight | `manifold/src/backends/manager.ts:131` | Store the latest requested id when switching is true and re-run setActive with it in the finally block (3-4 lines). |
| L20 | manifold-ui | Solo-mode selector offers three behaviours but soloMode is behaviourally inert | `manifold/src/console/Drawers.tsx:142` | Collapse to a single non-selectable label until train_masked lands in the C API, or disable the two unimplemented options with a 'pending core […] |
| L21 | manifold-ui | FeedbackController vestiges: unused seed/undoDepth options, never-called engine-interface […] | `manifold/src/feedback/controller.ts:57` | Delete seed/undoDepth options and maxUndo; trim ControllerEngine to the members actually called; change dislike to (output: Float32Array) and update […] |
| L22 | manifold-ui | 5 of 12 design primitives are dead (Panel, StatusLine, ControlAxis, CurvePlot, Sparkline) | `manifold/src/primitives/index.ts:1` | Delete the five unused primitives (Sparkline/CurvePlot may return with the deferred diagnostics suite — re-add then). Optionally promote […] |
| L23 | manifold-ui | Dual backend catalogues: BACKENDS duplicates OUTPUT_MODES; OutputControl/toOutputControl dead | `manifold/src/dock/output-state.ts:40` | Delete OutputControl/toOutputControl and ModeIconComponent; delete BACKENDS and use modeDesc.label/description in Drawers (or derive BACKENDS from […] |
| L24 | manifold-ui | ConsoleApp global-listener effects run without dependency arrays — re-subscribed on every […] | `manifold/src/console/ConsoleApp.tsx:684` | Hold the verdict handlers in refs (or useEffectEvent) and give both effects [] / [inputs] deps, matching Manifold's existing onMoveRef pattern. |
| L25 | ml-core | 16 KB loss-history buffer baked into every firmware MLP instance with zero product consumers | `nisps/ml/storage.hpp:200` | Decide plumb-or-delete now. Smallest: delete lh_/loss_hist_buf/loss_history()/loss_history_count_ (train already returns final loss) and the […] |
| L26 | ml-core | dislike_multiplier_ is upstream-InterfaceRL bookkeeping that drives nothing | `nisps/ml/feedback.hpp:406-407` | Delete dislike_multiplier_, its update/halving logic, the accessor, and the test line — or, if the upstream behaviour it was meant to drive is […] |
| L27 | ml-core | nisps/core/fixed_buffer.hpp is dead code | `nisps/core/fixed_buffer.hpp:1-77` | Delete fixed_buffer.hpp, test_fixed_buffer.cpp, the CMake/ctest entry, and the MAP.md:8 mention. Trivially revivable from git if a consumer ever […] |
| L28 | ml-core | get_weights() forces a permanent whole-net scratch copy (flat_buf) and double-copies on every […] | `nisps/ml/storage.hpp:199` | Add a copy_weights_to(std::span<float> dst) (and set from span, which already exists) and switch feedback.hpp's take_snapshot/push_undo/nudge to it; […] |
| L29 | ml-core | OutputChain's firmware story is aspirational — pipeline chains have browser-only consumers | `nisps/pipeline/output_chain.hpp:12-13` | Either wire InputChain/OutputChain into the firmware control tick (peripherals/ModeBase) as the named vision-5 requirement, or soften the header […] |
| L30 | satellites-hygiene | ALIGNMENT.md declares the VCV module dead while vcv/ is actively maintained | `ALIGNMENT.md:72` | Delete the ALIGNMENT.md:72 entry (the module now DOES consume nisps/ directly — the entry's own 'if revived' condition is met and closed). While […] |
| L31 | satellites-hygiene | MAP.md documents firmware/MEMLCelium-upstream/ which does not exist on disk or in any git ref | `MAP.md:30` | Delete the MAP.md:30 entry (the port provenance is already captured in ALIGNMENT.md's geometric-dislike entry and nisps/ml file headers; upstream […] |
| L32 | satellites-hygiene | Agent-worktree fragment committed to main (.claude/worktrees/agent-ae87fe47/…) | `.claude/worktrees/agent-ae87fe47/playground/js/shapeseq […]` | git rm -r .claude/worktrees/ (the ignore rule then holds for the future). Also consider pruning the 30+ stale worktree-agent-*/worktree-wf_* local […] |
| L33 | satellites-hygiene | vcv/test/ is dead: smoke test includes a nonexistent header, tests the retired 2×12 shape, and […] | `vcv/test/smoke_test.cpp:6` | git rm -r vcv/test/ and vcv/Makefile.dist (update BUILDING.md's two references). In osc_server.hpp, delete the unreachable reply-to-sender branch and […] |
| L34 | satellites-hygiene | Data race: audio thread reads imlShadow's example vectors while the worker may be mutating them | `vcv/src/MEMLNaut.cpp:514` | Have the worker deep-copy features/labels into pending staging buffers (alongside pendingWeights, e.g. under stagingMutex) BEFORE setting […] |
| L35 | satellites-hygiene | Full-state JSON serialize/parse runs on Rack's audio thread | `vcv/src/MEMLNaut.cpp:474` | Move JSON build/parse to the existing worker thread: stage the raw string in/out with the same atomic-flag pattern already used for oscStagedJson, […] |
| L36 | satellites-hygiene | Dead root planning docs and dead data/ directory from retired eras | `NISPS_CORE_EXTRACTION_PLAN.md` | git rm both NISPS_CORE_* files (history preserves them; docs/specs/plans/ is the sanctioned home for plans) and git rm -r data/. If the CSVs matter […] |
| L37 | schemas-codegen | modeEngineId switch in manifold hand-duplicates schema engine_id and silently drops new modes […] | `manifold/src/console/model.ts:324` | Delete the switch; route on mode.engineId with the single named exception (sound_analysis_midi → 'analysis', or add an optional analysis-engine […] |
| L38 | schemas-codegen | Two divergent TS applyCurve implementations for the per-output curve knob | `manifold/src/console/model.ts:278` | Pick one formula (mapping.ts's, which is spec-anchored and has a sane 0.5=linear midpoint), delete the other, and re-export the survivor; update […] |
| L39 | schemas-codegen | seed-midi-devices.ts silently clobbers the canonical hand-tunable device schemas it created | `codegen/seed-midi-devices.ts:11` | Delete the script (its purpose is served; it lives in git history and synth-midi-cc.json keeps provenance), or make it skip existing files. Drop the […] |
| L40 | schemas-codegen | Mode schema content is partially placeholder: 20 params across paf_synth/channel_strip/xiasri […] | `schemas/modes/paf_synth.json:16` | Accept as dated debt but record it (ALIGNMENT.md entry): name the placeholder slots during the first preset-curation pass per mode, or shrink […] |
| L41 | specs-docs | MAP.md documents a phantom vendored firmware (firmware/MEMLCelium-upstream/) that exists […] | `MAP.md:30` | Delete the MAP.md:30 bullet (or, if the snapshot exists untracked on another machine and is genuinely wanted, commit it — but the smallest fix is […] |
| L42 | specs-docs | aimmersive-clone-spec.md specs a retired product (SolidJS clone of a-immersive) and still […] | `docs/specs/aimmersive-clone-spec.md:7` | Move to docs/specs/_archive/ with deprecated-by front matter (or reclassify kind: finding, dated, noting it describes the deployed vanilla app); […] |
| L43 | specs-docs | feedback-modes-port-spec.md is an executed port plan masquerading as an evolving spec, with […] | `docs/specs/feedback-modes-port-spec.md:6` | Archive it (kind: plan, status: executed, deprecated-by: docs/adr/rl-feedback-design.md + code). The ADR + feedback.hpp are the surviving truth; […] |
| L44 | specs-docs | BUILD-PLAN.md still status:active though executed, with stale locked-decision facts | `docs/specs/plans/BUILD-PLAN.md:3` | Flip to status: executed, add a two-line 'what survives' note (non-snap-node gotcha → MAP), update the MAIN registry row. |
| L45 | specs-docs | engine-architecture.md's 'One Engine, Two Skins' framing is superseded but the file carries no […] | `docs/specs/engine-architecture.md:7` | Add a supersession header pointing at one-core-engine-refactor §0, retitle/trim to what remains authoritative: the browser-side EngineApi seam […] |
| L46 | specs-docs | vcv-module.md is a three-era palimpsest whose 'retained for reference' sections state stale […] | `docs/specs/vcv-module.md:169` | Prune pass: delete the 2→12-era architecture/prerequisite/Phase-0 text (git keeps it), keep the current 8→16 contract + threading/persistence […] |
| L47 | specs-docs | ALIGNMENT.md staleness: dead VCV deferred-debt entry, mitigation pointing at a deleted file, […] | `ALIGNMENT.md:72` | Delete :72; repoint #5's source reference to upstream MusicallyEmbodiedML memlp (or the archive branch); prune the 2026-04-29 resolved entries. |
| L48 | specs-docs | 818 lines of dead nisps-core extraction relics at repo root | `NISPS_CORE_EXTRACTION_PLAN.md` | git rm both files (history retains them; the executed outcome is already narrated in ALIGNMENT's resolved log). |
| L49 | specs-docs | MAP.md and AGENT-REFERENCE.md still carry pre-P5 'modes slice the default 126 outputs / TS […] | `MAP.md:155` | Update the three sentences to past-tense schema-real reality (or delete the convention bullet — MAP's manifold section already states it correctly). |
| L50 | specs-docs | backends/dock/inputs spec trio grounds itself in a deleted world (playground paths, a-app.js […] | `docs/specs/inputs-spec.md:14` | One pass across the three files: mark the audit/grounding sections as historical ('as of 2026-06-27, pre-refactor'), fix the handful of dead-path […] |
| L51 | tests-build | Parity runner headers document 4 stages / blob v1 while the code implements 7 stages / v5 | `tests/cpp/parity_check.cpp:14-45` | Rewrite both file headers' payload/stage lists to match the v5 blob (or replace the duplicated prose lists with a one-line pointer to the stage […] |
| L52 | tests-build | parity-check.sh FAIL branch is unreachable under set -e | `scripts/parity-check.sh:18` | Wrap: `if node "$TESTS_DIR/parity_diff.mjs" ...; then echo PASS; else status=$?; echo FAIL; exit $status; fi` — or delete the dead branch and let the […] |
| L53 | wasm-bridge | fetch + indirect-eval glue loading duplicated in two files; build could emit an ES module […] | `manifold/src/engine/wasm-iml.ts:56` | Add `-sEXPORT_ES6=1` (and `-sENVIRONMENT=web,worker` stays) to build-wasm.sh, replace both eval blocks with a single shared `await import(/* […] |
| L54 | wasm-bridge | Worklet WASM loader carries dead scaffolding and a silent-stub failure trap | `manifold/src/engine/worklet/nisps-processor.ts:104` | Delete the unused `imports` object, the 'c' branch, `exMap`, and the duplicate second loop. Replace the generic `() => 0` stub with a throwing stub […] |
| L55 | wasm-bridge | Retired playground's dist/ build artifacts still tracked in git | `playground/dist` | git rm -r playground/dist and remove the leftover untracked directory; add playground/ to .gitignore or delete the folder entirely. |
### Style (15)
| ID | Area | Finding | Where | Fix |
|----|------|---------|-------|-----|
| ST1 | dsp-engines-modes | Engine header comments describe implementations that don't exist (doc drift cluster) | `nisps/engines/breakor.hpp:4` | Rewrite the four comment blocks to describe the actual code; drop stream-plan tense. |
| ST2 | firmware | input_router.hpp is a zero-logic speculative layer with one consumer | `firmware/MEMLNaut-NISPS/glue/input_router.hpp:18-21` | Delete input_router.hpp; have the .ino call bind_peripherals directly (or rename bind_peripherals to wire_inputs inside peripherals.hpp). Drop the […] |
| ST3 | manifold-backends-inputs | Stale comments/docs: 'inputs-spec.md is absent', 'fixed WASM head = 2', 'Editor → BackendId' | `manifold/src/inputs/types.ts:10` | Delete/correct the three claims; one small comment pass. |
| ST4 | manifold-ui | GROUP_COLOR maps duplicated in four files and keyed on obsolete JSX-era group names | `manifold/src/console/shared-ui.tsx:83` | One shared map in model.ts (or a hash-to-palette function over arbitrary group strings, which would actually work for schema groups); delete the […] |
| ST5 | manifold-ui | Minor prop/doc lint: duplicate onReroll/onRandomise, unused CompositeStage mode prop, probe […] | `manifold/src/console/ConsoleApp.tsx:1092` | Collapse to one onReroll prop; drop the mode prop; rewrite the probe header to describe the actual surface. |
| ST6 | ml-core | feedback.hpp header comment asserts the retracted 'geometric push NOT ported' claim, […] | `nisps/ml/feedback.hpp:8-12` | Rewrite the header's mode summary to match the four current modes and the Geometric default (fold into the dead-modes deletion commit). |
| ST7 | ml-core | MAP.md and math.hpp still describe the pre-P3/P4 world (interim TS jolt/OU, playground curve […] | `MAP.md:79-81` | Update MAP.md:79-81 to describe exploration.ts as a thin timer-driver over the WASM core, and replace math.hpp's playground-mirror paragraph with the […] |
| ST8 | satellites-hygiene | License story is self-contradictory: MPL-2.0 repo, 'proprietary' VCV plugin manifest | `LICENSE:1` | Set plugin.json license to MPL-2.0 (or GPL-3.0-or-later per VCV convention) and drop the 'in spirit' hedge in iml.hpp — one licence, stated once. |
| ST9 | schemas-codegen | schema.json's own description states the direction of truth backwards | `schemas/schema.json:5` | Rewrite the description sentence: schemas are the canonical contract; codegen emits C++ and TS; provenance notes live in params_notes.md. |
| ST10 | schemas-codegen | codegen/README.md is wholesale stale (playground paths, phantom temp-dir, resolved stream-1 […] | `codegen/README.md:6` | Rewrite codegen/README.md against current reality (or shrink it to a pointer at MAP.md §codegen); drop port-solidjs from ci.yml triggers. |
| ST11 | schemas-codegen | codegen/templates/ are dead 'reference' files already drifting from the real emitters | `codegen/templates/cpp_schema.hpp.template:4` | Delete codegen/templates/ and the README line pointing at it. |
| ST12 | schemas-codegen | Duplicated helper block across the two codegen scripts, justified by a false rationale comment | `codegen/generate-midi-devices.ts:17` | Extract a tiny codegen/lib.ts with the shared helpers and correct the comment (keep both drivers). Or just fix the comment and accept the 40 lines. |
| ST13 | schemas-codegen | synth-midi-cc.json lives at repo root; its only machine consumer is the seed script | `synth-midi-cc.json:1` | Move to schemas/midi_devices/synth-midi-cc.sources.json (one-line path update in the seed script — moot if that script is deleted per the clobber […] |
| ST14 | tests-build | test_helpers.hpp documents NISPS_EXPECT as non-fatal but it throws; NISPS_ASSERT is a plain […] | `tests/cpp/test_helpers.hpp:10-11` | Smallest fix: correct the comment to say both are fatal and delete the NISPS_ASSERT alias (or keep the alias, one line). Only implement true […] |
| ST15 | wasm-bridge | Doc/comment drift lagging the P1P4 refactors | `MAP.md:79` | One doc-sync pass: fix MAP.md:79, rewrite the nisps/wasm/README paragraph and either complete or delete its API table (point at bindings.cpp as the […] |