Skip to content

fix(desktop): render pinned search sessions#37590

Open
ferminquant wants to merge 7 commits into
NousResearch:mainfrom
ferminquant:fix/desktop-pinned-search-session
Open

fix(desktop): render pinned search sessions#37590
ferminquant wants to merge 7 commits into
NousResearch:mainfrom
ferminquant:fix/desktop-pinned-search-session

Conversation

@ferminquant

@ferminquant ferminquant commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Render pinned Desktop sessions that were pinned from search even when they are absent from the normal loaded sidebar session list.
  • Hydrate any persisted pinned session IDs that cannot be resolved from loaded sessions or cached search-result metadata via GET /api/sessions/{id} after app reload.
  • Add shared helpers for resolving pins and detecting missing pinned IDs, with focused regression coverage for reload hydration candidates and _lineage_root_id behavior.

Root Cause

Pinned session IDs were persisted correctly, but the Desktop sidebar rendered pinned rows by resolving those IDs only against the current loaded $sessions list. Search can return sessions that are not in that loaded list, so pinning a search-only result stored the pin but left the pinned section with no session record to render. The previous component-local search-result cache fixed the immediate render, but did not survive a full app reload.

Fix

  • Keep using loaded sessions first so fresh sidebar data wins over fallback metadata.
  • Keep the search-result fallback so a newly pinned search-only row appears immediately.
  • On reload, detect persisted pinned IDs that are missing from both sources and fetch their full session metadata from GET /api/sessions/{id}.
  • Match the Desktop session-detail endpoint shape, which returns the raw SessionInfo object.
  • Avoid retry loops by remembering failed hydration lookups for the current component lifetime.

Test Plan

  • npm run build
  • npm run test:ui -- src/store/session.test.ts
  • npm run type-check
  • npx eslint src/store/session.ts src/store/session.test.ts src/hermes.ts src/app/chat/sidebar/index.tsx
  • git diff --check

Verification Notes

  • The regression tests now cover:

    • search-only pinned sessions resolving from fallback metadata;
    • loaded sessions taking precedence over stale fallback metadata;
    • _lineage_root_id fallback resolution;
    • missing pinned IDs being identified for reload hydration;
    • lineage-root pins not being hydrated when a loaded continuation tip already resolves them.
  • Verified against the actual affected local session from [Bug]: Desktop pinned search result does not appear when session is hidden from normal list #37582:

    • 20260602_133018_d1c043 / desktop ui bug exists in the local session DB;
    • it is excluded from the normal list_sessions_rich(..., include_children=False) sidebar list;
    • it appears when children are included;
    • the same bug* FTS query pattern used by /api/sessions/search?q=bug returns that session;
    • GET /api/sessions/20260602_133018_d1c043-equivalent DB lookup returns the raw session record;
    • a temporary focused Vitest check confirmed the new reload path identifies that exact pinned ID as missing, calls /api/sessions/20260602_133018_d1c043, and then resolves it into the pinned session list.
  • Headless Desktop UI repro was run for real on this PR branch/build:

    • Built Desktop with npm run build, producing install stamp 8d134f7502a0 (fix/desktop-pinned-search-session).
    • Launched the built Electron/Desktop app against the real local Hermes state DB using Electron headless/Ozone mode and CDP.
    • In the headless live Desktop UI, searched for bug and shift-click pinned the search result for the affected session (20260602_133018_d1c043).
    • Confirmed Desktop persisted localStorage['hermes.desktop.pinnedSessions'] as ["20260602_133018_d1c043"].
    • Reloaded the Desktop app.
    • After reload with an empty search field, the sidebar rendered:
      • PINNED
      • desktop ui bug
      • SESSIONS 4/12
    • This confirms the pinned row survives reload even though the session is absent from the normal loaded session list.
  • Negative control on current origin/main without this PR fix was also run with the same real local state DB:

    • Built Desktop from origin/main at b34ee80741db2fdf188dcdc5c5caa78ee72642ff with a clean install stamp (dirty: false).
    • Launched that built Electron/Desktop app headlessly with CDP against the same local Hermes DB.
    • Persisted the same pinned ID (["20260602_133018_d1c043"]) and reloaded the Desktop app.
    • After reload, localStorage still contained the pinned ID, but the sidebar rendered the empty pinned-state copy (Shift click to pin a chat) and did not render desktop ui bug.
    • This reproduces the release/main failure mode and distinguishes it from the fixed PR build, where the same pinned ID renders as desktop ui bug after reload.
  • Portable fresh-fixture negative control was also run on unpatched origin/main so the repro does not depend on the reporter's private local DB:

    • Inserted a new parent session repro_parent_20260602_newpin and child session repro_child_20260602_newpin titled new pinned child repro into a backed-up local state.db test run.
    • Confirmed GET /api/sessions/repro_child_20260602_newpin returned the raw child session record with parent_session_id = repro_parent_20260602_newpin and message_count = 1.
    • Confirmed the normal Desktop sidebar list (/api/sessions?limit=50&offset=0&min_messages=1&archived=exclude&order=recent) did not include the child session.
    • Launched the unpatched Desktop build from origin/main at b34ee8074 headlessly with CDP, persisted localStorage['hermes.desktop.pinnedSessions'] as ["repro_child_20260602_newpin"], and reloaded.
    • After reload, localStorage still contained the new pinned ID, but the sidebar rendered PINNED with Shift click to pin a chat and did not render new pinned child repro.
    • Removed the temporary DB fixture rows and restored the previous pinned localStorage after the repro.

