Skip to content

fix(desktop): keep busy tile transcripts stable - #96386

Closed
Dolverin wants to merge 1 commit into
NousResearch:mainfrom
Dolverin:fix/desktop-multitab-transcript-reload
Closed

Dolverin wants to merge 1 commit into
NousResearch:mainfrom
Dolverin:fix/desktop-multitab-transcript-reload

Conversation

@Dolverin

Copy link
Copy Markdown

What does this PR do?

Hardens the workspace-tile transcript reconciliation introduced by #95600 / #94255 so a persisted snapshot cannot replace a tile's live output or make unrelated tabs jump.

reconcileTileTranscripts() currently gates every tile with the main pane's global $busy state. That produces both failure directions:

  • an idle tile is not reconciled while the main pane is busy;
  • a busy tile is still reconciled while the main pane is idle, so a stale persisted snapshot can overwrite its live transcript.

The async read also captures tile presence before getLatestSessionMessages(), allowing a result to land after the tile becomes busy, closes, or is rebound.

Changes

  • decide liveness per tile runtime from $sessionStates (busy, awaitingResponse, needsInput, or turnLive);
  • check that runtime-specific liveness both before and after the async storage read;
  • re-read the live stored-session/runtime pair after the await so closed or rebound tiles discard stale results;
  • prune signature entries for tiles that have closed, including after a successful earlier reconciliation;
  • preserve idle-tile refresh while the main pane is busy;
  • leave the main-pane backfill and empty-transcript behavior unchanged.

This is complementary to #95770 (cheap no-change fetch gating) and #96215 (warm-cache refresh on explicit Bot Chat reopen); neither protects an active tile from an in-flight persisted snapshot.

Regression proof

On current origin/main / v0.20.6, with only a test-harness compatibility default for the old busyRef signature:

use-background-sync.test.ts: 5 failed, 26 passed

The five failures are the intended contracts:

  1. idle tile still reconciles while the main pane is busy;
  2. busy tile is skipped while the main pane is idle;
  3. tile becoming busy during the read rejects the snapshot;
  4. tile closing during the read rejects the snapshot;
  5. closed-tile signature is pruned.

On this branch:

use-background-sync.test.ts: 31 passed
TypeScript typecheck (all three desktop tsconfigs): passed
Scoped ESLint: passed
Production desktop build + assert-dist-built: passed
git diff --check: passed

Scope

Only these files change:

  • apps/desktop/src/app/contrib/hooks/use-background-sync.ts
  • apps/desktop/src/app/contrib/hooks/use-background-sync.test.ts

@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/desktop Electron desktop app (apps/desktop/*) sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state labels Aug 27, 2026
@Enough1122

Copy link
Copy Markdown

AI code review — automated review for reference; please use your judgment.

fix(desktop): keep busy tile transcripts stable — correctly scopes tile reconciliation to per-runtime live state.

  • apps/desktop/src/app/contrib/hooks/use-background-sync.ts:239 — tileRuntimeOwnsLiveState(runtimeId) checks $sessionStates.get()[runtimeId] for busy || awaitingResponse || needsInput || turnLive. Replaces prior busyRef = $busy (primary pane only), which clobbered a busy tile's streaming assistant message with a stale persisted fetch, and also blocked idle-tile background deliveries when primary was busy.
  • Prunes signatureRef for closed tiles (tile:${storedId} keys not in current $sessionTiles) so map doesn't leak per ever-opened tile (fix(desktop): reconcile workspace-tile transcripts on sessions.changed #94255). Re-checks tileStillPresent() and tileRuntimeOwnsLiveState after await getLatestSessionMessages to discard racy snapshots when tile goes busy or closes mid-read.
  • use-background-sync.test.ts covers: active refresh still works, idle tile reconciles while main busy, busy tile skipped, racy-busy and closing discard, signature pruning. The prior updaterCallCount>0 test now also asserts background delivery text preserved.

Approve.

@teknium1

teknium1 commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Thanks — this landed on main via #101214 (6d061ed) with your commit/credit preserved. Your commit was cherry-picked as-is (tests trimmed to the three distinct invariants). Closing the original in favor of the merged salvage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have 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.

4 participants