#6 sandbox: true на обоих BrowserWindow (раньше false). Preload
использует только contextBridge + ipcRenderer (оба sandbox-safe),
никаких Node-built-ins. OS-уровневый sandbox изолирует renderer
от GPU/IPC процессов; даже RCE в зависимости renderer'а не
получит Node-доступа через preload.
#17 self-host шрифтов через @fontsource/* пакеты. Раньше тянулись
с fonts.googleapis.com — внешняя CSP-зависимость + отсутствие
интернета = шрифты не загружались. Теперь .woff/.woff2 в bundle
(22 файла × 15-30KB = ~500KB).
Подкрутили CSP: убрали https://fonts.* origins, добавили
connect-src 'self', base-uri 'self', frame-ancestors 'none'.
#22 src/main/logger.ts — структурный лог с уровнями
(debug/info/warn/error) и ротацией. Пишет в
%APPDATA%/Exercise Reminder/logs/latest.log (≤1MB) и
дублирует в console. При 1MB latest.log → prev.log
(предыдущий prev.log удаляется). LAUDE_DEBUG=1 включает
debug-уровень.
Подключён в hot paths: store (corrupt/atomic write fails),
updater (silent check errors), gsi-server (bad requests,
handler throws), games/registry (GSI start, reconcile, match_end
summary), games/dota2 (rejected token, POST_GAME detection).
Особенно полезно для диагностики «челленджи не срабатывают»:
лог покажет (а) пришёл ли вообще GSI payload (token verify),
(б) детектировался ли POST_GAME, (в) сколько challenges были
enabled и которые из них дали 0 reps.
Logger — единственный файл с `eslint-disable no-console` (он
намеренно дублирует в stderr).