Run nested graphs on their own boards and publish child outcomes - #69
Merged
Merged
Conversation
The single-child composites accept optional outcome keys at construction. These composites are the nested machines (both runtimes) and the history states. The DSL adds outcomeCode/outcomeName parameters to all four .SubGraph overloads. When the child run reaches a terminal, the composite writes the child machine's LastOutcome code and name. It writes them through its stamped parent-level context, before it returns its own Result. A parent can then branch on the child's outcome with the ordinary data-built switch. Rules: - A terminal with no declared outcome publishes 0 and the empty string. Hosts route on non-zero codes. - Failure terminals also publish. - A history lift-back re-entry publishes only when the resumed session ends. - An exception that escapes node logic is a fault, not a terminal. It publishes nothing. - The constructor rejects Node-scoped keys and names the key. - The validator warns when a key is declared but the child graph declares no outcome codes. Serialization payload version 11: the key names ride the existing subgraph entries and the history composite kinds. Only the names ride — the value types are fixed (int/string), and the names are exclusive to the history kinds. Deserialized composites rebuild name-bound through the new Unbound factories. They resolve against the machine's bound boards at each publish, with targeted miss and type-mismatch errors. Pre-v11 payloads read outcome-key-free. The publish is two typed Sets on the existing boards — 0 B on both runtimes (AllocationGate cases). New parity scenarios pin the behavior across sync/async and full-run/stepped; the parity harness gained scenario-owned board binding. A deep suspend/resume case covers the durable boundary. JSON and MessagePack round-trips cover the wire. The public API baselines record the new surface.
The machine-wrapping composites accept an optional ports declaration: SubGraphPorts.OwnBoard().In(source, childKey).Out(childKey, parentKey). These composites are the nested machines (both runtimes), the history states, and the parallel family per region. All four .SubGraph overloads gain a ports argument; .Parallel gains ParallelRegion entries. When a ports declaration is present, the composite creates one blackboard from the child graph's own declared Graph schema at construction. It substitutes that board for the Graph slot when it stamps the child, before any schema validation runs. Global forwards unchanged; Node scratch was already per-machine. Effects: - Sibling sub-graphs no longer collide on same-named keys. - A child no longer sees everything its parent holds. - A graph authored standalone with its own schema nests under any parent. Inputs are literal or parent-key sources. The composite resolves them in the parent context. It applies them in declaration order at every fresh child start, after the owned board resets to its registered defaults. A history lift-back re-entry applies nothing — the board, like the position, is the survived state. Outputs copy in declaration order at the child's terminal, on success and on failure, before the composite returns. The parallel composites re-apply outputs at the join, so several regions that target one parent key resolve in region order. Construction enforces per-pair type equality (generic pairs behind the non-generic ISubGraphPort). Application is typed copies — 0 B on both runtimes (AllocationGate cases). Construction rejections name the key: ports over a child with no Graph schema, foreign target or source keys, and Node-scoped parent-side keys. Owned boards are durable artifacts. Owning composites implement IOwnedBoardProvider. The machines expose a recursive EnumerateOwnedBoards() walk with node-index-path identity. Hosts persist each board with BlackboardSerializer and restore into a fresh machine's enumeration before ResumeDeep. A full artifact loop proves this on both runtimes: suspend mid-child, serialize the snapshot and the boards, restore, resume to completion. New parity scenarios pin the behavior across sync/async and full-run/stepped. Serialization payload version 12: the subgraph entries gain the owns-board flag and a ports list. Composites carry sparse per-region ports. Each port rides as direction, source key name or field-model literal, target name, and the runtime-stable value type name. The read side rebuilds everything name-bound through new Unbound forms and resolves at each application with targeted errors. A deserialized child graph carries no schema, so the host binds a Graph-scoped board on the rebuilt child machine to supply the board to own. Pre-v12 payloads read shared-board and port-free. Validator: - Info on owns-board presence. - Warning when two output ports target the same parent key (regions of one parallel included). - Best-effort Warning on an input target the child never reads statically. - The conflicting-child-schema warning no longer fires for board-owning composites. Their child subtrees compare against the child's own schema. The public API baselines record the new surface.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two features for hierarchical graphs. Both keep sync/async parity and the 0 B hot path.
Publish a child's terminal outcome to declared keys
The single-child composites (nested machines, history states) accept optional outcome keys at construction. The DSL adds
outcomeCode/outcomeNameparameters to all four.SubGraphoverloads. When the child run reaches a terminal, the composite writes the child machine'sLastOutcomecode and name through its stamped parent-level context. A parent can then branch on the child's outcome with the ordinary data-built switch.0and the empty string.Run a nested graph on its own board with declared ports
The machine-wrapping composites (nested machines, history states, parallel regions) accept an optional ports declaration:
SubGraphPorts.OwnBoard().In(source, childKey).Out(childKey, parentKey). When present, the composite creates one blackboard from the child graph's own Graph schema and substitutes it when it stamps the child. Sibling sub-graphs stop colliding on same-named keys, and a child stops seeing the parent's whole board.EnumerateOwnedBoards()walks them with node-index-path identity, so hosts persist and restore them aroundResumeDeep.Verification