Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions crates/namir-clap/src/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,9 +215,9 @@ impl<'a> PluginGuiImpl for NamirMainThread<'a> {
/// on the day the answer starts being transmitted, and
/// `tests/clap_host_gui.rs` carries a live record of the swallowing.
///
/// **Upstream status (issue #144, as of 2026-08-30): not reported.** A search of
/// `prokopyl/clack` for `set_size` returns nothing, so there is no upstream ticket to track
/// and no released version to move to — crates.io publishes only 0.1.0 and 0.1.1. The fix is
/// **Upstream status (issue #144): reported as `prokopyl/clack#101`.** There is now a ticket
/// to track, but still no released version to move to — crates.io publishes only 0.1.0 and
/// 0.1.1, neither yanked. The fix is
/// one line in that trampoline (`Ok(...is_ok())`, as its siblings already read). Nor is the
/// version *pinned*: `Cargo.toml` declares `"0.1.1"`, i.e. `^0.1.1`, and it is the committed
/// `Cargo.lock` that holds it — so a `cargo update` that picks up a fixed 0.1.2 is what
Expand Down
6 changes: 4 additions & 2 deletions crates/namir-platform/src/thread_priority.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,10 @@
//! spin or deadlock up there can take a machine down. The module now targets the policy minimum
//! plus a fixed offset -- 11 on Linux, 25 on macOS -- which is the band JACK and PipeWire settled
//! on for the same reason. The constant and its full argument are in `unix::RT_PRIORITY_ABOVE_MIN`
//! below; **it is not yet recorded in D-13.2**, whose text still reads "at that policy's maximum
//! priority", and doing so is the follow-up this change owes `docs/02-architecture.md`.
//! below, and **D-13.2 now carries the matching `*Consequence*` note** (changelog 0.36). That
//! decision's original text still reads "at that policy's maximum priority" and deliberately still
//! does: a decision is superseded by an appended note here, never silently rewritten, so read the
//! two together. This comment claimed the record was still owed until that note landed.
//!
//! **What the macOS path is not (issue #81).** `pthread_setschedparam` with `SCHED_FIFO` is *not*
//! how Darwin grants an audio thread real-time scheduling. CoreAudio-grade threads there are
Expand Down
3 changes: 2 additions & 1 deletion docs/02-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -3416,7 +3416,8 @@ S-1 is the largest and gates the most numbers — **complete, 2026-08-05.** S-2
| R-15 | **New, from M9a, 2026-08-08 — found while landing D-18.6's `clack-host` dev-dependency.** `xtask attribution` can list in THIRD-PARTY-NOTICES.md a crate the shipped binaries do not contain. It walks `cargo metadata`'s **single unified resolve**, keeping any edge that carries a `Normal` `dep_kinds` entry (`xtask/src/cargo_meta.rs:82-91`, the test itself at `:83-87`) — but that resolve does not decouple features by dependency kind, so features a **dev**-dependency turns on sit on the same node as the shipped one. The trigger condition is therefore precise rather than vague: **any dev-dependency edge that enables an *optional normal* dependency of a crate the shipped graph already reaches.** That optional dependency becomes a `Normal` edge out of a package the walk visits, and it is attributed. Live instance, which is why this row exists rather than being theoretical: `clack-extensions` 0.1.1 declares `clack-host` `optional` under `[dependencies]` (its `Cargo.toml:118-121`), so enabling that crate's own `clack-host` feature from `namir-clap`'s `[dev-dependencies]` puts `clack-host` into the attribution file — recorded as measured at `crates/namir-clap/Cargo.toml:93-105`, and readable without re-running it from those two manifests plus `cargo_meta.rs`. The error direction is the safe one and that is why this is **Low**: the walk over-approximates and cannot *omit* a shipped crate, so NFR-LIC-030 is never under-served. What it costs is still real — an attribution file naming crates the binary does not carry is a false statement about what Namir redistributes, and `xtask attribution` becomes a gate that fails for a reason no reviewer can act on, which is the shape D-18.5's own reasoning warns about. | Low | The detector already exists and needs no new tool: **`cargo tree -e normal` disagreeing with `xtask attribution`** is exactly this condition, which is why D-18.6's third landing gate pairs those two commands rather than either alone. Run both whenever a dev-dependency edge is added or its feature list changes, and read a crate that `attribution` lists while `cargo tree -e normal` cannot reach as this row, not as a real leak. Avoided rather than fixed today — `clack-extensions`' `clack-host` feature is off, so the notices file is correct as it stands. The fix, when a harness finally needs those host halves, is to resolve dependency kind per shipped path rather than per unified-resolve node (`--filter-platform` plus a per-root walk, or the resolution `cargo tree -e normal` already performs); **M9b owns it**, per D-18.6's dated landing note. |
| R-16 | **New, from M13, 2026-08-11 — found by the Linux packaging lane while writing `install.sh`, not by anyone looking at the UI.** Both products draw through `baseview` 0.2.2, whose **only Unix backend is X11 + GLX**; there is no Wayland backend in any published `baseview` version. A Wayland-only session therefore needs XWayland present, and a session without it gets no window at all. This has been observed rather than inferred: M12's own status subsection records `cargo run -p namir-app` starting audio and then panicking inside `baseview`'s X11 window open, with `xvfb-run` not helping because that path needs a GLX-capable display. **That last clause is imprecise, and M9b measured the actual mechanism** (2026-08-12): an Xvfb display *is* GLX-capable — Mesa 25.2.8 / llvmpipe reports direct rendering, a 4.5 core profile and **320** GLXFBConfigs on it — but **none of those 320 is sRGB-capable**, and `egui-baseview`'s default `GlConfig` asks for one (`baseview`'s `get_fb_attribs` passes `GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, config.srgb as i32`), so `glXChooseFBConfig` matches nothing and `find_best_visual_config_for_gl` panics on its own `.expect("Could not fetch framebuffer config")` — `NoValidFBConfig`, followed by the main thread panicking on the resulting `RecvError`. Neither `LIBGL_DRI3_DISABLE`, `LIBGL_ALWAYS_INDIRECT`, `GALLIUM_DRIVER=softpipe` nor `MESA_GL_VERSION_OVERRIDE` produces an sRGB config on that stack. Namir does not choose this: `namir_ui::open_blocking` passes only a title and a size and takes `EguiWindowSettings::default()` for the rest. The practical consequence is narrower and more useful than "needs a GLX-capable display": **a software-GL X server cannot run either product's window, so no headless CI or container can execute the `Verify: M` UI scripts**, which is why `docs/manual-tests/fr-ui-020-*.md` and `fr-ui-070-*.md` still read NOT EXECUTED after M9b attempted exactly that. The risk is not that the constraint exists — D-15.2 pinned this stack knowingly — but that **M13 is the milestone at which it stops being a developer's problem and becomes a user's**: before M13 the only way to run Namir on Linux was to build it, and anyone building it already had the X11 development headers. An installed binary reaches people who did not. Wayland is the default session on current Fedora, Ubuntu and RHEL, and several distributions are actively removing their X11 sessions rather than merely defaulting away from them. | Medium | Stated rather than fixed, because fixing it is a windowing-stack migration and not a packaging change: `packaging/linux/install.sh` reports the runtime constraint alongside its `libGL.so.1` check, `packaging/linux/README.md` records it, and `docs/user-guide.md`'s Known Limitations carries it in the user's own words. The upgrade path is known to be closed at both ends and was checked at M13 rather than assumed: `baseview` 0.3.0 renames `WindowOpenOptions` to `WindowSettings` and still has no Wayland backend, and the newest published `egui-baseview` (0.6.0, the pinned one) requires `baseview` 0.2.2 — so the pin is forced, not merely conservative. Revisit when a `baseview` with a Wayland backend exists, or when XWayland stops being present by default on a tier-2 platform, whichever comes first. |
| R-17 | **New, from M9b's P0 decision pass, 2026-08-12 — created by D-18.7, not discovered in existing code.** D-18.7 puts `clack-extensions`' `clack-host` feature behind the non-default `namir-clap` feature `host-ext-tests`, which keeps it out of the default-feature resolve `xtask attribution` walks and out of the shipped cdylib. That confinement holds only for as long as nothing turns the feature on in a build that ships. **Adding `--all-features` to any build or release command silently links `clack-host` into the cdylib**, and unlike R-15 — whose error direction is over-attribution, and therefore safe — this one's is a real dependency entering a shipped artifact. Distinct from R-15 and not a restatement of it: R-15 is about the tool naming a crate the binary does not contain; R-17 is about the binary coming to contain one. Confirmed unexploited at the time of writing: `.github/workflows/release.yml:110`, `:232` and `:293` all run plain `cargo build --release --workspace`, and `ci.yml:315` runs `cargo build --release -p namir-clap --lib`. | Low | Nothing mechanical guards the linkage itself, and that is stated rather than glossed. What exists is a late detector: `xtask attribution` goes red, because the feature being on is exactly the Arm A condition that adds the `clack-host` row — so the failure surfaces on the next merge, as an attribution error rather than as a linkage error. The cheap discipline is that `--all-features` never appears in a build or release command in this repository; the durable fix, if one is ever wanted, is the same per-shipped-path resolution R-15 already nominates, which would make the question answerable directly instead of by proxy. Re-read this row whenever `namir-clap`'s `[features]` table gains a second entry. |
| R-18 | **New, from M9b, 2026-08-12 — found by NFR-PERF-040's benchmark on its first run, not predicted.** NFR-PERF-040 caps plugin instantiation at 200 ms and the requirement passes — but **the margin is a property of the user's library size, not of plugin code.** Measured on §2's reference machine over D-2.4's repetitions (`crates/namir-clap/benches/plugin_instantiation.rs`): with an empty index the whole instantiation is **0.24 ms** worst-case; with a 10 000-entry index it is **163.8 ms**, and **172.6 ms** at 192 kHz / 4096-frame blocks. Namir's own work is ~0.17 ms of that — constructing the entire six-stage chain is 37–119 µs, and a 16× change in audio configuration moves the total by under 0.1 ms. Essentially the entire figure is one thing: parsing `library-index.json` in `LibraryService::open_default`, ~161 ms for 10 000 entries (7.57 MB) at roughly **16 µs per entry**. Two consequences, and the first is the risk. **The parse alone reaches the 200 ms ceiling at about 12 000–12 500 entries**, so a user whose library exceeds FR-LIB-020's own stated 10 000-file scale breaches NFR-PERF-040 on a machine at least as fast as the reference one — a requirement failing on user data rather than on a code change, which no regression test would ever catch. Second, chain construction could regress a hundredfold before the total moved, which is why that benchmark reports the create and activate halves separately per arm rather than only their sum. | Medium | Not mitigated today, and the honest statement is that nothing in M9b addressed it — the benchmark measures and asserts, it does not fix. The cheap partial mitigations, none taken: parse the index lazily or off the instantiation path so a plugin becomes processable before its library is browsable; store the index in a format whose read is not a full JSON materialisation (D-12.3 chose a single JSON document and its rationale is about diffability, which a sidecar or a lazily-indexed form need not give up); or cap what a plugin instance loads eagerly. Whichever is chosen wants a decision rather than a patch, because FR-LIB-020's 10 000-file figure and this requirement's 200 ms figure were set independently and this row is where they were first measured against each other. Re-measure with `cargo build --release --bench plugin_instantiation -p namir-clap` before and after any change to `LibraryService::open_default` or the index format. |
| R-18 | **New, from M9b, 2026-08-12 — found by NFR-PERF-040's benchmark on its first run, not predicted.** NFR-PERF-040 caps plugin instantiation at 200 ms and the requirement passes — but **the margin is a property of the user's library size, not of plugin code.** Measured on §2's reference machine over D-2.4's repetitions (`crates/namir-clap/benches/plugin_instantiation.rs`): with an empty index the whole instantiation is **0.24 ms** worst-case; with a 10 000-entry index it is **163.8 ms**, and **172.6 ms** at 192 kHz / 4096-frame blocks. Namir's own work is ~0.17 ms of that — constructing the entire six-stage chain is 37–119 µs, and a 16× change in audio configuration moves the total by under 0.1 ms. Essentially the entire figure is one thing: parsing `library-index.json` in `LibraryService::open_default`, ~161 ms for 10 000 entries (7.57 MB) at roughly **16 µs per entry**. Two consequences, and the first is the risk. **The parse alone reaches the 200 ms ceiling at about 12 000–12 500 entries**, so a user whose library exceeds FR-LIB-020's own stated 10 000-file scale breaches NFR-PERF-040 on a machine at least as fast as the reference one — a requirement failing on user data rather than on a code change, which no regression test would ever catch. Second, chain construction could regress a hundredfold before the total moved, which is why that benchmark reports the create and activate halves separately per arm rather than only their sum. | Medium | Not mitigated today, and the honest statement is that nothing in M9b addressed it — the benchmark measures and asserts, it does not fix. The cheap partial mitigations, none taken: parse the index lazily or off the instantiation path so a plugin becomes processable before its library is browsable; store the index in a format whose read is not a full JSON materialisation (D-12.3 chose a single JSON document and its rationale is about diffability, which a sidecar or a lazily-indexed form need not give up); or cap what a plugin instance loads eagerly. Whichever is chosen wants a decision rather than a patch, because FR-LIB-020's 10 000-file figure and this requirement's 200 ms figure were set independently and this row is where they were first measured against each other. Re-measure with `cargo build --release --bench plugin_instantiation -p namir-clap` before and after any change to `LibraryService::open_default` or the index format. **Mitigated at M14 (issue #22); this row's "not mitigated today" above is the M9b statement and is kept as written.** The first of the three cheap mitigations was taken: `LibraryService::open` no longer reads the index at all. A loader thread parses it once per path, shared across instances, so instantiation no longer carries the parse and a plugin becomes processable before its library is browsable. The requirement's margin is therefore no longer a property of the user's library size, which was the substance of the risk. What the change does **not** retire: the parse still costs ~161 ms for 10 000 entries, it is merely off the instantiation path, so a library browsed immediately after load still waits for it — and `LIBRARY_INDEX_BUDGET` guards that wait rather than removing it. Deferring the load also created a defect of its own, fixed later in the same pass: two recall jobs resolved references against the still-empty index (issue #96, PR #145's review finding 1). |
| 0.37 | 2026-08-30 | **Four prose sites that had gone false against the code beside them, corrected after the issue-tracker pass merged.** **§22 R-18** said "not mitigated today" while M14 had already taken the first of the three mitigations it lists — the index is off the instantiation path — so the row gains a note saying what was taken, what it does *not* retire (the ~161 ms parse still exists, merely deferred, guarded by `LIBRARY_INDEX_BUDGET`), and that deferring it created issue #96's empty-index resolution defect in turn; the M9b statement above it is kept as written rather than edited. `namir-platform`'s `thread_priority` module claimed the `min + 10` change was "not yet recorded in D-13.2" after 0.36 recorded it. `namir-clap`'s `gui.rs` claimed `set_size` had "no upstream ticket to track" after it was filed as `prokopyl/clack#101`. And `docs/manual-tests/fr-ui-110-brand-mark.md` attributed its headless-window failure to GLX; the cause is sRGB, measured as 240 healthy `GLXFBConfig`s with the sRGB flag clear on every one (issue #143). No behaviour changes. Recorded because each of the four was a claim a reader would have believed, and three of them were written by the same work that then invalidated them. |

---

Expand Down
4 changes: 4 additions & 0 deletions docs/03-implementation-roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -6085,6 +6085,10 @@ NFR-LIC-030, NFR-BUILD-020). Add bundle-and-inspect lanes on all three platforms
as the benchmark does puts the breach near 10 700 entries**, and FR-LIB-020's 10 000 is a floor on
what Namir claims to support. No regression test can ever catch this, because nothing regresses.
Take the index off the instantiation path.
*Done at M14 (2026-08-27, issue #22), ahead of this phase — noted here rather than struck from the
bullet.* `LibraryService::open` no longer parses the index; a loader thread reads it once per path
and shares that across instances. §22 R-18 carries the full note, including what the change does
not retire. The rest of this phase — the human sessions — is untouched by it.
- **R-11 / issue #23 — signing.** If 1.0 is a public release, a quarantined macOS plugin has no
user-visible load path at all and the CI signing route is unreachable because the keychain import
is unbuilt. Decide, then build or record.
Expand Down
12 changes: 10 additions & 2 deletions docs/manual-tests/fr-ui-110-brand-mark.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,16 @@ called `Result::unwrap()` on an `Err` value: RecvError

`cargo run -p namir-app` reached further -- it started audio (`namir: audio stream started`,
`48000 Hz, 256-frame buffer`) and then panicked at the same `baseview` X11 call. `xvfb-run` did not
help: `baseview` 0.2.2's X11 path needs a GLX-capable display and `Xvfb` provides none. No CLAP host
was available there either.
help. No CLAP host was available there either.

**Correction (2026-08-30, issue #143).** The observation above stands; the explanation attached to it
did not. It was recorded as "`baseview` 0.2.2's X11 path needs a GLX-capable display and `Xvfb`
provides none", and that is wrong. The cause is **sRGB**, not GLX: `GlConfig::srgb` defaults to
`true`, and `glXChooseFBConfig` then matches none of Xvfb's configs. Measured under Xvfb -- 240
healthy `GLXFBConfig`s available, the sRGB flag clear on every one. `namir-ui` now retries with
`srgb: false` when the first attempt fails (`open_with_srgb_fallback`), and CI runs the interface
headless on every push (`headless-window`, asserting a frame count rather than an exit status). So a
headless window is no longer a blocker for the `Verify: M` UI scripts; executing them still is.

## Executed run on Windows (M12, 2026-08-11)

Expand Down
Loading