From 98df2b7552b394d6d51aeefa26b8aeb7682bb7e0 Mon Sep 17 00:00:00 2001 From: Diego Imbert Date: Fri, 5 Jun 2026 22:32:02 +0200 Subject: [PATCH] revert: drop unsuccessful path-stabilization heuristics + leftover [draft-sync] logs --- .../src/lib/components/ScriptBuilder.svelte | 78 +++---------------- frontend/src/lib/userDraft.svelte.ts | 44 ++--------- frontend/src/lib/userDraftDbSyncer.svelte.ts | 10 +-- .../scripts/edit/[...path]/+page.svelte | 3 - 4 files changed, 15 insertions(+), 120 deletions(-) diff --git a/frontend/src/lib/components/ScriptBuilder.svelte b/frontend/src/lib/components/ScriptBuilder.svelte index 445386ac9d..80594a8ca2 100644 --- a/frontend/src/lib/components/ScriptBuilder.svelte +++ b/frontend/src/lib/components/ScriptBuilder.svelte @@ -78,7 +78,7 @@ import { writable } from 'svelte/store' import { defaultScriptLanguages, processLangs } from '$lib/scripts' import DefaultScripts from './DefaultScripts.svelte' - import { getContext, onMount, setContext, tick, untrack } from 'svelte' + import { getContext, onMount, setContext, untrack } from 'svelte' import EditorHeader from './EditorHeader.svelte' import AutosaveIndicator from './AutosaveIndicator.svelte' import LabelsInput from './LabelsInput.svelte' @@ -345,12 +345,6 @@ let pathError = $state('') let loadingSave = $state(false) - console.log('[draft-sync] ScriptBuilder script-tag: enter', { - userDraftPath, - initialPath, - contentLen: script.content?.length ?? 0, - isEmpty: script.content == '' - }) if (script.content == '') { // Suspend autosave around the bootstrap mutations — seeding the // editor with the template's `initialCode` is a programmatic @@ -361,7 +355,13 @@ // new drafts). Resumed in the async `.finally` so language // switches AFTER bootstrap (which also call `initContent`) sync // normally. - console.log('[draft-sync] ScriptBuilder: bootstrap branch START', userDraftPath) + // + // NOTE: this is a best-effort partial defense — on warm in-app + // navs it suppresses the seed/template writes, but on a HARD + // PAGE RELOAD the Path widget's `$userStore`-gated mutation + // cascade lands AFTER our `restartSync` and still POSTs. Fixing + // that robustly needs a different "first user edit" signal + // (e.g. Monaco input event) that hasn't been wired up yet. UserDraft.stopSync('script', userDraftPath) if (template === 'wac_python') { script.modules = { @@ -378,50 +378,9 @@ } } } - console.log('[draft-sync] ScriptBuilder: calling initContent', userDraftPath) - initContent(script.language, script.kind, template).finally(async () => { - console.log('[draft-sync] ScriptBuilder: initContent finally', userDraftPath, { - pathAlreadySet: !!script.path - }) - // The `Path` widget assigns `script.path` from its - // `$effect.pre` gated on `$workspaceStore + $userStore` — - // which on a HARD-REFRESH-then-first-nav can be unset when - // we get here. Worse, even on a warm reload the widget's - // `reset()` mutates `meta` in TWO steps (`{owner: ''}` then - // `meta.owner = username`), producing two distinct path - // writes — exiting on the first non-empty value lets the - // second one POST as the user's "first edit". - // - // Wait for `script.path` to STABILIZE: same value across - // two consecutive ticks. `tick()` returns only after Svelte - // has flushed pending effects, so a stable read means the - // Path widget's cascade settled and any intermediate - // mutations were already swallowed under suspension. - // - // Bounded so a stuck mount can't disable autosave forever. - let waited = 0 - let last: string | undefined = undefined - let stable = 0 - while (waited < 30) { - await tick() - waited++ - const cur = script.path - if (cur && cur === last) { - stable++ - if (stable >= 2) break - } else { - last = cur - stable = 0 - } - } - console.log('[draft-sync] ScriptBuilder: path stable → restartSync', userDraftPath, { - ticks: waited, - path: script.path - }) + initContent(script.language, script.kind, template).finally(() => { UserDraft.restartSync('script', userDraftPath) }) - } else { - console.log('[draft-sync] ScriptBuilder: bootstrap SKIPPED (content non-empty)', userDraftPath) } async function isTemplateScript() { @@ -466,16 +425,8 @@ // doesn't run `inferArgs` on an empty `script.content` and toast // "Could not parse code". If a template script is then loaded // below we re-seed with the `templateScript=true` variant. - console.log('[draft-sync] initContent: SYNC seed content', userDraftPath, { - language, - template - }) script.content = initialCode(language, kind, template, false) - console.log('[draft-sync] initContent: awaiting isTemplateScript', userDraftPath) const templateScript = await isTemplateScript() - console.log('[draft-sync] initContent: post-await', userDraftPath, { - hasTemplateScript: !!templateScript - }) if (templateScript) { script.content = initialCode(language, kind, template, true) } @@ -585,11 +536,6 @@ try { script.schema = script.schema ?? emptySchema() try { - console.log('[draft-sync] editScript: inferArgs', { - language: script.language, - contentLen: script.content?.length ?? 0, - contentHead: script.content?.slice(0, 80) - }) const result = await inferArgs( script.language, script.content, @@ -604,12 +550,6 @@ script.has_preprocessor = result?.has_preprocessor || undefined } } catch (error) { - console.error('[draft-sync] editScript: inferArgs threw', { - language: script.language, - contentLen: script.content?.length ?? 0, - contentHead: script.content?.slice(0, 80), - error - }) sendUserToast(`Could not parse code, are you sure it is valid?`, true) } diff --git a/frontend/src/lib/userDraft.svelte.ts b/frontend/src/lib/userDraft.svelte.ts index dd8585657c..ee6255a9e3 100644 --- a/frontend/src/lib/userDraft.svelte.ts +++ b/frontend/src/lib/userDraft.svelte.ts @@ -444,13 +444,8 @@ export const UserDraft = { const ws = resolveWorkspace(opts) const mk = mapKey(ws, itemKind, path) const entry = entries.get(mk) - if (entry) { - entry.syncSuspended = true - console.log('[draft-sync] stopSync (entry live)', mk) - } else { - pendingSuspensions.add(mk) - console.log('[draft-sync] stopSync (queued, no entry yet)', mk) - } + if (entry) entry.syncSuspended = true + else pendingSuspensions.add(mk) }, /** @@ -463,13 +458,9 @@ export const UserDraft = { restartSync(itemKind: UserDraftItemKind, path: string, opts?: UserDraftOptions): void { const ws = resolveWorkspace(opts) const mk = mapKey(ws, itemKind, path) - const hadPending = pendingSuspensions.delete(mk) + pendingSuspensions.delete(mk) const entry = entries.get(mk) if (entry) entry.syncSuspended = false - console.log('[draft-sync] restartSync', mk, { - entryLive: !!entry, - clearedPending: hadPending - }) }, /** @@ -651,13 +642,8 @@ function acquireEntry( const existing = entries.get(mk) if (existing) { existing.count++ - console.log('[draft-sync] acquireEntry: reuse', mk, { newCount: existing.count }) return } - console.log('[draft-sync] acquireEntry: NEW', mk, { - hasSeed: defaultValue !== undefined, - pendingSuspended: pendingSuspensions.has(mk) - }) // Seed the cell with the caller's `defaultValue` (deep-cloned so the // cell owns its copy and the caller's baseline can't alias it). This is // how editors report the deployed/draft state until the user edits — @@ -705,42 +691,22 @@ function acquireEntry( const stored = cell.val if (stored !== undefined) readFieldsRecursively(stored.value) const next = stored === undefined ? undefined : JSON.stringify(stored) - if (next === lastSerialized) { - console.log('[draft-sync] effect: no change', mk, { - nextLen: next?.length ?? 0 - }) - return - } - const nextLen = next?.length ?? 0 - const diff = nextLen - (lastSerialized?.length ?? 0) + if (next === lastSerialized) return lastSerialized = next if (skipNextWrite) { skipNextWrite = false - console.log('[draft-sync] effect: SWALLOW (skipNextWrite seed)', mk, { - nextLen, - diff - }) return } const entry = entries.get(mk) if (entry?.skipNextSync) { entry.skipNextSync = false - console.log('[draft-sync] effect: SWALLOW (skipNextSync)', mk, { nextLen, diff }) return } // `syncSuspended` swallows the POST but still advances // `lastSerialized` (above) so when sync resumes the next // real change is detected as a change — only the writes // made during suspension are dropped from the server's view. - if (entry?.syncSuspended) { - console.log('[draft-sync] effect: SWALLOW (syncSuspended)', mk, { nextLen, diff }) - return - } - console.log('[draft-sync] effect: POST', mk, { - nextLen, - diff, - stack: new Error().stack?.split('\n').slice(1, 6).join('\n') - }) + if (entry?.syncSuspended) return void UserDraftDbSyncer.save({ workspace, itemKind, diff --git a/frontend/src/lib/userDraftDbSyncer.svelte.ts b/frontend/src/lib/userDraftDbSyncer.svelte.ts index 0fcee34861..e2e33ae524 100644 --- a/frontend/src/lib/userDraftDbSyncer.svelte.ts +++ b/frontend/src/lib/userDraftDbSyncer.svelte.ts @@ -147,9 +147,6 @@ const pendingSaveOpts = new Map() async function postSave(opts: UserDraftDbSyncerSaveOpts): Promise { const key = draftKey(opts.workspace, opts.itemKind, opts.path) - console.log('[draft-sync] postSave START (sending POST)', key, { - valueIsNull: opts.value === null - }) try { const resp = await DraftService.saveDraft({ workspace: opts.workspace, @@ -172,9 +169,8 @@ async function postSave(opts: UserDraftDbSyncerSaveOpts): Promise { // newer `save()` that arrived during the POST replaces the entry // and must survive for the next flush / debouncer round. if (pendingSaveOpts.get(key) === opts) pendingSaveOpts.delete(key) - console.log('[draft-sync] postSave SUCCESS', key) } catch (e) { - console.error('[draft-sync] postSave FAILED', key, e) + console.error('UserDraftDbSyncer.save failed', e) } } @@ -288,10 +284,6 @@ export const UserDraftDbSyncer = { async save(opts: UserDraftDbSyncerSaveOpts): Promise { const key = draftKey(opts.workspace, opts.itemKind, opts.path) - console.log('[draft-sync] UserDraftDbSyncer.save called', key, { - immediate: !!opts.immediate, - valueIsNull: opts.value === null - }) // Track the latest unconfirmed save BEFORE entering the pipeline // so the unload flush has something to send even if the page // hides before the debouncer fires. diff --git a/frontend/src/routes/(root)/(logged)/scripts/edit/[...path]/+page.svelte b/frontend/src/routes/(root)/(logged)/scripts/edit/[...path]/+page.svelte index 05a87f2c59..b344741eab 100644 --- a/frontend/src/routes/(root)/(logged)/scripts/edit/[...path]/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/scripts/edit/[...path]/+page.svelte @@ -150,7 +150,6 @@ // fires. `initialPath = ''` also makes ScriptBuilder open the // metadata drawer on mount. Strip the single-use flag last. if (page.url.searchParams.get('new_draft') === 'true') { - console.log('[draft-sync] route: new_draft branch START', draftPath) // Suspend autosave for the whole new-draft bootstrap: the seed // `setDraftAndMeta` AND ScriptBuilder's `initContent` (which // fills `script.content` from a template) are both @@ -172,11 +171,9 @@ } as unknown as EditableScript initialPath = '' savedScript = structuredClone(empty) - console.log('[draft-sync] route: about to setDraftAndMeta(empty)', draftPath) scriptHandle.setDraftAndMeta(empty, {}) fullyLoaded = true renderEditor = true - console.log('[draft-sync] route: new_draft branch DONE', draftPath) return } if (hash) {