From f57bf4fb6ddde74369a9cc88f318ba2c43cc3af9 Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Sun, 2 Aug 2026 06:39:31 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20fix=20audit=20citations=20=E2=80=94=20b?= =?UTF-8?q?ad=20line=20ranges,=20misattributed=20evidence,=20overclaimed?= =?UTF-8?q?=20severity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Corrects a false unguarded-include claim, re-scopes hot-path evidence to what's actually linked, replaces a nonexistent Fork-A collision scenario, fixes several miscounted TUs/libraries, re-grades two Fork-B items from Blocker to Major/non-blocker, and adds missing owners/done-conditions. --- .../lambda-w1-t1-build-toolchain.md | 245 +++++++++++++----- 1 file changed, 181 insertions(+), 64 deletions(-) diff --git a/docs/product/audit-notes/lambda-w1-t1-build-toolchain.md b/docs/product/audit-notes/lambda-w1-t1-build-toolchain.md index 9977d4b..481689c 100644 --- a/docs/product/audit-notes/lambda-w1-t1-build-toolchain.md +++ b/docs/product/audit-notes/lambda-w1-t1-build-toolchain.md @@ -13,12 +13,14 @@ and nowhere else. **Verification posture.** I am on Windows and cannot build or run anything on Linux. Every claim below is grounded in a file I opened in this worktree — the tree's own CMake, or a -vendored header/source under `vendor/` — and every line number was re-read immediately before -being written down. Where a claim depends on runtime behaviour I cannot observe (does REAPER's -Linux build call `SWELL_dllMain`? does GCC accept this TU?), it is marked `[verify — Linux]` -with the exact check, and it is **not** stated as fact. To get the vendored citations I ran -`git submodule update --init` for all three submodules inside this worktree; nothing in the -tracked tree was modified. +vendored header/source under `vendor/` — not asserted from memory of similar SDKs. Where a +claim depends on runtime behaviour I cannot observe (does REAPER's Linux build call +`SWELL_dllMain`? does GCC accept this TU?), it is marked `[verify — Linux]` with the exact +check, and it is **not** stated as fact. A subsequent citation pass caught several +line-number and attribution errors in this draft (corrected in place, disclosed at the point +each occurs) — grounded-in-a-file is a floor, not a guarantee against a mis-copied line range. +To get the vendored citations I ran `git submodule update --init` for all three submodules +inside this worktree; nothing in the tracked tree was modified. Severity vocabulary, per the dispatch: **Blocker** = the Linux build cannot produce a loadable artifact; **Major** = it builds but is degraded or unshippable; **Minor** = ergonomics, @@ -30,8 +32,13 @@ names the reason for the band rather than asserting a number. ## 1. Baseline — what the build does today, per platform **Two loadable modules, 84 pure libraries declared under `src/core/`, 91 CTest targets, -three vendored submodules.** (84 = the `reasampler_pure_library` + bare `add_library` count -across the 19 `src/core/**` CMake files; 46 of them are on the extension's link line.) +three vendored submodules.** (84 = the `reasampler_pure_library` + bare `add_library` count, +grep-verified library-by-library across the 18 `src/core/**` CMake files that declare any — +`src/core/CMakeLists.txt` and `src/core/instrument/CMakeLists.txt` declare none, only +`add_subdirectory` calls (plus one test in the latter); there are 20 CMake files under +`src/core/**` total, matching the 19 figure in §1d below, which counts *test* declarations +across the whole tree, not `src/core/**` alone. 46 of the 84 are on the extension's link +line.) - Root `CMakeLists.txt` is 95 lines and declares **no targets at all**: it owns the version string (`:24`), the channel fork (`:40–54`), the `configure_file` for @@ -55,8 +62,8 @@ across the 19 `src/core/**` CMake files; 46 of them are on the extension's link ### 1a. The REAPER extension (`reaper_reasampler`) -Declared as a `MODULE` library over 39 shell TUs plus `${LICE_SRC}` -(`src/app/CMakeLists.txt:8–51`), linking 46 pure static libraries (`:52`) with +Declared as a `MODULE` library over 39 listed TUs (38 shell TUs + `app/main.cpp` itself) +plus `${LICE_SRC}` (`src/app/CMakeLists.txt:8–51`), linking 46 pure static libraries (`:52`) with `${SDK_INC} ${WDL_INC}` on the include path (`:56`). Output naming is `PREFIX ""` + channel-derived `OUTPUT_NAME`, pinned to the top of the build tree by `LIBRARY_OUTPUT_DIRECTORY`/`ARCHIVE_OUTPUT_DIRECTORY` (`:65–69`). @@ -73,6 +80,13 @@ So on Linux the build system today produces `reaper_reasampler.so` with the SWEL compiled in and **no dialog resource of any kind**. `README.md:67–68` states this is "expected, not a bug" — the tree is honest about it. +**Open question, macOS-only, unresolved by this doc.** The APPLE branch adds +`${SWELL}/swell-modstub.mm` (`:79`), an Objective-C++ TU, but `CMakeLists.txt:26` is +`project(reaper_reasampler VERSION 1.4.0 LANGUAGES CXX)` — OBJCXX is never enabled anywhere +in the tree. Whether CMake resolves a `.mm` source against a plain CXX-only project (some +Apple-toolchain configurations infer Objective-C++ from the extension regardless; others do +not) is unverified from this tree and untestable without a macOS box. `[verify — macOS]`. + Three build-input facts I verified in `vendor/` and that hold in the extension's favour: - `vendor/WDL/WDL/swell/swell-modstub-generic.cpp` is entirely inside @@ -91,9 +105,16 @@ Three build-input facts I verified in `vendor/` and that hold in the extension's `SWELL_PushClipRegion`/`SWELL_SetClipRegion` (`:1027–1028`) and `SWELL_PopClipRegion` (`:1048`). All six are entries in SWELL's API table (`vendor/WDL/WDL/swell/swell-functions.h:857`, `:863`, `:877`, `:885–887`), so the - modstub resolves them from the host. No fifth LICE TU is implied: the `_LICE_RGB2HSV` + modstub resolves them from the host. No fifth LICE TU is implied: the `_LICE_RGB2HSV_invtab` table lives inside `lice.cpp` itself (`:3040`), and the one non-Win32-only helper `utf8makechar` is a file-static defined at `lice_textnew.cpp:26` and used at `:241–242`. + This is a link-success inference, not a from-first-principles proof — the actual proof is + V1, a successful build. One loose end: SWELL's own `Makefile:106–107` lists + `lice_colorspace.o` among the LICE objects *its* build needs — a fifth TU we don't compile. + It stays unreconciled but harmless: `lice_colorspace.cpp` defines the real `LICE_RGB2HSV` + function (declared `lice.h:577`, distinct from the `_invtab` table above), and grepping our + four LICE_SRC TUs and all of `src/` for a call to it returns nothing — SWELL needs the TU + for its own generic-GDK backend, which our four-TU slice never invokes into. - `CMAKE_POSITION_INDEPENDENT_CODE ON` (`CMakeLists.txt:30`) is already set. This is the single most commonly missed setting for a `MODULE` target fed by `STATIC` libraries on Linux, and it is correct today. @@ -109,7 +130,8 @@ non-goal: `src/core/instrument/CLAUDE.md` (Non-goals/guardrails) and `src/shell/instrument/CLAUDE.md` (same). `src/shell/panel/CLAUDE.md` carries a third instance ("Windows-only (D5) — font/GDI/HFONT choices assume Windows"). -Inside the gate: a 19-source `vst3_sdk` static library (`:15–39`) pinned to SDK tag +Inside the gate: a 20-source `vst3_sdk` static library (`:16–38`, grep-counted; lines 28/29/37 +are comments) pinned to SDK tag `v3.7.9_build_61` (confirmed by `git describe --tags` in `vendor/vst3sdk`), and the `reasampler_vst` MODULE (`:44–81`) which additionally compiles `dllmain.cpp` and `moduleinit.cpp` directly (`:78–79`, with the linker-stripping rationale at `:75–77`) plus @@ -152,8 +174,11 @@ surface**; see §3 for the one generator-shape caveat. - `:364–366` — "Install is copy-in + restart… there is no server-side rollout." What that document does **not** contain: any mention of code signing, macOS notarization, a -CI runner, or a Linux install path. I grepped it for `sign|notariz|CI|platform|linux` — the -only hits are the lines above. There is no CI configuration anywhere in the repo (`ls -a` on +CI runner, or a Linux install path. I grepped it for `sign|notariz|CI|platform|linux` — one +hit is not cited above (`:393`, "artifacts (stable + beta) per platform", the same dev-ops +handoff as `:307–310`); the rest are the lines above. The substantive conclusion — no +signing/notarization/CI/Linux-install-path mention anywhere — holds regardless. There is no +CI configuration anywhere in the repo (`ls -a` on the root shows `.git`, `.gitignore`, `.gitmodules`, `CLAUDE.md`, `CMakeLists.txt`, `LICENSE`, `README.md`, `cmake`, `docs`, `src`, `tests`, `vendor` — no `.github`, no `.gitlab-ci.yml`), and no `install()` rule in any of the 23 CMake files. So "the pipeline" is entirely notional @@ -211,7 +236,15 @@ Blocker only because the binary loads and the actions work. **The route worth considering first.** `swell-functions.h:606–608` documents an escape hatch: *"If you are using CreateDialog() and creating a child window, you can use a resource ID of 0, which creates an opaque child window. Instead of passing a DLGPROC, you should pass -a (WNDPROC) routine that returns LRESULT (and cast it to DLGPROC)."* `src/resource.rc:18–22` +a (WNDPROC) routine that returns LRESULT (and cast it to DLGPROC)."* The header comment is +not just documentation — the implementation confirms both halves of it: +`swell-dlg-generic.cpp:291–292` is `SWELL_DialogResourceIndex *p=resById(reshead,resid); if +(!p&&resid) return 0;`, and `resById` (`:34–43`) walks from a (here, null) `reshead` and +returns 0 on a miss. That confirms the graceful-failure claim behind "Major rather than +Blocker" above (a null `reshead` plus a non-zero `resid` returns 0, not a crash — matching +`panel_window.cpp:137`'s `if (!g_panel.hwnd) return;`), **and** that `resid == 0` skips the +lookup entirely (`&&resid` short-circuits), which is the escape hatch this paragraph is about. +`src/resource.rc:18–22` shows `IDD_BANK_PANEL` is precisely that: a `WS_CHILD` dialog with an empty body and zero controls, whose comment at `:8–10` says "the bank_panel shell owns every pixel and draws the sample grid with LICE in `WM_PAINT`". If the panel is created with resource id 0 on @@ -243,14 +276,26 @@ optimize-and-ship incantation, and `README.md:130–131` tells the user to copy is accepted and ignored. Because no `CMAKE_BUILD_TYPE` default is set, the resulting build passes **no `-O` flag at all** to GCC/Clang — not `-O0` explicitly, just the compiler's unoptimized default. The failure is silent in both directions: the command succeeds, and the -artifact lands at `build/reaper_reasampler.so` rather than the documented `build/Release/` -path (see Λ-08). +artifact lands at `build/reaper_reasampler.so` — a path the docs never state for a +single-config generator (a gap, not an error; see Λ-08). -This matters more here than the usual "forgot to set the build type", because `CLAUDE.md`'s -own performance guardrails section states that the header-inline hot paths (`peaks` -envelope compute, `Voice::advanceFrame`, the three envelope evaluators) "presume an -**optimizing** build", and there is no LTO configured to recover any of it. A Linux user -following the README would ship a binary whose hottest loops did not inline. +This matters more here than the usual "forgot to set the build type" — but only for `peaks`. +`CLAUDE.md`'s §"Build and test" (not §"Performance guardrails") states, at `CLAUDE.md:54`, +that the header-inline hot paths "presume an **optimizing** build", with no LTO configured to +recover it if they don't. `peaks`' `computeEnvelope`/`lastFrameAboveThreshold` is one of +those paths and IS linked into the extension (`src/app/CMakeLists.txt:52`), so the finding +holds for Fork A as stated. It does **not** extend to `Voice::advanceFrame` or the three +`envelopes.h` evaluators (named at `src/core/instrument/CLAUDE.md:342`, not in root +`CLAUDE.md`) — those belong to `sampler_core`, which `src/app/CMakeLists.txt:53–55` states is +deliberately NOT linked into the extension and which only enters a build at all inside the +Windows-gated `reasampler_vst` target (§1b). On Fork A that code never compiles into +`reaper_reasampler` in the first place; the unoptimized-build risk to it is a Fork B concern, +not this extension-only one. A Linux user following the README's `--config Release` would +ship an extension binary whose `peaks` hot path did not inline. + +**Done when:** `cmake --build build` on Linux (Ninja/Make, no `--config`) produces a build +whose compile commands (`compile_commands.json` or verbose build log) show an explicit `-O` +flag, and the README/CLAUDE.md ship instruction names the single-config-generator path. **Sequencing note.** This is not strictly Linux-specific — Ninja on Windows has the same shape — but it becomes load-bearing the moment Linux is a shipped platform, because Makefiles @@ -272,11 +317,19 @@ modules — the rationale for the duplication is at `CMakeLists.txt:75–77`); `-fvisibility=default` and every non-static symbol in both modules — all of LICE, all the WDL helpers, every pure library's exported functions, and the whole C++ standard-library instantiation set — lands in the module's dynamic symbol table. On Windows this is a -non-issue: each DLL has its own symbol table and per-module resolution. On Linux ELF, symbols -from `dlopen`'d objects can participate in global interposition, so `reaper_reasampler.so`'s -`LICE_FillRect` and a hypothetical `reasampler_9000.so`'s `LICE_FillRect` are candidates to -bind to the same definition. That is a class of bug that presents as "the VST editor draws -with the extension's LICE build" and is invisible on Windows. +non-issue: each DLL has its own symbol table and per-module resolution. `[verify — Linux]` +on ELF, symbols from `dlopen`'d objects CAN participate in global interposition — but whether +they actually do here depends on REAPER's own `dlopen` flags (`RTLD_GLOBAL` vs `RTLD_LOCAL`) +and whether REAPER's executable is linked `--export-dynamic`, neither of which this tree can +answer. The scenario this doc originally named — a hypothetical `reasampler_9000.so` — does +not exist on Fork A: per Λ-10 and `src/shell/instrument/CMakeLists.txt:9`, the VST3 target is +gated `WIN32 AND EXISTS ...` and is never built on Linux unless D1 selects Fork B. The +Fork-A-real risk is different: any **second REAPER extension** loaded in the same REAPER +process (a third-party `.so` in `UserPlugins/`) that links its own copy of LICE/WDL and also +defaults to `-fvisibility=default` is a candidate to interpose with `reaper_reasampler.so`'s +`LICE_FillRect` and the rest of the exported LICE/WDL symbol set. That is a class of bug that +presents as "one extension's LICE draws with another extension's LICE build" and is invisible +on Windows, where each DLL keeps its own symbol table. **Why hiding is safe here.** The two symbols that genuinely must be exported already say so explicitly: `reaper_plugin.h:52` defines @@ -288,6 +341,11 @@ thing to confirm on a Linux box is that `swell-modstub-generic.cpp:135`'s `SWELL survives — it carries its own `__attribute__ ((visibility ("default")))` at that line, so it should, but the modstub is compiled by *our* build, not SWELL's. +*Shared macOS/Linux — the visibility default and the LICE-TU duplication (`CMakeLists.txt:75–77`) +apply the same way under the APPLE branch's Mach-O two-level namespace, though the concrete +Fork-A-real interposition mechanism above is ELF-specific; macOS's own dynamic-linker symbol +resolution is a separate `[verify]` this doc does not attempt.* + --- ### Λ-04 — nothing links a thread library, and `` is in the extension's include graph @@ -300,10 +358,14 @@ should, but the modstub is compiled by *our* build, not SWELL's. **What breaks.** No CMake file in the tree calls `find_package(Threads)` or links `Threads::Threads`/`-lpthread`/`-ldl` (grep-verified across all 23). Two separate exposures: -- **Extension.** `panel_state.h:23` includes `` on non-Win32. I did not find a - pthread *call* in the panel's own code, so this may be an include-only dependency that - needs no link flag on a modern glibc (where the pthread symbols are in libc proper). That - makes it Minor, and `[verify — Linux]`. +- **Extension.** `panel_state.h:23` includes `` on non-Win32 — and it is not the + only entry point: `vendor/reaper-sdk/sdk/reaper_plugin.h:50` includes `` + unconditionally in its non-Windows branch, so it is in **every** shell TU's include graph + (every `.cpp` that includes `reaper_plugin.h`/`reaper_plugin_functions.h`), not just the + one panel file. This strengthens rather than changes the finding: I still did not find a + pthread *call* in `src/shell/`'s own code, so this may be an include-only dependency that + needs no link flag on a modern glibc (where the pthread symbols are in libc proper). Stays + Minor, and `[verify — Linux]`. - **VST3 target.** `flock.cpp` is unambiguous: on non-Windows it calls `pthread_mutexattr_init`/`_settype`/`_destroy` (`:71–76`), `pthread_mutex_init` (`:74`), `pthread_mutex_destroy` (`:89`), `_lock` (`:105`), `_unlock` (`:121`) and `_trylock` @@ -314,6 +376,13 @@ should, but the modstub is compiled by *our* build, not SWELL's. Adding `find_package(Threads REQUIRED)` + `target_link_libraries(... Threads::Threads)` is correct on all three platforms and costs nothing on Windows. +**Done when:** the extension links with `Threads::Threads` removed (proving the include-only +half needs no flag, or naming the symbol that forces it — see V5), and, if Fork B is taken, +`reasampler_vst` links clean with `Threads::Threads` present. + +*Shared macOS/Linux — `reaper_plugin.h:50`'s non-Windows `` include and +`flock.cpp`'s non-Windows pthread calls apply identically to the APPLE branch.* + --- ### Λ-05 — `reaper_plugin.h`'s SWELL include resolves only by coincidence of the `-I` line @@ -345,7 +414,7 @@ fail only on Linux/macOS. Worth pinning before more targets are added. **Citations.** `src/app/CMakeLists.txt:8–51` and `:71–98`; `src/shell/actions/drag_out_win.cpp:8`, `:249`, `:282`. -**Mechanism.** All 39 shell TUs are listed unconditionally (`:8–51`); the platform block at +**Mechanism.** All 38 shell TUs (plus `app/main.cpp`, 39 listed TUs total) are listed unconditionally (`:8–51`); the platform block at `:71–98` only *adds* sources, never removes or substitutes any. Every TU therefore enters a Linux compile, and the only thing standing between the build and a compile error is each file's own `#ifdef` discipline. Today that discipline is uneven in a way worth recording: @@ -374,28 +443,40 @@ extension's. GCC 8 requires an explicit `-lstdc++fs` for `std::filesystem`; GCC libstdc++ ≥ 9 do not. `README.md:51` states only "A C++17 compiler" as the prerequisite, and `cmake_minimum_required(VERSION 3.19)` (`CMakeLists.txt:1`) says nothing about the compiler. On any current distro toolchain this is a non-issue; it becomes one on an older CI image. -Twelve other files in `src/shell/` also use `` (grep), but they are all in the -extension target, which shares the same fix. +Twelve other files in `src/shell/` also use `` (grep-verified), but one — +`src/shell/instrument/instrument_bake.cpp` — is in the `reasampler_vst` target, not the +extension: eleven in `reaper_reasampler`, sharing the fix below; the twelfth shares it only +if/when Fork B builds that target on Linux. + +**Done when:** `capture_paths_tests` links on the oldest toolchain the phase targets without +`-lstdc++fs` added explicitly, or that flag is added and documented as the floor. + +*Shared macOS/Linux — `capture_paths` and the eleven extension-side `` users +compile identically under the APPLE branch; whether Apple's libc++ needs the same kind of +minimum-toolchain floor as libstdc++ does is a separate `[verify]` this doc has not checked, +but the coverage-hole shape (undocumented compiler floor) is identical either way.* --- -### Λ-08 — the artifact path and the install instruction are both wrong for a single-config generator +### Λ-08 — the install instruction is silent about single-config generators, and no path is documented for Linux at all **Severity: Minor. Effort: S — a doc fix, or an `install()` rule if packaging is ever automated.** **Citations.** `src/app/CMakeLists.txt:68` (`LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}"`); -`README.md:130–133`; `CLAUDE.md` §"Install / reload"; +`README.md:130–131`; `CLAUDE.md` §"Install / reload"; `docs/product/versioning-and-release.md:364–366`. -**What breaks.** On a multi-config generator CMake appends the config directory, giving the -documented `build/Release/`. On Ninja/Make the module lands directly at -`build/reaper_reasampler.so`, so the README's "copy from `build/Release/`" instruction has no -matching directory on Linux. Compounding it, there is no `install()` rule anywhere in the 23 -CMake files, and no target directory is documented for Linux at all — `CLAUDE.md` and -`README.md:132` say "REAPER's `UserPlugins/` folder (Options → Show REAPER resource path)", -which is a correct user-facing instruction but not a path a pipeline can use. I could not -confirm REAPER's Linux resource-path root from the SDK: grepping -`vendor/reaper-sdk/sdk/` for `UserPlugins` and `reaper_*.so` returns nothing — the -`reaper_*.dll|dylib|so` filename convention stated in `CLAUDE.md` is not documented in the +**What breaks — a gap, not an error.** `README.md:130–131` and `CLAUDE.md` §"Install / +reload" both already carry the multi-config qualifier verbatim ("`build/Release/` on a +multi-config generator — not the default `Debug/` output") — so the instruction is not +wrong, it is simply silent about the other case. On a multi-config generator CMake appends +the config directory, giving the documented `build/Release/`. On Ninja/Make the module lands +directly at `build/reaper_reasampler.so`, and neither doc names that path. Compounding it, +there is no `install()` rule anywhere in the 23 CMake files, and no target directory is +documented for Linux at all — `CLAUDE.md` and `README.md:132` say "REAPER's `UserPlugins/` +folder (Options → Show REAPER resource path)", which is a correct user-facing instruction but +not a path a pipeline can use. I could not confirm REAPER's Linux resource-path root from the +SDK: grepping `vendor/reaper-sdk/sdk/` for `UserPlugins` and `reaper_*.so` returns nothing — +the `reaper_*.dll|dylib|so` filename convention stated in `CLAUDE.md` is not documented in the vendored headers. `[verify — Linux]`. --- @@ -420,6 +501,28 @@ GCC/Clang compile as `-std=gnu++17` rather than `-std=c++17`. Not a bug; a hygie worth flipping while the flags are being set, since it is the difference between "portable C++17" and "whatever GNU extensions happened to be used". +**Coverage hole: the link-succeeds-but-load-fails asymmetry is never stated.** GNU `ld` does +not error on undefined symbols in a shared object by default — MSVC's linker does. For a +`MODULE` REAPER `dlopen()`s, that means a Linux build can link `reaper_reasampler.so` clean +and only discover a missing symbol when REAPER tries to load it (`dlopen` failing, or a +partially-resolved symbol table if a satisfying-by-coincidence symbol exists elsewhere in the +process — a real risk given Λ-03's global-interposition finding above). Neither `Threads` +(Λ-04) nor visibility (Λ-03) named this asymmetry, and it undercuts §5's framing of V1 +(`cmake --build build`) as "the single highest-information action available" — a clean build +on Linux does not carry the same guarantee a clean Windows build does. `-Wl,--no-undefined` +(GNU ld) would restore the MSVC-equivalent fail-at-link-time behavior; whether it is needed +here specifically depends on whether `SWELL_PROVIDED_BY_APP`'s function-pointer design (§1a) +already accounts for every symbol the modstub doesn't itself define — that is `[verify — +Linux]`, since it requires an actual GCC/Clang link to observe. + +**Done when:** the Linux build either adds `-Wl,--no-undefined` (or the equivalent) so an +undefined-symbol build failure surfaces at link time, or a stated reason is recorded for why +`SWELL_PROVIDED_BY_APP` makes the gap moot. + +*Shared macOS/Linux — `-Wall -Wextra`, `CMAKE_CXX_EXTENSIONS OFF`, and the GNU-ld-vs-MSVC-ld +asymmetry (macOS's linker also does not default to erroring on undefined symbols) apply +identically to the APPLE branch.* + --- ### Λ-10 — the submodule story costs a Linux developer a large download for a target that is gated off @@ -470,8 +573,10 @@ pass. build` works bare on Linux; the `-C Debug` in the README's example is harmless there. - **SWELL API coverage for the LICE slice** — all six SWELL GDI entry points LICE needs are in the modstub-resolved table (`swell-functions.h:857`, `:863`, `:877`, `:885–887`). -- **The LICE slice needs no fifth TU on Linux** (`lice.cpp:3040` colorspace table, - `lice_textnew.cpp:26` file-static `utf8makechar`). +- **The LICE slice needs no fifth TU on Linux** (`lice.cpp:3040` `_LICE_RGB2HSV_invtab` + table, `lice_textnew.cpp:26` file-static `utf8makechar`) — an inference from V1's link + success plus these two spot checks, not exhaustive; see §1a's fuller caveat, including the + unreconciled `lice_colorspace.o` in SWELL's own `Makefile:106–107`. - **`swell-modstub-generic.cpp` compiles against its own includes** — it uses `printf` (`:61`), which arrives via `swell.h` → `swell-types.h:29` (`#include `). The `dlopen`/`readlink` block (`:69–131`) is behind `SWELL_LOAD_SWELL_DYLIB`, which this build @@ -506,13 +611,16 @@ one-line-ish but load-bearing (Λ-02 optimization, Λ-03 visibility), one Major/ change to any product decision on record. **The unknown that dominates the band:** none of the extension's source portability is -audited here. The build-system work is small; whether the 39 shell TUs *compile and behave* +audited here. The build-system work is small; whether the 38 shell TUs *compile and behave* against SWELL's Win32 subset is T2's answer, and it, not this list, sets the phase's size. ### Fork B — extension + ReaSampler 9000 on Linux -Everything in Fork A, plus the following. Each item is a Blocker for the VST3 artifact — -without it there is no loadable `.vst3`. +Everything in Fork A, plus the following. Three of five are Blockers under the severity +vocabulary defined above ("the Linux build cannot produce a loadable artifact"): B1, B2, B3. +B4 downgrades to Major — the module still loads; only the editor fails to attach without it. +B5 is not a technical blocker at all — it is a documentation act, though one that must +precede the others in sequence (see §6 Stage 5). - **B1 — the entry point is the wrong one, and the right one is already vendored.** `src/shell/instrument/CMakeLists.txt:78` compiles @@ -522,11 +630,14 @@ without it there is no loadable `.vst3`. `#if defined(_MSC_VER) && defined(DEVELOPMENT)` (`:43`, `:96`) and `#ifdef __cplusplus` (`:58`, `:87`). Linux needs `public.sdk/source/main/linuxmain.cpp`, which is present in the slice and exports - `ModuleEntry`/`ModuleExit` (`linuxmain.cpp:47–52`, `:59–82`). Both are **mandatory** — the + `ModuleEntry`/`ModuleExit` (`extern "C"` declarations `linuxmain.cpp:47–52`; definitions + `:59–67` and `:72–82` respectively). Both are **mandatory** — the SDK's own loader refuses the module without either (`public.sdk/source/vst/hosting/module_linux.cpp:189–204`, error strings "The shared library does not export the required 'ModuleEntry' function" and the same for `ModuleExit`). - `GetPluginFactory` is checked at `:205`. **Effort S.** + `GetPluginFactory` is checked at `:205`. **Effort S** — a `CMakeLists.txt` source swap + (`dllmain.cpp` → `linuxmain.cpp`) plus the platform `if()`; both files are already vendored, + nothing to write from scratch. - **B2 — the artifact is a directory bundle, not a file.** Today the target sets `SUFFIX ".vst3"` on a MODULE (`src/shell/instrument/CMakeLists.txt:106–111`), producing a @@ -551,12 +662,13 @@ without it there is no loadable `.vst3`. reads `/proc/self/exe`, strips to the directory, and `dlopen`s `libSwell.so` beside the host binary (`:81–98`), then calls `SWELL_set_app_main(SWELLAppMain)` (`:112–114`). That reference makes `SWELLAppMain` a **required definition in the plugin** — it is declared at - `swell-types.h:137` as "to be implemented by app". Whether REAPER's Linux install ships a + `swell-types.h:137` as "to be implemented by app (if using swellappmain.mm)". Whether + REAPER's Linux install ships a `libSwell.so` beside its executable is `[verify — Linux]`, and this route makes the VST3 load-bearing on being hosted by REAPER specifically, which is consistent with D5's REAPER-only stance but is a new runtime coupling to state explicitly. - **Route B3b — build SWELL into the module.** `vendor/WDL/WDL/swell/Makefile:100–104` - enumerates the 13 SWELL TUs (`swell.o swell-ini.o swell-miscdlg-generic.o + enumerates the 14 SWELL TUs (`swell.o swell-ini.o swell-miscdlg-generic.o swell-wnd-generic.o swell-menu-generic.o swell-kb-generic.o swell-dlg-generic.o swell-gdi-generic.o swell-misc-generic.o swell-gdi-lice.o swell-generic-headless.o swell-generic-gdk.o swell-appstub-generic.o swell-modstub-generic.o`) and `:106–107` the 8 @@ -577,7 +689,11 @@ without it there is no loadable `.vst3`. defined as on other platforms." The whole `namespace Linux` block is `#if SMTG_OS_LINUX` (`:209`, closed `:284`), so it compiles in automatically. The **build-side** cost is nil (the IIDs are already vendored — see §3); the **source-side** cost is `editor_platform.cpp` - and every timer in the editor, which is T2's surface. **Effort: build S, source L.** + and every timer in the editor, which is T2's surface. **Effort: build S, source L** — build + is S because no new CMake source is needed (the SDK block compiles in automatically); source + is L because it is a genuine window/run-loop rewrite (an X11 embed contract with no Win32 + analogue, not a mechanical port) and outside this doc's own portability scope to size + further. - **B5 — a settled product decision has to be reversed, in writing.** D5 ("Windows-only, VST3-only, REAPER-only") is recorded as a locked decision with "a mac/Linux build" named @@ -585,7 +701,9 @@ without it there is no loadable `.vst3`. and again under Non-goals/guardrails) and `src/shell/instrument/CLAUDE.md` (Non-goals/guardrails). `src/shell/panel/CLAUDE.md` carries a third dependent statement. Fork B is not just more work — it invalidates three recorded invariants and needs Daniel's - explicit reversal before any code moves. + explicit reversal before any code moves. **Effort S** — no code or CMake change of its own, + a rewrite of the three `CLAUDE.md` passages naming D5 plus Daniel's sign-off; the L-sized + cost sits in B3/B4, not here. **The asymmetry worth naming.** Fork A's build work is small and its unknown is source portability. Fork B's build work is genuinely large (B2, B3) *and* carries an unbounded @@ -601,7 +719,7 @@ are different-shaped commitments. | # | Question | Exact check | |---|---|---| -| V1 | Does the extension compile at all under GCC/Clang? | `cmake -B build -S . -G Ninja && cmake --build build 2>&1 \| tee build.log` — this is the single highest-information action available and gates most of T2's list too. | +| V1 | Does the extension compile at all under GCC/Clang? | `cmake -B build -S . -G Ninja && cmake --build build 2>&1 \| tee build.log` — the single highest-information action available and gates most of T2's list too, with one caveat (Λ-09): a clean link is not the same guarantee here as on MSVC, since GNU `ld` does not error on undefined symbols in a `MODULE` by default — V1 answers "does it compile and link", not "does it load". | | V2 | Does `ctest` pass all 91 targets? | `cmake -B build -S . -G Ninja && cmake --build build && ctest --test-dir build --output-on-failure` (no `-C` needed on Ninja). | | V3 | Does REAPER's Linux build call `SWELL_dllMain` in a `reaper_*.so`, populating the API table? | Drop the built `.so` in `~/.config/REAPER/UserPlugins/`, restart, and check the extension's actions appear. A null table shows as immediate crash or dead actions. | | V4 | Where exactly is `UserPlugins/` on Linux, and is `reaper_*.so` the right filename glob? | Options → Show REAPER resource path in a Linux REAPER. The SDK headers do not document either — grep of `vendor/reaper-sdk/sdk/` for `UserPlugins` and `reaper_*.so` returns nothing. | @@ -610,7 +728,7 @@ are different-shaped commitments. | V7 | With Λ-01 fixed by the resource-id-0 route, does `SWELL_CreateDialog` return a usable child HWND for the docker? | Panel toggle action in a Linux REAPER. | | V8 | Does `std::filesystem` need `-lstdc++fs` on the chosen toolchain? | Build `capture_paths_tests` alone; the link error names it. | | V9 | Does REAPER's Linux install ship a `libSwell.so` beside its executable (Fork B, route B3a)? | `ls $(dirname $(readlink -f $(which reaper)))/libSwell.so`. | -| V10 | Which `uname -m` values must the VST3 bundle carry (Fork B, B2)? | `uname -m` on each target — REAPER ships x86_64, aarch64 and armv7l Linux builds, and `module_linux.cpp:158–163` keys the bundle directory off exactly this string. | +| V10 | Which `uname -m` values must the VST3 bundle carry (Fork B, B2)? | `uname -m` on each target. `module_linux.cpp:158–163` keys the bundle directory off exactly this string (tree-grounded and verified); which architectures REAPER itself ships for Linux (x86_64, aarch64, armv7l is the claim) is an external fact with no source in this tree — `[verify]` against REAPER's own download page before relying on it. | ### `[Daniel]` — genuine product/scope calls @@ -618,9 +736,10 @@ are different-shaped commitments. |---|---|---| | D1 | **Fork A or Fork B?** Extension only, or extension + ReaSampler 9000 on Linux? | §4 states both costs. The choice is scope, and D5 makes it a reversal of a recorded decision, not a technical lookup. | | D2 | If Fork B: **route B3a (dlopen the host's `libSwell.so`, requires defining `SWELLAppMain`) or B3b (vendor and build full SWELL, adds GDK/GTK3 + FreeType + Fontconfig + OpenGL)?** | B3b is a dependency-surface expansion; the dispatch requires that be flagged rather than chosen. B3a is cheaper but couples the plugin's UI to being hosted by REAPER specifically. | -| D3 | Is **macOS in or out** of the same phase? | Λ-01 is a *shared* macOS/Linux fix (both branches carry the identical broken comment block, `src/app/CMakeLists.txt:83–86` vs `:94–97`), and `swell_resgen.php` output is shared per `README.md:120`. Doing both at once is cheaper than doing them serially — but that is a scope call, and macOS additionally implies signing/notarization, which `versioning-and-release.md` does not mention anywhere. | +| D3 | Is **macOS in or out** of the same phase? | Λ-01, Λ-03, Λ-04, Λ-07 and Λ-09 are all marked *shared macOS/Linux* at their own sections above (Λ-01: identical broken comment block, `src/app/CMakeLists.txt:83–86` vs `:94–97`, and shared `swell_resgen.php` output per `README.md:120`; Λ-03/04/07/09: same mechanism under the APPLE branch, detailed at each). Doing both at once is cheaper than doing them serially — but that is a scope call, and macOS additionally implies signing/notarization (not mentioned anywhere in `versioning-and-release.md`) plus its own open question this doc does not resolve: whether `swell-modstub.mm` (`src/app/CMakeLists.txt:79`) even compiles under a CXX-only `project()` (`CMakeLists.txt:26`, no `OBJCXX`) — see §1a. | | D4 | Does **CI get built in this phase**, and on what runner? | `versioning-and-release.md:307–311` and `:361–363` hand "the pipeline" to dev-ops and commit to three platform artifacts per channel, but no CI exists in the repo on any platform. Adding it is infrastructure and needs an explicit ask. | -| D5 | Is the Linux artifact **shipped or developer-only** for 1.x? | Determines whether Λ-02 (Release build) and Λ-08 (install path) are must-fix or nice-to-have, and whether a Linux `install()` rule is worth authoring. | +| D6 | Is the Linux artifact **shipped or developer-only** for 1.x? | Determines whether Λ-02 (Release build) and Λ-08 (install path) are must-fix or nice-to-have, and whether a Linux `install()` rule is worth authoring. (Numbered D6, not D5, to avoid colliding with the settled product decision D5 — "Windows-only, VST3-only, REAPER-only" — cited above at §1b and B5.) | +| D7 | Λ-01's fork: **resource-id-0 escape hatch, or the resgen route?** §6 Stage 2 gates on T2's read of `panel_window.cpp`, and V7 presupposes the route is already picked. | It is a source-code + build-pipeline tradeoff (a `panel_window.cpp` DLGPROC→WNDPROC change vs. a PHP build step + generated-file handling) with no code-only right answer — needs Daniel's call once T2 reports, same as D1/D2. | Deliberately **not** listed as `[Daniel]`, because reading more would settle them: whether `arrange_drop_win.cpp`/`instrument_drop_win.cpp` need platform gating (read the files — T2); @@ -655,7 +774,7 @@ non-Windows sibling, the source list in `src/app/CMakeLists.txt:8–51` gains it partition. Strictly after T2 and after Stage 2 (the resgen/shim TU, if that route is taken, is one of the entries). -**Stage 4 — packaging.** Λ-08, plus whatever D5 decides. Depends on Stage 1 (there is no +**Stage 4 — packaging.** Λ-08, plus whatever D6 decides. Depends on Stage 1 (there is no point documenting an install path for an unoptimized artifact) and on V4. **Stage 5 — Fork B, only if D1 says so.** Internally ordered: B1 (entry point swap, S) → @@ -692,5 +811,3 @@ parallel track owns each of these and I have deliberately not investigated furth 7. `src/core/capture/capture_paths.cpp` — `normalizeSlashes` lowercases on `_WIN32` only, and `tests/test_capture_paths.cpp:19–46` already encodes the case-sensitive POSIX contract. The test is written; the behaviour under a case-sensitive filesystem is unverified. -8. `src/shell/instrument/reasampler_editor.h:30` — unguarded `#include `. Only - matters under Fork B.