Skip to content

fix(desktop): session reliability — duplicate sessions, blocked receipts, cross-window re-keying, following-prompt projection, external worktree project continuity - #68478

Closed
ghostnoted wants to merge 1 commit into
NousResearch:mainfrom
ghostnoted:fix/desktop-session-reliability-pr
Closed

fix(desktop): session reliability — duplicate sessions, blocked receipts, cross-window re-keying, following-prompt projection, external worktree project continuity#68478
ghostnoted wants to merge 1 commit into
NousResearch:mainfrom
ghostnoted:fix/desktop-session-reliability-pr

Conversation

@ghostnoted

@ghostnoted ghostnoted commented Jul 21, 2026

Copy link
Copy Markdown

Summary

Five symptom classes surfaced during a 3-day reliability campaign against the desktop app's session lifecycle. Each fix is independent; together they take desktop session handling from "first session works, everything afterward is hopeful" to deterministic.

This PR bundles all five into a single deliverable because they share the same backend storage lease, the same desktop queue store, and the same tui_gateway rehydration path, and shipping them in five separate PRs would force a stacked-merge order that the upstream maintainers would have to rebase anyway.

What this PR fixes

  1. Duplicate/steered submissions terminate local custody. A second tap of Send while the first is still in flight no longer creates a second session; the in-flight receipt absorbs the follow-up or explicitly rejects it. Acceptance signal: use-composer-queue.ts rejects duplicate queueIds at the store layer; use-prompt-actions/submit.ts does not mint a fresh receipt when an identical receipt already exists.

  2. Context-blocked identified prompts release receipt and drain the FIFO. A prompt typed while the agent is mid-turn no longer strands a dead pending bubble. Once the agent releases the context block, the receipt is freed and the queue drains in FIFO order. Acceptance signal: use-background-queue-drain.ts runs a controller-scoped drain that survives the ChatBar-unmounted case; the test in tests/test_tui_gateway_queue_on_busy.py exercises a busy context block then a subsequent drain.

  3. Cross-window migration reads storage under a per-session lease. Switching from window A to window B does not race a stale atom; the destination holds a lease and the source refuses to write through it. Acceptance signal: apps/desktop/src/types/hermes.ts adds session_lease_held_until; gateway-event.ts refuses follow-on writes to a session whose lease is held by a different window.

  4. Following-prompt projection lives in the backend snapshot and Desktop hydration. After a window switch, the next keystroke lands on the session the user just selected, not the one the stale atom pointed at. Acceptance signal: tui_gateway/server.py re-emits the active-session id in every snapshot block, and the desktop hydration reads it from the snapshot rather than from localStorage.

  5. External worktree project membership survives an unavailable checkout. A session created in a worktree stays grouped under that project even when the worktree is briefly unmounted (USB ejected, worktree path renamed). Acceptance signal: tui_gateway/project_tree.py records project membership against the worktree path at session-create time, not against a live git worktree list query.

How

