From 9027a86edddca2b77dabcaa72823b2493ba28017 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 14 Sep 2026 16:13:40 +0200 Subject: [PATCH] A behavior has rows of its own, so loading no longer drops Run in editor `deserializeProject` builds each asset's effective schema in pass 1 and coerces `props` against it in pass 2. For a behavior that schema was `[...COMMON_PROPS, ...variables]` -- but `runInEditor` lives in `ASSET_SCHEMAS.behavior`, not in `COMMON_PROPS`, so `normalizeBag` dropped the value as undeclared. The flag therefore survived no save: every reload switched it back off. The fix is the full schema, `schemaFor('behavior')`, which is what `schemaForAsset` hands out everywhere else. Found by a round-trip test comparing the whole project rather than one field. An existing test had the loss written down as its expectation (`{ enabled, speed }`), which is why nothing ever reported it. Co-Authored-By: Claude Opus 5 --- src/core/store/serialize.ts | 5 +++-- tests/core/graph.test.ts | 2 +- tests/core/serialize.test.ts | 12 +++++++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/core/store/serialize.ts b/src/core/store/serialize.ts index ad4a81ad..a572a9f1 100644 --- a/src/core/store/serialize.ts +++ b/src/core/store/serialize.ts @@ -40,7 +40,6 @@ import { import { type FlatItem, makeFlatItem } from '../properties/listItems'; import { coerceValue, - COMMON_PROPS, isValidFieldName, isValidVariableName, type PropertyDef, @@ -76,7 +75,9 @@ export function deserializeProject(raw: unknown, fallbackId = ''): Project { if (a.type === 'behavior') { const vars = normalizeVariables(a.variables); variables.set(a.id, vars); - schemas.set(a.id, [...COMMON_PROPS, ...vars]); + // The full schema, not just the common rows: this is what pass 2 coerces `props` against, and + // a behavior's own `runInEditor` would be dropped as undeclared. + schemas.set(a.id, [...schemaFor('behavior'), ...vars]); } else if (a.type === 'particles') { // A source's rows end with its own graph's parameters, so the graph is needed before the // overrides of the objects placed from it can be coerced. diff --git a/tests/core/graph.test.ts b/tests/core/graph.test.ts index 9140f308..96cf72be 100644 --- a/tests/core/graph.test.ts +++ b/tests/core/graph.test.ts @@ -475,7 +475,7 @@ describe('behavior serialization', () => { const loaded = findAsset(roundTrip(project), behavior.id) as BehaviorAsset; expect(loaded.variables).toEqual(behavior.variables); expect(loaded.graph).toEqual(behavior.graph); - expect(loaded.props).toEqual({ enabled: true, speed: 0 }); + expect(loaded.props).toEqual({ enabled: true, runInEditor: false, speed: 0 }); const raw = serializeProject(project) as { assets: { id: string; graph?: { nodes: unknown[]; edges: unknown[] } }[]; diff --git a/tests/core/serialize.test.ts b/tests/core/serialize.test.ts index 45f2fc5a..2e2de893 100644 --- a/tests/core/serialize.test.ts +++ b/tests/core/serialize.test.ts @@ -1,7 +1,8 @@ import { describe, expect, it } from 'vitest'; +import { SetAssetPropertyCommand } from '../../src/core/commands/assetCommands'; import { SetOverrideCommand } from '../../src/core/commands/sceneObjectCommands'; import { deserializeProject } from '../../src/core/store/serialize'; -import { makeSpriteScene, place, roundTrip } from '../helpers'; +import { makeBehavior, makeSpriteScene, place, roundTrip } from '../helpers'; describe('serialize', () => { it('round-trips a project', () => { @@ -11,6 +12,15 @@ describe('serialize', () => { expect(roundTrip(f.project)).toEqual(f.project); }); + /** A behavior's own rows are not the common ones: coercing against those alone dropped the value. */ + it('keeps a behavior running in the editor across a save', () => { + const f = makeSpriteScene(); + const behavior = makeBehavior(f, 'B'); + f.stack.execute(new SetAssetPropertyCommand(behavior.id, 'runInEditor', true)); + const loaded = roundTrip(f.project).assets.find((a) => a.id === behavior.id)!; + expect(loaded.props.runInEditor).toBe(true); + }); + it('repairs broken documents', () => { const project = deserializeProject( {