Reliability: zero-window keepalive + overlay/log hardening; TS7 + oxlint/oxfmt (0.0.50) - #55
Merged
Conversation
…pawn main widgetsack is a tray app, but Tauri's default exits the process when the last window closes. Zero windows is reachable BY DESIGN: an empty-primary `main` self-destructs to reclaim its renderer (overlay.ts setMainWindowVisible), so a layout whose widgets all sit on one secondary monitor lives on a single overlay window — and any transient failure to spawn it (monitor not yet enumerated at logon, WebView2 hiccup, monitor DDC-switched away) silently ended the process. This was the "didn't autostart" failure of 2026-07-10. - keepalive.rs (new): should_prevent_exit pure seam (+tests); on_zero_windows schedules a rate-limited (30s) respawn of the hidden `main`, whose own Canvas init re-runs the reconcile cycle — spawning overlays for whatever monitors exist NOW, then self-destroying again if the primary is still empty. Retries pace themselves off the event cycle (no polling); one attempt in flight; self-reschedules if window creation itself fails. - main.rs: .run(context) → .build(context).run(handler); ExitRequested with code None is prevented. Tray Quit (app.exit(0) → code Some(0)) still exits. - command.rs: respawn_main_hidden made pub(crate) + a `reason` log tag shared by the layout-watcher and keepalive recovery paths. Runtime-verified with a debug --multi instance on a layout whose only populated monitor key matches no connected display: the app survives repeated zero-window cycles (keepalive → respawn → self-destroy) and recovers the overlay as soon as the layout regains a real monitor. cargo test (200 incl. 2 new), clippy, fmt all clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
….0.50) Follow-ups to the zero-window keepalive, from the post-incident review: - log_client command (command.rs) + overlay.ts logClient helper: overlay lifecycle failures (reconcile, spawn, self-fit, main destroy/recreate) now land in the persistent rotating log file as target "client" instead of dying with the webview console — the 2026-07-10 postmortem cost hours because these paths were console-only. - populatedMonitorKeys returns null on a load_layout/parse failure and reconcileOverlays skips the pass entirely — "couldn't read the layout" is no longer treated as "layout is empty" (which closed every working overlay on a transient IPC hiccup). - reconcileOverlays is single-flight with a trailing rerun (new pure core/singleFlight.ts + tests): concurrent passes (init, layout_changed, topology watcher) no longer race to create the same overlay label. - displaywatch.rs: hidden-window WM_DISPLAYCHANGE watcher respawns `main` the moment a monitor returns while the app is at zero windows — instant recovery instead of keepalive's 30s retry. (Deliberately a hidden top-level window, not HWND_MESSAGE: message-only windows don't receive broadcast messages.) - GSMTC SessionManager failure now degrades gracefully (media disabled, app starts) instead of a startup .expect crash; the rx consumer became `while let` so a closed channel can't hot-spin. - cargo update (24 crates within semver; no tauri/wry/softbuffer bumps were available — the upstream softbuffer !dc.is_null() panic remains open). - Version 0.0.50. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- typescript 6.0.3 -> 7.0.2 (native compiler): zero code changes needed; tsc --noEmit drops from ~8.0s to ~1.7s (fast mode; a bimodal ~6.3s mode — suspected Defender scanning the native binary — still beats TS6). - typescript-eslint has no TS7 support (peer <6.1.0, crashes on the changed API), so lint moves to oxlint 1.73 (.oxlintrc.json): typescript/unicorn/oxc/ react plugins, correctness category + explicit overrides mirroring the old recommended tiers. Zero-warnings gate unchanged (--max-warnings=0). Must-keeps preserved: typescript/no-explicit-any, react/rules-of-hooks, react/exhaustive-deps. KNOWN COVERAGE LOSS: 14 of 16 react-hooks v7 (React-Compiler-era) rules have no oxlint equivalent yet — the #54 fixes stay in code but are no longer machine-enforced. Also lost: react/prop-types (moot under TS), react/no-deprecated. - Formatting moves from Prettier to oxfmt 0.58 (.oxfmtrc.json via --migrate=prettier: tabs, single quotes, no trailing commas, width 100). Covers TS/TSX/JS/JSX/CSS/JSON/MD/HTML — no lost coverage; the one-time reformat touched 4 files, whitespace-only. - eslint-disable comments renamed to oxlint-disable (tool-assisted, 11 files); legacy @typescript-eslint/* rule names inside them still resolve. A handful of newly-surfaced correctness findings fixed (useless spread fallbacks, new Array(n) holes, unsafe ?. in tests). - Removed devDeps: eslint, @eslint/js, typescript-eslint, eslint-plugin-react, eslint-plugin-react-hooks, eslint-config-prettier, globals, prettier. - AGENTS.md formatting/linting section updated. Gates: tsc clean, oxfmt+oxlint clean, 2768 unit tests, build, check:docs, 34 Playwright e2e. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reliability (the "didn't autostart" incident, 2026-07-10)
Root cause: widgetsack is a tray app, but Tauri's default exits when the last window closes — and zero windows is reachable by design (an empty-primary `main` self-destructs to reclaim its renderer, leaving layouts with one populated secondary monitor hanging on a single overlay window). Any transient spawn failure ended the process silently.
Toolchain: TypeScript 7 + oxlint + oxfmt
Verification
🤖 Generated with Claude Code