Files
hermes-agent/apps/desktop/src/lib
Zeus-Deus 26b4315e09 fix(desktop): profile-scope persisted unread and stop cold-boot storage clobber
Address review on the persisted unread dots, plus a latent data-loss bug
in the shared persistence helper that the restart e2e exposed.

Review findings:

- Session ids are caller-supplied and each profile backend is its own
  namespace, while the desktop's lists routinely mix profiles (cron and
  messaging slices are always cross-profile; recents are too in
  all-profiles mode). Both persisted records are now bucketed per
  profile - nested records keyed by the ROW's own profile
  (normalizeProfileKey, absent -> default), never the live gateway's,
  except the live busy->idle edge with no loaded row, which can only
  come from the active gateway. Same-id sessions in different profiles
  no longer share watermarks or markers.
- Markers are now bounded (200 per profile, oldest evicted) and cleaned
  up when a session leaves the user's world: forgetSessionUnread() is
  wired into removeSession, archiveSession, and the settings
  permanent-delete path (which bypasses the other two).

Cold-boot clobber (found by the restart e2e after the refactor):

- persistentAtom wrote its value back to storage immediately at
  creation. On a cold boot the bundle can evaluate against a storage
  snapshot that has not caught up yet, so that echo overwrote real
  records with the fallback. Creation is now read-only; only actual
  changes persist. Regression-tested in persisted.test.ts.
- The read side of the same race is handled in session-unread.ts: the
  first list arrival re-reads both records from storage (readable by
  then) and merges them under the in-memory state, so a boot that
  seeded empty atoms adopts the disk state instead of re-seeding every
  row and burying the unread gap. Unread listeners are also disabled in
  secondary windows - their partial list view must not write the
  primary's whole-record state (same isolation rule as session tiles).

Tests: cross-profile same-id regression, live-edge profile fallback,
forgetSessionUnread cleanup, marker cap, persistentAtom creation
read-only; the restart e2e passes again end to end.
2026-08-15 01:21:40 -07:00
..
…
…
2026-07-24 16:36:59 +00:00
…
…