Statemachine Scope und Variable Scope #91

Closed
opened 2026-09-14 14:07:05 +02:00 by admin · 3 comments

Ich hätte gerne für Behavior Variablen und State Machine Zustand die möglichkeit einen Scope festzulegen.

Bei variablen scope: global (= projektweit), scene (= fast global, aber nur innerhalb der scene), object (oder andere passender name = behaviors vom selben objekt haben zugriff drauf), lokal (oder anderer passender name = nur das eigene behavior - ich glaube das ist der stand jetzt)

bei statemachine scope: ähnlich, lass uns das durchplanen

soweit so gut, gibt es noch andere stellen die wir beachten müssen? grundsätzlich erstmal durchplanen vor umsetzung - genau rückfragen stellen. danach wenn alles klar ist => umsetzen.

Ich hätte gerne für Behavior Variablen und State Machine Zustand die möglichkeit einen Scope festzulegen. Bei variablen scope: global (= projektweit), scene (= fast global, aber nur innerhalb der scene), object (oder andere passender name = behaviors vom selben objekt haben zugriff drauf), lokal (oder anderer passender name = nur das eigene behavior - ich glaube das ist der stand jetzt) bei statemachine scope: ähnlich, lass uns das durchplanen soweit so gut, gibt es noch andere stellen die wir beachten müssen? grundsätzlich erstmal durchplanen vor umsetzung - genau rückfragen stellen. danach wenn alles klar ist => umsetzen.
admin added the state-machine label 2026-09-14 14:07:05 +02:00
admin added this to the Development project 2026-09-14 14:07:05 +02:00
admin moved this to Next in Development on 2026-09-14 14:07:11 +02:00
Owner

Ist-Stand, gemessen am Code

Bevor wir entscheiden — was heute tatsaechlich da ist:

Ebene Heute
Deklaration BehaviorAsset.variables: PropertyDef[] (core/model/Asset.ts:31) — das Property-Schema des Behaviors. Default an der Deklaration und in asset.props.
Laufzeit entity.props — es gibt keinen eigenen Variablenspeicher. BehaviorSystem.ts:335: variables: entity.props. Key = Variablenname, flach, ein Bag pro Behavior-Entity.
Zugriff eigen Variables-Node, read: (ctx) => ctx.variables. Keine Namensaufloesung zur Laufzeit — der Port-Key ist der Bag-Key.
Zugriff fremd Properties-Node Typ Behavior: asEntity(input('object'))?.props (nodes/data/Properties.ts:30).
Run behavior { ...ctx, variables: { ...program.defaults }, states: {} } (GraphInterpreter.ts:139) — frisch, teilt nichts, behaelt nichts.
State Instance.states: Record<machineId, stateId>, pro Behavior-Instanz (BehaviorSystem.ts:43).
Projekt-/Szenenebene Nichts. NodeContext kennt kein globals; eine Szene hat genau eine Property (backgroundColor).

Der heutige Stand ist also lokal — genauer: pro Behavior-Instanz = pro Szenenobjekt. Und dieser
Satz Variablen ist zugleich das Property-Schema des Objekts. Genau daran haengt alles Weitere.

Andere Stellen, die mitbetroffen sind

