Start new behavior variables with Keep switched off

Keep is a choice the author makes per variable. Switching it on for
every new variable meant most behaviors carried state across runs that
nobody asked for, and the toggle had to be turned off by hand.

- AddVariableCommand no longer adds a new variable to keptVariables.
  Keep is only switched on by hand.
- Existing projects are unchanged. Their keptVariables stay as saved.
- The comment in Asset.ts describes the new default.
- The counter fixture in keep-state.test.ts now keeps count
  explicitly instead of relying on the old default.

Tests:
- ./check.sh: 2706 tests, CHECK EXIT 0
- e2e 1, 6, 19: ALL PASSED, 172 checks
- 19c-ii expects [] after adding, then ["var1"], [] and ["var1"]
  after undo, all clicked with the real mouse
- Before the fixtures were adjusted, 8 unit tests failed on the new
  default

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-09-28 12:02:40 +02:00
1 parent 38e0f656be
commit cf6565d108
4 files changed
+12 -14

No files matched your search

+1 -3
View File
@@ -25,7 +25,7 @@ import { requireBehavior, requireVariableOwner } from './require';
/**
* The variable's default value is written to `props` as well. On a behavior a new variable starts
* out kept between calls (#229).
* at its default every call; "Keep" is switched on by hand (#229).
*/
export class AddVariableCommand implements Command {
readonly label: string;
@@ -47,14 +47,12 @@ export class AddVariableCommand implements Command {
throw new CommandError(`Variable "${this.def.key}" already exists`);
asset.variables.push(this.def);
asset.props[this.def.key] = this.def.default;
if (asset.type === 'behavior') setKeptVariable(asset, this.def.key, true);
}
undo(project: Project): void {
const asset = requireVariableOwner(project, this.assetId);
asset.variables = asset.variables.filter((v) => v.key !== this.def.key);
delete asset.props[this.def.key];
if (asset.type === 'behavior') setKeptVariable(asset, this.def.key, false);
}
}
+1 -1
View File
@@ -36,7 +36,7 @@ export interface SceneAsset extends AssetBase {
*
* `keptVariables` names the variables a `Run behavior` call hands on to the next call from the same
* node on the same object (#229); the others start at their default every call. Absent keeps none,
* so a project from before the switch runs as it did – a variable added since starts out kept.
* so a project from before the switch runs as it did, and a new variable starts out not kept.
*/
export interface BehaviorAsset extends AssetBase {
type: 'behavior';
+4 -4
View File
@@ -36,11 +36,11 @@ import {
/**
* A behavior that counts its calls in `count` and hands the new count out as `out1`. `steps`
* counts the same way but is switched off, so it shows what an unkept variable does meanwhile.
* counts the same way but is left off, so it shows what an unkept variable does meanwhile.
*/
function counter(fixture: ProjectFixture): BehaviorAsset {
const callee = makeBehavior(fixture, 'Counter', ['count', 'steps']);
fixture.stack.execute(new SetVariableKeptCommand(callee.id, 'steps', false));
fixture.stack.execute(new SetVariableKeptCommand(callee.id, 'count', true));
const edit = graphEditor(fixture, callee);
const start = edit.addNode(BEHAVIOR_START);
const end = edit.addNode(BEHAVIOR_END);
@@ -105,10 +105,10 @@ function threeCalls(fixture: ProjectFixture, callee: BehaviorAsset): unknown[][]
}
describe('Keep on a behavior variable (#229)', () => {
it('starts on for a variable added to a behavior, and off on a Variables asset', () => {
it('starts off for a variable added to a behavior, and on a Variables asset', () => {
const fixture = makeProject();
const behavior = makeBehavior(fixture, 'B', ['count']);
expect(behavior.keptVariables).toEqual(['count']);
expect('keptVariables' in behavior).toBe(false);
const shared = new CreateAssetCommand('variables', 'Shared');
fixture.stack.execute(shared);
fixture.stack.execute(new AddVariableCommand(shared.asset.id, 'score', 'number'));
+6 -6
View File
@@ -129,7 +129,7 @@ export async function nestedBehavior(t) {
await shot('37-nested-behavior');
// 19c-ii. Keep (#229) is a switch on each variable of the called behavior, not on the node: a new
// variable starts kept, a click on its Keep button lets it go, and undo keeps it again.
// variable starts unkept, a click on its Keep button keeps it, and undo lets it go again.
const kept = async () => JSON.stringify((await asset('nb-double')).keptVariables ?? []);
const nodeKeys = Object.keys((await asset('nb-caller')).graph.nodes.find((n) => n.id === 'nb-call').params);
await t.select({ kind: 'asset', assetId: 'nb-double' });
@@ -147,13 +147,13 @@ export async function nestedBehavior(t) {
await undo();
await undo();
check(
'a variable has a Keep switch: on when added, off by a click, on by the next, off after undo',
'a variable has a Keep switch: off when added, on by a click, off by the next, on after undo',
!nodeKeys.includes('keepState') &&
keptAdded === '["var1"]' &&
keptAdded === '[]' &&
Boolean(keepButton) &&
keptClicked === '[]' &&
keptReclicked === '["var1"]' &&
keptUndone === '[]' &&
keptClicked === '["var1"]' &&
keptReclicked === '[]' &&
keptUndone === '["var1"]' &&
(await kept()) === '[]',
`node=${nodeKeys} added=${keptAdded} button=${JSON.stringify(keepButton)} clicked=${keptClicked} reclicked=${keptReclicked} undone=${keptUndone} after=${await kept()}`,
);