Skip to content

Deduplicate Hermes session scan across workspaces#170

Merged
luckeyfaraday merged 1 commit into
mainfrom
fix/hermes-session-scan-dedup
Jun 20, 2026
Merged

Deduplicate Hermes session scan across workspaces#170
luckeyfaraday merged 1 commit into
mainfrom
fix/hermes-session-scan-dedup

Conversation

@luckeyfaraday

Copy link
Copy Markdown
Owner

Summary

readHermesSessions(workspace) re-ran the entire workspace-independent Hermes corpus scan — the ~/.hermes/state.db query plus a full readFile + JSON.stringify of up to MAX_PROVIDER_ROWS (1000) session files — and only narrowed to the workspace with a final match. PR #154 ("Improve multi-workspace review handoffs", first shipped in v0.1.9) began refreshing agent sessions for every open workspace tab, so an N-workspace review fanned this single scan into N concurrent full re-reads of the same corpus, multiplying peak heap by the workspace count.

This is the Hermes analogue of the codex amplification fixed in #169, and the last unbounded per-workspace session-scan path flagged in the v0.1.9 heap-OOM investigation.

Change

  • Add memoizeAsyncWithTtl (client/electron/ttl-cache.ts): a small async memoizer that shares one in-flight promise per TTL window and never caches rejections — the same semantics as the existing inline codex metadata cache.
  • Split readHermesSessions into:
    • scanHermesSessions() — the workspace-independent scan, now run once per CACHE_TTL_MS (30s) and shared across all workspaces.
    • readHermesSessions(workspace) — filters the shared result with hermesSessionMatchesWorkspace and stamps the per-workspace workspace field.

Eleven open workspaces now perform one corpus scan per 30s window instead of eleven concurrent ones. The session-building logic is otherwise unchanged.

Behavior notes

  • No-database fallback: the on-disk fallback scan previously triggered when the DB produced no rows for the current workspace; hoisted to the shared scan, it now triggers when the DB produces no rows at all. For the common case (every session present in state.db) the result is identical.
  • Trade-off: the shared scan retains parsed metadata (including each session's ≤300 KB searchText, which the per-workspace match needs) for the 30s TTL. This is bounded by the existing MAX_PROVIDER_ROWS × 300 KB cap and is far below the multi-GB transient spike it replaces.

Verification

  • npm run build:electron — clean
  • npm run test:electron — 133/133 pass, including 3 new ttl-cache tests (in-flight sharing, TTL expiry, rejections not cached)

Context

Root cause: PR #154 (b4c801c) turned one active-workspace session scan into one-per-workspace. The codex/claude side is bounded in #169; this PR closes the Hermes side.

🤖 Generated with Claude Code

readHermesSessions() re-ran the entire workspace-independent ~/.hermes scan
-- reading and JSON-decoding up to MAX_PROVIDER_ROWS session files -- once per
open workspace. After PR #154 began refreshing agent sessions for every open
workspace tab, an N-workspace review fanned this into N concurrent full
re-reads of the same corpus, multiplying peak heap by the workspace count.

Scan the corpus once per CACHE_TTL_MS via a new memoizeAsyncWithTtl helper and
apply the per-workspace match to the shared result. This closes the last
unbounded per-workspace session-scan path from the v0.1.9 OOM investigation,
complementing the codex/claude bounding in #169.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@luckeyfaraday luckeyfaraday force-pushed the fix/hermes-session-scan-dedup branch from 8e7aed1 to 24d2e9d Compare June 20, 2026 16:14
@luckeyfaraday luckeyfaraday merged commit 0df89ab into main Jun 20, 2026
@luckeyfaraday luckeyfaraday deleted the fix/hermes-session-scan-dedup branch June 20, 2026 16:18
@luckeyfaraday luckeyfaraday restored the fix/hermes-session-scan-dedup branch June 20, 2026 16:20
@luckeyfaraday luckeyfaraday deleted the fix/hermes-session-scan-dedup branch June 20, 2026 16:20
JJPuertas pushed a commit to JJPuertas/Athena that referenced this pull request Jun 20, 2026
The codex `~/.codex/sessions` metadata scan (bounded in luckeyfaraday#169) carried its
own inline TTL cache with identical semantics to the `memoizeAsyncWithTtl`
helper added for the Hermes scan dedup (luckeyfaraday#170): share one in-flight promise
per TTL window, never cache rejections. Fold the codex cache into the shared
helper to remove the duplicated cache bookkeeping. No behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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