feat(service): the Mac's held read is woken by Neon Realtime rather than its poll
Draft
CH
10d ago+117+117+012345678901234567890123456789-11-11-01234567890123456789
Stacked on #3266 (luke/neon-realtime-service-wake). Released desktops keep holding GET /api/mac-commands until the version gate cuts them off, so this PR makes that held read wait on Realtime too. Desktops on #3285 don't use this route.
Change
macCommandsFornow waits throughlookedUntilfrom #3266, in place ofEffect.repeat(Schedule.spaced(POLL))+timeoutOption(HOLD). Between looks it waits for Neon Realtime to report that the account's unclaimed commands changed. The heartbeat on arrival, the 20 s hold, and the claim at the end are unchanged.- New
macCommandsWaitingLiveStatement(userId)inmac-command-store.ts:SELECT id FROM mac_command WHERE user_id = $1 AND claimed_at IS NULL. It selects ids only, never the command text, and has no clock window. The read's own look still checksCLAIM_WINDOW. It mirrors #3274'smacCommandsLiveStatement, which the Mac subscribes to and which also carries what to run. It's kept separate so that the conflict with #3274 stays to adjacent additions.
Tests
mac-command-store.test.ts(PGlite): the wake statement returns the account's unclaimed commands by id only, drops one once it's claimed, and reads nothing of another account's.mac-commands-app.test.ts: with a scriptedLiveWake, a held read answers a command queued while it's held as soon as the watch says the rows changed, with noTestClockadvance. It also watches only the account's own statement. Without the change the test times out, because the read waits out its poll.
pnpm check:affected passes except apps/desktop/src/main/app-menu.test.ts, which is known to fail in the sandbox.
Made with Luke
Summary by cubic
Makes the Mac's held GET /api/mac-commands read wake on Neon Realtime instead of its 500 ms poll, so queued commands are answered as soon as they're queued.
macCommandsFornow waits on Realtime between looks through the newmacCommandsWaitingLiveStatement, which selects the account's unclaimed command ids only. It falls back to the poll only where the deployment has no Realtime. The heartbeat, 20 s hold, and final claim are unchanged.