Zu deiner Frage, ob es noch welche gibt — ja, sechs:

  1. Instanz-Override. Variablen erscheinen am platzierten Objekt im Inspector und sind pro Instanz
    ueberschreibbar (objectSection.ts, bei Container-Inhalten zusaetzlich instanceEdits). Bei einer
    geteilten Variable ist das bedeutungslos — die Zeile darf dort nicht aussehen wie heute.
  2. World.syncToModel (runtime/World.ts:184) schreibt zur Laufzeit abgewichene Werte als Override
    zurueck. Im Play harmlos (Wegwerf-Kopie), im Editor-Run (runInEditor) ist es eine echte
    Projektaenderung mit commit()
    . Eine globale Variable hat kein Objekt, an dem der Wert haengt.
  3. Properties-Node. Der heutige Weg zu fremdem Zustand liest entity.props. Wandert eine Variable
    in einen geteilten Bag, ist sie dort nicht mehr zu sehen — das Beispielspiel baut seinen Muenzzaehler
    genau so (Object by name -> Properties).
  4. Run behavior. Nested Calls bekommen frische Variablen. Geteilte muessten sie sehen — eine
    Semantikaenderung, die in #59 schon als Schmerzpunkt steht.
  5. Persistenz. normalizeVariables (core/store/serialize.ts:238) baut die Deklaration beim Laden
    neu auf. Ein neues Feld scope braucht dort eine eigene Zeile, sonst ueberlebt es das Speichern,
    aber nicht das Laden (dieselbe Falle wie zuletzt bei GraphEdge.label in #52).
  6. Szenen-Scope ist heute nicht messbar. Ein Projekt hat mehrere Szenen, aber genau eine
    activeSceneId, und ein Lauf kennt genau eine Szene. Ohne Load scene (#39) ist scene zur
    Laufzeit nicht von global unterscheidbar — die Wahl waere baubar, aber nicht pruefbar.

Offene Entscheidungen

A — Deklarationsort und Namenskollision (die Hauptweiche)

Heisst global, dass der Name der Schluessel ist, teilen sich zwei Behaviors mit je einer Variablen
score automatisch eine. Folge: ein Tippfehler ist eine stille zweite Variable, und bei
widersprechendem Typ oder Default muss jemand entscheiden, welcher gilt.

  • A1 Deklaration bleibt am Behavior, jede Variable bekommt ein Feld scope; der Name ist der
    geteilte Schluessel. Wenig UI. Der Editor muesste Konflikte melden.
  • A2 Eigener Deklarationsort je Scope: globale Variablen am Projekt, Szenen-Variablen an der Szene,
    Objekt-Variablen am Objekt; das Behavior benutzt sie nur. Eindeutig, keine Kollision — kostet je
    eine Inspector-Liste und eine zweite Quelle fuer die Ports des Variables-Node.

B — Wohin legt sich der Objekt-Scope?

Nicht nach owner.props — dort liegen x, y, texture, und syncToModel schriebe sie zurueck.
Es braeuchte einen eigenen Bag an der Owner-Entity, nach dem Vorbild von Entity.animation /
Entity.emits ("kept out of props, which the world writes back").
Und: ein Behavior kann gar keinen Owner haben (parent: Entity | null; das "click anywhere"-Behavior
ist genau so gebaut). Worauf faellt object dann zurueck — auf lokal, oder ist es ein Fehler?

C — Lebensdauer und Reset

Gilt global fuer die Dauer eines Laufs (beim Play-Start zurueck auf Default), oder soll es einen
Lauf ueberdauern und gespeichert werden? Ersteres ist der kleine Schritt; Letzteres waere der Anfang
eines Savegames.

D — State-Machine-Scope: wo steht er?

Vorschlag: am Maschinen-Asset, nicht pro Node. Eine Maschine ist ein Asset mit einer Id, und der
Zustand haengt schon heute an dieser Id (states[machineId]). Ein Feld scope am StateMachineAsset
ist ein Wort im Modell und macht #59 ueberfluessig. Die Alternative — Scope pro Set state-Node —
liesse dieselbe Maschine an zwei Stellen verschieden wohnen.

E — Reihenfolge

Der State-Machine-Scope ist der kleinere, sofort messbare Schritt (ein Feld am Asset, ein Bag mehr im
BehaviorSystem, NodeContext.states bekommt eine zweite Quelle). Der Variablen-Scope zieht Inspector,
Overrides, Properties-Node und Persistenz mit. Sollen wir sie trennen — erst D, dann A?


Kein Umbau, bevor A bis E entschieden sind.

## Ist-Stand, gemessen am Code Bevor wir entscheiden — was heute tatsaechlich da ist: | Ebene | Heute | |---|---| | Deklaration | `BehaviorAsset.variables: PropertyDef[]` (`core/model/Asset.ts:31`) — das Property-Schema des Behaviors. Default an der Deklaration *und* in `asset.props`. | | Laufzeit | **`entity.props`** — es gibt keinen eigenen Variablenspeicher. `BehaviorSystem.ts:335`: `variables: entity.props`. Key = Variablenname, flach, ein Bag pro Behavior-Entity. | | Zugriff eigen | `Variables`-Node, `read: (ctx) => ctx.variables`. **Keine Namensaufloesung zur Laufzeit** — der Port-Key *ist* der Bag-Key. | | Zugriff fremd | `Properties`-Node Typ *Behavior*: `asEntity(input('object'))?.props` (`nodes/data/Properties.ts:30`). | | `Run behavior` | `{ ...ctx, variables: { ...program.defaults }, states: {} }` (`GraphInterpreter.ts:139`) — frisch, teilt nichts, behaelt nichts. | | State | `Instance.states: Record<machineId, stateId>`, pro Behavior-Instanz (`BehaviorSystem.ts:43`). | | Projekt-/Szenenebene | **Nichts.** `NodeContext` kennt kein `globals`; eine Szene hat genau eine Property (`backgroundColor`). | Der heutige Stand ist also `lokal` — genauer: **pro Behavior-Instanz = pro Szenenobjekt**. Und dieser Satz Variablen *ist* zugleich das Property-Schema des Objekts. Genau daran haengt alles Weitere. ## Andere Stellen, die mitbetroffen sind Zu deiner Frage, ob es noch welche gibt — ja, sechs: 1. **Instanz-Override.** Variablen erscheinen am platzierten Objekt im Inspector und sind pro Instanz ueberschreibbar (`objectSection.ts`, bei Container-Inhalten zusaetzlich `instanceEdits`). Bei einer geteilten Variable ist das bedeutungslos — die Zeile darf dort nicht aussehen wie heute. 2. **`World.syncToModel`** (`runtime/World.ts:184`) schreibt zur Laufzeit abgewichene Werte als Override zurueck. Im Play harmlos (Wegwerf-Kopie), im **Editor-Run (`runInEditor`) ist es eine echte Projektaenderung mit `commit()`**. Eine globale Variable hat kein Objekt, an dem der Wert haengt. 3. **`Properties`-Node.** Der heutige Weg zu fremdem Zustand liest `entity.props`. Wandert eine Variable in einen geteilten Bag, ist sie dort nicht mehr zu sehen — das Beispielspiel baut seinen Muenzzaehler genau so (`Object by name` -> `Properties`). 4. **`Run behavior`.** Nested Calls bekommen frische Variablen. Geteilte muessten sie sehen — eine Semantikaenderung, die in #59 schon als Schmerzpunkt steht. 5. **Persistenz.** `normalizeVariables` (`core/store/serialize.ts:238`) baut die Deklaration beim Laden neu auf. Ein neues Feld `scope` braucht dort eine eigene Zeile, sonst ueberlebt es das Speichern, aber nicht das Laden (dieselbe Falle wie zuletzt bei `GraphEdge.label` in #52). 6. **Szenen-Scope ist heute nicht messbar.** Ein Projekt hat mehrere Szenen, aber genau eine `activeSceneId`, und ein Lauf kennt genau eine Szene. Ohne `Load scene` (#39) ist `scene` zur Laufzeit **nicht von `global` unterscheidbar** — die Wahl waere baubar, aber nicht pruefbar. ## Offene Entscheidungen ### A — Deklarationsort und Namenskollision (die Hauptweiche) Heisst `global`, dass der **Name** der Schluessel ist, teilen sich zwei Behaviors mit je einer Variablen `score` automatisch eine. Folge: ein Tippfehler ist eine stille zweite Variable, und bei widersprechendem Typ oder Default muss jemand entscheiden, welcher gilt. - **A1** Deklaration bleibt am Behavior, jede Variable bekommt ein Feld `scope`; der Name ist der geteilte Schluessel. Wenig UI. Der Editor muesste Konflikte melden. - **A2** Eigener Deklarationsort je Scope: globale Variablen am Projekt, Szenen-Variablen an der Szene, Objekt-Variablen am Objekt; das Behavior *benutzt* sie nur. Eindeutig, keine Kollision — kostet je eine Inspector-Liste und eine zweite Quelle fuer die Ports des `Variables`-Node. ### B — Wohin legt sich der Objekt-Scope? **Nicht** nach `owner.props` — dort liegen `x`, `y`, `texture`, und `syncToModel` schriebe sie zurueck. Es braeuchte einen eigenen Bag an der Owner-Entity, nach dem Vorbild von `Entity.animation` / `Entity.emits` (*"kept out of `props`, which the world writes back"*). Und: **ein Behavior kann gar keinen Owner haben** (`parent: Entity | null`; das "click anywhere"-Behavior ist genau so gebaut). Worauf faellt `object` dann zurueck — auf lokal, oder ist es ein Fehler? ### C — Lebensdauer und Reset Gilt `global` fuer die Dauer **eines Laufs** (beim Play-Start zurueck auf Default), oder soll es einen Lauf ueberdauern und gespeichert werden? Ersteres ist der kleine Schritt; Letzteres waere der Anfang eines Savegames. ### D — State-Machine-Scope: wo steht er? Vorschlag: **am Maschinen-Asset**, nicht pro Node. Eine Maschine ist ein Asset mit einer Id, und der Zustand haengt schon heute an dieser Id (`states[machineId]`). Ein Feld `scope` am `StateMachineAsset` ist ein Wort im Modell und macht **#59** ueberfluessig. Die Alternative — Scope pro `Set state`-Node — liesse dieselbe Maschine an zwei Stellen verschieden wohnen. ### E — Reihenfolge Der State-Machine-Scope ist der kleinere, sofort messbare Schritt (ein Feld am Asset, ein Bag mehr im `BehaviorSystem`, `NodeContext.states` bekommt eine zweite Quelle). Der Variablen-Scope zieht Inspector, Overrides, `Properties`-Node und Persistenz mit. Sollen wir sie trennen — erst D, dann A? --- Kein Umbau, bevor A bis E entschieden sind.
Owner

Entschieden — Frage/Antwort-Runde vom 2026-09-14

Die Planungsfragen aus dem Kommentar oben sind durchgesprochen. Das ist der Stand, gegen den gebaut wird:

Frage Entscheidung
A Deklarationsort und Schlüssel Deklaration bleibt am Behavior, neues Feld scope an der Variablen-Deklaration. Der Name ist der geteilte Schlüssel — zwei Behaviors mit je einer globalen score teilen sich eine.
A2 Typ-/Default-Konflikt Typkonflikt = Fehler (blockiert den Lauf, rot). Default-Konflikt = Warnung (gelb), die erste Deklaration gewinnt.
B Scope object Wird nicht gebaut. Der Objektfall bleibt über Object by name → Properties lösbar; kommt als eigenes Issue zurück, wenn er wehtut.
G Scope scene Wird nicht gebaut. Ohne Load scene (#39) ist er zur Laufzeit nicht von global unterscheidbar — also erst mit #39, dann ist er messbar.
C Lebensdauer Pro Lauf. Bei jedem Play-Start frisch aus den Defaults, beim Stop weg. Nichts wird zurückgeschrieben — auch im Editor-Run nicht.
E Objekt-Inspector Eine globale Variable verschwindet aus dem Inspector des platzierten Objekts. Vorhandene Instanz-Overrides fallen beim Umstellen weg.
F Zugriff im Graphen Der bestehende Variables-Node bleibt transparent: er zeigt weiterhin alle Variablen des Behaviors und leitet intern in den richtigen Bag um. Bestehende Graphen brechen beim Umstellen nicht.
D State-Machine-Scope Feld scope am StateMachineAsset (nicht pro Set state-Node). Der Zustand hängt schon heute an der Asset-Id. Schließt #59 mit.

Was daraus folgt

Das Feld hat genau zwei Werte, local (heute) und global. Es entstehen genau zwei neue Speicher — ein laufweiter Variablen-Bag und ein laufweiter States-Bag — beide beim Play-Start frisch.

Drei Punkte leite ich daraus ab, ohne dass sie eigens entschieden wurden:

  • Der globale States-Bag folgt derselben Regel wie C: Startzustand bei jedem Play-Start.
  • Run behavior sieht die globalen Bags; nested Calls bekommen weiterhin frische lokale Variablen.
  • normalizeVariables braucht eine Zeile für scope, sonst überlebt das Feld das Speichern, aber nicht das Laden — dieselbe Falle, die bei GraphEdge.label in #52 zugeschnappt wäre.

Nicht Teil dieser Runde

object- und scene-Scope (siehe B und G) sowie jede Form von Persistenz über den Lauf hinaus (C) — Letzteres wäre der Anfang eines Savegames und gehört in ein eigenes Issue.

## Entschieden — Frage/Antwort-Runde vom 2026-09-14 Die Planungsfragen aus dem Kommentar oben sind durchgesprochen. Das ist der Stand, gegen den gebaut wird: | | Frage | Entscheidung | |---|---|---| | **A** | Deklarationsort und Schlüssel | Deklaration bleibt am Behavior, neues Feld `scope` an der Variablen-Deklaration. **Der Name ist der geteilte Schlüssel** — zwei Behaviors mit je einer globalen `score` teilen sich eine. | | **A2** | Typ-/Default-Konflikt | **Typkonflikt = Fehler** (blockiert den Lauf, rot). **Default-Konflikt = Warnung** (gelb), die erste Deklaration gewinnt. | | **B** | Scope `object` | **Wird nicht gebaut.** Der Objektfall bleibt über `Object by name` → `Properties` lösbar; kommt als eigenes Issue zurück, wenn er wehtut. | | **G** | Scope `scene` | **Wird nicht gebaut.** Ohne `Load scene` (#39) ist er zur Laufzeit nicht von `global` unterscheidbar — also erst mit #39, dann ist er messbar. | | **C** | Lebensdauer | **Pro Lauf.** Bei jedem Play-Start frisch aus den Defaults, beim Stop weg. **Nichts wird zurückgeschrieben — auch im Editor-Run nicht.** | | **E** | Objekt-Inspector | Eine globale Variable **verschwindet** aus dem Inspector des platzierten Objekts. Vorhandene Instanz-Overrides fallen beim Umstellen weg. | | **F** | Zugriff im Graphen | Der bestehende `Variables`-Node bleibt **transparent**: er zeigt weiterhin alle Variablen des Behaviors und leitet intern in den richtigen Bag um. Bestehende Graphen brechen beim Umstellen nicht. | | **D** | State-Machine-Scope | Feld `scope` am **`StateMachineAsset`** (nicht pro `Set state`-Node). Der Zustand hängt schon heute an der Asset-Id. **Schließt #59 mit.** | ### Was daraus folgt Das Feld hat genau zwei Werte, `local` (heute) und `global`. Es entstehen genau **zwei** neue Speicher — ein laufweiter Variablen-Bag und ein laufweiter States-Bag — beide beim Play-Start frisch. Drei Punkte leite ich daraus ab, ohne dass sie eigens entschieden wurden: - Der globale States-Bag folgt derselben Regel wie **C**: Startzustand bei jedem Play-Start. - `Run behavior` sieht die globalen Bags; nested Calls bekommen weiterhin frische **lokale** Variablen. - `normalizeVariables` braucht eine Zeile für `scope`, sonst überlebt das Feld das Speichern, aber nicht das Laden — dieselbe Falle, die bei `GraphEdge.label` in #52 zugeschnappt wäre. ### Nicht Teil dieser Runde `object`- und `scene`-Scope (siehe B und G) sowie jede Form von Persistenz über den Lauf hinaus (**C**) — Letzteres wäre der Anfang eines Savegames und gehört in ein eigenes Issue.
Owner

Umgesetzt — aber mit gekippter Entscheidung A

Entscheidung A ist verworfen. Der Scope sitzt nicht mehr auf der Deklaration; es gibt
PropertyDef.scope nicht mehr. Ausloeser war die Frage, ob eine global gesetzte Variable in
einem anderen Behavior am Variables-Node vorgeschlagen wird. Gemessen: nein — die
Vorschlagsliste kam aus asset.variables des eigenen Behaviors, ohne Freitext-Ausweg.
Teilnehmen hiess damit: den Namen in jedem Behavior noch einmal deklarieren, mit Tippfehler
als stiller zweiter Variable und ohne jede Uebersicht, welche geteilten Werte ein Projekt hat.

Was stattdessen gebaut ist

Ein eigener Asset-Typ Variables: Deklarationen und sonst nichts — kein Graph, keine
Position, nie platziert, kein eigener Tab. Der Variables-Node waehlt eines ueber die neue
Einstellung variablesAsset (leer = eigene Variablen wie bisher) und liest/schreibt dann
einen Wert pro Schluessel fuer den ganzen Lauf. Das Muster ist geborgt, nicht erfunden:
Set state waehlt seine Maschine genauso.

Damit faellt weg: globalConflicts samt Lauf-Stopp bei Kind-Konflikt (A2 wird
gegenstandslos), SetVariableScopeCommand, der Scope-Picker, beide Objekt-Inspector-Filter.
Eine Behavior-Variable ist ausnahmslos wieder eine Objekt-Property. Unterm Strich 72 Zeilen
weniger Code
. Zwei Variables-Assets duerfen denselben Namen fuehren, ohne sich zu treffen —
das Asset ist der Schluessel, nicht der Name.

Entscheidung C bleibt: Der Bag entsteht beim Play-Start aus den Deklarationen und wird nie
zurueckgeschrieben — auch der Editor-Run veraendert das Projekt nicht. B und G (Scope
object/scene) sind weiterhin bewusst nicht gebaut.

Nachgezogen: volle Symmetrie zum Inspector

Waehrend eines Laufs zeigte der Inspector den Startwert aus dem Dokument, und eine Aenderung
dort wirkte erst beim naechsten Play. Gecacht war dabei nie etwas (GraphInterpreter legt nur
Knoten ohne read in den Pulse-Cache, der Variablen-Knoten hat eines) — es fehlte die
Leitung. Jetzt zwei benannte: Inspector.liveVariables zeigt die laufenden Werte und faellt
beim Ende des Laufs von selbst auf die Deklaration zurueck; adoptDeclarations() uebernimmt
einen Schluessel nur dort, wo das Dokument ihn seit dem letzten Abgleich geaendert hat —
ohne diese Bedingung reisst jede beliebige Projektaenderung den laufenden Zaehler auf den
Startwert zurueck.

D / #59 — unveraendert

StateMachineAsset.scope steht wie gebaut, Scope als Typ traegt jetzt nur noch die
Maschinen. Nachgebessert wurde nur die Darstellung: die Scope-Zeile war handgebaut (eigenes
HStack, Ghost-Dropdown) und begann ~34 px weiter links als jeder Wert darueber — sie geht
jetzt durch createFieldRow wie jede andere enum-Zeile, die Erklaerungen neben
Local/Global sind aus dem Menue raus.

Zwei Funde nebenbei

  1. "Run in editor" ueberlebte kein Speichern — deserializeProject coercte Behaviors gegen
    [...COMMON_PROPS, ...variables], wo runInEditor nicht drinsteht. Eigener Commit
    9027a86, weil es nichts mit diesem Issue zu tun hat.
  2. Die Vorschlagsliste wurde doppelt berechnet (nodeSection und portSection) — die
    Gegenprobe dazu biss zuerst nicht, die eigentliche Anforderung hing allein am Browser-Test.
    Jetzt eine Funktion, variableChoices, mit eigenem Unit-Test.

Belege

./check.sh CHECK EXIT 0, 999 Tests in 78 Dateien
e2e Modul 32 ALL PASSED, dazu Module 4/19/20/21/31 gegen den Umbau
Gegenproben 6, jede beisst isoliert an ihrer eigenen Wache
Commits d1fcfe8 (erster Entwurf, Scope an der Deklaration) → 8143130 (Umbau auf das Asset)

Wiki: Behaviors (Shared state neu geschrieben), Concepts (Glossar-Eintrag), Architecture,
State machines (#59).

## Umgesetzt — aber mit gekippter Entscheidung A **Entscheidung A ist verworfen.** Der Scope sitzt nicht mehr auf der Deklaration; es gibt `PropertyDef.scope` nicht mehr. Ausloeser war die Frage, ob eine global gesetzte Variable in einem *anderen* Behavior am `Variables`-Node vorgeschlagen wird. Gemessen: nein — die Vorschlagsliste kam aus `asset.variables` des eigenen Behaviors, ohne Freitext-Ausweg. Teilnehmen hiess damit: den Namen in jedem Behavior noch einmal deklarieren, mit Tippfehler als stiller zweiter Variable und ohne jede Uebersicht, welche geteilten Werte ein Projekt hat. ### Was stattdessen gebaut ist Ein eigener Asset-Typ **Variables**: Deklarationen und sonst nichts — kein Graph, keine Position, nie platziert, kein eigener Tab. Der `Variables`-Node waehlt eines ueber die neue Einstellung `variablesAsset` (leer = eigene Variablen wie bisher) und liest/schreibt dann **einen Wert pro Schluessel fuer den ganzen Lauf**. Das Muster ist geborgt, nicht erfunden: `Set state` waehlt seine Maschine genauso. Damit faellt weg: `globalConflicts` samt Lauf-Stopp bei Kind-Konflikt (**A2** wird gegenstandslos), `SetVariableScopeCommand`, der Scope-Picker, beide Objekt-Inspector-Filter. Eine Behavior-Variable ist ausnahmslos wieder eine Objekt-Property. Unterm Strich **72 Zeilen weniger Code**. Zwei Variables-Assets duerfen denselben Namen fuehren, ohne sich zu treffen — das Asset ist der Schluessel, nicht der Name. **Entscheidung C bleibt:** Der Bag entsteht beim Play-Start aus den Deklarationen und wird nie zurueckgeschrieben — auch der Editor-Run veraendert das Projekt nicht. **B und G** (Scope `object`/`scene`) sind weiterhin bewusst nicht gebaut. ### Nachgezogen: volle Symmetrie zum Inspector Waehrend eines Laufs zeigte der Inspector den Startwert aus dem Dokument, und eine Aenderung dort wirkte erst beim naechsten Play. Gecacht war dabei nie etwas (`GraphInterpreter` legt nur Knoten **ohne** `read` in den Pulse-Cache, der Variablen-Knoten hat eines) — es fehlte die Leitung. Jetzt zwei benannte: `Inspector.liveVariables` zeigt die laufenden Werte und faellt beim Ende des Laufs von selbst auf die Deklaration zurueck; `adoptDeclarations()` uebernimmt einen Schluessel **nur dort, wo das Dokument ihn seit dem letzten Abgleich geaendert hat** — ohne diese Bedingung reisst jede beliebige Projektaenderung den laufenden Zaehler auf den Startwert zurueck. ### D / #59 — unveraendert `StateMachineAsset.scope` steht wie gebaut, `Scope` als Typ traegt jetzt nur noch die Maschinen. Nachgebessert wurde nur die Darstellung: die Scope-Zeile war handgebaut (eigenes `HStack`, Ghost-Dropdown) und begann ~34 px weiter links als jeder Wert darueber — sie geht jetzt durch `createFieldRow` wie jede andere `enum`-Zeile, die Erklaerungen neben `Local`/`Global` sind aus dem Menue raus. ### Zwei Funde nebenbei 1. **"Run in editor" ueberlebte kein Speichern** — `deserializeProject` coercte Behaviors gegen `[...COMMON_PROPS, ...variables]`, wo `runInEditor` nicht drinsteht. Eigener Commit `9027a86`, weil es nichts mit diesem Issue zu tun hat. 2. **Die Vorschlagsliste wurde doppelt berechnet** (`nodeSection` und `portSection`) — die Gegenprobe dazu biss zuerst nicht, die eigentliche Anforderung hing allein am Browser-Test. Jetzt eine Funktion, `variableChoices`, mit eigenem Unit-Test. ### Belege | | | |---|---| | `./check.sh` | `CHECK EXIT 0`, **999 Tests** in 78 Dateien | | e2e Modul 32 | `ALL PASSED`, dazu Module 4/19/20/21/31 gegen den Umbau | | Gegenproben | 6, jede beisst isoliert an ihrer eigenen Wache | | Commits | `d1fcfe8` (erster Entwurf, Scope an der Deklaration) → `8143130` (Umbau auf das Asset) | Wiki: *Behaviors* (Shared state neu geschrieben), *Concepts* (Glossar-Eintrag), *Architecture*, *State machines* (#59).
admin moved this to Done in Development on 2026-09-14 18:10:14 +02:00
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: meleki/feuerwerk#91