nightly 2026-09-07 (v0.3.13-beta.22): it never built on Linux — a cfg! that was never compiled out, and a libc bump that broke the Python runtime - #315
Merged
Conversation
…latest
`origin/master` did not compile on Linux at all — verified against a pristine
checkout before touching anything. The dev host moved to Arch/Omarchy in
September and nothing had compiled the workspace there since. Two independent
causes, neither reachable from Windows CI:
1. `bridge.rs` chose the exec shell with `cfg!(windows)` — a *runtime* boolean —
where `#[cfg(windows)]` was meant. Both arms are therefore type-checked on
every platform, while the four helpers the Windows arm calls
(`strip_outer_quotes`, `classify_windows_command`, `git_bash_path`,
`WinShell`) are `#[cfg(windows)]` and absent on Linux: 5x E0425/E0433, so
`nanna-scripting` and with it the `nanna` binary could not build. Split into
`#[cfg(windows)]` / `#[cfg(not(windows))]` bindings. No behaviour change on
Windows — the same arm is selected, just at compile time rather than runtime.
2. `libc 0.2.187` corrected `POSIX_SPAWN_SETSID` from `c_int` to `c_short` on
linux-gnu (glibc really does store spawn flags in a `short`), but
`rustpython-vm 0.5.0` hands that constant to
`nix::spawn::PosixSpawnFlags::from_bits_retain`, typed `c_int` — E0308,
killing the `python` feature. The fix is merged upstream but unreleased
(RustPython PR #8343, 2026-07-22; 0.5.0 is still the newest on crates.io), so
`libc` is held at 0.2.186. `rustpython-stdlib 0.5.0` itself requires
`libc ^0.2.183`, so the buildable window is only 0.2.183..=0.2.186 — a bare
`cargo update` lands outside it every time.
Also removes `normalize_drive_paths`'s `#[cfg(not(windows))]` identity stub: its
only non-test caller is `#[cfg(windows)]` and its tests are
`#[cfg(all(test, windows))]`, so it was dead on every platform and merely
invisible while the crate never compiled here.
Dependency freshness (the run's standalone sweep):
- `ocrs 0.12 -> 0.13` + `rten 0.24 -> 0.26`. This unblocks a pin three previous
runs recorded as "not ours to fix": `ocrs 0.13.0` ships against `rten 0.26`,
so the pair moves together, `rten`/`rten-tensor` stay unified at one version,
and `OcrEngineParams { detection_model, recognition_model }` — the exact call
predicted to fail — compiles with zero source changes.
- `wide 1.6 -> 1.7`, `playwright-rs 0.16 -> 0.17`, `deno_core 0.410 -> 0.411`,
plus the compatible `cargo update` sweep. `malachite-bigint` re-pinned to
0.9.2 as documented. `criterion 0.8 -> "0.7"` rejected again: it is a
downgrade.
- Frontend: `@tiptap/* 3.31.3`, `vitest 4 -> 5`, `@lucide/vue 1.42`,
`vue-router 5.3.1`, `@playwright/test 1.63`, `happy-dom 20.14`,
`@vue/test-utils 2.5`, `postcss 8.5.28` and the Tauri plugins — all with zero
source changes. `vuedraggable` deliberately held at ^4.1.0 (its `latest`
dist-tag points at the older Vue-2 line) and `typescript` at 5.9.3 (`vue-tsc`
still cannot load TS 7).
Verified on Linux: `cargo build --workspace --exclude nanna-gui` green;
`cargo test --workspace --exclude nanna-gui` **1683 passed / 0 failed / 12
ignored** across 47 test binaries; `cargo clippy --workspace --all-targets
--exclude nanna-gui` 0 errors with no new warnings in the changed regions;
frontend `vue-tsc --noEmit` clean, 238/238 vitest, `pnpm build` green. The built
Linux daemon boots against a scratch config to `Daemon ready`, answers
`GET /health` with `{"status":"ok","version":"0.3.11","uptime_secs":1}`, handles
SIGTERM cleanly, and logs zero panics.
Co-Authored-By: Claude Opus 5 <[email protected]>
The `libc <= 0.2.186` hold added in the previous commit is the third dependency constraint in this repo whose only enforcement was a ROADMAP line saying "remember to redo this after `cargo update`". The other two became `dep_version_unification.rs` in August; this one joins them rather than becoming another note the next run has to rediscover. `held_back_crates_stay_below_their_ceiling` is the mirror of the existing guard: that one catches a crate resolving to *two* versions, this one catches a crate resolving to *one version that is too new*. Both failure modes come from the same place — a dependency of ours requiring a version range that includes a release it does not itself compile against — and cargo picks the newest member of that range every time. Design notes: - `CeilingCrate` carries `lift_when` alongside `reason` and `remedy`. A ceiling holds back upstream fixes, so it is a liability; recording the condition that retires it means the pin gets deleted on purpose instead of renewed forever. For `libc` that condition is "rustpython publishes anything after 0.5.0" — the fix is already merged upstream. - Version comparison is positional and dependency-free, matching the sibling lockfile parser. It compares components numerically on purpose: as text, "0.2.9" sorts *above* "0.2.186", which would make the guard silently useless. A negative-space test pins that. - Like the unification guard, it fails if a guarded crate leaves the graph, so a dead ceiling is removed deliberately rather than passing forever. Verified it catches the real regression rather than only being written: with `cargo update -p libc --precise 0.2.189` re-applied it reports `libc resolved to 0.2.189 but must stay at or below 0.2.186` together with the remedy command and the lift condition. 5 tests, 0.00s. Co-Authored-By: Claude Opus 5 <[email protected]>
The Windows-only `compile-tests` job carries a comment explaining why it runs on Windows: only Windows exercises `#[cfg(windows)]`, where the service layer lives. That reasoning is correct, and the job stays. It is also exactly one-sided, and the missing half cost this project a completely broken Linux build that went unnoticed for however long the port had been sitting there. Both breaks fixed earlier in this branch — the `cfg!(windows)` misuse in `bridge.rs` and the `libc`/`rustpython-vm` type mismatch — are plain compile errors. A Linux `cargo test --no-run` would have caught each on the commit that introduced it. No amount of Windows CI could: **a platform gate is only tested by the platform it excludes.** `compile-tests-linux` is a straight `ubuntu-latest` mirror of the existing job, same pinned toolchain, same `--workspace --exclude nanna-gui --locked`, same 30-minute bound (the shared dependency graph dominates and is the same graph). `nanna-gui` stays excluded on Linux for the existing reason plus one more: the Tauri crate needs WebKitGTK system packages there, which would turn a smoke check into a provisioning job. Linux GUI coverage is deliberately not claimed by this workflow, and is filed in ROADMAP.md as an open question rather than quietly implied. Co-Authored-By: Claude Opus 5 <[email protected]>
Base bumped 0.3.11 -> 0.3.13 across all four version files (root `[package]`, `[workspace.package]`, `gui/package.json`, `gui/src-tauri/tauri.conf.json`), beta counter 20 -> 22. Both increments are deliberate. The base must move on every release because the updater compares **base semver only** — the `-beta.N` suffix never reaches the installer — so shipping a new beta at an unchanged base is invisible to every installed client. And the numbering skips a step because PR #285 is still unmerged and already claims v0.3.12-beta.21; taking 0.3.12 here would collide with it. `release.yml`'s version guard simulated locally before committing — both conditions hold: base == tauri.conf.json version (0.3.13), and base != the version `.updater/latest.json` advertises (0.3.10). `tauri.conf.json` re-parsed after editing, since two invalid keys once blocked a bundle *after* the tag was cut. Re-verified green after the bump: `cargo build --workspace --exclude nanna-gui` clean, and the `Cargo.lock` diff is **21 workspace version lines and nothing else** — no dependency moved as a side effect. `.updater/latest.json` is deliberately untouched: it carries a minisign signature over the built installer, which only the signed release build can produce. Committing it here would advertise an update whose signature matches no binary. Publishing stays the human's two post-merge commands, recorded in the PR. Co-Authored-By: Claude Opus 5 <[email protected]>
physics515
pushed a commit
that referenced
this pull request
Sep 8, 2026
PR #315 landed first and superseded two of this branch's three concerns, so the conflicts resolve asymmetrically rather than by preferring one side wholesale: - **Dependency and version files take master.** This branch's 2026-08-28 freshness sweep and its `v0.3.12-beta.21` prep are both older than #315's, which already carries `ocrs 0.13`/`rten 0.26`, the `libc 0.2.186` ceiling and a 0.3.13 base. Keeping this branch's versions would walk the tree backwards and collide with the release guard. `Cargo.lock` is master's plus cargo's own correction adding `lopdf` to the graph, which this branch's `pdf.read` registration genuinely needs — verified with `cargo metadata --locked`. - **The dependency guard takes THIS branch's design.** Both sides extended `dep_version_unification.rs`. This branch replaced the hardcoded `remedy` string with a `Remedy::{PinBackTo, Manual}` enum that *derives* the pin-back command from the versions actually observed in the lockfile — and it is right for a reason master's version demonstrates: the entry #315 shipped hardcoded `@0.10.0` while the graph had already drifted to `0.11.0`, so it printed a `cargo update -p ...@<gone>` that errors instead of fixing anything. The ceiling guard from #315 (`held_back_crates_stay_below_their_ceiling`, which holds `libc` at 0.2.186) is kept and layered on top; it stays a plain string because a ceiling has exactly one resolved version, not a split, so `Remedy::describe`'s "at least one stray" contract does not apply to it. - Everything else auto-merged, including `ROADMAP.md` and `crates/nanna-daemon/src/server.rs`. Co-Authored-By: Claude Opus 5 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this run found
origin/masterdid not build on Linux. Not "had warnings", not "a test failed" —cargo build --workspaceproduced no binary. The dev host moved to Arch/Omarchy in September and, as far as thisrun can tell, nothing had compiled the workspace there since. Verified against a pristine
origin/masterbefore touching anything, so this is a pre-existing break, not something thedependency sweep introduced.
Two independent causes, and the same reason neither was caught: a platform gate is only tested by
the platform it excludes.
1. A runtime
cfg!where a compile-time#[cfg]was meantcrates/nanna-scripting/src/bridge.rs:746picked theexecshell with:cfg!(windows)is a boolean expression, not conditional compilation. It selects the correctbranch at runtime — so this was never a behaviour bug — but both arms are still type-checked on
every platform, and the four helpers the Windows arm calls (
strip_outer_quotes,classify_windows_command,git_bash_path,WinShell) are#[cfg(windows)]and genuinely absenton Linux. Five
E0425/E0433, andnanna-scripting— hence thenannabinary — could not compile.Fixed by splitting into
#[cfg(windows)]/#[cfg(not(windows))]bindings. No behaviour change onWindows: the same arm is selected, just at compile time. Net −7/+5 lines.
Also removed
normalize_drive_paths's#[cfg(not(windows))]identity stub — its only non-test calleris
#[cfg(windows)]and its tests are#[cfg(all(test, windows))], so it was dead on everyplatform and merely invisible while the crate never compiled here. Deleted rather than
#[allow]-ed.2.
libc 0.2.187broke the vendored Python runtimelibc corrected
POSIX_SPAWN_SETSIDfromc_inttoc_shorton linux-gnu — correctly; glibc reallydoes store spawn flags in a
short. Butrustpython-vm 0.5.0hands that constant straight tonix::spawn::PosixSpawnFlags::from_bits_retain, whichnix 0.30types asc_int.E0308atrustpython-vm-0.5.0/src/stdlib/posix.rs:1812, killing thepythonfeature.The fix is already merged upstream and never released — RustPython #8343 "Fix building against
new libc", merged 2026-07-22;
0.5.0is stillthe newest on crates.io. So
libcis held at 0.2.186.That window is narrower than it looks:
rustpython-stdlib 0.5.0itself requireslibc ^0.2.183, soexactly four releases satisfy both constraints — which is why a bare
cargo updatelands outside itevery single time.
Increments landed
fix(scripting)— both Linux build fixes + the dependency freshness sweep. These could not besplit: neither is green without the other, so the smallest green unit contains both.
test(storage)— a version-ceiling guard, the mirror of the existing unification guard.ci—compile-tests-linux, anubuntu-latestmirror of the Windows test-compile job.chore(release)— v0.3.13-beta.22 prep.Dependency freshness
ocrs 0.12 → 0.13+rten 0.24 → 0.26— unblocks a pin that three previous runs recorded as"not ours to fix".
ocrs 0.13.0shipped againstrten 0.26, so the pair moves together,rten/rten-tensorstay unified, andOcrEngineParams { detection_model, recognition_model }—the exact call the roadmap predicted would fail — compiles with zero source changes.
wide 1.6 → 1.7,playwright-rs 0.16 → 0.17,deno_core 0.410 → 0.411, plus the compatiblecargo updatesweep.malachite-bigintre-pinned to 0.9.2 as documented.criterion 0.8 → "0.7"(a downgrade, as the roadmap warns);typescript 7(vue-tscstill cannot load it);vuedraggableleft at^4.1.0— a blanketpnpm update --latestwould have silently downgraded it to the Vue-2 line, so packages wereupgraded explicitly instead.
@tiptap/* 3.31.3,vitest 4 → 5,@lucide/vue 1.42,vue-router 5.3.1,@playwright/test 1.63,happy-dom 20.14,@vue/test-utils 2.5,postcss 8.5.28, Tauri plugins— all with zero source changes.
The new guard
held_back_crates_stay_below_their_ceilingcatches a crate resolving to one version that is toonew, where the August guard catches two versions.
CeilingCratecarries alift_whenfieldbeside
reason/remedy: a ceiling holds back upstream fixes, so recording the condition thatretires it means the pin gets deleted on purpose rather than renewed forever.
Version comparison is positional and dependency-free, and compares components numerically on
purpose — as text,
"0.2.9"sorts above"0.2.186", which would make the guard silently useless. Anegative-space test pins that.
Verified it fires, not just that it was written: re-applying
cargo update -p libc --precise 0.2.189makes it reportlibc resolved to 0.2.189 but must stay at or below 0.2.186with the remedyand the lift condition. 5 tests, 0.00s.
Verification
All on Linux, after the fixes:
cargo build --workspace --exclude nanna-guicargo test --workspace --exclude nanna-guicargo clippy --workspace --all-targets --exclude nanna-guicargo build --release -p nanna-daemonvue-tsc --noEmit/pnpm test/pnpm buildReal-binary check. The built Linux daemon, booted against a scratch config (
NANNA_CONFIG_PATH--data-dir+--no-pid-file, ports 51997/51998 — never the operator's), reachesDaemon ready,serves IPC and health, answers
GET /healthwith{"status":"ok","version":"0.3.11","uptime_secs":1}, handles SIGTERM cleanly, and logs zeropanics. The only errors are Ollama being unreachable (not installed on this host), which the
readiness-wait path handles as designed rather than by burning retry budget.
Benchmark (
widetouchesnanna-simd, so the sweep is perf-relevant):Inside budget at every size.
bench/BASELINE.mdwas deliberately not updated: the recordednumbers are Windows, these are Linux, and the box was at load average 112 with a second Rust build
running — three uncontrolled variables at once. Contention can only have hurt these numbers, so
passing under it is a sound pass, but it is not a measurement worth enshrining. Filed as a re-measure.
Not verified
cargo buildandcargo testare green; the desktop app is not part ofthat claim, and
nanna-guistays excluded from the new CI job because it needs WebKitGTK systempackages. Filed.
It has now: it passed on this PR's first CIcompile-tests-linuxitself has never run.run in 7m54s, with no extra system packages needed. (Kept here rather than deleted so the claim
is traceable — it was written before the run and is now settled.)
Environment notes (need your attention)
documented worktree setup entirely. You fixed it mid-run. One leftover I fixed myself:
/mnt/deepmem/.pnpm-storewas still root-owned, failing everypnpm installwithERR_PNPM_EACCES; Ichowned it to you.nanna-nightly.lockfrom 2026-09-02 and an orphaned../nanna-nightlyworktree wereleft by a run that died early (its branch had no commits, so nothing was lost). Both cleared.
mummu-dev-routineran concurrently for most of this session. Load average hit 112 with87% iowait, several
rust-lldlinkers at ~4 GB each; onecargo testwas killed outright andcargowas SIGKILLed twice early on. Everything here was re-run to completion at-j 3/-j 4, butthe two nightlies contending for one disk is worth scheduling around.
RELEASE_NOTES.mdon master describesv0.3.11-beta.20, PR nightly 2026-08-28 (v0.3.12-beta.21): live-and-wrong code — reactions that inverted their meaning, two unbounded maps, and read_pdf that never worked #285 prepares v0.3.12-beta.21, and the newest published tag is
v0.3.10-beta.19.
Post-merge — cutting the release
🤖 Generated with Claude Code