fix(apps): strip draft-only path/summary from deployed app value

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) <noreply@anthropic.com>
This commit is contained in:
Diego Imbert
2026-06-22 09:16:31 +02:00
co-authored by Claude Opus 4.8
parent ea466a9ede
commit 3210bf37a4
@@ -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>('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