From 009facda7d2a03ca3bd27f698c0f786fa10ce12f Mon Sep 17 00:00:00 2001 From: Guilhem Lemouel Date: Wed, 19 Aug 2026 17:25:40 +0200 Subject: [PATCH] fix(frontend): discard a variable check the wizard has moved on from The post-await guard compared only the path, and the path is built from the review step's fields -- so picking an existing resource stops the wizard minting one without changing it. A check already in flight then answered for a branch nobody was on, and a `true` disabled Finish over a path the run no longer writes. The cleanup cannot help: it cancels a pending timer, not a live request. Both sides of the await now ask the same question. Co-Authored-By: Claude Opus 5 (1M context) --- .../AddDataTableWizard.svelte | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/frontend/src/lib/components/workspaceSettings/AddDataTableWizard.svelte b/frontend/src/lib/components/workspaceSettings/AddDataTableWizard.svelte index e600730c36..2e19cbea82 100644 --- a/frontend/src/lib/components/workspaceSettings/AddDataTableWizard.svelte +++ b/frontend/src/lib/components/workspaceSettings/AddDataTableWizard.svelte @@ -122,26 +122,35 @@ */ let pathTakenError = $state('') let variableCheck: ReturnType | undefined = undefined + /** + * Whether a check for `path` still answers the question on screen. Read once before the + * request and again after it: the cleanup can cancel a pending timer but not a request + * already in flight, so an answer that arrives late has to re-earn its relevance. `path` + * alone is not enough -- it is built from the review step's fields, so picking an existing + * resource stops the wizard minting one without changing it. + */ + function pathStillChecked(path: string): boolean { + return ( + path === resourcePath && wiz.step === 3 && mintsResource && !!path && path !== claimedPath + ) + } + $effect(() => { const path = resourcePath - if (wiz.step !== 3 || !mintsResource || !path) { - pathTakenError = '' - return - } // A path this wizard already wrote is not somebody else's to protect -- the same // exemption the hard check in `finish()` makes, or a retry would refuse its own secret // and leave Finish permanently disabled. - if (path === claimedPath) { + if (!pathStillChecked(path)) { pathTakenError = '' return } clearTimeout(variableCheck) variableCheck = setTimeout(async () => { const taken = await VariableService.existsVariable({ workspace: $workspaceStore!, path }) - // Two checks can be in flight at once and resolve out of order. Answering for a path - // that is no longer the one on screen is not merely stale: a `false` for an older - // path would clear the error guarding the path actually about to be written. - if (path !== resourcePath) return + // Two checks can be in flight at once and resolve out of order. A `false` for a path + // nobody is on any more would clear the error guarding the one about to be written; + // a `true` would disable Finish over a path this run stopped caring about. + if (!pathStillChecked(path)) return pathTakenError = taken ? 'a variable already exists at this path' : '' }, 500) return () => clearTimeout(variableCheck)