d2c2b0b6d4
The green "finished — unread" session dot lived only in the transient $unreadFinishedSessionIds atom, written by a live busy->idle edge the renderer had to witness. Closing and reopening the app grayed out every dot, and a session that finished while the app was closed could never be flagged at all. Add a persisted layer (session-unread.ts), ported from the webui's proven design: - Seen watermarks (hermes.desktop.sessionSeenCounts): the message_count last acknowledged per session, keyed by the durable lineage id (same rule as session colors). A row whose live count exceeds its watermark paints unread on every list refresh - this reconstructs dots after a restart AND surfaces sessions that finished while the app was closed. First sight of an unknown session seeds the watermark so a fresh install doesn't light up every row. - Explicit finish markers (hermes.desktop.unreadFinishedSessions): the live edge, persisted, covering the gap before the sidebar list refreshes its counts. Opening a session acks both; the selected session's watermark tracks its live count so on-screen activity never reads as unread. Chat and cron rows get full watermark treatment; messaging rows keep explicit markers only, so inbound messages don't paint false completion dots. Profile switches keep persisted markers (keyed by durable id) and only wipe the transient paint layer, so a round-trip repaints them. Covered by store unit tests and an e2e spec that boots the app three times: dot appears on a background finish, survives a restart, clears on open, and stays cleared after another restart.