-
Notifications
You must be signed in to change notification settings - Fork 522
fix(desktop): invalidate artifact previews after deletion #5394
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
SummerC0zyR0ck
wants to merge
4
commits into
apache:main
Choose a base branch
from
SummerC0zyR0ck:fix/managed-artifact-preview-lifecycle
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
bd17e25
fix(desktop): invalidate artifact previews after deletion
SummerC0zyR0ck 3aafcc9
test(desktop): verify previews close on Host disconnect
SummerC0zyR0ck 2a113a9
fix(desktop): reopen artifact previews after reconnect
SummerC0zyR0ck f6876ab
test(runtime-host): assert the Artifact epoch moved past 177
SummerC0zyR0ck File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
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
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
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
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
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
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
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2 — A deletion missed while reconnecting leaves the deleted preview live for the rest of its TTL.
artifact.changedis a transient frame with no revision or replay, andRuntimeHostReconnectingConnectiononly rebinds this listener to the replacement connection. The existing preview scope is not closed when availability is lost. A reachable sequence is: Desktop prepares an HTML preview; its remote/SSH/WSL Host connection drops; the still-running Host deletes the Artifact through another Client, Deep Research rollback, or Session purge; the invalidation is emitted while no Desktop subscription exists; Desktop reconnects and receives only future frames. The local preview server therefore keeps serving the deleted snapshot for up to 30 minutes. The new reconnect test itself establishes the non-replay behavior by forwarding only frames emitted by the replacement connection, so the PR's deletion guarantee does not hold across a connection gap.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the careful review, @me2seeks — the concern is fair, and it sent us back through the Desktop connection model in detail. Here is what we found, and where we would value your guidance.
On the Desktop, the reconnecting path you described does not appear to exist. RuntimeHostReconnectingConnection is constructed only by the CLI/TUI clients; no Desktop (main-process) code path builds one. So the "listener is rebound to the replacement connection while the old preview scope stays open" mechanism does not apply to the Desktop.
For the connections the Desktop does use:
So we could not construct a Desktop sequence where a deletion is published while no subscription exists and the connection stays open. We removed the availability-based hook we had tried, because it only applies to reconnecting connections and would never fire here.
We may well be missing a path. If you have a specific one in mind — a transport, a mount, or a client we overlooked — we would be glad to hook the release to whatever signal actually fires there. Could you point us at it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1 — A normal Desktop reconnect permanently disables artifact previews for that target.
The concrete path is the candidate cleanup, not
RuntimeHostReconnectingConnection:DesktopRuntimeHostCandidateImplcallsdisposeClientIpcwhenconnection.closedsettles;registerHostClientIpcthen callsmanagedArtifactPreview.closeScope(scope.targetEpoch)(runtime-host-boot.ts:1913).closeScopeadds that epoch toretiredScopes(managed-artifact-preview.ts:74rejects every retired scope). However,createDesktopRuntimeHostCandidatederivesscope.targetEpochfromipcMain.epoch(runtime-host-desktop-candidate.ts:549), and the Desktop manager creates every replacement candidate with the sametarget.epoch(runtime-host-desktop-manager.ts:1135). The replacement therefore reuses an already-retired scope.I reproduced this on
9bd1819f6142d477726f8a9b5760aa5325df00f9:prepare('same-epoch') → closeScope('same-epoch') → prepare('same-epoch')returnsError: Preview owner is closed. After a normal WSL/SSH/local reconnect, existing preview leases are released but every later artifact preview for that target stays unavailable.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for tracing the concrete path — you are right.
closeScope(targetEpoch)released the existing leases but also permanently retired an epoch that Desktop reuses across replacement candidates.I fixed this by reopening the scope only after the replacement candidate successfully registers. Teardown still retires the scope and closes all old leases, so requests cannot create previews during the reconnect gap.
The regression test now verifies the complete lifecycle: the old preview URL becomes unreachable after disconnect, and a replacement candidate using the same
targetEpochcan create and serve a new preview.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @me2seeks — the P1 you found is fixed on the current head
6c444a5c.Teardown still retires the scope and releases the old leases, and the replacement candidate reopens the scope only after it registers, so a normal reconnect can prepare previews again on the same target epoch.
The regression test now covers the full lifecycle: the old URL fails after the disconnect, and a replacement candidate on the same
targetEpochcan prepare and serve a new preview.Could you take another look at the current head when you have a moment? The required approval is the only thing left on my side.