Repository navigation
Conversation
Contributor
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a focused preview-session synchronization bug fix that preserves known tabs during stale or in-flight list refreshes while retaining authoritative cleanup after fresh responses. The production diff is small and isolated, and the accompanying regression tests cover the changed lifecycle behavior. You can add or adjust custom eligibility rules. Learn more. |
macroscopeapp
Bot
dismissed
their stale review
October 3, 2026 01:39
Dismissing prior approval to re-evaluate d90ee72
2 tasks done
saphid
force-pushed
the
upstream-preview-session-sync
branch
from
October 3, 2026 04:12
d90ee72 to
cea63ae
Compare
macroscopeapp
Bot
dismissed
their stale review
October 3, 2026 04:12
Dismissing prior approval to re-evaluate cea63ae
saphid
force-pushed
the
upstream-preview-session-sync
branch
from
October 4, 2026 06:55
cea63ae to
ded5188
Compare
Contributor
Author
|
Review requested
Logged so this PR shows when a maintainer was asked to review it. |
Contributor
Author
|
Review requested
Logged so this PR shows when a maintainer was asked to review it. |
…st retry Main moved Effect reactivity to effect/reactivity, so the sync test now imports it from there. It also checks that main's retry after an event overtakes the first list still runs, and completes, alongside the waiting guard. The guard makes the old waiting check in that retry redundant. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
macroscopeapp
Bot
dismissed
their stale review
October 14, 2026 03:55
Dismissing prior approval to re-evaluate fde221d
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
An agent can open a Browser tab in the background (
preview_openwithopen: false) while the thread's Browser panel is closed. If a preview event from another thread in the same environment arrives after that, reopening the first thread's Browser panel hides the background tab until a fresh session list comes back. The tab's desktop state (zoom, back/forward, viewport) is dropped as well. Locally the tab is missing for about 120 ms; on a slow remote link it is missing for the whole list round trip. When a list request is already in flight, the remounting panel also sends a second one.Why this qualifies
This is a small, focused correctness fix under CONTRIBUTING: a session the client already knows about should not be discarded because of a response that predates it. There is no linked issue and no prior maintainer agreement. Tab lifetime, defaults and contracts are unchanged.
Change
When the panel mounts,
usePreviewSessionrefreshes the cachedpreview.listand, on main, immediately reconciles the cached result that is replayed while the refresh is waiting. That cached list predates the background tab, so the tab is removed. On main this is usually hidden: in the same microtask the hook replays the environment's latest preview event, which re-adds the tab if that event belongs to this thread. When the latest event belongs to another thread, nothing re-adds the tab until the fresh list arrives.With this change the list is evaluated once on mount, and an in-flight request is reused instead of being refreshed again. Only completed, non-waiting successful responses are reconciled. A stale cached result replayed during a refresh can no longer remove known tabs or their desktop overlay state. A completed, authoritative list can still remove a tab that was really closed.
Main has since added a retry: when an event overtakes the first list, the hook asks for the list again instead of treating the partial index as loaded. That retry still runs alongside this change; its waiting state isn't reconciled, and the fresh answer completes it.
Verification
At head
fde221d4c1after merging main11fd54fcbb(Linux, 2026-10-14):vp test run apps/web/src/components/preview/previewSessionSync.test.ts apps/web/src/previewStateStore.test.ts: 2 files, 31 passed. After the merge, the test importseffect/reactivity(main moved it). The overtaken-list test now also checks that main's retry fires and completes; that assertion fails if the retry is removed.vp run typecheckinapps/web: exit 0.vp linton both changed files: exit 0.Earlier evidence at
ded51886d3follows. Environment: macOS 26.5.2 arm64, packaged desktop app on isolated state with a loopback fixture page. Verified on upstream main 8fb068c498 (before) and this branch rebased onto it (after).Reproduction (one project, threads A and B):
preview_openfor A with{ url, open: false, reuseExistingTab: false }.A read-only per-frame DOM trace of the panel's tab labels gives these results:
preview.listheld 3 s on the serverThe second run simulates a slow remote link. It pauses the server's real
preview.listhandler for 3 s through the owned backend's inspector, and the same pause was applied to both builds. No client code, responses or stores were mocked.Early reopen frame with the delayed list. Before: only the original Browser tab. After: the background tab is kept (URL label until its title refreshes).
Full timeline with the delayed list:
Videos: before, delayed · after, delayed · before, natural timing · after, natural timing
If the latest environment event is the background tab's own event (the single-thread case), main re-adds the tab before paint, and both builds look the same. That is why a single-thread reproduction does not show the bug.
The regression tests run the real hook, atom registry and preview store; only the transport is substituted. Against main's unchanged
usePreviewSession.ts,vp test run apps/web/src/components/preview/previewSessionSync.test.ts apps/web/src/previewStateStore.test.tsgives 2 failed and 29 passed (exit 1):preserves a local tab while cached absence is waiting for a fresh response: main issues a duplicate list request while one is already in flight (expected 0 requests, got 1).refreshes a completed cache without treating its old absence as authoritative: main drops the known tab when the cached list is replayed (expectedlocal, gotnull).At this head, the same command passes 31 of 31 (exit 0).
The following ran at this head, each with exit 0:
vp run --filter @t3tools/web typecheckvp lint --report-unused-disable-directives apps/web/src/components/preview/previewSessionSync.test.ts apps/web/src/components/preview/usePreviewSession.tsvp fmt --check apps/web/src/components/preview/previewSessionSync.test.ts apps/web/src/components/preview/usePreviewSession.tsvp test run apps/web/src/components/preview: 174 of 174 passedvp run build:desktopgit diff --checkSurfaces
apps/web, and that is the client exercised here.ChatViewandPreviewView. The fix applies there unchanged, but it was not captured on web.preview_opentakes the same path.Not checked
A real remote/relay or tunnel connection was not exercised, and the server-hosted web Browser wasn't captured. The loss of desktop overlay state for the background tab is covered by the regression test and was not shown separately in a real client.
CodeRabbit's docstring-coverage warning is not adopted. The hook and the new nearby comment explain lazy-list evaluation and authoritative responses; adding boilerplate to meet a percentage would conflict with the repository's documentation guidance.
Implementation: Claude Code (exact Claude model not recorded). Earlier review and desktop capture: GPT-6.1 Sol, Claude Opus 5.5 and GPT-6 Astra via T3 Code. Merge with main, test update and body refresh: Claude Opus 5.5 (Claude Code) via T3 Code; review GPT-6.1 Sol.
🤖 Generated with Claude Code