agent: dev launcher — own Linux env-var contract, add dev:tauri, doc sweep

The 2026-05-12 engine-slop pass removed runtime std::env::set_var mutation from src-tauri/src/lib.rs and replaced it with warn_if_x11_env_unset_on_wayland. With no launcher owning the contract, Linux Wayland dogfood was about to regress.

run.sh:
- now owns Linux launcher env defaults via case "$(uname -s)"
  LIBCLANG_PATH=/usr/lib64/llvm21/lib64 (user-set wins)
  WEBKIT_DISABLE_DMABUF_RENDERER=1 (always on Linux; user-set wins)
  GDK_BACKEND=x11, WINIT_UNIX_BACKEND=x11 (Wayland only; user-set wins)
- 60s Vite readiness timeout
- detects early Vite exit (kill -0 + wait) instead of hanging forever
- trap installed before wait so Ctrl-C cleans up
- args forwarded: ./run.sh --release etc.
- non-exec final Tauri launch preserved so cleanup trap fires

package.json:
- "dev:tauri": "./run.sh" — canonical discoverable dev command

Docs:
- README, dev-setup, architecture-map runtime + launcher pages updated with the new contract; canonical command is npm run dev:tauri (./run.sh as direct equivalent)
- dev-launcher-and-scripts.md replaces the incorrect "kills process group" claim with honest "kills the spawned npm process"
- dev-setup.md path /CORBEL-Projects/magnotia → /CORBEL-Projects/transcription-app
- gpu-tuning/plan.md gets a superseded note rather than rewriting the original rationale
- engine-slop-residuals.md gains Area F (packaged-binary launcher contract) with honest wrapper-vs-.desktop trade-off documented

Verification: bash -n run.sh; shellcheck clean; cargo check -p magnotia green; npm pkg get 'scripts.dev:tauri' returns "./run.sh"; stale-ref sweep clean across living docs.
This commit is contained in:
2026-05-12 22:05:33 +01:00
parent db654deecc
commit 792fb5ea08
11 changed files with 227 additions and 73 deletions

View File

