Skip to content

feat(desktop): emit system.wake on window foreground (#1653)#1797

Draft
mabry1985 wants to merge 1 commit into
mainfrom
feat/1653-desktop-system-wake
Draft

feat(desktop): emit system.wake on window foreground (#1653)#1797
mabry1985 wants to merge 1 commit into
mainfrom
feat/1653-desktop-system-wake

Conversation

@mabry1985

Copy link
Copy Markdown
Contributor

What

Completes the third ADR 0074 lifecycle event. The server-side bus contract for system.wake shipped with #1653 (reserved in the seam/config); this wires the desktop origination: on WindowEvent::Focused(true) the Tauri shell POSTs system.wake to the sidecar's existing /api/events/publish, which broadcasts it on the event bus so lifecycle_hooks reactions (resume interrupted work, refresh state, check inbox) fire on wake.

Design

  • Debounced to once per 60 s — focus fires on every alt-tab, so a WakeSignal (sidecar port + a throttle Instant, in Tauri managed state) suppresses rapid re-focus. It's seeded to "now" at setup, so the window's own boot focus is suppressed (no redundant system.wake right after app.loaded).
  • Fire-and-forget via tauri::async_runtime::spawn; a down/booting sidecar just logs at debug. Never blocks the UI thread; the throttle lock is dropped before the await.
  • Reuses the existing reqwest + port-resolution plumbing — no new endpoint.

Scope

Foreground-focus is the v1 signal. True OS sleep/wake (laptop lid, display sleep) has no Tauri primitive — it'd need native NSWorkspace.didWakeNotification / Windows WM_POWERBROADCAST observers, a deliberate later lift.

Verification

  • cargo check passes (compiled lib.rs clean in a worktree, after stubbing the bundled sidecar binary that isn't present there — the stub is not committed).
  • DRAFT because the actual wake behavior can't be CI-verified — it needs a desktop build + manual test: run the app, blur it >60 s, refocus, and confirm a system.wake broadcast (e.g. a lifecycle_hooks reaction fires, or watch the sidecar /api/events stream). No Python/CI surface changes.

Closes the desktop system_wake follow-up on #1653.

🤖 Generated with Claude Code

Completes the third ADR 0074 lifecycle event. On WindowEvent::Focused(true)
the desktop shell POSTs `system.wake` to the sidecar's /api/events/publish,
which broadcasts it on the event bus so `lifecycle_hooks` reactions fire on
wake. Debounced to once per 60s (a WakeSignal holding the sidecar port + a
throttle clock in Tauri managed state) so an alt-tab doesn't spam it; the
boot focus is seeded inside the window so it doesn't double-fire after
app.loaded. Fire-and-forget — a down/booting sidecar just logs.

Foreground-focus is the v1 signal; true OS sleep/wake (NSWorkspace /
power-broadcast) would be a native follow-up.

Closes the desktop system_wake follow-up on #1653.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e08a5d41-82b9-40fe-ba11-932973979ee7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/1653-desktop-system-wake

Comment @coderabbitai help to get the list of available commands.

@mabry1985

Copy link
Copy Markdown
Contributor Author

Review (pre-merge): design is clean — debounce, boot-focus suppression, fire-and-forget all correct, and I confirmed the server-side contract it targets exists (system.wake registered in graph/lifecycle/dispatch.py:33, /api/events/publish at operator_api/routes.py:601, payload shape {topic, data} matches and passes the namespacing guard).

One gap to close before it goes ready: maybe_signal_wake POSTs with no Authorization header. /api/events/publish runs behind the default-deny A2AAuthMiddleware (a2a_impl/auth.py), which only enforces bearer when a token is configured. So:

  • Default desktop (loopback, no token) → works.
  • Desktop with auth.token / A2A_AUTH_TOKEN set (e.g. tailnet-exposed, per the fleet-frontend pattern) → the POST 401s and system.wake silently never fires. It degrades gracefully (debug log) but the feature is dead for exactly those instances.

The existing chat_stream command already shows the fix — it takes the token and adds req.header("Authorization", token) (lib.rs:617-618). maybe_signal_wake runs native-side with no webview, so it'd need to resolve the token itself (env A2A_AUTH_TOKEN, or the resolved config). Worth wiring, and worth adding to the manual test matrix: test once with a token configured too.

Otherwise 👍 — keep as draft until the desktop-build manual test (blur >60s → refocus → confirm one system.wake broadcast).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant