agent: engine slop pass — DSP, typed errors, regex parsing, tracing, audit fixes

External code review on 2026-05-12 rated the codebase 4/10 across audio DSP, error typing, JS injection, env-var safety, ALSA parsing, and async logging. This commit lands the prognosis-level fixes plus three audit follow-ups.

Audio/DSP:
- StreamingResampler/rubato confirmed in the live capture path
- regression test at 12 kHz (rms < 0.01, ~40 dB) catches naive decimation
- near-Nyquist test at 9 kHz (rms < 0.05, ~26 dB) exercises transition band

Core errors:
- Other(String) removed; ProviderNotRegistered introduced
- Io variant restructured as struct with kind/message/raw_os_error
- FileNotFound display quotes paths
- Configuration variant removed (unused)

Core types:
- ModelId, EngineName backed by Cow<'static, str>; const borrowed ctor
- Megabytes::from_gb takes u64 (was f64)
- AudioSamples::sample_rate is NonZeroU32; zero-rate defensive branch removed

Capture:
- /proc/asound/cards parsing rewritten as anchored regex (OnceLock)
- regression test covers product names with embedded colons
- monitor_pattern_detection test restored alongside the regex test
- DEAD_SILENCE_FLOOR promoted to module-level with rationale
- DEVICE_VALIDATION_MS, SILENCE_RMS_FLOOR documented with field-observation rationale
- RMS validation loop made idiomatic
- eprintln! migrated to tracing with structured fields and targets

Tauri startup:
- unsafe std::env::set_var removed; ensure_x11_on_wayland renamed to warn_if_x11_env_unset_on_wayland (launcher/wrapper owns env-var contract)
- DB init + log prune + preferences load collapsed to one block_on
- build_preferences_script rewrites JS injection from JSON.parse string to direct object literal plus malformed-JSON guard and unit tests
- WebKitGTK microphone auto-grant logs warning at startup
- tracing subscriber initialised at top of run() (warn,magnotia=info,... on stderr; honors RUST_LOG); previously eprintln→tracing migration was silent because no subscriber existed

Filename counter:
- RECORDING_COUNTER uses SeqCst

Tests: cargo test --workspace --lib green (322 passed, 0 failed across 10 crates).

Three independent audits (original cleanup → Wren → fresh Codex subagent) concur on no critical findings.

Deferred to docs/superpowers/plans/2026-05-12-engine-slop-residuals.md: storage-layer typed errors, remaining eprintln→tracing sweep, capture actor-model refactor, property-based DSP testing, frontend/backend error boundary cleanup.
This commit is contained in:
2026-05-12 22:03:58 +01:00
parent b463c32f17
commit db654deecc
14 changed files with 557 additions and 208 deletions

View File

@@ -52,6 +52,8 @@ tauri-plugin-notification = "2"
# Serialisation
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# Async runtime (spawn_blocking for inference)
tokio = { version = "1", features = ["rt", "sync"] }

View File

@@ -60,7 +60,7 @@ fn recording_filename() -> String {
.unwrap_or_default();
let secs = duration.as_secs();
let nanos = duration.subsec_nanos();
let counter = RECORDING_COUNTER.fetch_add(1, Ordering::Relaxed);
let counter = RECORDING_COUNTER.fetch_add(1, Ordering::SeqCst);
format!("magnotia-{secs}-{nanos:09}-{counter:04}.wav")
}

View File

