Aus der Zwischenfrage vom 2026-10-02 (bei #216): Der User will Behaviors als eigene Verzweigungs-Nodes bauen, etwa für Eingaben. Dafür reicht der eine Flow-Port an Start und End nicht. Die sechs offenen Punkte sind vor dem Anlegen per Rückfrage geklärt, siehe Entschieden.
Nachtrag vom selben Tag: Der Node On behavior gehört jetzt mit in dieses Issue (Wunsch des Users: „gleich mit rein, alles in einem Wisch“). Seine fünf Punkte sind ebenfalls per Rückfrage geklärt, siehe On behavior.
Stand heute
Start und End haben je genau einen festen Flow-Port (exec). Deklarieren lassen sich nur Wert-Ports, der Name exec ist dafür gesperrt (isValidPortName in src/core/graph/nested.ts).
Run behavior hat einen Flow-Eingang und einen Flow-Ausgang. Seine Wert-Ports leitet es aus den deklarierten Ports von Start und End des gewählten Behaviors ab (derivePorts in src/core/graph/nodes/flow/RunBehavior.ts).
Der Aufruf beginnt immer am exec des Start (oder an jedem On start, wenn es kein Start gibt, #230) und geht am Aufrufer immer am exec weiter, auch wenn das End nie erreicht wurde (call in src/core/graph/GraphInterpreter.ts).
End beendet den inneren Lauf nicht. Es legt nur seine Eingangswerte ab, andere Zweige laufen weiter. Wird es mehrmals erreicht, gelten die Werte des letzten Durchgangs.
Es zählt ein Start und ein End je Graph, ein zweites wird ignoriert.
Entschieden (Antworten des Users)
Der feste exec bleibt immer da und verhält sich wie heute.Run behavior geht am exec immer weiter. Die zusätzlichen Ausgänge feuern zusätzlich, wenn ihr Eingang am End erreicht wurde. Bestehende Graphen ändern sich nicht.
Wird das End in einem Aufruf über mehrere Flow-Eingänge erreicht, feuert nur der zuletzt erreichte. Ein Aufruf geht also höchstens an einem Zusatz-Ausgang weiter. Das passt zu den Werten, bei denen auch der letzte Durchgang gilt.
Zusatz-Ports am Start:Run behavior bekommt je Port einen eigenen Flow-Eingang. Kommt der Puls am Eingang open an, beginnt der innere Lauf nur am gleichnamigen Ausgang open des Start. Der feste exec des Start feuert nur, wenn der Aufruf über exec hereinkam.
Anlegen im Inspector als weitere Art flow in der bestehenden Port-Liste von Start und End: ein Knopf, eine Liste, Umbenennen, Löschen und Sortieren wie bei Wert-Ports. Am Node stehen Flow-Ports trotzdem immer oben, vor den Werten.
Reihenfolge am Aufrufer: zuerst der Zusatz-Ausgang, dann exec. exec liest sich damit als „danach weiter“, wie completed an einer Schleife.
Umfang: die Flow-Ports und, seit dem Nachtrag, On behavior. Ein Input-Action-Asset ist nicht Teil dieses Issues, siehe Nicht dabei.
Zu bauen
Port-Art flow für deklarierte Ports an Start (Ausgänge) und End (Eingänge). portDefOf liefert dafür einen Port vom Typ exec, ohne Feld zum Eintippen.
Run behavior zeigt die Flow-Ports des Start als Flow-Eingänge und die des End als Flow-Ausgänge, unter dem festen exec und über den Werten (execFirst sortiert das schon so).
Interpreter, Eingang:run bekommt den betretenen Port schon heute mit (entry). Run behavior gibt ihn im call weiter, und call folgt am Start diesem Port statt fest exec.
Interpreter, Ausgang:End merkt sich, über welchen Flow-Eingang es zuletzt betreten wurde. call gibt das neben den Werten zurück, und der Aufrufer folgt erst diesem Ausgang, dann exec.
Aufrufstellen nachziehen: Umbenennen und Löschen eines deklarierten Ports ziehen die Kanten an den Run behavior-Nodes schon heute nach (src/core/commands/portCommands.ts). Das muss für Flow-Ports mitlaufen.
Palette und Beschreibung von Start, End und Run behavior nennen die Flow-Ports. benchmark/engine-prompt.md und das Wiki (Behaviors) ebenso.
Vorschläge für die Kleinigkeiten (nicht einzeln gefragt)
Namen: Ein Flow-Port teilt sich den Namensraum mit den Wert-Ports desselben Nodes. exec bleibt gesperrt.
Werte gelten für alle Flow-Ports gemeinsam. Jeder Flow-Eingang von Run behavior übergibt dieselben Wert-Eingänge, und am End gibt es eine Wertliste, nicht eine je Flow-Eingang.
Erreicht der Lauf nur den festen exec des End oder gar kein End, feuert kein Zusatz-Ausgang, nur exec.
Behavior ohne Start (Einstieg über On start, #230): Es gibt keine Zusatz-Eingänge, weil sie am Start deklariert werden. Zusatz-Ausgänge am End gehen trotzdem.
Ein Behavior, das selbst an einem Objekt läuft und nicht aufgerufen wird: Die Zusatz-Ausgänge des Start verhalten sich wie sein exec heute. Beim Bau nachlesen, was das genau ist, und im Wiki festhalten.
Art eines Ports nachträglich ändern (Wert zu flow oder zurück): Die Kanten an diesem Port bleiben liegen und werden rot, wie bei jeder anderen Typänderung. Gelöschte Ports hinterlassen Geister-Kanten wie heute.
Trace im laufenden Graph: Der gefolgte Zusatz-Ausgang leuchtet wie jede andere Flow-Kante.
Tests (Vorschlag)
Unit, Ports:flow deklarieren, umbenennen, löschen und sortieren an Start und End, und Run behavior zeigt dieselben Ports in derselben Reihenfolge. Namensregeln.
Unit, Interpreter:
Aufruf über exec verhält sich wie heute (Bestand).
Aufruf über einen Zusatz-Eingang beginnt nur am gleichnamigen Ausgang.
Ein erreichter Zusatz-Eingang am End feuert erst seinen Ausgang, dann exec.
Zwei erreichte Eingänge: nur der letzte feuert.
Kein End erreicht: nur exec.
Verschachtelter Aufruf: Der gemerkte Eingang des inneren End sickert nicht in den äußeren Aufruf durch.
In einer Schleife: Jede Runde entscheidet neu.
Unit, Commands: Umbenennen und Löschen eines Flow-Ports ziehen die Kanten an den Aufrufstellen nach, ein Undo-Schritt.
e2e mit echter Maus im Graph-Modul: Flow-Port im Inspector anlegen, am Run behavior verbinden, im Play prüfen, welcher Zweig gelaufen ist. Dazu ein Screenshot des Nodes mit mehreren Flow-Pins, angesehen.
Gegenprobe je Regel.
On behavior
Anlass: Ein Behavior soll sich wie ein Event verwenden lassen. Beispiel „Confirm“: Innen hängen On key down (Space) und On key down (Enter) am End, außen steht überall nur On Confirm und dahinter die Logik. Mit Run behavior geht das nicht, weil ein Aufruf einen Puls am Flow-Eingang braucht und die Events des aufgerufenen Graphs nie feuern.
On behavior ist ein Event-Node ohne Flow-Eingang mit der Einstellung Behavior. Das gewählte Behavior lebt an der Einsatzstelle, solange das äußere Behavior läuft.
Entschieden (Antworten des Users)
Außen feuert nur etwas, wenn ein innerer Puls das End erreicht. Es feuert der Ausgang, der dem zuletzt erreichten Flow-Eingang des End entspricht. Das gilt auch für den festen exec: Er feuert nur, wenn End.exec der zuletzt erreichte war. Ein innerer Puls, der das End nicht erreicht, ist außen unsichtbar. (Anders als bei Run behavior, dessen exec immer feuert.)
Alle inneren Events feuern, wie bei einem Behavior, das direkt am Objekt hängt: Tasten, Maus, On update, On timer, Kollisionen, Animation, State-Events und auch On start, On enable, On disable und On destroy im Takt des äußeren Behaviors.
Alle Variablen des inneren Behaviors leben, solange das äußere läuft, je Einsatzstelle ein eigener Satz.Keep spielt hier keine Rolle. Zwei On behavior-Nodes mit demselben Behavior teilen nichts.
Start feuert nie. Seine Wert-Ports werden zu Dateneingängen von On behavior (getippt oder verbunden), innen am Start lesbar und bei jedem inneren Puls frisch gelesen. Die Wert-Ports des End werden Ausgänge mit den Werten des Pulses, der das End erreicht hat.
Verschachtelung beliebig tief: Ein On behavior darf in einem Behavior stehen, das selbst über On behavior eingesetzt ist. In einem per Run behavior aufgerufenen Behavior bleibt es stumm, wie heute alle Events dort. Ein Kreis (A hört auf B, B auf A) wird als Fehler am Node gemeldet.
Vorschläge für die Kleinigkeiten (nicht einzeln gefragt)
Der Node heißt wie sein Behavior:On Confirm, ohne Wahl On behavior. Er liegt in der Palette bei den Events.
Der äußere Ausgang feuert nach dem inneren Puls, nicht mittendrin. Erreicht ein innerer Puls das End mehrmals, feuert außen einmal der letzte.
Flow-Ports des Start erscheinen an On behavior nicht, weil der Node keine Flow-Eingänge hat.
Timer, lokale State Machines und sonstiger Node-Zustand des inneren Behaviors leben wie die Variablen je Einsatzstelle.
Kein Feld Order am Node. Die inneren Events tragen ihr eigenes Order und laufen in ihrer Event-Art mit allen anderen mit.
Run behavior im inneren Behavior geht wie überall.
Gewähltes Behavior ohne End: Der Node warnt, weil er nie feuern kann.
Run in editor: Das innere Behavior läuft dort, wo das äußere läuft.
Trace: Ist das innere Behavior im Graph-Tab offen, leuchtet es wie ein aufgerufenes.
Zu bauen
Node OnBehavior (Event) mit derivePorts: Wert-Ports des Start als Eingänge, exec plus Flow- und Wert-Ports des End als Ausgänge.
Laufzeit: Je On behavior-Node einer Instanz ein eigener Interpreter samt Variablen und States für das innere Behavior, rekursiv für dessen eigene On behavior-Nodes. Die Event-Sammlung des BehaviorSystem läuft über alle diese Interpreter.
Nach jedem inneren Puls: Wurde das End erreicht, startet am äußeren Node ein Puls am passenden Ausgang mit den Werten des End.
Umbenennen und Löschen deklarierter Ports ziehen auch die Kanten an On behavior-Nodes nach.
Problem-Meldungen am Node: kein Behavior gewählt, Behavior weg, hört auf sich selbst (auch über Umwege), kein End.
Tests (Vorschlag)
Unit: inneres On key down feuert außen; Puls ohne End feuert nicht; letzter End-Eingang gewinnt, exec nur bei End.exec; Werte hinein (frisch je Puls) und heraus; Variablen leben je Einsatzstelle und werden nicht geteilt; On start und On destroy innen im Takt des äußeren; zwei Stufen tief; stumm unter Run behavior; Kreis wird gemeldet und läuft nicht.
e2e: „Confirm“ mit zwei Tasten bauen, außen On Confirm, im Play mit echter Tastatur drücken. Screenshot des Nodes, angesehen.
Gegenprobe je Regel.
Nicht dabei
Input-Action-Asset (Tasten an einer Stelle definieren, Umbelegen, Gamepad). Mit On behavior und den Flow-Ports lässt sich eine Eingabe als Behavior bauen, das sich wie ein Event verwenden lässt. Ob das reicht, zeigt sich am echten Graph. Wenn nicht, ist das Asset ein eigenes Issue.
Die Taste als Wert (ein Ausgang key an den Tasten-Events, „Any key“).
Mehrere Start- oder End-Nodes je Graph.
Ein End, das den inneren Lauf abbricht (echtes Return).
Aus der Zwischenfrage vom 2026-10-02 (bei #216): Der User will Behaviors als eigene Verzweigungs-Nodes bauen, etwa für Eingaben. Dafür reicht der eine Flow-Port an `Start` und `End` nicht. Die sechs offenen Punkte sind vor dem Anlegen per Rückfrage geklärt, siehe *Entschieden*.
**Nachtrag vom selben Tag:** Der Node `On behavior` gehört jetzt mit in dieses Issue (Wunsch des Users: „gleich mit rein, alles in einem Wisch“). Seine fünf Punkte sind ebenfalls per Rückfrage geklärt, siehe *`On behavior`*.
## Stand heute
- **`Start` und `End` haben je genau einen festen Flow-Port** (`exec`). Deklarieren lassen sich nur Wert-Ports, der Name `exec` ist dafür gesperrt (`isValidPortName` in `src/core/graph/nested.ts`).
- **`Run behavior` hat einen Flow-Eingang und einen Flow-Ausgang.** Seine Wert-Ports leitet es aus den deklarierten Ports von `Start` und `End` des gewählten Behaviors ab (`derivePorts` in `src/core/graph/nodes/flow/RunBehavior.ts`).
- **Der Aufruf beginnt immer am `exec` des `Start`** (oder an jedem `On start`, wenn es kein `Start` gibt, #230) und geht am Aufrufer immer am `exec` weiter, auch wenn das `End` nie erreicht wurde (`call` in `src/core/graph/GraphInterpreter.ts`).
- **`End` beendet den inneren Lauf nicht.** Es legt nur seine Eingangswerte ab, andere Zweige laufen weiter. Wird es mehrmals erreicht, gelten die Werte des letzten Durchgangs.
- Es zählt ein `Start` und ein `End` je Graph, ein zweites wird ignoriert.
## Entschieden (Antworten des Users)
1. **Der feste `exec` bleibt immer da und verhält sich wie heute.** `Run behavior` geht am `exec` immer weiter. Die zusätzlichen Ausgänge feuern zusätzlich, wenn ihr Eingang am `End` erreicht wurde. Bestehende Graphen ändern sich nicht.
2. **Wird das `End` in einem Aufruf über mehrere Flow-Eingänge erreicht, feuert nur der zuletzt erreichte.** Ein Aufruf geht also höchstens an einem Zusatz-Ausgang weiter. Das passt zu den Werten, bei denen auch der letzte Durchgang gilt.
3. **Zusatz-Ports am `Start`:** `Run behavior` bekommt je Port einen eigenen Flow-Eingang. Kommt der Puls am Eingang `open` an, beginnt der innere Lauf nur am gleichnamigen Ausgang `open` des `Start`. Der feste `exec` des `Start` feuert nur, wenn der Aufruf über `exec` hereinkam.
4. **Anlegen im Inspector als weitere Art `flow`** in der bestehenden Port-Liste von `Start` und `End`: ein Knopf, eine Liste, Umbenennen, Löschen und Sortieren wie bei Wert-Ports. Am Node stehen Flow-Ports trotzdem immer oben, vor den Werten.
5. **Reihenfolge am Aufrufer:** zuerst der Zusatz-Ausgang, dann `exec`. `exec` liest sich damit als „danach weiter“, wie `completed` an einer Schleife.
6. **Umfang:** die Flow-Ports und, seit dem Nachtrag, `On behavior`. Ein Input-Action-Asset ist nicht Teil dieses Issues, siehe *Nicht dabei*.
## Zu bauen
- **Port-Art `flow`** für deklarierte Ports an `Start` (Ausgänge) und `End` (Eingänge). `portDefOf` liefert dafür einen Port vom Typ `exec`, ohne Feld zum Eintippen.
- **`Run behavior`** zeigt die Flow-Ports des `Start` als Flow-Eingänge und die des `End` als Flow-Ausgänge, unter dem festen `exec` und über den Werten (`execFirst` sortiert das schon so).
- **Interpreter, Eingang:** `run` bekommt den betretenen Port schon heute mit (`entry`). `Run behavior` gibt ihn im `call` weiter, und `call` folgt am `Start` diesem Port statt fest `exec`.
- **Interpreter, Ausgang:** `End` merkt sich, über welchen Flow-Eingang es zuletzt betreten wurde. `call` gibt das neben den Werten zurück, und der Aufrufer folgt erst diesem Ausgang, dann `exec`.
- **Aufrufstellen nachziehen:** Umbenennen und Löschen eines deklarierten Ports ziehen die Kanten an den `Run behavior`-Nodes schon heute nach (`src/core/commands/portCommands.ts`). Das muss für Flow-Ports mitlaufen.
- **Palette und Beschreibung** von `Start`, `End` und `Run behavior` nennen die Flow-Ports. `benchmark/engine-prompt.md` und das Wiki (*Behaviors*) ebenso.
## Vorschläge für die Kleinigkeiten (nicht einzeln gefragt)
- **Namen:** Ein Flow-Port teilt sich den Namensraum mit den Wert-Ports desselben Nodes. `exec` bleibt gesperrt.
- **Werte gelten für alle Flow-Ports gemeinsam.** Jeder Flow-Eingang von `Run behavior` übergibt dieselben Wert-Eingänge, und am `End` gibt es eine Wertliste, nicht eine je Flow-Eingang.
- **Erreicht der Lauf nur den festen `exec` des `End` oder gar kein `End`,** feuert kein Zusatz-Ausgang, nur `exec`.
- **Behavior ohne `Start`** (Einstieg über `On start`, #230): Es gibt keine Zusatz-Eingänge, weil sie am `Start` deklariert werden. Zusatz-Ausgänge am `End` gehen trotzdem.
- **Ein Behavior, das selbst an einem Objekt läuft** und nicht aufgerufen wird: Die Zusatz-Ausgänge des `Start` verhalten sich wie sein `exec` heute. Beim Bau nachlesen, was das genau ist, und im Wiki festhalten.
- **Art eines Ports nachträglich ändern** (Wert zu `flow` oder zurück): Die Kanten an diesem Port bleiben liegen und werden rot, wie bei jeder anderen Typänderung. Gelöschte Ports hinterlassen Geister-Kanten wie heute.
- **Trace im laufenden Graph:** Der gefolgte Zusatz-Ausgang leuchtet wie jede andere Flow-Kante.
## Tests (Vorschlag)
- **Unit, Ports:** `flow` deklarieren, umbenennen, löschen und sortieren an `Start` und `End`, und `Run behavior` zeigt dieselben Ports in derselben Reihenfolge. Namensregeln.
- **Unit, Interpreter:**
- Aufruf über `exec` verhält sich wie heute (Bestand).
- Aufruf über einen Zusatz-Eingang beginnt nur am gleichnamigen Ausgang.
- Ein erreichter Zusatz-Eingang am `End` feuert erst seinen Ausgang, dann `exec`.
- Zwei erreichte Eingänge: nur der letzte feuert.
- Kein `End` erreicht: nur `exec`.
- Verschachtelter Aufruf: Der gemerkte Eingang des inneren `End` sickert nicht in den äußeren Aufruf durch.
- In einer Schleife: Jede Runde entscheidet neu.
- **Unit, Commands:** Umbenennen und Löschen eines Flow-Ports ziehen die Kanten an den Aufrufstellen nach, ein Undo-Schritt.
- **e2e mit echter Maus** im Graph-Modul: Flow-Port im Inspector anlegen, am `Run behavior` verbinden, im Play prüfen, welcher Zweig gelaufen ist. Dazu ein Screenshot des Nodes mit mehreren Flow-Pins, angesehen.
- Gegenprobe je Regel.
## `On behavior`
Anlass: Ein Behavior soll sich wie ein Event verwenden lassen. Beispiel „Confirm“: Innen hängen `On key down (Space)` und `On key down (Enter)` am `End`, außen steht überall nur `On Confirm` und dahinter die Logik. Mit `Run behavior` geht das nicht, weil ein Aufruf einen Puls am Flow-Eingang braucht und die Events des aufgerufenen Graphs nie feuern.
`On behavior` ist ein Event-Node ohne Flow-Eingang mit der Einstellung *Behavior*. Das gewählte Behavior lebt an der Einsatzstelle, solange das äußere Behavior läuft.
### Entschieden (Antworten des Users)
1. **Außen feuert nur etwas, wenn ein innerer Puls das `End` erreicht.** Es feuert der Ausgang, der dem zuletzt erreichten Flow-Eingang des `End` entspricht. Das gilt auch für den festen `exec`: Er feuert nur, wenn `End.exec` der zuletzt erreichte war. Ein innerer Puls, der das `End` nicht erreicht, ist außen unsichtbar. (Anders als bei `Run behavior`, dessen `exec` immer feuert.)
2. **Alle inneren Events feuern**, wie bei einem Behavior, das direkt am Objekt hängt: Tasten, Maus, `On update`, `On timer`, Kollisionen, Animation, State-Events und auch `On start`, `On enable`, `On disable` und `On destroy` im Takt des äußeren Behaviors.
3. **Alle Variablen des inneren Behaviors leben, solange das äußere läuft, je Einsatzstelle ein eigener Satz.** `Keep` spielt hier keine Rolle. Zwei `On behavior`-Nodes mit demselben Behavior teilen nichts.
4. **`Start` feuert nie.** Seine Wert-Ports werden zu Dateneingängen von `On behavior` (getippt oder verbunden), innen am `Start` lesbar und bei jedem inneren Puls frisch gelesen. Die Wert-Ports des `End` werden Ausgänge mit den Werten des Pulses, der das `End` erreicht hat.
5. **Verschachtelung beliebig tief:** Ein `On behavior` darf in einem Behavior stehen, das selbst über `On behavior` eingesetzt ist. In einem per `Run behavior` aufgerufenen Behavior bleibt es stumm, wie heute alle Events dort. Ein Kreis (A hört auf B, B auf A) wird als Fehler am Node gemeldet.
### Vorschläge für die Kleinigkeiten (nicht einzeln gefragt)
- **Der Node heißt wie sein Behavior:** `On Confirm`, ohne Wahl `On behavior`. Er liegt in der Palette bei den Events.
- **Der äußere Ausgang feuert nach dem inneren Puls,** nicht mittendrin. Erreicht ein innerer Puls das `End` mehrmals, feuert außen einmal der letzte.
- **Flow-Ports des `Start`** erscheinen an `On behavior` nicht, weil der Node keine Flow-Eingänge hat.
- **Timer, lokale State Machines und sonstiger Node-Zustand** des inneren Behaviors leben wie die Variablen je Einsatzstelle.
- **Kein Feld `Order` am Node.** Die inneren Events tragen ihr eigenes `Order` und laufen in ihrer Event-Art mit allen anderen mit.
- **`Run behavior` im inneren Behavior** geht wie überall.
- **Gewähltes Behavior ohne `End`:** Der Node warnt, weil er nie feuern kann.
- **Run in editor:** Das innere Behavior läuft dort, wo das äußere läuft.
- **Trace:** Ist das innere Behavior im Graph-Tab offen, leuchtet es wie ein aufgerufenes.
### Zu bauen
- Node `OnBehavior` (Event) mit `derivePorts`: Wert-Ports des `Start` als Eingänge, `exec` plus Flow- und Wert-Ports des `End` als Ausgänge.
- Laufzeit: Je `On behavior`-Node einer Instanz ein eigener Interpreter samt Variablen und States für das innere Behavior, rekursiv für dessen eigene `On behavior`-Nodes. Die Event-Sammlung des `BehaviorSystem` läuft über alle diese Interpreter.
- Nach jedem inneren Puls: Wurde das `End` erreicht, startet am äußeren Node ein Puls am passenden Ausgang mit den Werten des `End`.
- Umbenennen und Löschen deklarierter Ports ziehen auch die Kanten an `On behavior`-Nodes nach.
- Problem-Meldungen am Node: kein Behavior gewählt, Behavior weg, hört auf sich selbst (auch über Umwege), kein `End`.
### Tests (Vorschlag)
- **Unit:** inneres `On key down` feuert außen; Puls ohne `End` feuert nicht; letzter End-Eingang gewinnt, `exec` nur bei `End.exec`; Werte hinein (frisch je Puls) und heraus; Variablen leben je Einsatzstelle und werden nicht geteilt; `On start` und `On destroy` innen im Takt des äußeren; zwei Stufen tief; stumm unter `Run behavior`; Kreis wird gemeldet und läuft nicht.
- **e2e:** „Confirm“ mit zwei Tasten bauen, außen `On Confirm`, im Play mit echter Tastatur drücken. Screenshot des Nodes, angesehen.
- Gegenprobe je Regel.
## Nicht dabei
- **Input-Action-Asset** (Tasten an einer Stelle definieren, Umbelegen, Gamepad). Mit `On behavior` und den Flow-Ports lässt sich eine Eingabe als Behavior bauen, das sich wie ein Event verwenden lässt. Ob das reicht, zeigt sich am echten Graph. Wenn nicht, ist das Asset ein eigenes Issue.
- **Die Taste als Wert** (ein Ausgang `key` an den Tasten-Events, „Any key“).
- **Mehrere `Start`- oder `End`-Nodes je Graph.**
- **Ein `End`, das den inneren Lauf abbricht** (echtes Return).
meleki
changed title from Behavior: mehrere Flow-Ports an Start und End to Behavior: mehrere Flow-Ports an Start und End, und der Event-Node `On behavior`2026-10-02 18:58:27 +02:00
admin
added this to the Development project 2026-10-02 18:59:22 +02:00
admin
moved this to Next in Development on 2026-10-02 18:59:25 +02:00
Gebaut wie im Issue festgelegt, beide Teile in einem Commit.
Was sich ändert
Port-Art Flow:+ Input am Start und + Output am End bieten jetzt auch Flow an (Antwort 4). Am Node stehen Flow-Ports über den Werten.
Run behavior:
Ein Flow-Port des Start wird ein weiterer Flow-Eingang. Ein Aufruf über open beginnt innen nur am Ausgang open (Antwort 3).
Ein Flow-Port des End wird ein weiterer Flow-Ausgang. Zuerst feuert der zuletzt erreichte, danach exec (Antworten 2 und 5). exec feuert weiterhin immer (Antwort 1).
On <Name>: Jedes andere Behavior mit End-Node steht in der Palette zusätzlich als Event.
Das gehörte Behavior läuft am Node mit allen eigenen Events (Antwort 2) und einem eigenen Satz Variablen je Node (Antwort 3).
Außen feuert der Ausgang, über den das End zuletzt erreicht wurde, exec nur bei End.exec (Antwort 1).
Die Wert-Ports des Start sind Eingänge des Nodes, die des End Ausgänge. Das Start selbst feuert dort nie (Antwort 4).
Verschachtelung geht beliebig tief. Unter Run behavior bleibt ein On behavior stumm. Ein Kreis meldet am Node „A behavior cannot listen to itself“ und wird nicht gestartet (Antwort 5).
Der Anlass „Confirm“: Innen hängen zwei On key down (Space, Enter) am Flow-Port pressed des End. Außen steht nur On Confirm mit dem Ausgang pressed.
Was ich selbst festgelegt habe
On behavior hat kein Feld Order. Die inneren Events tragen ihr eigenes.
Der äußere Ausgang feuert erst, wenn der innere Ablauf durch ist. Erreicht er das End zweimal, feuert außen einmal der letzte.
Ohne End-Node steht ein Behavior nicht als On … in der Palette. Ein schon gesetzter Node meldet dann „No End node – never fires“.
Ein im laufenden Spiel gelöschter On-Node beendet sein Behavior mit On disable und On destroy.
Die Sounds der Entity stoppt nur das Behavior, das am Objekt hängt, nicht ein gehörtes.
Flow-Ports gibt es nur an Start und End, nicht an Pack oder Expand.
Ein Behavior, das direkt am Objekt läuft: Die Zusatz-Ausgänge seines Start feuern nie, so wie sein exec. Das war oben als „beim Bau nachlesen“ markiert.
Under the hood
kinds.ts kennt die Art flow (reist als exec). AddPortsCommand lässt sie nur an Start und End zu.
CallResult trägt entry, den betretenen Flow-Eingang. call im GraphInterpreter folgt am Start diesem Port und gibt ein Ending zurück (Port und Werte). Das End merkt sich den Port im Frame des aufgerufenen Graphs, er sickert also nicht nach außen durch.
BehaviorSystem.syncHeard hält je On behavior-Node eine weitere Instance mit eigenem Interpreter und eigenen Variablen (host, variables), rekursiv. Nach jedem Puls reicht surface ein erreichtes End an den hörenden Node weiter, und von dort weiter nach oben.
namesBehavior fasst Run behavior und On behavior zusammen: Port-Commands, Clipboard, Doppelklick auf den Node.
Neue Unit-Tests: 19 in tests/core/flow-ports.test.ts, 19 in tests/core/on-behavior.test.ts mit echter Welt, der Rest im Katalog.
Vier neue e2e-Checks in Modul 19: Flow-Port per Maus am End anlegen, On Confirm im Graph, der Aufruf über den Zusatz-Ausgang im Play, Enter und Space mit echter Tastatur.
19 Unit-Gegenproben greifen. Eine griff zuerst nicht (gelöschter Node behält seine Instanz), dafür gibt es jetzt einen Test.
Beide Browser-Gegenproben greifen.
Ein Screenshot angesehen:On Confirm als Event-Node mit exec und pressed, am Aufruf der zweite Flow-Pin über dem ersten.
Die e2e-Summe 1680 ist gerechnet (1676 + 4). Volle Suite und Smokes liefen nicht, das macht der CI-Lauf.
Nicht im Browser geprüft:
Flow-Eingänge am Start, die Verschachtelung, der Kreis und Werte durch On behavior (nur Unit-Tests)
der Palette-Eintrag On … und der Doppelklick auf den Node
alles nach dem Build
Ohne Test: dass die Sounds der Entity weiterlaufen, wenn ein gehörtes Behavior endet.
Nicht dabei
Wie oben: das Input-Action-Asset, die Taste als Wert, mehrere Start- oder End-Nodes und ein End, das den inneren Lauf abbricht.
Commit: 46c620a. Wiki: Behaviors (zwei neue Absätze), Architecture und Development (Summe).
Gebaut wie im Issue festgelegt, beide Teile in einem Commit.
## Was sich ändert
- **Port-Art *Flow*:** `+ Input` am `Start` und `+ Output` am `End` bieten jetzt auch *Flow* an (Antwort 4). Am Node stehen Flow-Ports über den Werten.
- **`Run behavior`:**
- Ein Flow-Port des `Start` wird ein weiterer Flow-Eingang. Ein Aufruf über `open` beginnt innen nur am Ausgang `open` (Antwort 3).
- Ein Flow-Port des `End` wird ein weiterer Flow-Ausgang. Zuerst feuert der zuletzt erreichte, danach `exec` (Antworten 2 und 5). `exec` feuert weiterhin immer (Antwort 1).
- **`On <Name>`:** Jedes andere Behavior mit `End`-Node steht in der Palette zusätzlich als Event.
- Das gehörte Behavior läuft am Node mit allen eigenen Events (Antwort 2) und einem eigenen Satz Variablen je Node (Antwort 3).
- Außen feuert der Ausgang, über den das `End` zuletzt erreicht wurde, `exec` nur bei `End.exec` (Antwort 1).
- Die Wert-Ports des `Start` sind Eingänge des Nodes, die des `End` Ausgänge. Das `Start` selbst feuert dort nie (Antwort 4).
- Verschachtelung geht beliebig tief. Unter `Run behavior` bleibt ein `On behavior` stumm. Ein Kreis meldet am Node „A behavior cannot listen to itself“ und wird nicht gestartet (Antwort 5).
- **Der Anlass „Confirm“:** Innen hängen zwei `On key down` (Space, Enter) am Flow-Port `pressed` des `End`. Außen steht nur `On Confirm` mit dem Ausgang `pressed`.
## Was ich selbst festgelegt habe
- **`On behavior` hat kein Feld *Order*.** Die inneren Events tragen ihr eigenes.
- **Der äußere Ausgang feuert erst, wenn der innere Ablauf durch ist.** Erreicht er das `End` zweimal, feuert außen einmal der letzte.
- **Ohne `End`-Node** steht ein Behavior nicht als `On …` in der Palette. Ein schon gesetzter Node meldet dann „No End node – never fires“.
- **Ein im laufenden Spiel gelöschter `On`-Node** beendet sein Behavior mit `On disable` und `On destroy`.
- **Die Sounds der Entity** stoppt nur das Behavior, das am Objekt hängt, nicht ein gehörtes.
- **Flow-Ports gibt es nur an `Start` und `End`**, nicht an `Pack` oder `Expand`.
- **Ein Behavior, das direkt am Objekt läuft:** Die Zusatz-Ausgänge seines `Start` feuern nie, so wie sein `exec`. Das war oben als „beim Bau nachlesen“ markiert.
## Under the hood
- `kinds.ts` kennt die Art `flow` (reist als `exec`). `AddPortsCommand` lässt sie nur an `Start` und `End` zu.
- `CallResult` trägt `entry`, den betretenen Flow-Eingang. `call` im `GraphInterpreter` folgt am `Start` diesem Port und gibt ein `Ending` zurück (Port und Werte). Das `End` merkt sich den Port im Frame des aufgerufenen Graphs, er sickert also nicht nach außen durch.
- `BehaviorSystem.syncHeard` hält je `On behavior`-Node eine weitere `Instance` mit eigenem Interpreter und eigenen Variablen (`host`, `variables`), rekursiv. Nach jedem Puls reicht `surface` ein erreichtes `End` an den hörenden Node weiter, und von dort weiter nach oben.
- `namesBehavior` fasst `Run behavior` und `On behavior` zusammen: Port-Commands, Clipboard, Doppelklick auf den Node.
## Tests (auf `melektaus-atx`)
```
Gate → 215 Files, 3319 Tests, CHECK EXIT 0 (3278 + 41)
e2e 19 → ALL PASSED – 39 checks (+4 für Block 19e-ii)
e2e 1-6,17-24,26,36,51 → ALL PASSED – 628 checks
```
- **Neue Unit-Tests:** 19 in `tests/core/flow-ports.test.ts`, 19 in `tests/core/on-behavior.test.ts` mit echter Welt, der Rest im Katalog.
- **Vier neue e2e-Checks** in Modul 19: Flow-Port per Maus am `End` anlegen, `On Confirm` im Graph, der Aufruf über den Zusatz-Ausgang im Play, Enter und Space mit echter Tastatur.
- **19 Unit-Gegenproben greifen.** Eine griff zuerst nicht (gelöschter Node behält seine Instanz), dafür gibt es jetzt einen Test.
- **Beide Browser-Gegenproben greifen.**
- **Ein Screenshot angesehen:** `On Confirm` als Event-Node mit `exec` und `pressed`, am Aufruf der zweite Flow-Pin über dem ersten.
- Die e2e-Summe **1680** ist gerechnet (1676 + 4). Volle Suite und Smokes liefen nicht, das macht der CI-Lauf.
- **Nicht im Browser geprüft:**
- Flow-Eingänge am `Start`, die Verschachtelung, der Kreis und Werte durch `On behavior` (nur Unit-Tests)
- der Palette-Eintrag `On …` und der Doppelklick auf den Node
- alles nach dem Build
- **Ohne Test:** dass die Sounds der Entity weiterlaufen, wenn ein gehörtes Behavior endet.
## Nicht dabei
Wie oben: das Input-Action-Asset, die Taste als Wert, mehrere `Start`- oder `End`-Nodes und ein `End`, das den inneren Lauf abbricht.
Commit: `46c620a`. Wiki: *Behaviors* (zwei neue Absätze), *Architecture* und *Development* (Summe).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Aus der Zwischenfrage vom 2026-10-02 (bei #216): Der User will Behaviors als eigene Verzweigungs-Nodes bauen, etwa für Eingaben. Dafür reicht der eine Flow-Port an
StartundEndnicht. Die sechs offenen Punkte sind vor dem Anlegen per Rückfrage geklärt, siehe Entschieden.Nachtrag vom selben Tag: Der Node
On behaviorgehört jetzt mit in dieses Issue (Wunsch des Users: „gleich mit rein, alles in einem Wisch“). Seine fünf Punkte sind ebenfalls per Rückfrage geklärt, sieheOn behavior.Stand heute
StartundEndhaben je genau einen festen Flow-Port (exec). Deklarieren lassen sich nur Wert-Ports, der Nameexecist dafür gesperrt (isValidPortNameinsrc/core/graph/nested.ts).Run behaviorhat einen Flow-Eingang und einen Flow-Ausgang. Seine Wert-Ports leitet es aus den deklarierten Ports vonStartundEnddes gewählten Behaviors ab (derivePortsinsrc/core/graph/nodes/flow/RunBehavior.ts).execdesStart(oder an jedemOn start, wenn es keinStartgibt, #230) und geht am Aufrufer immer amexecweiter, auch wenn dasEndnie erreicht wurde (callinsrc/core/graph/GraphInterpreter.ts).Endbeendet den inneren Lauf nicht. Es legt nur seine Eingangswerte ab, andere Zweige laufen weiter. Wird es mehrmals erreicht, gelten die Werte des letzten Durchgangs.Startund einEndje Graph, ein zweites wird ignoriert.Entschieden (Antworten des Users)
execbleibt immer da und verhält sich wie heute.Run behaviorgeht amexecimmer weiter. Die zusätzlichen Ausgänge feuern zusätzlich, wenn ihr Eingang amEnderreicht wurde. Bestehende Graphen ändern sich nicht.Endin einem Aufruf über mehrere Flow-Eingänge erreicht, feuert nur der zuletzt erreichte. Ein Aufruf geht also höchstens an einem Zusatz-Ausgang weiter. Das passt zu den Werten, bei denen auch der letzte Durchgang gilt.Start:Run behaviorbekommt je Port einen eigenen Flow-Eingang. Kommt der Puls am Eingangopenan, beginnt der innere Lauf nur am gleichnamigen AusgangopendesStart. Der festeexecdesStartfeuert nur, wenn der Aufruf überexechereinkam.flowin der bestehenden Port-Liste vonStartundEnd: ein Knopf, eine Liste, Umbenennen, Löschen und Sortieren wie bei Wert-Ports. Am Node stehen Flow-Ports trotzdem immer oben, vor den Werten.exec.execliest sich damit als „danach weiter“, wiecompletedan einer Schleife.On behavior. Ein Input-Action-Asset ist nicht Teil dieses Issues, siehe Nicht dabei.Zu bauen
flowfür deklarierte Ports anStart(Ausgänge) undEnd(Eingänge).portDefOfliefert dafür einen Port vom Typexec, ohne Feld zum Eintippen.Run behaviorzeigt die Flow-Ports desStartals Flow-Eingänge und die desEndals Flow-Ausgänge, unter dem festenexecund über den Werten (execFirstsortiert das schon so).runbekommt den betretenen Port schon heute mit (entry).Run behaviorgibt ihn imcallweiter, undcallfolgt amStartdiesem Port statt festexec.Endmerkt sich, über welchen Flow-Eingang es zuletzt betreten wurde.callgibt das neben den Werten zurück, und der Aufrufer folgt erst diesem Ausgang, dannexec.Run behavior-Nodes schon heute nach (src/core/commands/portCommands.ts). Das muss für Flow-Ports mitlaufen.Start,EndundRun behaviornennen die Flow-Ports.benchmark/engine-prompt.mdund das Wiki (Behaviors) ebenso.Vorschläge für die Kleinigkeiten (nicht einzeln gefragt)
execbleibt gesperrt.Run behaviorübergibt dieselben Wert-Eingänge, und amEndgibt es eine Wertliste, nicht eine je Flow-Eingang.execdesEndoder gar keinEnd, feuert kein Zusatz-Ausgang, nurexec.Start(Einstieg überOn start, #230): Es gibt keine Zusatz-Eingänge, weil sie amStartdeklariert werden. Zusatz-Ausgänge amEndgehen trotzdem.Startverhalten sich wie seinexecheute. Beim Bau nachlesen, was das genau ist, und im Wiki festhalten.flowoder zurück): Die Kanten an diesem Port bleiben liegen und werden rot, wie bei jeder anderen Typänderung. Gelöschte Ports hinterlassen Geister-Kanten wie heute.Tests (Vorschlag)
flowdeklarieren, umbenennen, löschen und sortieren anStartundEnd, undRun behaviorzeigt dieselben Ports in derselben Reihenfolge. Namensregeln.execverhält sich wie heute (Bestand).Endfeuert erst seinen Ausgang, dannexec.Enderreicht: nurexec.Endsickert nicht in den äußeren Aufruf durch.Run behaviorverbinden, im Play prüfen, welcher Zweig gelaufen ist. Dazu ein Screenshot des Nodes mit mehreren Flow-Pins, angesehen.On behaviorAnlass: Ein Behavior soll sich wie ein Event verwenden lassen. Beispiel „Confirm“: Innen hängen
On key down (Space)undOn key down (Enter)amEnd, außen steht überall nurOn Confirmund dahinter die Logik. MitRun behaviorgeht das nicht, weil ein Aufruf einen Puls am Flow-Eingang braucht und die Events des aufgerufenen Graphs nie feuern.On behaviorist ein Event-Node ohne Flow-Eingang mit der Einstellung Behavior. Das gewählte Behavior lebt an der Einsatzstelle, solange das äußere Behavior läuft.Entschieden (Antworten des Users)
Enderreicht. Es feuert der Ausgang, der dem zuletzt erreichten Flow-Eingang desEndentspricht. Das gilt auch für den festenexec: Er feuert nur, wennEnd.execder zuletzt erreichte war. Ein innerer Puls, der dasEndnicht erreicht, ist außen unsichtbar. (Anders als beiRun behavior, dessenexecimmer feuert.)On update,On timer, Kollisionen, Animation, State-Events und auchOn start,On enable,On disableundOn destroyim Takt des äußeren Behaviors.Keepspielt hier keine Rolle. ZweiOn behavior-Nodes mit demselben Behavior teilen nichts.Startfeuert nie. Seine Wert-Ports werden zu Dateneingängen vonOn behavior(getippt oder verbunden), innen amStartlesbar und bei jedem inneren Puls frisch gelesen. Die Wert-Ports desEndwerden Ausgänge mit den Werten des Pulses, der dasEnderreicht hat.On behaviordarf in einem Behavior stehen, das selbst überOn behavioreingesetzt ist. In einem perRun behavioraufgerufenen Behavior bleibt es stumm, wie heute alle Events dort. Ein Kreis (A hört auf B, B auf A) wird als Fehler am Node gemeldet.Vorschläge für die Kleinigkeiten (nicht einzeln gefragt)
On Confirm, ohne WahlOn behavior. Er liegt in der Palette bei den Events.Endmehrmals, feuert außen einmal der letzte.Starterscheinen anOn behaviornicht, weil der Node keine Flow-Eingänge hat.Orderam Node. Die inneren Events tragen ihr eigenesOrderund laufen in ihrer Event-Art mit allen anderen mit.Run behaviorim inneren Behavior geht wie überall.End: Der Node warnt, weil er nie feuern kann.Zu bauen
OnBehavior(Event) mitderivePorts: Wert-Ports desStartals Eingänge,execplus Flow- und Wert-Ports desEndals Ausgänge.On behavior-Node einer Instanz ein eigener Interpreter samt Variablen und States für das innere Behavior, rekursiv für dessen eigeneOn behavior-Nodes. Die Event-Sammlung desBehaviorSystemläuft über alle diese Interpreter.Enderreicht, startet am äußeren Node ein Puls am passenden Ausgang mit den Werten desEnd.On behavior-Nodes nach.End.Tests (Vorschlag)
On key downfeuert außen; Puls ohneEndfeuert nicht; letzter End-Eingang gewinnt,execnur beiEnd.exec; Werte hinein (frisch je Puls) und heraus; Variablen leben je Einsatzstelle und werden nicht geteilt;On startundOn destroyinnen im Takt des äußeren; zwei Stufen tief; stumm unterRun behavior; Kreis wird gemeldet und läuft nicht.On Confirm, im Play mit echter Tastatur drücken. Screenshot des Nodes, angesehen.Nicht dabei
On behaviorund den Flow-Ports lässt sich eine Eingabe als Behavior bauen, das sich wie ein Event verwenden lässt. Ob das reicht, zeigt sich am echten Graph. Wenn nicht, ist das Asset ein eigenes Issue.keyan den Tasten-Events, „Any key“).Start- oderEnd-Nodes je Graph.End, das den inneren Lauf abbricht (echtes Return).Behavior: mehrere Flow-Ports an Start und Endto Behavior: mehrere Flow-Ports an Start und End, und der Event-Node `On behavior`Gebaut wie im Issue festgelegt, beide Teile in einem Commit.
Was sich ändert
+ InputamStartund+ OutputamEndbieten jetzt auch Flow an (Antwort 4). Am Node stehen Flow-Ports über den Werten.Run behavior:Startwird ein weiterer Flow-Eingang. Ein Aufruf überopenbeginnt innen nur am Ausgangopen(Antwort 3).Endwird ein weiterer Flow-Ausgang. Zuerst feuert der zuletzt erreichte, danachexec(Antworten 2 und 5).execfeuert weiterhin immer (Antwort 1).On <Name>: Jedes andere Behavior mitEnd-Node steht in der Palette zusätzlich als Event.Endzuletzt erreicht wurde,execnur beiEnd.exec(Antwort 1).Startsind Eingänge des Nodes, die desEndAusgänge. DasStartselbst feuert dort nie (Antwort 4).Run behaviorbleibt einOn behaviorstumm. Ein Kreis meldet am Node „A behavior cannot listen to itself“ und wird nicht gestartet (Antwort 5).On key down(Space, Enter) am Flow-PortpresseddesEnd. Außen steht nurOn Confirmmit dem Ausgangpressed.Was ich selbst festgelegt habe
On behaviorhat kein Feld Order. Die inneren Events tragen ihr eigenes.Endzweimal, feuert außen einmal der letzte.End-Node steht ein Behavior nicht alsOn …in der Palette. Ein schon gesetzter Node meldet dann „No End node – never fires“.On-Node beendet sein Behavior mitOn disableundOn destroy.StartundEnd, nicht anPackoderExpand.Startfeuern nie, so wie seinexec. Das war oben als „beim Bau nachlesen“ markiert.Under the hood
kinds.tskennt die Artflow(reist alsexec).AddPortsCommandlässt sie nur anStartundEndzu.CallResultträgtentry, den betretenen Flow-Eingang.callimGraphInterpreterfolgt amStartdiesem Port und gibt einEndingzurück (Port und Werte). DasEndmerkt sich den Port im Frame des aufgerufenen Graphs, er sickert also nicht nach außen durch.BehaviorSystem.syncHeardhält jeOn behavior-Node eine weitereInstancemit eigenem Interpreter und eigenen Variablen (host,variables), rekursiv. Nach jedem Puls reichtsurfaceein erreichtesEndan den hörenden Node weiter, und von dort weiter nach oben.namesBehaviorfasstRun behaviorundOn behaviorzusammen: Port-Commands, Clipboard, Doppelklick auf den Node.Tests (auf
melektaus-atx)tests/core/flow-ports.test.ts, 19 intests/core/on-behavior.test.tsmit echter Welt, der Rest im Katalog.Endanlegen,On Confirmim Graph, der Aufruf über den Zusatz-Ausgang im Play, Enter und Space mit echter Tastatur.On Confirmals Event-Node mitexecundpressed, am Aufruf der zweite Flow-Pin über dem ersten.Start, die Verschachtelung, der Kreis und Werte durchOn behavior(nur Unit-Tests)On …und der Doppelklick auf den NodeNicht dabei
Wie oben: das Input-Action-Asset, die Taste als Wert, mehrere
Start- oderEnd-Nodes und einEnd, das den inneren Lauf abbricht.Commit:
46c620a. Wiki: Behaviors (zwei neue Absätze), Architecture und Development (Summe).