Security / Privacy Impact

  • No backend/API changes.
  • No credentials, tokens, chat IDs, message content, or database files are added. Local session IDs/titles appear only as non-secret verification handles in the PR evidence.
  • No new dependencies.

Scope Boundaries

  • Narrowly fixes rendering for sessions pinned from search results that are not present in the normal loaded session list, including after app reload when the session can be hydrated from the existing session API.
  • Preserves existing _lineage_root_id durable pin behavior.
  • Does not address the separate unexpected parent_session_id behavior described in the issue context.
  • Does not rewrite sidebar loading, search APIs, or session persistence.

Checklist

Code

  • I've read the Contributing Guide.
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.).
  • I searched for existing PRs to make sure this isn't a duplicate; PR fix(desktop): triage batch of GUI quality-of-life fixes #37536 is adjacent coordination, not a duplicate of this pinned-search fix.
  • My PR contains only changes related to this fix.
  • Relevant checks pass for this Desktop TypeScript change: npm run build, npm run test:ui -- src/store/session.test.ts, npm run type-check, targeted npx eslint, and git diff --check.
  • I've added tests for my changes.
  • I've tested on my platform: WSL2 with built Electron/Desktop in headless/Ozone/CDP mode.

Documentation & Housekeeping

  • Documentation update: N/A — no user-facing behavior, config, or CLI docs changed.
  • cli-config.yaml.example update: N/A — no config keys changed.
  • CONTRIBUTING.md / AGENTS.md update: N/A — no architecture or workflow changed.
  • Cross-platform impact considered: renderer/session metadata change only; no OS-specific file/process/terminal behavior changed.
  • Tool descriptions/schemas update: N/A — no tool behavior changed.

For New Skills

  • N/A — this PR does not add or modify a skill.

Screenshots / Logs

  • Headless Desktop UI verification and negative-control logs are summarized above.

Fixes #37582

@mohamedorigami-jpg

Copy link
Copy Markdown
Contributor

One thing I would change before merge: this only caches the search result in component state. It fixes the immediate pin action, but the pin can disappear again after a full app reload if the session is still outside the normal loaded page.

The API already has GET /api/sessions/{id}; hydrating missing pinned ids from that endpoint would make the fix durable without widening the sidebar list.

@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have labels Jun 2, 2026
- Keep both pin-rendering fixes (getMissingPinnedSessionIds,
  resolvePinnedSessions) and upstream's mergeWorkingSessions
- Both sets of tests preserved
- session.ts: indexSessionsByPinnedId + mergeWorkingSessions coexist
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P3 Low — cosmetic, nice to have type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Desktop pinned search result does not appear when session is hidden from normal list

3 participants