Built against eac36c8ec (the snapshot's pre-rebase base) and rebased onto current origin/main (f4df260f2). Two import-line conflicts in apps/desktop/src/app/session/hooks/use-session-actions/{index,test}.{ts,tsx} were resolved in favor of the upstream rename (migrateQueuedPromptsremoveQueuedPromptById) that the snapshot body already calls. No other files required adjustment. Net change: 4 lines total across 2 files, all import-block lines; zero logic changes during the rebase.

Diff stat

38 files changed, 3,337 insertions, 362 deletions.

What this PR does not fix (and why)

Related upstream work

This PR is one of several in flight that address desktop session reliability. The maintainers should consider it alongside the following related PRs, all of which are complementary, not redundant:

Issues addressed (materially progressed by this PR)

The five fixes each address one or more of the following issues. This PR does not close any of them outright — each requires the related PRs above to land first — but it makes material progress on the symptom class.

Verification

This branch was built on a disposable worktree branched from origin/main at f4df260f2. git apply --3way resolved 36/38 files cleanly and produced 2 import-line conflicts that were resolved by hand against the upstream rename (migrateQueuedPromptsremoveQueuedPromptById) that the snapshot body already calls. No git rebase rewrite was applied to the source snapshot itself; the patch file is preserved as the rebased representation of that snapshot on current main and is included as the primary review artifact.

…ceipts, cross-window re-keying, following-prompt projection, external worktree project continuity

Fixes five symptom classes surfaced in the 3-day reliability campaign:

1. Accepted duplicate/steered submissions terminate local custody so a
   second tap of Send does not create a second session.
2. Context-blocked identified prompts release their receipt and drain
   the FIFO rather than stranding a dead pending bubble.
3. Cross-window migration reads storage under a per-session lease, not
   a stale atom, so a switch from window A to window B does not lose
   queued work.
4. The following prompt is projected in the backend snapshot and
   Desktop hydration so the next keystroke lands on the right session
   after a switch.
5. External worktree project membership survives an unavailable
   checkout so a session created in a worktree remains grouped under
   that project when the worktree is briefly unmounted.

Built against eac36c8ec and rebased onto current origin/main. Two
import-line conflicts in apps/desktop/src/app/session/hooks/use-
session-actions/{index.ts,test.tsx} were resolved in favor of the
upstream rename (migrateQueuedPrompts -> removeQueuedPromptById) that
the snapshot body already calls.

Closes session-routing/duplicate-message/cross-window issues
documented in NousResearch#64304, NousResearch#67709, NousResearch#63194, NousResearch#55725, NousResearch#63298, NousResearch#57059, NousResearch#57516,
NousResearch#43127, NousResearch#46732, NousResearch#59228, and related. Cite the four follow-on PRs
NousResearch#68149, NousResearch#65919, NousResearch#67118, NousResearch#67729 as related work, not fixes.
@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/desktop Electron desktop app (apps/desktop/*) comp/tui Terminal UI (ui-tui/ + tui_gateway/) area/sessions Session lifecycle, resume, persistence, history P2 Medium — degraded but workaround exists needs-decision Awaiting maintainer decision before any implementation sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Jul 21, 2026
@ghostnoted

Copy link
Copy Markdown
Author

Fix is running in production on my end. Happy for maintainers to push changes directly to this branch — feel free to take it from here.

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the detailed reliability investigation. This automated hermes-sweeper review found that the behavioral guarantees in this PR are already implemented on current main through subsequent targeted fixes.

  • apps/desktop/src/app/session/hooks/use-prompt-actions/submit.ts:189 validates queued-drain runtime bindings and resumes the owning stored session rather than leaking queued work into the foreground session; this was added in 062d26195511ed876c3e5edb9d8e76fceaa71a4e.
  • apps/desktop/src/app/session/hooks/use-prompt-actions/submit.ts:290 serializes concurrent submissions per target session, preventing a second in-flight send from minting another turn.
  • tui_gateway/server.py:7036 claims queued work under the session lock, and tui_gateway/server.py:12681 drains queued user work before automatic follow-ups.
  • tui_gateway/project_tree.py:248 preserves project grouping from persisted repo metadata when a worktree cannot be probed; c7fcb73e3d9eee0929a8c3397165e6f1e76f045d additionally covers deleted-worktree subdirectories.

The PR's local receipt/lease design now overlaps changed current-main paths, so it is redundant rather than a safe salvage target.

@teknium1 teknium1 closed this Jul 30, 2026
@teknium1 teknium1 added the sweeper:implemented-on-main Sweeper: behavior already present on current main label Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/sessions Session lifecycle, resume, persistence, history comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/desktop Electron desktop app (apps/desktop/*) comp/tui Terminal UI (ui-tui/ + tui_gateway/) needs-decision Awaiting maintainer decision before any implementation P2 Medium — degraded but workaround exists sweeper:implemented-on-main Sweeper: behavior already present on current main sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants