Fix audit citations, counts, and the L2-07/L2-09 mechanisms after code-review verification

This commit is contained in:
2026-08-02 06:39:36 -04:00
parent 893455594f
commit b1726b0ad3
@@ -65,10 +65,11 @@ is direct evidence the pure layer was written with a non-Windows target in mind.
### 1.2 `tests/` — portable.
91 test TUs. Only `tests/test_capture_paths.cpp` forks on `_WIN32`, and it asserts the
non-Windows behaviour explicitly (lines 2833, 5051). Six other files matched a
`C:\`/`C:/` grep — every hit is opaque *string data* fed to a pure function
non-Windows behaviour explicitly (lines 2833, 5051). A `C:\`/`C:/` grep over `tests/` hits
six files total, one of which is `test_capture_paths.cpp` itself; the five OTHER files — every
hit opaque *string data* fed to a pure function
(`test_bank_model.cpp:186187,243245`, `test_bake_wire.cpp:77`, `test_drag_out.cpp:291293,388`,
`test_origin_ledger.cpp:56`, `test_sample_usage.cpp:502`), not platform behaviour. They pass
`test_origin_ledger.cpp:56`, `test_sample_usage.cpp:502`) — are not platform behaviour. They pass
identically on Linux.
### 1.3 `app/` — portable.
@@ -85,37 +86,64 @@ type, no conditional. The one hazard is behavioural, not structural — see **L2
`_WIN32|WIN32|__APPLE__|_MSC_VER|__linux__|__GNUC__|_WIN64` over `src/**/*.{cpp,h}`). Their
distribution is the story:
| Area | Guarded blocks | Shape |
| Area | Lines | Shape |
|---|---|---|
| `shell/instrument/` (VST3 editor + embed) | 44 of 65 | Whole-TU `#ifdef _WIN32``#endif` — Windows-only by design |
| `shell/panel/` | 9 | Include-selection + two small `#else` bodies |
| `shell/persist/`, `shell/capture/`, `shell/actions/` | 6 | Trash fallback, `localtime_r`, the SWELL drag-out branch |
| `core/` | 1 | The case-fold |
| `src/resource.h`, `src/ext_keys.h` | comments only | — |
| `shell/instrument/` (VST3 editor + embed) | 51 of 65 | Whole-TU `#ifdef _WIN32``#endif` — Windows-only by design |
| `shell/panel/` | 8 | Include-selection + two small `#else` bodies |
| `shell/persist/`, `shell/capture/`, `shell/actions/` | 4 | Trash fallback, `localtime_r`, the SWELL drag-out branch |
| `core/` | 2 | The case-fold |
Two `shell/` directories are absent from this table because they were swept and came back
completely clean, not because they went unchecked: `shell/view/` and `shell/bank_ops/` both
return **zero** hits on the same grep (confirmed). Naming them here, since the table above is
by hit-count and gives a reader no way to tell a zero-hit sweep from an unswept directory.
`src/resource.h` and `src/ext_keys.h` mention platform names only in comments — zero hits on
this specific grep (re-run and confirmed), so they are not a row of this distribution; see
`resource.h`/`resource.rc` under §7 for their actual (build-system) relevance.
The extension's guards are all *complete*: every `#ifdef _WIN32` that gates a **definition**
has an `#else` (`draw_kit.cpp:12`, `capture.cpp:218`, `panel_audition.cpp:36,58`,
`panel_state.h:20`, `prune_fs.cpp:169`, `drag_out_win.cpp:8`); the ones without an `#else`
gate only an `#include` or a single statement (`panel_window.cpp:19,148`, `prune_fs.cpp:35`,
`draw_kit.h:27`, `capture_paths.cpp:18`). **There is no undefined-symbol gap in the extension.**
`draw_kit.h:27`, `capture_paths.cpp:18`). **There is no undefined-symbol gap from this guard
structure** — every `#ifdef` that needs an `#else` has one. This is narrower than "no
undefined-symbol gap in the extension" full stop: L2-02's `GetCurrentProcessId`
(`instrument_drop_win.cpp:59`) is an undefined symbol on Linux precisely because it carries
**no** `#ifdef` at all, so it falls outside what this guard-completeness check measures.
The instrument's guards are complete in the opposite direction: `reasampler_editor.h:156269`
wraps the entire paint/input/timer/`wndProc`/`invalidate` family in `#ifdef _WIN32`, and
`editor_platform.cpp:298306` stubs only the three IPlugView overrides that must exist. Every
`invalidate()` call site is inside a guard (verified: 7 calls in `editor_session.cpp` at
119, 126, 139, 141, 150, 156, 174 — all within the `#ifdef _WIN32` regions opened at 105 and
173; `editor_controls.cpp`, `editor_models.cpp`, `instrument_bake.cpp` have zero). So the
editor *links* off Windows — it just does nothing.
`invalidate()` call site is inside a guard (verified exhaustively over `src/shell/instrument/`:
53 call sites across eight files — `editor_input.cpp` (5), `editor_input_browse.cpp` (11),
`editor_input_chrome.cpp` (10), `editor_input_curve.cpp` (4), `editor_input_deck.cpp` (9),
`editor_input_waveform.cpp` (4), `editor_platform.cpp` (3, excluding the definition itself at
`:65`), and `editor_session.cpp` (7, at 119, 126, 139, 141, 150, 156, 174, excluding a
same-named mention inside a comment at `:106`) — each file whole-file- or whole-region-guarded
by `#ifdef _WIN32` (e.g. `editor_input_browse.cpp:8``:177`); `reasampler_editor.h:266` is
the declaration, not a call site. `editor_controls.cpp`, `editor_models.cpp`,
`instrument_bake.cpp` have zero call sites, confirmed). So the editor *links* off Windows — it
just does nothing.
### 1.5 What the platform layer actually provides (vendor-verified)
The extension's non-Windows path is SWELL-provided-by-host
(`SWELL_PROVIDED_BY_APP` → function pointers resolved through `SWELL_dllMain`,
`vendor/WDL/WDL/swell/swell-modstub-generic.cpp:135145`). On Linux REAPER's own
`libSwell.so` is built with `swell-gdi-lice.o`, `-DSWELL_LICE_GDI`, `-DSWELL_FREETYPE` and
`-lfontconfig` (`vendor/WDL/WDL/swell/Makefile:100107,141151`) — i.e. the GDI SWELL
answers with is the **LICE-backed, freetype/fontconfig** one, not the null stub in
`swell-gdi-generic.cpp` (that file is `#ifndef SWELL_PROVIDED_BY_APP`, line 24).
`vendor/WDL/WDL/swell/swell-modstub-generic.cpp:135145`). `[verify — Linux]` **What is stated
here about REAPER's own `libSwell.so` is only evidence about WDL's own Makefile, not about what
Cockos actually ships** — `vendor/WDL/WDL/swell/Makefile:100107,141151` shows
`swell-gdi-lice.o`/`-DSWELL_LICE_GDI` built whenever GDK is enabled (`ifndef NOGDK`, `:111`),
with `-DSWELL_FREETYPE` gated behind `ifndef NOFREETYPE` (`:144`) and `-lfontconfig` gated a
level deeper behind `ifndef NOFONTCONFIG` (`:149`) — i.e. the Makefile makes the LICE-backed,
freetype/fontconfig SWELL the *default* build, not a certainty about the binary REAPER
distributes. **Every downstream claim built on "REAPER's Linux SWELL is the fontconfig-backed
GDK build" inherits this same unstated assumption** — that includes L2-09's entire mechanism,
the GDK-backend evidence cited for L2-07, and the cursor (`GDK_*`) / modifier-key
(`GetAsyncKeyState`) claims below, none of which are re-flagged individually; this paragraph is
their one shared source of doubt. Contrast with the null stub in `swell-gdi-generic.cpp`
(`#ifndef SWELL_PROVIDED_BY_APP`, line 24), which is not in play for a REAPER-hosted extension
either way.
Everything the panel layer calls was checked by name against
`vendor/WDL/WDL/swell/swell-functions.h` and `swell-types.h`. Present and real:
@@ -143,11 +171,18 @@ Everything the panel layer calls was checked by name against
(`swell-generic-gdk.cpp:24392441`), so `panel_state.h:409411` works verbatim.
- `GetTickCount` is SWELL-provided (`swell-functions.h:81`), so `panel_drag.cpp:308,324` — the
only unguarded Win32-looking calls in the panel — are fine.
- `SWELL_InitiateDragDropOfFileList` **does exist on Linux** and is a real implementation, not a
stub: `swell-generic-gdk.cpp:35633592` (`gdk_drag_begin` + a nested `SWELL_RunMessageLoop`
until capture drops, with a 500 ms no-motion timeout). The headless backend
(`swell-generic-headless.cpp:246`) and macOS (`swell-dlg.mm:3534`) also define it; it is
declared at `swell-functions.h:1011`. **The doc's claim holds.**
- `SWELL_InitiateDragDropOfFileList` **does exist on Linux**, and is a GDK implementation on
the GDK backend: `swell-generic-gdk.cpp:35633592` sets up a hidden drop-source window and
spins a nested `SWELL_RunMessageLoop` until capture drops, with a 500 ms no-motion timeout;
the actual `gdk_drag_begin` call is inside the shared `dropSourceWndProc` helper it invokes
(`:3446`), not textually inside the `35633592` range itself. The headless backend defines
the **same-named function as an empty no-op stub**
`swell-generic-headless.cpp:246248` is `void SWELL_InitiateDragDropOfFileList(...) { }` — so
the "not a stub" framing only holds for the GDK backend specifically. The two backends are
mutually exclusive (`swell-generic-headless.cpp:28` is `#ifndef SWELL_TARGET_GDK`), so nothing
breaks in a GDK build, but a reader should not take "does exist on Linux" to mean "is
implemented in every Linux SWELL backend." macOS (`swell-dlg.mm:3534`) also defines it; it is
declared at `swell-functions.h:1011`.
- LICE itself is portable: `lice.h:31` includes `swell-types.h` off Windows, `lice.cpp:22`
includes `swell.h`, `LICE_SysBitmap::__resize` has a SWELL framebuffer path
(`lice.cpp:179182`), and `LICE_CachedFont::DrawTextImpl` carries real non-Windows branches
@@ -216,8 +251,20 @@ here says a gap exists — but nothing rules it out either, and the failure mode
opaque.
**Severity: Blocker (conditional)** `[verify — Linux]` — a silent load refusal with no
diagnostic is the worst possible first-run experience for a port.
**Effort: S.**
diagnostic is the worst possible first-run experience for a port. **Scope note:** the
all-or-nothing mechanism itself is identical on Windows too
(`reaper_plugin_functions.h:4546` — "an older version of REAPER may not succeed in loading",
no platform fork in the loader) — nothing about the *mechanism* is Linux-specific, only whether
*this particular* build happens to be missing an entry is unknown. Graded here as a Linux
Blocker on failure-mode quality alone (per the Mechanism above), the same axis L2-04 is
graded on below, despite L2-04's mechanism being certain rather than speculative — see L2-04's
calculus note. Arguably this belongs as a cross-platform robustness note rather than a
Linux-exclusive finding; left here because the fix is trivial and worth doing regardless of
platform.
**Effort: S** — confirmed against the two Direction options below: the `ShowConsoleMsg`
diagnostic is one new line on the existing failure branch (`main.cpp:293`), and the
`REAPERAPI_MINIMAL` swap follows a pattern already used elsewhere in this codebase
(`panel_window.cpp:2631`, `panel_audition.cpp:1216`) rather than inventing a new one.
**Direction.** Either switch `main.cpp` to `REAPERAPI_MINIMAL` + an explicit `WANT` list (the
other TUs already do this — e.g. `panel_window.cpp:2631`, `panel_audition.cpp:1216`), or keep
the full load but print the failure count via `rec->GetFunc("ShowConsoleMsg")` before returning
@@ -226,8 +273,11 @@ the full load but print the failure count via `rec->GetFunc("ShowConsoleMsg")` b
### L2-04 — every persisted floating-point number is `LC_NUMERIC`-dependent, in both directions
**Location (writers):** `src/core/json/json.cpp:3943` (`%.17g` — the bank index, view model and
tracking ledger all serialize through it), `src/core/model/provenance.cpp:40` (`%.17g` — the
provenance blob inside the bank JSON), `src/core/capture/tail_control.cpp:74` (`%.17g` — the
per-project tail setting).
provenance blob inside the bank JSON). Two writers, not three: `tail_control.cpp:74` is a
comment describing the format ("Byte-identical to the former snprintf writer:
`{"mode":%d,"manualMs":%.17g}`"), not a write site — the actual per-project tail-setting write
is `tail_control.cpp:7879`, which already goes through `json::numToStr` (the same
`json.cpp:3943` codec cited above), so it is the same writer, not a third one.
**Location (readers):** `src/core/json/json.cpp:190196` (`std::strtod`),
`src/core/wire/wire.cpp:121130` (`Cursor::fieldDouble`, `std::strtod`),
`src/core/capture/render_settings.cpp:178186` (`std::stod` over REAPER's own `P_RAZOREDITS`).
@@ -236,7 +286,7 @@ per-project tail setting).
comma-decimal locale the writers emit `1,5`, which makes the bank JSON *structurally invalid*
(an extra separator inside an object) — the whole index fails to parse on the next load. The
readers are honestly fail-closed (all three require whole-token consumption:
`json.cpp:194`, `wire.cpp:127`, `render_settings.cpp:183`), so they degrade to "malformed"
`json.cpp:194`, `wire.cpp:127`, `render_settings.cpp:184`), so they degrade to "malformed"
rather than silently truncating — but that means a razor range or a bank field simply
disappears. On Windows the CRT's start-up locale is `"C"` and nothing in this codebase calls
`setlocale`, which is why this has never fired. On Linux the process locale is far more likely
@@ -247,11 +297,19 @@ when built with `SWELL_SUPPORT_GTK` (`swell-generic-gdk.cpp:366`; the `#else` br
process can do the same.
**Severity: Major** — builds and runs; under a non-C `LC_NUMERIC` the bank index is written
unparseable and the project's whole bank is lost on reload.
**Effort: M** — three writers and three readers, all in `core/`, all unit-testable; the fix is
unparseable and the project's whole bank is lost on reload. **Calculus note:** unlike L2-03
(graded Blocker above on zero direct evidence, purely on failure-mode quality), this finding's
mechanism is confirmed by reading the actual writer/reader call sites, and its failure mode —
the entire bank index becomes unparseable — is at least as severe as L2-03's. It is graded only
Major here because it additionally requires a non-C `LC_NUMERIC` in the hosting process, a
likelihood factor L2-03 does not apply to its own claim. Reading both on the same axis (either
both by failure-mode quality, or both by likelihood) would put them closer together than
Blocker/Major suggests; resolving that is a scope call for whoever prioritizes the two, not a
fact this audit can settle statically.
**Effort: M** — two writers and three readers, all in `core/`, all unit-testable; the fix is
a locale-independent path (`std::to_chars`/`std::from_chars`, C++17, or an explicit
`std::locale::classic()`-bound stream), not a `setlocale` call in a plugin.
**Direction.** Make the number codec locale-independent at its three writers and three readers;
**Direction.** Make the number codec locale-independent at its two writers and three readers;
add a pure test that pins the emitted text for a fractional value. Do **not** "fix" this by
calling `setlocale` — an extension must not mutate the host's locale.
`[verify — Linux]` — read `LC_NUMERIC` inside a running REAPER-Linux process (e.g.
@@ -316,25 +374,43 @@ having on Windows too.
instead. Reading the GDK backend: on a URI-list selection-notify, SWELL resolves the top-level
window, walks down with `ChildWindowFromPoint` (which **is** descending — `for(;;)` loop,
`swell-wnd-generic.cpp:69566981`), and `SendMessage(cw, WM_DROPFILES, (WPARAM)gobj, 0)`
(`swell-generic-gdk.cpp:1622`). The default child proc forwards `WM_DROPFILES` up to the parent
only when the window *lacks* `WS_EX_ACCEPTFILES` (`swell-wnd-generic.cpp:76807688`) — i.e. the
opt-in on SWELL is an ex-style bit, not an API call, and the message reaches the window under
the pointer either way. So the panel's own `dlgProc` plausibly receives the drop **without any
opt-in at all**. Two supporting details check out: SWELL's `DragQueryFile` reads the same
`DROPFILES` layout the handler assumes (`swell-wnd-generic.cpp:7730+`), and SWELL's `DragFinish`
is a documented no-op ("caller will free hdrops", `:77257728`) while SWELL itself
`GlobalFree`s the handle right after `SendMessage` (`swell-generic-gdk.cpp:16231624`) — so
`panel_window.cpp:59` calling `DragFinish` is safe, not a double-free.
(`swell-generic-gdk.cpp:1622`). The default child proc forwards an *unhandled* `WM_DROPFILES`
up to the parent only when the window *lacks* `WS_EX_ACCEPTFILES`
(`swell-wnd-generic.cpp:7681`) — but that bit's only effect is to **suppress** the up-forward;
it does not enable acceptance, and the message already reaches the window under the pointer via
the `ChildWindowFromPoint` descent regardless of the bit's state. So the panel's own `dlgProc`
plausibly receives the drop **without any opt-in at all**, but the earlier "ex-style bit, not
an API call" framing was backwards about what the bit does. The real SWELL opt-in surface for
`WS_EX_ACCEPTFILES` is the dialog *resource*, not a runtime call: `swell_resgen.pl:10` and
`swell_resgen.php:204` translate a dialog's `WS_EX_ACCEPTFILES` style to
`SWELL_DLG_WS_DROPTARGET`, and `swell-dlg-generic.cpp:320321` sets
`h->m_exstyle |= WS_EX_ACCEPTFILES` from that flag at dialog-creation time. `src/resource.rc:19`
declares `STYLE WS_CHILD` only — no `WS_EX_ACCEPTFILES` — so the bit is off on Linux regardless
of anything `panel_window.cpp` does at runtime. That makes L2-07's real seam `src/resource.rc` +
resgen, the SAME work as L2-06 and §7 bullet 1, not an independent one-liner: a runtime
`SetWindowLong(..., GWL_EXSTYLE, ... | WS_EX_ACCEPTFILES)` would only suppress the
unhandled-drop forward-to-parent — it would not change whether the drop reaches `dlgProc` in
the first place, which already happens via the `ChildWindowFromPoint` descent independent of
the bit. Note also the doc's own conclusion is better-supported than it knew: `src/resource.rc:2122`
is `BEGIN`/`END` — zero child controls — so the GDK `ChildWindowFromPoint` descent has nowhere
to land but the panel HWND itself. Two supporting details still check out: SWELL's
`DragQueryFile` reads the same `DROPFILES` layout the handler assumes
(`swell-wnd-generic.cpp:7730+`), and SWELL's `DragFinish` is a documented no-op ("caller will
free hdrops", `:77257728`) while SWELL itself `GlobalFree`s the handle right after
`SendMessage` (`swell-generic-gdk.cpp:16231624`) — so `panel_window.cpp:59` calling
`DragFinish` is safe, not a double-free.
**Severity: Major `[verify — Linux]`** — if the routing does *not* reach a docked child dialog,
one of the three ingest surfaces (file drop onto the bank panel) is silently dead with no error.
The evidence above says it probably works; it is not proof.
**Effort: S** — if the verify fails, the fix is
`SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_ACCEPTFILES)`
(both symbols exist: `swell-functions.h:319320`, `swell-types.h`).
**Direction.** Verify first, then either do nothing or add the ex-style set in the `#else`.
`[verify — Linux]` drop a WAV onto the docked panel and check whether `WM_DROPFILES` reaches
`dlgProc`.
The evidence above says it probably works; it is not proof, and it cannot be exercised until
the panel itself renders (T1's resgen wiring — see L2-06).
**Effort:** no independent source-side fix exists — this is the same resgen work already
costed under L2-06 / §7 bullet 1. Do not add an `Effort: S` line here; adding the
`SetWindowLong` call would compile, do nothing observable, and send an implementer looking for
a bug that isn't where they'd look.
**Direction.** No separate fix. Once the resgen work lands the panel dialog with
`WS_EX_ACCEPTFILES` set from the resource, verify: `[verify — Linux]` drop a WAV onto the
docked panel and confirm `WM_DROPFILES` reaches `dlgProc`.
### L2-08 — the VST3 instrument has no Linux editor, and no automatable parameters to fall back to
**Location:** `src/shell/instrument/editor_platform.cpp:3742` (`isPlatformTypeSupported`
@@ -345,8 +421,8 @@ off Windows). `reasampler_processor.cpp:360365` still hands the host a `ReaSa
**Mechanism.** The VST3 SDK *does* support Linux — `kPlatformTypeX11EmbedWindowID`
(`vendor/vst3sdk/pluginterfaces/gui/iplugview.h:79`), `Linux::IRunLoop` with
`registerEventHandler`/`registerTimer` (`:267279`), `Linux::IEventHandler` (`:224`),
`Linux::ITimerHandler` (`:238`), and a `linuxmain.cpp` module entry
`registerEventHandler`/`registerTimer` (`:267279`), `Linux::IEventHandler` (`:223`),
`Linux::ITimerHandler` (`:239`), and a `linuxmain.cpp` module entry
(`vendor/vst3sdk/public.sdk/source/main/linuxmain.cpp`). The instrument simply does not
implement any of it. The consequence compounds: with `isPlatformTypeSupported` false for every
type, the host falls back to a **generic parameter UI** — and a grep for
@@ -381,25 +457,41 @@ already *is* that seam; it needs a sibling, not a rewrite.
**Location:** `src/shell/panel/draw_kit.cpp:7077` (`loadFont`), and the two literal faces it is
called with — Segoe UI and Consolas, per `draw_kit.h:6364`.
**Mechanism.** On Linux `CreateFont` goes through fontconfig: `FcPatternAddString(pat, FC_FAMILY,
lfFaceName)``FcFontMatch` (`swell-gdi-lice.cpp:450487`). `FcFontMatch` never fails — it
returns the best available substitute. So `loadFont`'s failure branch (`draw_kit.cpp:74`,
"if (!hf) return") will not fire, and the kit will draw in *some* font with different metrics.
Consequences: the kit's end-ellipsis clipping (`draw_kit.h:7074`) is tuned against Segoe's
advance widths, so labels will clip at different points; `Font::ValueMono` is only tabular if
fontconfig's substitution for "Consolas" happens to be monospaced, which is a distro-config
property, not a guarantee. The two WCAG `static_assert`s (`draw_kit.cpp:5354`) are on **pixel
height and weight**, not on the face, so they still hold — the contrast entitlement is not at
risk.
**Mechanism.** On Linux `CreateFont` goes through fontconfig when built with
`-DSWELL_FONTCONFIG` (gated per the `[verify — Linux]` note in §1.5):
`FcPatternAddString(pat, FC_FAMILY, lfFaceName)``FcConfigSubstitute`/`FcDefaultSubstitute`
`FcFontMatch` (`swell-gdi-lice.cpp:450487`). Whether `FcFontMatch` itself can return failure is
a claim about fontconfig's own internals — fontconfig is not vendored here, so it is dropped
rather than asserted uncited (this doc's own evidence-discipline rule). What the vendored code
does show: even a successful match doesn't guarantee a loadable face —
`swell-gdi-lice.cpp:480484` only sets `face` when `FcPatternGetString(hit, FC_FILE, …)`
resolves to a nonempty string AND the following `FT_New_Face` succeeds; either step can fail,
leaving `face` NULL, and in this build path there is no further fallback — the
`MatchFont`/LiberationSans/DejaVuSans list at `:493539` compiles only in the `#else`
(`#ifndef SWELL_FONTCONFIG`) branch. However `swell-gdi-lice.cpp:400401,561,564` show
`CreateFont` always allocates and returns a non-null `HGDIOBJ__*` regardless of whether `face`
resolved — the failure is recorded internally (`font->typedata = NULL`), not as a null return —
so `draw_kit.cpp:74`'s own `if (!hf) return` guard does **not** catch this failure mode; `hf`
comes back non-null either way. Whatever degraded outcome exists here comes from
`LICE_CachedFont`'s own handling of a null-`typedata` font (§1.5 already cites real
non-Windows branches in `lice_textnew.cpp:236249,829,10271028,1040`), not from `loadFont`'s
stated guard — `[verify — Linux]`. The two WCAG `static_assert`s (`draw_kit.cpp:5354`) are on
**pixel height and weight**, not on the face, so they still hold regardless.
**Severity: Minor** — cosmetic; nothing breaks, layout gets uglier and possibly clipped.
**Effort: S.**
**Direction.** Add a platform fallback face list at the one place the faces are chosen
(`draw_kit.cpp`'s `loadFont` call sites — the header already promises "the face is chosen here so
a change is one line", `:69`). DejaVu Sans / DejaVu Sans Mono are the safe Linux defaults; note
that SWELL's own no-fontconfig fallback list names LiberationSans/DejaVuSans and
LiberationMono/DejaVuSansMono (`swell-gdi-lice.cpp:505507`), which is a reasonable precedent to
copy. This same change fixes macOS (San Francisco / Menlo), one code path.
**Severity: Minor** `[verify — Linux]` — cosmetic at best; whether it degrades further than
wrong metrics (e.g. to no text at all) rests on the unconfirmed downstream null-face handling
above, not on anything this audit can read statically.
**Effort: S** — confirmed against the actual call sites: five total (`draw_kit.cpp:154158`,
four using "Segoe UI", one "Consolas"), each a single string-literal argument. A platform
fallback is a `#ifdef`-guarded literal swap at those five sites, not a new mechanism —
`draw_kit.cpp:69`'s comment ("the face is chosen here so a change is one line") describes one
call's literal, not all five; the S estimate rests on there being only five call sites, not on
that comment's wording.
**Direction.** Add a platform fallback face list at the five `loadFont` call sites
(`draw_kit.cpp:154158`). DejaVu Sans / DejaVu Sans Mono are the safe Linux defaults; SWELL's
own no-fontconfig fallback list names LiberationSans/DejaVuSans and
LiberationMono/DejaVuSansMono (`swell-gdi-lice.cpp:505507`), a reasonable precedent to copy.
This same change fixes macOS (San Francisco / Menlo), one code path.
### L2-10 — OS drag-out on SWELL loses the copy-only mask, the readiness probe, and the outcome
**Location:** `src/shell/actions/drag_out_win.cpp:249282` (the `#else` branch);
@@ -417,7 +509,10 @@ so (c) is inert today. Note the call path is already Linux-correct in one import
`handOffToOs` releases capture and resets drag state (`panel_drag.cpp:240241`) *before*
invoking the drag, which matters because SWELL's implementation takes capture on its own hidden
window and spins a nested `SWELL_RunMessageLoop` until capture drops
(`swell-generic-gdk.cpp:35433552`) — with a 500 ms no-motion timeout that has no Windows analog.
(`swell-generic-gdk.cpp:35753584`, inside `SWELL_InitiateDragDropOfFileList` — the function
`drag_out_win.cpp:270` actually calls; the body is identical to the sibling
`SWELL_InitiateDragDrop` at `:35313560`, which is where an earlier draft of this citation
pointed) — with a 500 ms no-motion timeout that has no Windows analog.
**Severity: Minor** — the feature works; one safety property becomes conventional rather than
structural.
@@ -435,7 +530,9 @@ structurally impossible" as a Windows-only claim in any future spec text.
case-**insensitive**, so the same `#ifdef _WIN32` under-folds there — a pre-existing macOS
defect this audit surfaces but does not own.
**Severity: Minor** (no Linux defect). **Effort: S.**
**Severity: Minor** (no Linux defect). **Effort: S** — confirmed: the one platform conditional
in `core/` is a single `#ifdef _WIN32` block (`capture_paths.cpp:1820`) with no other call
site depending on the token; a predicate swap is a one-block change.
**Direction.** No Linux action. If macOS is ever targeted, the predicate wants to be
"case-insensitive filesystem", not "Windows".
@@ -466,7 +563,18 @@ Recorded so the sweep's negative results are as auditable as its findings.
four were the symbol name appearing in a *comment* (`render_settings.h:53`,
`ext_state_read.h:20`, `action_registry.h:7`) or a correct `<cmath>` `std::abs`
(`velocity_curve.cpp:229`, `<cmath>` at `:6`), and `processor_reload.cpp`'s `std::unique_ptr`
comes from `reasampler_processor.h:12`. **No missing include found.**
comes from `reasampler_processor.h:12`. **No missing include found — but this only covers the
ten headers above.** The two headers most likely to produce the classic
MSVC-transitively-provides failure under libstdc++, `<vector>` and `<string>`, were excluded
from that sweep; re-run including them: **45 files use `std::vector` without `#include
<vector>`, and 52 use `std::string` without `#include <string>`** (raw grep-diff counts,
unread). `<map>` and `<set>` add 2 and 3 more respectively. These 102 raw hits were **not**
individually verified the way the five above were (that would mean reading 102 files); the
claim this sweep actually supports is "up to 102 files may rely on transitive inclusion for
`<vector>`/`<string>`/`<map>`/`<set>`, unconfirmed one by one" — not "no missing include
found." The two header cases that matter most for the extension's own build
(`shell/capture/capture_orchestrator.h`, `src/ext_keys.h`) are confirmed satisfied
transitively.
- **Templates / two-phase lookup.** Exactly 7 templates in the tree
(`core/wire/bytes.h:25,61`, `core/wire/ext_state_read.h:37`,
`core/instrument/engine/play_params.h:191,203,208,218`). None derives from a dependent base,
@@ -564,11 +672,12 @@ changed behaviour; **AT RISK** = depends on an unverified assumption.
| TCP/MCP embed strip | **GONE** | `REAPER_FXEMBED_WM_IS_SUPPORTED` returns 0 | `reasampler_embed.cpp:135141` |
| Kit fonts (Segoe UI / Consolas) | **DIFFERENT** | fontconfig substitutes silently; metrics and ellipsis points shift; ValueMono may lose tabular alignment | `swell-gdi-lice.cpp:450487` |
| OS drag-out copy-only guarantee | **DIFFERENT** | No effect mask; copy-only is conventional not structural | `drag_out_win.cpp:257270` |
| OS drag-out readiness/outcome | **DIFFERENT** | Probe degrades to "non-empty"; return is always `true`; 500 ms no-motion timeout has no Windows analog | `drag_out_win.cpp:276278`, `swell-generic-gdk.cpp:35473548` |
| OS drag-out readiness/outcome | **DIFFERENT** | Probe degrades to "non-empty"; return is always `true`; 500 ms no-motion timeout has no Windows analog | `drag_out_win.cpp:276278`, `swell-generic-gdk.cpp:35793580` |
| Panel file-drop ingest | **AT RISK** | Depends on SWELL delivering `WM_DROPFILES` without the Win32 opt-in | `panel_window.cpp:145150` |
| Bank index float round-trip | **AT RISK** | Unparseable under a non-C `LC_NUMERIC` | `json.cpp:3943,190196` |
| Extension load | **AT RISK** | Any one unresolved API name in ~869 = silent refusal | `main.cpp:292293` |
| Path case sensitivity | **DIFFERENT (correct)** | No case-fold; correct for Linux, and the tests already assert it | `capture_paths.cpp:1820` |
| Prune reclaim on a symlinked bank file | **AT RISK** | `fs::directory_iterator` + `is_regular_file()` follows symlinks (C++17); size is read from the target via `file_size()` but `fs::remove` deletes the link, not the target — prune reports N bytes reclaimed and reclaims zero. Symlinked media folders are far more idiomatic on Linux than Windows. | `prune_fs.cpp:113124` |
| Audition / preview | **INTACT** | `preview_register_t` fork matches the SDK; `PlayPreview`/`StopPreview` are REAPER API | `panel_audition.cpp:3663` |
| Docking, menus, cursors, keyboard, wheel, capture rollback, tooltips | **INTACT** | All SWELL-provided; verified call-by-call | §1.5 |
| Capture pillar (offline + realtime), persist, tracking, prune *computation* | **INTACT** | Pure `core/` + REAPER API only | §1.1 |
@@ -594,7 +703,7 @@ changed behaviour; **AT RISK** = depends on an unverified assumption.
distro decides whether L2-09 is cosmetic or a real readability regression on numeric readouts.
6. **`SWELL_InitiateDragDropOfFileList` acceptance semantics.** Whether a GDK-initiated file drag
is accepted as a copy by common targets (a file manager, another DAW), and whether the 500 ms
no-motion timeout (`swell-generic-gdk.cpp:35473548`) cancels a slow user gesture.
no-motion timeout (`swell-generic-gdk.cpp:35793580`) cancels a slow user gesture.
7. **Prune against an in-use file.** Play an item from a bank file, prune it, confirm the audio
survives until the fd closes and the file is then unrecoverable — the concrete demonstration
behind L2-05(b).
@@ -624,12 +733,18 @@ Dependency order only — this is not a plan, and it assigns no waves.
3. **Make the panel appear.** L2-06 (the missing-dialog diagnostic) alongside T1's resgen wiring.
The diagnostic should land first so the resgen step can be confirmed rather than assumed.
4. **Then, and only then, verify.** `[verify — Linux]` items 1, 3, 5, 6, 7 all need a running
panel. Item 2 is already answered by step 2.
panel. Item 2 is already answered by step 2. **The critical path for this step runs through
the parallel build-system track, not this one:** "a running panel" means T1's resgen wiring
(§7 bullet 1) has landed — this doc doesn't own that work and doesn't know its schedule, so
step 3's "alongside T1's resgen wiring" is doing real scheduling work, not just sequencing
flavor. Anyone driving this ordering sketch needs to coordinate with T1 before step 4 can
start, not just after step 3 finishes on this track's own items.
5. **Data integrity.** L2-04 (locale-independent number codec) — pure `core/` work, unit-testable
on Windows, and it should land before any Linux user saves a project. Sequenced after the
verify only so its urgency is known; the *work* is not blocked.
6. **Deletion safety.** L2-05 (XDG trash or a platform-aware confirm) — gated on `[Daniel]` (2).
7. **Ingest opt-in, if needed.** L2-07 — strictly gated on `[verify — Linux]` (3).
7. **Ingest opt-in, if needed.** L2-07 — no independent fix; it is the resgen work already
sequenced in step 3, strictly gated on `[verify — Linux]` (3) to confirm it's needed at all.
8. **Cosmetics.** L2-09 (fonts), L2-10 (drag-out doc). Independent of everything above.
9. **Separately, if ruled in.** L2-08 — the instrument's X11 editor. No dependency in either
direction on 18 except L2-01, which the shared `draw_kit` needs regardless.
@@ -669,7 +784,7 @@ Dependency order only — this is not a plan, and it assigns no waves.
| L2-04 | Persisted floats are `LC_NUMERIC`-dependent, both directions | Major | M | both |
| L2-05 | Prune loses trash AND the locked-file backstop | Major | M | extension |
| L2-06 | Panel dialog never created; silent no-op | Major | S | extension |
| L2-07 | No drop-accept opt-in on SWELL; ingest-by-drop unverified | Major `[verify]` | S | extension |
| L2-07 | No drop-accept opt-in on SWELL; real seam is resgen, same as L2-06 | Major `[verify]` | — (see L2-06) | extension |
| L2-08 | No Linux VST3 editor; no parameters to fall back to | Major | L | instrument |
| L2-09 | Segoe UI / Consolas substitute silently via fontconfig | Minor | S | both |
| L2-10 | SWELL drag-out: no copy mask, no probe, no outcome | Minor | S | extension |