Repository navigation
fix: read push survives a locked Mac, and imsg-read says the screen is locked - #133
Conversation
…olates the live channel collect() deleted pendingReads[chat] after five failed attempts. Only --all is dropped now (it cannot be bounded); a per-chat intent keeps retrying at the backoff ceiling (once a minute) until the Mac confirms it or a newer inbound cancels it. Dropping one resurrected a dot the user had already read through intent.seen: after a night with the gateway's screen locked, two intents sat at 441 and 409 attempts and cleared within minutes of the unlock once they were kept. Keeping them is safe because the snapshot compare and --through already refuse to consume a newer inbound. Two tests: a prolonged failure keeps the dot suppressed through seven attempts and recovery clears the queue with one more push; a newer inbound mid-retry cancels the intent, leaves the chat unread and sends nothing. The read-sync fixture now sets XDG_RUNTIME_DIR into its home. Without it a running blip-bridged answers underneath the tests on any machine that runs Blip, and the convergence test fails on clean main there. That test's budget goes from 20 s to 60 s: 96 collector spawns take 10-16 s on an idle 16-core box and tip over 20 s with anything else running.
Behind the lock screen every app's windows vanish from System Events, so a logged-in, awake Mac with Messages open fails every --chat push with "Messages has no accessible window; log in and open Messages on the Mac", which sends the owner to a Messages that is open and fine. The defaults do this 25 minutes after the last touch: screen saver at 20 idle minutes, lock 5 minutes later. screen_locked() reads IOConsoleUsers[].CGSSessionScreenIsLocked from `ioreg -n Root -d1 -a` (no TCC grant involved; the key exists only while locked). select_chat() and accessibility() consult it only after System Events has already failed or found no window, and report the lock with the cure. The Accessibility-not-granted message keeps precedence. Run verbatim on a macOS 27.0.1 gateway under its Python 3.9.6 the function returns False for the live unlocked session; the locked branch and the parse failures are covered by tests on synthetic plists.
…queue README: the lock screen joins sleep and the login window as the third way a gateway goes dark, with why only the read push stops, the one-line fix (`defaults -currentHost write com.apple.screensaver idleTime -int 0`), its trade-off and undo, and a Linux-side check that needs no permission prompt. CLAUDE.md: the read-sync queue bullet says a per-chat intent is never dropped and why that is safe; the Mac read-push notes record the lock-screen finding and that imsg-read now names it.
WHAT: in split view the displayed chat's pinned tile or row gets a persistent selected fill (Style.selectedFillFor), following isShowing() by identity, not hover, focus or position; openThread() stops the pending peek timer and moves the sidebar cursor to the opened chat without calling cursorMoved(), so a committed open never arms another preview (onCursorChanged only builds rows: 'a peeked thread is not read' holds). Flyout unchanged. From Brandon Smith (github.com/bndnsmth, new contributor). WHY: clicking a chat after the keyboard cursor sat on the first row left the first row lit, or nothing lit once hover ended, while the right pane showed another conversation: misleading when replying. VERIFIED: merged with main, 748 bun pass (with the live socket hidden; see #133), the 2 new ui tests fail on the old BlipView; door two-way; Fred approved 2026-10-06. LR-T: blip, sidebar, selection, split-view, ui, bndnsmth LR-D: blip LR-R: #134 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BeRMdPWNC2myz9U8hEeeCi
|
Merged, thank you Ian. A night of 441 failed attempts against a locked Mac, then a dot coming back for a conversation already read, is a great find, and "the Mac's screen is locked" is a far better message than sending someone to a Messages that is open and fine. Your fixture change also turned out to matter here more than you knew: with the persistent channel from #83 running on my machine, 15 read-sync tests on main were failing because the live socket answered underneath them; with your XDG_RUNTIME_DIR isolation all 750 pass. The new bridge is on my Mac and both Linux machines. One note for the record, not a change request: per-chat retries are now unbounded, so a permanent non-lock failure on an unlocked Mac would reopen the conversation once a minute; in practice opening it reads it and the snapshot retires the intent, and only push_read=thread or menu reads queue per-chat intents. |
…h asked for his X handle) LR-T: blip, change-log, weekly-notes, roster, #133, #134, bndnsmth, ianswope LR-D: blip Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BeRMdPWNC2myz9U8hEeeCi
What
Three changes, one cause.
A per-chat read intent is never dropped.
collect()used to deletependingReads[chat]after five failed attempts. Now only--allis dropped on failure (it cannot be bounded); a per-chat intent keeps retrying at the existing backoff ceiling (once a minute) until the Mac confirms it or a newer inbound cancels it. Two new tests: a prolonged failure keeps the dot suppressed through seven attempts and recovery clears the queue with one more push; a newer inbound mid-retry cancels the intent, leaves the chat unread and sends nothing. The read-sync fixture also setsXDG_RUNTIME_DIRinto its home: without that, a runningblip-bridgedanswers underneath the tests on any machine that runs Blip, and the convergence test fails on clean main there. That test's budget goes from 20 s to 60 s; it spawns 96 collector processes and takes 10–16 s on an idle 16-core box.imsg-readnames the lock screen. When System Events fails or finds no Messages window, it now readsIOConsoleUsers[].CGSSessionScreenIsLockedfromioreg -n Root -d1 -a(no TCC grant involved; the key exists only while locked) and reports "the Mac's screen is locked …" with the cure, instead of "log in and open Messages on the Mac". The Accessibility-not-granted message keeps precedence.Docs. README "Keeping the Mac awake" gets the lock screen as the third way a gateway goes dark, with the one-line fix, its trade-off and undo, and a Linux-side check. CLAUDE.md records the durable per-chat queue and the lock-screen finding.
Why
My gateway Mac mini never sleeps (
sleep 0,displaysleep 0), never logs out, and Messages is always open. Underpush_read=thread, every--chatpush still failed with exit 64 "Messages has no accessible window" from about 25 minutes after I last touched it until I unlocked it again, and I had been "logging in" several times a day without understanding why. The mini's loginwindow log has the answer: screen saver at 20 idle minutes (targetUserIdle = 1200, the default), lock 5 minutes later (sysadminctl -screenLock status), and behind the lock screencount windowsof process "Messages" is 0. The last failed push was at 10:26:05 today;LWScreenLock handleUnlockResultlogged the unlock at 10:26:07; Blip was healthy two minutes later.Two things made it worse than it had to be. The error text sent me to a Messages that was open and fine. And the collector dropped each per-chat intent after five attempts, so a conversation I had read in Blip came back as unread after the night. The intent was right; the Mac was just unreachable for it. Keeping the intent is safe because the read-state snapshot compare and
--throughalready refuse to consume a newer inbound, and the second new test pins that.The fix for the lock itself is on the Mac, not in Blip:
defaults -currentHost write com.apple.screensaver idleTime -int 0. I applied it today and confirmed from the loginwindow log that when the armed timer fired at 10:46:07 it re-readidleTime: 0and stood down. Nothing here touches #125's items (the per-poll snapshot, the blocking read action).How it was verified
bun testis green locally (748 pass, 0 fail).python3 bridge/mac/test_read.py35 OK (three new),test_read_verification.pyandtest_read_sync.pyOK, the CIpy_compileline passes.screen_locked()run verbatim on the gateway (macOS 27.0.1, Python 3.9.6) against the live, unlocked session returns False. The locked branch is covered by the synthetic-plist test; I have not locked the mini on purpose to see it live.CLAUDE.md→ "Read sync is an acknowledged, durable action queue" (per-chat intents are never dropped) and the "Marking read ON THE MAC" notes (the lock screen); both updated.