From 3210bf37a4cef9f15346a5b29dea7ef1728e150f Mon Sep 17 00:00:00 2001 From: Diego Imbert Date: Mon, 22 Jun 2026 09:16:31 +0200 Subject: [PATCH] fix(apps): strip draft-only path/summary from deployed app value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The visual app editor mirrors the typed `path` (on rename) and `summary` onto the autosaved App value so the home list / Review & Deploy can read them. Those are draft-only metadata with their own `app`-table columns — they shouldn't ride along into the deployed `app_version.value`. The Review & Deploy path already strips them (utils_draft_deploy); the direct editor deploy did not, leaving a stray `path`/`summary` in the deployed JSON (the `path` key being the exact field the draft/list queries read). Add an `appValueForDeploy` helper and apply it everywhere `$app` is used as the deploy/compare/diff value — the create/update sends, the saved baseline, the no-op deploy check, the fetched deployed value, and both diff sides — so the stored value stays clean and the no-op check stays symmetric (legacy values with a baked-in summary are stripped on both sides too). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../apps/editor/AppEditorHeader.svelte | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte b/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte index 5191e8cc97..6f11a39aab 100644 --- a/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte +++ b/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte @@ -211,6 +211,19 @@ }) }) + // The autosaved App carries draft-only `path`/`summary` mirrors (added by the + // two effects above so the home list / Review & Deploy can read a staged + // rename and the summary). They are NOT part of the deployed app value — both + // have their own `app`-table columns — so strip them before deploying, diffing, + // or comparing against the deployed value, keeping the deployed `value` JSON + // clean and the no-op deploy check honest. Mirrors `utils_draft_deploy`'s + // draft deploy, which destructures the same keys out. + function appValueForDeploy(value: any): any { + if (!value || typeof value !== 'object') return value + const { path: _p, summary: _s, ...rest } = value + return rest + } + const { history, jobsDrawerOpen, refreshComponents } = getContext('AppEditorContext') @@ -254,7 +267,7 @@ await AppService.createApp({ workspace: $workspaceStore!, requestBody: { - value: $app, + value: appValueForDeploy($app), path, summary: $summary, policy, @@ -268,7 +281,7 @@ invalidateWorkspacePaths($workspaceStore!) savedApp = { summary: $summary, - value: structuredClone($state.snapshot($app)), + value: appValueForDeploy(structuredClone($state.snapshot($app))), path: path, policy: policy, custom_path: customPath @@ -311,7 +324,7 @@ orderedJsonStringify( replaceFalseWithUndefined({ summary: $summary, - value: $app, + value: appValueForDeploy($app), path: newEditedPath || savedApp.path, policy, custom_path: customPath @@ -339,9 +352,13 @@ deployedBy = deployedApp.created_by - // Strip off extra information + // Strip off extra information. Also drop any draft-only `path`/`summary` + // keys baked into a previously-deployed value (legacy apps deployed before + // these were stripped) so the no-op deploy check and diff stay symmetric + // with the now-stripped editor value. deployedValue = replaceFalseWithUndefined({ ...deployedApp, + value: appValueForDeploy(deployedApp.value), id: undefined, created_at: undefined, created_by: undefined, @@ -356,7 +373,7 @@ workspace: $workspaceStore!, path: $appPath!, requestBody: { - value: $app!, + value: appValueForDeploy($app), summary: $summary, policy, path: npath, @@ -371,7 +388,7 @@ invalidateWorkspacePaths($workspaceStore!) savedApp = { summary: $summary, - value: structuredClone($state.snapshot($app)), + value: appValueForDeploy(structuredClone($state.snapshot($app))), path: npath, policy, custom_path: customPath @@ -613,7 +630,7 @@ deployed: deployedValue ?? savedApp, current: { summary: $summary, - value: $app, + value: appValueForDeploy($app), path: newEditedPath || savedApp.path, policy, custom_path: customPath @@ -741,7 +758,7 @@ bind:deployedValue currentValue={{ summary: $summary, - value: $app, + value: appValueForDeploy($app), path: newEditedPath || savedApp?.path, policy, custom_path: customPath @@ -785,7 +802,7 @@ deployed: deployedValue ?? savedApp, current: { summary: $summary, - value: $app, + value: appValueForDeploy($app), path: newEditedPath || savedApp.path, policy, custom_path: customPath