How it works: detect → judge → act → show
The pipeline behind every card: measure everything, judge it against your own baseline on-device, resolve what the process is, and only then surface a plain-language verdict with a safe action.
The pipeline in one picture
Anomalous runs one quiet loop on your Mac: detect → judge → act → show. Every tick it samples each running process, decides whether any of them is behaving abnormally for that specific program, works out what the program is and what to do about it, and only then draws a single plain-English diagnosis card in the menu-bar popover. Almost always the list is empty and you see nothing.
The important thing to know up front: the detection and the judgment happen on-device. The statistics that decide "this is abnormal" never leave your machine. When an on-device language model is involved, it explains what a process is; it never detects the anomaly. A network lookup only happens for a genuine mystery, it is anonymous by construction, and it is optional.
Detect
Detect answers "is anything wrong?" using only what your own Mac has learned is normal. Anomalous watches processes roughly every 90 seconds and keeps a running picture of what is typical for each program specifically — its CPU, memory, wake rate, disk, and on capable Macs GPU and network. When a process does far more than its own usual, in a way that would actually bother you, a rule votes to flag it. Most deviations never clear that bar, which is why the app is usually silent.
Under the hood
Baselines use robust statistics — the median and MAD (median absolute deviation), scaled by 1.4826 so a threshold reads like a familiar z-score — because a plain mean and standard deviation are inflated by the very spike being hunted and hide inside their own distortion (JudgmentCore.swift:12-63). Normal is also learned by time of day across 12 seasonal buckets (six 4-hour slots × weekday/weekend), so a 2am backup is normal at 2am but not at 2pm (JudgmentCore.swift:100-125). Rate rules (wakeups, disk, GPU, network) fire only when three conditions hold at once: a warm-up gate (at least 3 recorded observations), an absolute floor (e.g. wakeups ≥150/s and ≥3% CPU, disk ≥40 MB/s, GPU ≥40%, network ≥25 MB/s), and ≥8 MADs above the process's own median (DetectionRules.swift:439-474). Every rule runs each tick, not first-match, because agreement between rules is itself a confidence signal (AppState.swift:1147-1164). Baselines stay local; only anonymous signatures ever leave (BaselineStore.swift:136-138).
Judge
Judge turns a flag into a graded confidence score and a plain-English identity. Each candidate gets a score from 0 to 1 rather than a yes/no alarm, and only high-confidence findings (≥0.8) ever become a card or notification. Then Anomalous works out what the flagged program actually is, in a strict order that prefers certainty over guessing: first its own built-in, signed knowledge of well-known macOS processes; then, only if the process carries an app bundle id to anchor on, Apple's on-device language model. A genuine mystery daemon — no built-in entry, no bundle to anchor — is never sent to a model, because an honest "unknown" beats a confident invention.
Under the hood
Confidence starts at 0.8 for self-standing heritage rules or 0.5 for statistical rate rules, gains +0.3 per other rule agreeing (capped +0.4) and up to +0.35 for sheer magnitude, and loses 0.4 for memory flags when the whole machine is under memory pressure (the process may be a victim, not the culprit) (JudgmentCore.swift:174-202). A pure routing function decides per anomaly: thermal (kernel_task, reframed as "your Mac is hot"), hungApp (deterministic force-quit), deterministicUnknown (no corpus + no bundle), or model (JudgmentEngine.swift:68-77). The on-device model card is tool-calling: it reads processHistory, baseline, correlated, and corpusEntry over a frozen snapshot and must quote the detector's numbers verbatim rather than re-detect (JudgmentTools.swift:143-228). A currently-flagged process stops teaching its baseline, so a two-day runaway can never train the system that burning is normal (AppState.swift:694-719).
Act
Act offers the single safest thing to do, gated by how risky that action is. The card maps its safety tier to a concrete option: tier 1 is a one-click Quit, Restart, or Update; tier 2 warns first; tier 3 only explains and shows no button. Killing is done carefully — a graceful Quit by default, and only after re-confirming the process is still the exact one that was flagged.
Under the hood
Only tier 1 offers a button; tier ≥3 is explain-only (ProcessAction.swift:34-42). Termination re-checks the pid's start-time against the flagged identity before sending SIGTERM (SIGKILL only when you explicitly force), so pid reuse cannot cause a confident wrong kill (ProcessAction.swift:63-81). Root-owned processes are handled by a privileged helper if installed, otherwise Anomalous surfaces a copy-paste sudo killall <name> command rather than acting for you (AppState.swift:1566-1574). See Safety and actions for the full tier rules.
Show
Show renders one diagnosis card: what the process is, why it is probably hot, whether that is normal, a confidence note, and one action. For a process Anomalous doesn't recognize, the card first shows a conservative "unknown" state and, if lookup is on, a "looking this up…" indicator. Moments later it can upgrade to a researched identity captioned "Sourced by Anomalous" (adding "verified from community-reviewed sources" when the answer cleared verification), with the cited sources shown. Multiple flags on one process collapse into a single card, and related processes group into one insight, so you never get five notifications for one event.
Under the hood
The optional lookup POSTs an anonymous signature to /api/v1/discover: an instant 200 if the shared corpus already knows the identity, otherwise a 202 poll handle the app checks for up to ~3 minutes (DiscoveryController.php:57-117). The request type carries only process name, bundle id, app/OS version, install source, and anomaly type — there is literally no field for a file path, arguments, username, or hostname, and every request is written to a local SendLog before it is sent (DiscoveryClient.swift:33-51,195-212). Server-side, a draft is researched and then an independent verifier adversarially checks it against the fetched text of its own cited sources; only verified=true and confidence ≥0.7 auto-publishes it into the signed corpus so it grounds everyone's cards from then on (ProcessDiscoveryJob.php:35-140). See the lookup loop for the full flywheel.
Why silence is the default
Every stage is tuned so that Anomalous stays quiet unless it is genuinely useful to speak. Detection demands warm-up, an absolute floor, and 8 MADs of deviation before a rate rule fires; judgment surfaces only high-confidence findings and refuses to invent an identity for a mystery daemon; actions with any risk get no button at all. An app that cries wolf gets deleted, so Anomalous ships conservative and loosens only with data.
The detection and grounding logic is open: the sensor lives at anomalous-mac and the public process-identity data at anomalous-corpus (the corpus repo may still be private today; PRs are welcome).