| AppNotification | One entry in the InfoBar stack. Immutable, identity-stamped, and carries an optional action button so a notification can offer its own remedy (e.g. "restart the app" after a service install). |
| AppPaths | Resolves where the app keeps its state — <b>portable by default</b> (ADR-0024). On first access it picks one data root, once, in this order: ... The machine-scope service index (%ProgramData%) and the admin in-proc index are intentionally <i>not</i> redirected — that path is an install by nature, the opposite of portable. Resolution is silent: it must not call FileLog, because FileLog's directory comes from here (a cycle otherwise). |
| AppSettings | User-scope settings at %APPDATA%\find-my-files\settings.json — UI-owned, deliberately separate from the machine-scope service.json the service owns. A corrupt file degrades to defaults: warn, quarantine as .bad, and the next save starts clean. |
| DispatcherQueueDispatcher | Production IDispatcher: a thin wrapper over the UI thread's cached Microsoft.UI.Dispatching.DispatcherQueue (CLAUDE.md UI rules — cache on the UI thread, TryEnqueue from background threads). |
| ExceptionPolicy | The single home for the process-wide exception funnels (don't crash / don't hang / don't go silent). Three sources, three suppression rules: 1. XAML Microsoft.UI.Xaml.Application.UnhandledException — suppressed (Handled = true) and surfaced as an error InfoBar for the first FindMyFiles.Services.ExceptionPolicy.XamlStormBudget occurrences. Beyond that the process is in an exception storm: a crash marker is written and the exception is left unhandled so the process dies honestly. 2. System.AppDomain.UnhandledException — never suppressible by contract: log + crash marker, then the runtime terminates. 3. System.Threading.Tasks.TaskScheduler.UnobservedTaskException — always observed (the task is already dead; tearing the process down helps nobody) and surfaced as an error InfoBar. Log routing: every path writes to FileLog (%APPDATA%\find-my-files\logs\app.log); user-visible surfaces go through Notifier, which mirrors to the same log. Crash markers are written by the fatal paths (1-storm and 2) and read back + cleared by ReportPreviousCrash() on the next launch, so no crash is ever silent. |
| FileLog | Zero-dependency file logger for the app process. Covers everything on the C# side; the log directory is resolved by AppPaths (portable <exe>\data\logs by default, else %APPDATA%\find-my-files\logs) — the same dir the scope engine logs into. Thread-safe, single rotation generation. |
| Loc | Localized-string facade over the Windows App SDK ResourceLoader (PRI built from Strings/<lang>/Resources.resw). Code keys are flat identifiers (Area_Thing, e.g. Status_Preparing); XAML strings come from x:Uid instead. The Override seam lets unit tests resolve keys without a PRI in the test host. |
| Notifier | Process-wide notification funnel. Anything (global handlers, background tasks, the engine callback) can post from any thread; the ViewModel subscribes and marshals to the UI InfoBar stack. Posts are also mirrored to the file log so nothing the user saw is missing from a bug report. |
| ScopeFolderPicker | Folder picker for scope mode (ADR-0024). Unpackaged WinUI 3 requires a picker to be associated with the app window's HWND before it is shown — it throws otherwise — so this always initializes with WindowHandle. |
| ScopePaths | Pure path helpers for scope mode (ADR-0024/-0025). The engine treats scope roots as independent and would double-walk a nested pair, so the UI normalizes the chosen set; excludes must sit under a root to mean anything. No I/O — unit-tested directly. |
| ServiceProvisioner | The shared "make file search usable" steps, behind both the setup screen's one-click button and the management dialog's register action: register the fmf-engine service elevated, then wait for its pipe to start serving and relaunch so this (unelevated, empty-fake) instance comes back connected. The engine transport is chosen once at startup, so a relaunch is how a freshly registered service takes effect. Instance-based so the elevated setup, the pipe probe, the relaunch and the inter-probe delay are injectable boundaries (ADR-0022) — production code uses Real, which wires the real statics; tests drive fakes. |
| ServiceSetup | In-app service setup — the GUI half ADR-0016 left to a terminal: detects the fmf-engine SCM registration (read-only, works unelevated) and drives fmf-service.exe install/start so the one-time elevation never needs PowerShell. Mutations are strictly user-initiated (the notification button); install is idempotent on the service side. |
| ShellOps | Shell-facing operations, centralized so every failure path notifies the user instead of crashing. Targets launch via explorer.exe to shed the process's elevation (CLAUDE.md UI rules). |
| TaskExtensions | Extensions for safely launching fire-and-forget System.Threading.Tasks.Tasks (CLAUDE.md rule: never _ = SomeAsync()). |