@@ -25,14 +25,14 @@ fn whisper_model_id(size: &str) -> ModelId {
"distil-large" | "distil-large-v3" | "distillarge" => {
ModelId::new("whisper-distil-large-v3")
}
other => ModelId::new(other),
other => ModelId::new(other.to_string()),
}
}
fn parakeet_model_id(name: &str) -> ModelId {
match name {
"ctc-int8" => ModelId::new("parakeet-ctc-0.6b-int8"),
other => ModelId::new(other),
other => ModelId::new(other.to_string()),
}
}
@@ -121,7 +121,7 @@ pub async fn ensure_model_loaded(
model_id: &str,
concurrent: Option<bool>,
) -> Result<(), String> {
let model_id = ModelId::new(model_id);
let model_id = ModelId::new(model_id.to_string());
let entry = model_registry::find_model(&model_id)
.ok_or_else(|| format!("Unknown model: {model_id}"))?;

View File

@@ -5,10 +5,12 @@ mod commands;
mod tray;
use std::sync::Arc;
use std::sync::Once;
use std::time::Instant;
use sqlx::SqlitePool;
use tauri::Manager;
use tracing_subscriber::EnvFilter;
use magnotia_core::types::EngineName;
use magnotia_llm::LlmEngine;
@@ -20,6 +22,8 @@ use magnotia_storage::{database_path, get_setting, init as init_db, prune_error_
const ERROR_LOG_RETENTION_DAYS: i64 = 90;
use magnotia_transcription::LocalEngine;
static TRACING_INIT: Once = Once::new();
/// Shared app state holding the transcription engines and database pool.
pub struct AppState {
pub whisper_engine: Arc<LocalEngine>,
@@ -38,12 +42,15 @@ fn build_preferences_script(prefs_json: Option<String>) -> String {
if json.is_empty() {
return String::new();
}
// Serialise the JSON string as a JS string literal for safe embedding
let js_str = serde_json::to_string(&json).unwrap_or_else(|_| "\"\"".to_string());
let js_value: serde_json::Value = match serde_json::from_str(&json) {
Ok(value) => value,
Err(_) => return String::new(),
};
let js_obj = serde_json::to_string(&js_value).unwrap_or_else(|_| "{}".to_string());
format!(
r#"(function() {{
try {{
var p = JSON.parse({js_str});
var p = {js_obj};
var el = document.documentElement;
if (p.theme === 'system') {{
el.dataset.theme = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
@@ -67,6 +74,23 @@ fn build_preferences_script(prefs_json: Option<String>) -> String {
)
}
#[cfg(test)]
mod tests {
use super::build_preferences_script;
#[test]
fn preferences_script_injects_object_without_redundant_json_parse() {
let script = build_preferences_script(Some(r#"{"theme":"dark"}"#.to_string()));
assert!(script.contains("var p = {\"theme\":\"dark\"};"));
assert!(!script.contains("JSON.parse"));
}
#[test]
fn preferences_script_rejects_malformed_json() {
assert!(build_preferences_script(Some("not json".to_string())).is_empty());
}
}
/// Save preferences JSON to the SQLite settings table.
#[tauri::command]
async fn save_preferences(
@@ -87,23 +111,25 @@ async fn save_preferences(
/// WEBKIT_DISABLE_DMABUF_RENDERER=1 npm run tauri dev
/// ```
///
/// Detect the Wayland session at startup and apply the env vars before
/// anything else loads, so users do not need to remember the prefix and
/// the dev launcher / packaged app both work out of the box.
/// Detect the Wayland session at startup and warn when the process was not
/// launched with the safe Linux rendering env vars. Rust 2024 marks runtime
/// environment mutation unsafe in multi-threaded programs, so the app must be
/// launched with these values by the desktop file, package wrapper, or dev
/// shell rather than patching them here.
///
/// Inspired by Open-Whispr's similar Chromium self-relaunch (with
/// --ozone-platform=x11). Day 6 of the upgrade plan.
#[cfg(target_os = "linux")]
fn ensure_x11_on_wayland() {
let set_if_unset = |key: &str, value: &str, why: &str| {
fn warn_if_x11_env_unset_on_wayland() {
let warn_if_unset = |key: &str, value: &str, why: &str| {
if std::env::var_os(key).is_none() {
// SAFETY: setting env vars before any threads spawn (we are
// pre-Tauri-Builder here). This block is the only place these
// are written.
unsafe {
std::env::set_var(key, value);
}
eprintln!("[startup] Linux workaround: {key}={value} ({why})");
tracing::warn!(
target: "magnotia_startup",
key,
expected = value,
why,
"Linux rendering workaround env var is not set; configure the launcher instead of mutating process env at runtime"
);
}
};
@@ -115,7 +141,7 @@ fn ensure_x11_on_wayland() {
// magnotia idle CPU 12.30% → 2.80% of one core and idle GPU 17% → 10%.
// Apples to both X11 and Wayland sessions; users can opt back in by
// setting WEBKIT_DISABLE_DMABUF_RENDERER=0 explicitly.
set_if_unset(
warn_if_unset(
"WEBKIT_DISABLE_DMABUF_RENDERER",
"1",
"iGPU idle-cost workaround",
@@ -128,15 +154,32 @@ fn ensure_x11_on_wayland() {
// path.
let session_type = std::env::var("XDG_SESSION_TYPE").unwrap_or_default();
if session_type.eq_ignore_ascii_case("wayland") {
set_if_unset("GDK_BACKEND", "x11", "Wayland XWayland fallback");
set_if_unset("WINIT_UNIX_BACKEND", "x11", "Wayland XWayland fallback");
warn_if_unset("GDK_BACKEND", "x11", "Wayland XWayland fallback");
warn_if_unset("WINIT_UNIX_BACKEND", "x11", "Wayland XWayland fallback");
}
}
fn init_tracing() {
TRACING_INIT.call_once(|| {
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
EnvFilter::new(
"warn,magnotia=info,magnotia_lib=info,magnotia_audio=info,magnotia_startup=info",
)
});
let _ = tracing_subscriber::fmt()
.with_env_filter(env_filter)
.with_writer(std::io::stderr)
.try_init();
});
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
init_tracing();
#[cfg(target_os = "linux")]
ensure_x11_on_wayland();
warn_if_x11_env_unset_on_wayland();
// Capture Rust panics to disk so the diagnostic-report bundler in
// Settings → About can attach them. Local only; nothing transmitted.
@@ -178,36 +221,39 @@ pub fn run() {
builder
.setup(|app| {
// Initialise database (blocking in setup — runs once at startup)
// Initialise database and startup settings in one runtime entry.
let db_path = database_path();
let t0 = Instant::now();
let db = tauri::async_runtime::block_on(async { init_db(&db_path).await })
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
eprintln!("[startup] DB init: {:?}", t0.elapsed());
let (db, init_script) = tauri::async_runtime::block_on(async {
let t0 = Instant::now();
let db = init_db(&db_path)
.await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
tracing::info!(target: "magnotia_startup", elapsed_ms = t0.elapsed().as_millis(), "DB init complete");
// Prune old `error_log` rows so the table doesn't grow unbounded
// across months of dogfooding. Best-effort — a prune failure is
// not worth blocking startup over.
let t_prune = Instant::now();
let pruned = tauri::async_runtime::block_on(async {
prune_error_log(&db, ERROR_LOG_RETENTION_DAYS).await
});
match pruned {
Ok(n) if n > 0 => eprintln!(
"[startup] Error log prune: {n} rows removed (>{ERROR_LOG_RETENTION_DAYS}d) in {:?}",
t_prune.elapsed()
),
Ok(_) => {}
Err(e) => eprintln!("[startup] Error log prune failed: {e}"),
}
// Prune old `error_log` rows so the table doesn't grow unbounded
// across months of dogfooding. Best-effort — a prune failure is
// not worth blocking startup over.
let t_prune = Instant::now();
match prune_error_log(&db, ERROR_LOG_RETENTION_DAYS).await {
Ok(n) if n > 0 => tracing::info!(
target: "magnotia_startup",
rows_removed = n,
retention_days = ERROR_LOG_RETENTION_DAYS,
elapsed_ms = t_prune.elapsed().as_millis(),
"error log prune complete"
),
Ok(_) => {}
Err(e) => tracing::warn!(target: "magnotia_startup", error = %e, "error log prune failed"),
}
// Load saved preferences for webview injection
let t1 = Instant::now();
let prefs_json = tauri::async_runtime::block_on(async {
get_setting(&db, "magnotia_preferences").await.unwrap_or(None)
});
eprintln!("[startup] Preferences load: {:?}", t1.elapsed());
let init_script = build_preferences_script(prefs_json);
// Load saved preferences for webview injection.
let t1 = Instant::now();
let prefs_json = get_setting(&db, "magnotia_preferences").await.unwrap_or(None);
tracing::info!(target: "magnotia_startup", elapsed_ms = t1.elapsed().as_millis(), "preferences load complete");
let init_script = build_preferences_script(prefs_json);
Ok::<_, Box<dyn std::error::Error>>((db, init_script))
})?;
// Apply preferences to the main window (defined in tauri.conf.json)
if let Some(main_window) = app.get_webview_window("main") {
@@ -240,6 +286,11 @@ pub fn run() {
settings.set_enable_media_capabilities(true);
}
tracing::warn!(
target: "magnotia_startup",
"Linux WebKitGTK microphone permission requests are auto-granted for audio-only capture; other permission classes remain denied"
);
// Auto-grant microphone capture only. Other WebKitGTK
// permission requests are denied so future surfaces do
// not inherit camera/geolocation/pointer-lock access.
@@ -270,8 +321,10 @@ pub fn run() {
// Falling back means getUserMedia() prompts (or
// silently denies) instead of auto-granting,
// which is degraded but recoverable.
eprintln!(
"[startup] failed to configure webview media permissions: {e}",
tracing::warn!(
target: "magnotia_startup",
error = %e,
"failed to configure webview media permissions"
);
});
}
@@ -314,7 +367,7 @@ pub fn run() {
#[cfg(not(target_os = "android"))]
if let Err(e) = tray::setup(app) {
eprintln!("Failed to setup tray: {e}");
tracing::warn!(target: "magnotia_startup", error = %e, "failed to setup tray");
}
Ok(())