--- name: Core process-watch (meeting detection) type: architecture-map-page slice: 05-core-storage-hotkey-build last_verified: 2026/05/09 --- # Core process-watch > **Where you are:** [Architecture map](../README.md) → [Core, Storage, Hotkey, Build](README.md) → Core process-watch **Plain English summary.** A lightweight "is the user in a meeting right now?" probe. One signal only: poll the running-process list and match user-editable substrings. No mic-activity heuristic, no calendar integration. If the user has opted in, Magnotia surfaces a non-modal toast so they can choose to start recording. The app never starts recording on its own from this signal. ## At a glance - File: `crates/core/src/process_watch.rs` (123 LOC, ~40 of which are tests). - External deps: `sysinfo 0.35`. - Public surface: `ProcessLister` (struct), `list_running_process_names`, `match_meeting_patterns`. - Consumers: slice 2 (a Tauri command holds a long-lived `ProcessLister` behind `Mutex` and polls every 15 s). ## What's in here ### `ProcessLister` — `crates/core/src/process_watch.rs:20` ```rust pub struct ProcessLister { system: System } impl ProcessLister { pub fn new() -> Self; pub fn snapshot(&mut self) -> Vec; // lowercased exe names } impl Default for ProcessLister { ... } ``` Reusable wrapper around a `sysinfo::System`. The system is created once with `RefreshKind::nothing().with_processes(ProcessRefreshKind::nothing())` and refreshed in place via `refresh_processes(ProcessesToUpdate::All, true)` on every call. On a busy host (~300 processes), `System::new_with_specifics` followed by `refresh_processes` walks `/proc` cold and costs ~50–100 ms; reusing the same instance reuses sysinfo's per-process bookkeeping so subsequent refreshes are dominated by diffing rather than allocation. ### `list_running_process_names()` — `crates/core/src/process_watch.rs:59` Convenience function. Allocates a fresh `ProcessLister` per call. Hot paths should hold a long-lived `ProcessLister` instead. ### `match_meeting_patterns(process_names, patterns) -> Vec` — `crates/core/src/process_watch.rs:67` Pure function. Case-insensitive substring match. Returns the set of patterns that matched at least once, in input order, deduped. Empty / whitespace-only patterns are skipped so a stray blank entry in the user's list never matches everything. ## Data flow / contract - The slice-2 command holds the `ProcessLister` in `tauri::State` behind a `Mutex` and polls every 15 seconds. - Pattern list comes from user settings (the UI surfaces it as a comma-separated text box). - Match output drives a non-modal toast in the frontend. Magnotia never starts recording itself — the user decides. ## Tests 4 tests in `crates/core/src/process_watch.rs:84-122`: - `matches_are_case_insensitive_substrings` — happy path with `Zoom Meeting` / `Microsoft Teams` / `firefox`. - `empty_and_whitespace_patterns_are_ignored` — guard. - `matches_are_deduped` — duplicate `zoom` patterns produce one match. - `list_running_returns_something_on_this_host` — smoke against the test runner's own process list. ## Watch-outs - **`refresh_processes(ProcessesToUpdate::All, true)` is still ~5–10 ms even reused.** OK at 15 s cadence, would not be at sub-second cadence. - **Substring match is broad.** The pattern `"zoom"` matches a process named `zoomies` (a hypothetical screensaver). User-configurable so the user owns the false positive. - **Lowercased once, searched many times.** `process_names` are lowercased at snapshot time so the matcher itself can stay simple. If we ever want regex matching we will lose this optimisation. - **Linux / macOS / Windows process-name conventions differ.** `Zoom` on macOS, `zoom` on Linux, `Zoom.exe` on Windows. The case-insensitive substring match handles the case differences cleanly. The `.exe` suffix on Windows is fine because the pattern is a substring. - **Privacy.** Process names can leak metadata (other apps the user runs). The probe runs locally and the results never leave the machine, but it is a class of data to be careful with. ## See also - [Hardware probe](core-hardware-probe.md) — sibling probe. - [Slice 2 Tauri commands](../02-tauri-runtime/README.md) for the meeting-detection command that owns the long-lived `ProcessLister`.