From 1d9eed31032a3a32beaead60d6e18fb940c33dbb Mon Sep 17 00:00:00 2001 From: Guilhem Lemouel Date: Mon, 7 Sep 2026 14:50:51 +0200 Subject: [PATCH] fix(frontend): reload after the abandoned run stops, not when it is asked to MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `abandon()` stops the run at the next phase boundary; the request already sent still lands. Reloading the list at that moment could read it before that write committed, leaving the caller stale again — the thing the reload was added to fix. It now waits for `running` to clear, which is immediate for the common case of dismissing a finished import, with a cap so a run that never settles still ends in a reload. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_012fRjnaHLwjpHN84gNNxah9 --- .../components/home/ImportProjectModal.svelte | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/frontend/src/lib/components/home/ImportProjectModal.svelte b/frontend/src/lib/components/home/ImportProjectModal.svelte index d5f3cde7a4..b089e9fdda 100644 --- a/frontend/src/lib/components/home/ImportProjectModal.svelte +++ b/frontend/src/lib/components/home/ImportProjectModal.svelte @@ -83,11 +83,25 @@ // partway — so the list behind this dialog is stale either way, and only `finish()` // was reloading it. Closing a landed import with the X left an emptied-out home // showing its placeholder rows over a workspace that now holds a project. - if (!finishing && execution) onImported?.() + if (!finishing && execution) void reloadWhenSettled(execution, onImported) finishing = false onClose() } + /** + * Reloads the caller's list once the run is no longer writing. Immediate for a run that + * has finished, which is the common dismissal — but `abandon()` only stops the *next* + * phase, and the request already sent still lands, so a reload issued at that moment can + * read the list before that write commits and leave it stale again. The cap is there so a + * run that never settles still ends in a reload rather than in nothing. + */ + async function reloadWhenSettled(run: ImportExecution, reload: (() => void) | undefined) { + for (let i = 0; i < 60 && run.running; i++) { + await new Promise((resolve) => setTimeout(resolve, 250)) + } + reload?.() + } + // The wizard route asks step 1 which workspace to import into and step 2 which one it // is. Opened from inside a workspace both answers are already given, so the dialog // starts at the import itself and the plan is fixed rather than URL-driven.