From 7a30da91a8b83f8ee6a4448987865923c6f69561 Mon Sep 17 00:00:00 2001 From: monkey-w1n5t0n Date: Tue, 21 Jul 2026 14:12:55 +0200 Subject: [PATCH] =?UTF-8?q?docs(recon):=20memllib=20usage=20inventory=20?= =?UTF-8?q?=E2=80=94=20the=20=C2=A77.5=20prerequisite,=20with=20a=20wrinkl?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plan §5 names an inventory of the load-bearing memllib surface as the prerequisite for the vendor-vs-fork-pin decision. Built it from the firmware LINKER MAP rather than grep — the same method that would have caught daisysp (S8) mechanically instead of by reading. Result: all 24 compiled translation units contribute kept sections to the image; no discarded-only TU exists. memllib is not a daisysp situation. "The load-bearing subset" is ~1.8 MB across 84 files — essentially all of memllib minus examples/. There is no small subset to lift; synth/ alone is 1.3 MB and all three of its TUs link. The wrinkle, which was not visible when §7.5 was decided: src/memllib is not a third-party dependency, it is the LAB's shared library (MusicallyEmbodiedML/memllib), and our fork is 3 commits ahead but 31 commits BEHIND — +2034/-153 across exactly the files we link, including "new staticmlp", "jolts", "noise", "verb". Vendoring as-is freezes that gap permanently. Recommendation recorded: rebase the three small NISPS commits onto upstream main FIRST, then vendor. Same self-contained end state, but it does not silently discard work that sounds directly relevant, and it is the only moment that merge is cheap — after vendoring there is no upstream history to merge against. Flagged for an operator call before the PlatformIO cut, which this gates. --- docs/specs/plans/simplification-plan.md | 10 ++- docs/specs/recon/memllib-usage-inventory.md | 84 +++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 docs/specs/recon/memllib-usage-inventory.md diff --git a/docs/specs/plans/simplification-plan.md b/docs/specs/plans/simplification-plan.md index bf9e5b8..2062f4a 100644 --- a/docs/specs/plans/simplification-plan.md +++ b/docs/specs/plans/simplification-plan.md @@ -100,7 +100,15 @@ DESCRIPTIVE — it documents that engine voice spaces already square the value i One cut, no dual path. `firmware/useq-celium/` already proves the PIO + arduino-pico pattern in-repo. - `platformio.ini` with one `[env]` per firmware variant passing `-DMEMLNAUT_MODE_TYPE=`; selftest becomes a plain `-DNISPS_SELFTEST=1` env. Deletes: the sed/python machinery in `firmware-common.sh` that **mutates the committed .ino**, the `.ino` comment-registry, the entire `NISPS_ST_*` token-paste table (already silently missing the currently-active SLPWorkshop variant — L12), the sketch-tree symlink forest, and the global TFT_eSPI library mutation (handled via PIO lib config/build flags instead). -- **memllib consumption decision** (§7.5, depends on Phase 0.1): PIO `lib_deps` pin on the fork vs vendoring the actually-used subset. Prerequisite task: inventory which memllib surface (AudioDriver, MEMLNaut board, display/menu, MIDI) is load-bearing. +- **memllib consumption decision** (§7.5): the prerequisite inventory is DONE — + `../recon/memllib-usage-inventory.md`, built from the firmware linker map rather than grep. Result: + all 24 compiled TUs are linked, so "the load-bearing subset" is ~1.8 MB / 84 files — essentially + all of memllib minus `examples/`. There is no small subset to lift. It also surfaced a + consequence that was not visible when §7.5 was decided: memllib is the LAB's shared library and + our fork is 3 commits ahead but **31 behind** (+2034/−153 across the files we link, including + `new staticmlp`, `jolts`, `verb`). Vendoring freezes that gap permanently. Recommended: rebase the + three NISPS commits onto upstream `main` first, THEN vendor — it is the only moment that merge is + cheap. Operator call before the cut. - Then **S9**: a CI job compiling 2–3 representative envs with cached toolchain — firmware enters an automated gate for the first time. ## §6 Phase 5 — Vision-facing architecture (each item spec-first, own session) diff --git a/docs/specs/recon/memllib-usage-inventory.md b/docs/specs/recon/memllib-usage-inventory.md new file mode 100644 index 0000000..faea68d --- /dev/null +++ b/docs/specs/recon/memllib-usage-inventory.md @@ -0,0 +1,84 @@ +--- +kind: finding +date: 2026-07-21 +--- + +# memllib usage inventory — the prerequisite for the §7.5 ownership decision + +_Prerequisite named by `../plans/simplification-plan.md` §5: "inventory which memllib surface +(AudioDriver, MEMLNaut board, display/menu, MIDI) is load-bearing" before choosing between a +fork-pin and vendoring. The operator's §7.5 decision is **vendor the parts we use** ("everything +should be self contained in this repo"). This document is the evidence that decision needs, plus one +consequence that was not visible when it was made._ + +## Method + +Not grep. The firmware was built for real (`arduino-cli`, `rp2040:rp2040:solderparty_rp2350_stamp_xl`, +SLPWorkshop variant) and the **linker map** was read: `MEMLNaut-NISPS.ino.map` distinguishes +translation units that merely compiled from those contributing sections to the final image. This is +the same method that would have caught the daisysp finding (S8) mechanically instead of by reading. + +## Result: the compile surface is 24 TUs, and all 24 are linked + +| subdir | .cpp linked / present | headers | size | +|---|---|---|---| +| `audio/` | 4 / 4 | 12 | 132K | +| `hardware/` | 7 / 7 | 21 | 176K | +| `interface/` | 8 / 8 | 8 | 108K | +| `synth/` | 3 / 3 | 10 | 1.3M | +| `utils/` | 2 / 2 | 9 | 80K | +| **total** | **24 / 24** | **60** | **~1.8M** | + +Every translation unit reachable through the sketch-tree symlinks contributes kept sections to the +image — **no discarded-only TU exists**. memllib is therefore *not* a daisysp situation (41 .cpp +compiled, zero consumers). It is genuinely load-bearing. + +The library has 30 `.cpp` in total; the 6 not counted above live outside the five symlinked subdirs +(e.g. `examples/`) and are never compiled by the firmware build. Those are the only part safe to +drop outright. + +Firmware's direct include surface is just six entry points — `audio/AudioDriver.hpp`, +`hardware/memlnaut/MEMLNaut.hpp`, `hardware/memlnaut/Pins.hpp`, +`hardware/memlnaut/display/{View,SingleSelectView}.hpp`, `interface/MIDIInOut.hpp`, `PicoDefs.hpp`, +`utils/perf.hpp` — but the transitive closure is the whole of those five subdirs. + +**Consequence for vendoring: "the load-bearing subset" is ~1.8 MB / 84 files, i.e. essentially all +of memllib minus `examples/`.** There is no small subset to lift. `synth/` alone is 1.3 MB (mostly +`maximilian.cpp`) and all three of its TUs link. + +## The consequence that was not visible when §7.5 was decided + +`src/memllib` is not a third-party dependency. It is **the lab's shared library** +(`MusicallyEmbodiedML/memllib`), and our fork is: + +- **3 commits ahead** — `bf9691c` (swap memlp includes for nisps/core, inline RL utilities), + `32cc831` (newlib RNG fix), `b37fc53` (seed-helper rename). These are the NISPS-specific changes. +- **31 commits behind** — including `770a990 new staticmlp`, `9fcd459 jolts`, `d0d8a72 noise`, + `616b8e7 cc select`, `a8bf255 verb`, `671abbe screen ordering`, `e291192 l r input swap`. + Diffed against the five subdirs we link: **30 files changed, +2034 / −153**. + +Vendoring freezes that gap permanently and converts "we are behind the lab" into "we have our own +divergent copy". That may be exactly what is wanted — self-containment is a real goal, and the lab's +`main` is not obviously a branch we track — but it is a fork of a colleague-shared library, not a +snapshot of a vendor drop, and the 31 commits contain work (`staticmlp`, `jolts`) that sounds +directly relevant to this project. + +## Options, with what each costs + +1. **Vendor as decided.** Copy the five subdirs into the repo, drop the submodule and the symlink + forest, delete `examples/`. Repo grows ~1.8 MB. Self-contained, no submodule init, PlatformIO + gets a plain `lib/` — this materially simplifies the §5 migration. Cost: the 31 upstream commits + become a manual merge, forever. +2. **Vendor, but first rebase our 3 commits onto upstream `main`.** Same end state, except the + snapshot is current rather than 31 commits stale. Costs one merge now (the three commits are + small and mechanical), and it is the only moment when that merge is cheap. +3. **Fork-pin (status quo + Phase 0's fix).** Already works: the pin is reachable, CI is green, + fresh clones build. Not self-contained, and keeps the submodule friction PlatformIO would rather + not have. + +**Recommendation: option 2.** The operator's goal (self-contained) is satisfied identically by 1 and +2, but 2 does not silently discard `staticmlp`/`jolts`/`verb`. Doing the rebase after vendoring +means doing it against a copy that no longer has upstream history — i.e. never. + +This decision gates the PlatformIO migration (plan §5), which otherwise has to keep the submodule +and its symlink workaround.