Destroying a container used to take everything below it along. Now
Destroy can remove just the object itself and pass its direct children
on, so a behavior can keep working with what was inside.
- New input keep children (bool, default off). When on, only the
object goes. Its direct children move up to the object's parent, or
to the top level if there is none. Grandchildren stay where they are.
- New output children (object list) with the direct children that were
kept. It is empty when keep children is off.
- The children keep their place on screen. carriedTransform folds the
destroyed object's position, rotation and scale into their own, the
same as reparenting in the editor.
- Behaviors and the rigidbody of the object are always destroyed with
it and are not in the list. Moved up they would steer or simulate
the parent.
- World remembers which children it reparented, and syncToModel writes
their new parent into the scene. A reload of the model keeps it.
- Limit, read from the code only: in the editor run the contents of a
placed container are not baked. Moved children there last for the
run only.
Tests:
- ./check.sh: 170 files, 2478 tests, CHECK EXIT 0
- e2e 34 (the module with a Destroy node): ALL PASSED, 21 checks
- world-sync: world matrix of child and grandchild unchanged under a
rotated and scaled parent, parent survives syncFromModel, top level
case, already destroyed object gives []
- destroy-keep-children: real graph Destroy(Owner) -> Log(children)
logs Lid, Label without the grandchild Nail
- 3 counter-probes, all caught: no world transform (1 test fails), no
parent in the scene (1), behaviors and rigidbody moved up (2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A reference search over every export and method in src/ found code that
only tests still called, or that was never called at all:
- RemoveSceneObjectCommand: the product only removes through the
multi-object command; the tests now use that one too.
- NodeRegistry.requireNodeDef: a second function of the same name in
commands/require.ts raises a CommandError instead of throwing, and is
the one the product uses. The twin invited the wrong import.
- isTextureAsset, listContainers, AssetTabEditor.show,
FocusManager.destroy, KeyboardBridge.selectAll and the getters
ColorField.value, ListView.selected, NumberField.number.
Also: Theme.controlActive now matches the resting control colour, and a
new note asks what editing the graph during play would take.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A behavior object carries a "Run in editor" switch next to Enabled. When
it is on, the behavior ticks outside of Play and works on the saved model
directly - Unity semantics: what it moves is a real change, autosaved and
not undoable.
Input stays with the editor, so On key never fires while typing. The
editor run never bakes placed containers into the model, since that would
silently drop their instanceEdits.
syncToModel reports whether it wrote anything, and the dirty flag is only
raised on change - otherwise a behavior moving something every frame
would trigger ~60 saves a second.
Tooling:
- Prettier as the single formatting authority (110 columns, single quotes,
measured against the existing style); Markdown excluded because table-cell
padding turns the README glossary into 1000-character lines.
- Type-aware ESLint ending in eslint-config-prettier, so a rule always means
"this is a bug", never "this looks different".
- .editorconfig and an `npm run check` that runs typecheck, lint, format and
tests in one go.
Findings fixed on the first run: three more duplicate imports, four
String(unknown) calls (parameter types narrowed instead of casts), two dead
initialisers, two redundant casts, a console.log for a real error. Two
warnings were false here and are disabled with a reason in place.
Tests 132 -> 269:
- node-catalog.test.ts covers every node with a run(), with a guard that fails
when a new node has no row.
- behavior-events.test.ts covers the ten event nodes through a running World,
checking timing rather than existence.
- Input's state machine (press/release/releaseAll) is reachable without DOM
events now; the DOM handlers are thin adapters.
Compare fell back to == without a configured operator although the schema
default is <; aligned.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>