@@ -2,65 +2,107 @@
name: Dev launcher and scripts
type: architecture-map-page
slice: 05-core-storage-hotkey-build
last_verified: 2026/05/09
last_verified: 2026/05/12
---
# Dev launcher and scripts
> **Where you are:** [Architecture map](../README.md) → [Core, Storage, Hotkey, Build](README.md) → Dev launcher and scripts
**Plain English summary.** Two ways to start Magnotia in development. `run.sh` is the bespoke launcher that starts Vite first and then Tauri (avoiding a Tauri-spawned-second-Vite mess). The `package.json` scripts are the canonical npm-run interface — the same scripts CI uses for the frontend gate.
**Plain English summary.** `run.sh` is the canonical full-stack development launcher and is exposed through `npm run dev:tauri`. It starts Vite first, waits for it to bind port 1420, then runs Tauri with Tauri's own `beforeDevCommand` disabled so a second Vite instance does not race the first one. On Linux it also owns the rendering env-var contract that `src-tauri/src/lib.rs::warn_if_x11_env_unset_on_wayland` checks.
## At a glance
- Files: `run.sh` (636 bytes, +x), `package.json` (1,211 bytes), `package-lock.json` (109 KB).
- External tools: `npm`, `npx`, `curl`, `bash` (run.sh); the Node toolchain (package.json scripts).
- Consumers: `run.sh` is invoked by Jake on his Monolith. The `package.json` scripts are invoked by both CI (`npm run build`) and developers (`npm run dev`).
- Files: `run.sh` (+x), `package.json`, `package-lock.json`.
- External tools: `npm`, `npx`, `curl`, `bash` (`run.sh`); the Node toolchain (`package.json` scripts).
- Consumers: developers use `npm run dev:tauri` (canonical) or `./run.sh` (direct shell equivalent). CI uses package scripts such as `npm run build` and `npm run check`.
## `run.sh` — the local launcher
## `run.sh` — the full-stack dev launcher
```bash
#!/usr/bin/env bash
# Magnotia dev launcher. Starts Vite first, waits for it to bind port 1420,
# then runs Tauri without triggering a second Vite instance.
# Sets the Linux rendering env vars that warn_if_x11_env_unset_on_wayland
# (src-tauri/src/lib.rs) expects the launcher to own.
# For performance testing use: npm run tauri build
set -euo pipefail
cd "$(dirname "$0")"
export LIBCLANG_PATH=/usr/lib64/llvm21/lib64
case "$(uname -s)" in
Linux)
# Bindgen libclang path. Fedora 41/LLVM 21 default; user-set wins.
export LIBCLANG_PATH="${LIBCLANG_PATH:-/usr/lib64/llvm21/lib64}"
# iGPU idle-cost workaround. Set to 0 explicitly to opt out.
export WEBKIT_DISABLE_DMABUF_RENDERER="${WEBKIT_DISABLE_DMABUF_RENDERER:-1}"
if [ "${XDG_SESSION_TYPE:-}" = "wayland" ]; then
export GDK_BACKEND="${GDK_BACKEND:-x11}"
export WINIT_UNIX_BACKEND="${WINIT_UNIX_BACKEND:-x11}"
fi
;;
esac
printf 'Starting Vite dev server...\n' >&2
npm run dev:frontend &
VITE_PID=$!
until curl -sf http://localhost:1420 > /dev/null 2>&1; do sleep 0.5; done
printf 'Vite ready. Launching Tauri...\n' >&2
cleanup() {
kill "$VITE_PID" 2>/dev/null || true
}
trap cleanup EXIT INT TERM
npx tauri dev --config '{"build":{"beforeDevCommand":""}}'
# Wait up to 60s for Vite, bailing if it exits early.
for _ in $(seq 1 120); do
if curl -sf http://localhost:1420 > /dev/null 2>&1; then
break
fi
if ! kill -0 "$VITE_PID" 2>/dev/null; then
printf 'Vite dev server exited before becoming ready.\n' >&2
wait "$VITE_PID"
exit 1
fi
sleep 0.5
done
if ! curl -sf http://localhost:1420 > /dev/null 2>&1; then
printf 'Timed out waiting for Vite on http://localhost:1420\n' >&2
exit 1
fi
printf 'Vite ready. Launching Tauri...\n' >&2
npx tauri dev --config '{"build":{"beforeDevCommand":""}}' "$@"
```
### Why this exists
The default `npm run tauri dev` invokes `tauri dev`, which honours `beforeDevCommand` from `tauri.conf.json` and spawns its own Vite. If Vite is already running (eg during interactive iteration), you get two instances racing for port 1420. The launcher solves it by starting Vite explicitly and disabling Tauri's spawn via `--config '{"build":{"beforeDevCommand":""}}'`.
The default `tauri dev` path honours `beforeDevCommand` from `tauri.conf.json` and spawns Vite itself. That is convenient for the simple path, but during interactive iteration it can create a second Vite instance racing for port 1420. The launcher solves this by starting Vite explicitly and disabling Tauri's spawn via `--config '{"build":{"beforeDevCommand":""}}'`.
The launcher is also the dev-time owner of Linux rendering defaults. Rust startup no longer calls `std::env::set_var`; it only warns when the launcher contract was missed.
### Steps
1. `set -euo pipefail` — early exit on any command failure or unset variable.
2. `cd "$(dirname "$0")"` — anchor to the repo root.
3. **Hard-codes `LIBCLANG_PATH=/usr/lib64/llvm21/lib64`** — Fedora 41 default LLVM 21 path. **Breaks on Debian / Ubuntu / macOS.** Worth softening to `LIBCLANG_PATH=${LIBCLANG_PATH:-/usr/lib64/llvm21/lib64}` so a developer-set value wins.
4. Spawn `npm run dev:frontend &`; capture PID in `VITE_PID`.
5. Poll `curl -sf http://localhost:1420` every 500 ms until 200 OK.
6. Trap `EXIT INT TERM` to kill the Vite process group on script exit.
7. `npx tauri dev` with `beforeDevCommand` disabled.
3. On Linux, default `LIBCLANG_PATH` to Fedora's LLVM path only if the user has not set it already.
4. On Linux, default `WEBKIT_DISABLE_DMABUF_RENDERER=1`; users can opt out with `WEBKIT_DISABLE_DMABUF_RENDERER=0 ./run.sh`.
5. On Wayland sessions, default `GDK_BACKEND=x11` and `WINIT_UNIX_BACKEND=x11`; user-set values win.
6. Spawn `npm run dev:frontend &`; capture its PID.
7. Install a trap that kills the spawned `npm run dev:frontend` process on launcher exit/interrupt.
8. Poll `curl -sf http://localhost:1420` every 500 ms for up to 60 seconds; if Vite exits early or never becomes ready, fail instead of hanging forever.
9. Run `npx tauri dev` with `beforeDevCommand` disabled and forward any extra args (`./run.sh --release`).
### Linux rendering env-var contract
`src-tauri/src/lib.rs::warn_if_x11_env_unset_on_wayland` is a safety net. It warns when the process starts without env vars that must be set before WebKitGTK/WINIT initialise:
- `WEBKIT_DISABLE_DMABUF_RENDERER=1` — Linux-wide iGPU idle-cost workaround. Set `WEBKIT_DISABLE_DMABUF_RENDERER=0` before launching to opt out.
- `GDK_BACKEND=x11` and `WINIT_UNIX_BACKEND=x11` — only expected when `XDG_SESSION_TYPE=wayland`.
Development uses `run.sh` for that contract. Packaged Linux builds need an equivalent wrapper or `.desktop` `Exec=env` policy; see the residual packaging plan.
### `LIBCLANG_PATH` rationale
Bindgen (pulled by `whisper-rs-sys` and `llama-cpp-sys-2`) needs a `libclang.so` at build time. On Fedora's LLVM 21 packaging, the canonical location is `/usr/lib64/llvm21/lib64`. On Debian-family the path is `/usr/lib/llvm-N/lib`; on macOS Homebrew it's `$(brew --prefix llvm)/lib`. The hard-coded value means a clean `git clone` on a non-Fedora machine fails the first build until the developer sets `LIBCLANG_PATH` themselves.
Bindgen (pulled by `whisper-rs-sys` and `llama-cpp-sys-2`) needs a `libclang.so` at build time. On Fedora's LLVM 21 packaging, the canonical location is `/usr/lib64/llvm21/lib64`. On Debian-family the path is `/usr/lib/llvm-N/lib`; on macOS Homebrew it's `$(brew --prefix llvm)/lib`. `run.sh` now uses `LIBCLANG_PATH=${LIBCLANG_PATH:-/usr/lib64/llvm21/lib64}` on Linux so Fedora works out of the box while developer-set values win.
## `package.json` — npm scripts and deps
@@ -70,6 +112,7 @@ Bindgen (pulled by `whisper-rs-sys` and `llama-cpp-sys-2`) needs a `libclang.so`
"scripts": {
"dev": "npm run dev:frontend",
"dev:frontend": "svelte-kit sync && vite dev",
"dev:tauri": "./run.sh",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
@@ -79,9 +122,10 @@ Bindgen (pulled by `whisper-rs-sys` and `llama-cpp-sys-2`) needs a `libclang.so`
```
- `npm run dev` — frontend only (no Tauri). Useful for pure-UI iteration.
- `npm run dev:tauri` — canonical full-stack dev launcher; calls `./run.sh`.
- `npm run build` — Vite production build. CI gate.
- `npm run check``svelte-check` against `jsconfig.json`. CI gate.
- `npm run tauri` — passthrough. `npm run tauri dev`, `npm run tauri build`, etc.
- `npm run tauri` — passthrough for non-dev Tauri commands such as `npm run tauri build`.
### Runtime dependencies
@@ -104,8 +148,7 @@ Bindgen (pulled by `whisper-rs-sys` and `llama-cpp-sys-2`) needs a `libclang.so`
## Watch-outs
- **`run.sh` `LIBCLANG_PATH` is Fedora-specific.** A new developer on Ubuntu / macOS hits a build failure on first run and has to either edit `run.sh` or pre-set the env var. Worth the conditional default.
- **No `dev:fullstack` script.** The Tauri dev path goes through `npx tauri dev` (or `run.sh`), not through an npm script. A `npm run dev:fullstack` shortcut would make discovery easier.
- **The trap kills the spawned npm process, not a full process group.** If Vite-orphan leaks become a real dev annoyance, switch to starting a process group (`setsid`) and killing `-- -$VITE_PID` on Linux.
- **Tailwind v4 is in flight.** Major version, no PostCSS config. Cross-link to slice 1 for the consumed surface.
- **`@tauri-apps/api` is pinned to `2.10.1` (no caret).** Intentional because Tauri's JS bridge can shift behaviour subtly between minors. Pinned ensures we test what we ship.
- **Plugin versions are caret-ranged.** Compatible-change updates land transparently. `npm audit` (CI) catches advisories.

View File

@@ -78,5 +78,5 @@ The `panic = "abort"` setting is a deliberate trade-off:
## See also
- [CI pipeline](ci-pipeline.md) — uses this profile for release builds.
- [Dev launcher and scripts](dev-launcher-and-scripts.md) — `npm run tauri dev` uses the default debug profile.
- [Dev launcher and scripts](dev-launcher-and-scripts.md) — `npm run dev:tauri` uses the default debug profile via the canonical `run.sh` launcher.
- [Storage Cargo configuration](storage-overview.md) — the per-crate sqlx feature stripping.