tangl.vm.runtime¶
Phase-driven execution state used to advance a story graph one choice at a time.
Related design docs
Related notes
Core runtime types¶
- class ResolutionPhase(*values)[source]¶
Phases in a single resolution step.
Why¶
Defines the ordered pipeline for one frame and the vm phase-bus aggregation contracts used by
tangl.vm.dispatch.Key Features¶
Order –
INIT → VALIDATE → PLANNING → PREREQS → UPDATE → JOURNAL → FINALIZE → POSTREQS.Explicit reduction – each phase has a concrete aggregated result shape enforced by
do_*dispatch helpers.
Notes
PLANNINGin vm is side-effect-only provisioning; handlers must returnNone(non-NoneraisesTypeErrorindo_provision).JOURNALreturnsRecord | Iterable[Record] | None.FINALIZEreturnsRecord | None.
- class Frame(graph, cursor, output_stream=<factory>, return_stack=<factory>, local_behaviors=<factory>, ledger_local_behaviors=None, cursor_steps=0, cursor_trace=<factory>, last_redirect=None, redirect_trace=<factory>, step_base=0, step_observer=None, correlation_id=None, logger=None, meta=<factory>, causality_mode=CausalityMode.CLEAN, mark_soft_dirty_callback=None, escalate_to_hard_dirty_callback=None, _random=<factory>, selected_edge=None, selected_payload=None)[source]¶
Drives cursor traversal through the phase pipeline.
Why¶
Frame is the ephemeral execution context for one player action (one
resolve_choicecall). It moves the cursor through the graph by repeatedly callingfollow_edge, which runs the phase pipeline at each destination node. Redirects from PREREQS or POSTREQS cause the loop to continue; when the pipeline produces no redirect, the frame either pops the return stack or yields control back to the caller.Frame does NOT know about containers, scenes, or story semantics. It knows about nodes, edges, the phase pipeline, and the return stack. Container descent is handled by a prereq dispatch handler that detects
TraversableNode.is_containerand returns anenter()edge.Key Features¶
Pipeline execution —
follow_edgeruns phases in order, respectingentry_phasefor return edges that skip early phases.Redirect chaining — PREREQS and POSTREQS may return edges; the frame follows them in a loop until the pipeline completes cleanly.
Return stack — call edges (
return_phaseset) are pushed onto the stack. When the pipeline reaches a terminal, the stack is popped and the return edge is followed.Recursion safety —
resolve_choiceenforcesMAX_RESOLVE_DEPTH.
API¶
follow_edge(edge)— move cursor, run pipeline, return redirect or None.resolve_choice(edge)— loopfollow_edgeuntil terminal.goto_node(node)— optional stub-provision and jump (skip validation).
Notes
The output stream and return stack are shared references from the Ledger. After
resolve_choicereturns, the Ledger reads back the updated cursor, step counters, and any output that was appended to the stream.Examples
Basic pipeline — follow an edge to a leaf node:
>>> from tangl.core import Graph >>> from tangl.vm.traversable import TraversableNode, AnonymousEdge >>> from tangl.vm.runtime.frame import Frame >>> g = Graph() >>> a = TraversableNode(label="a", registry=g) >>> b = TraversableNode(label="b", registry=g) >>> frame = Frame(graph=g, cursor=a) >>> edge = AnonymousEdge(predecessor=a, successor=b) >>> result = frame.follow_edge(edge) >>> frame.cursor is b True >>> frame.cursor_steps 1 >>> result is None True
- class Ledger(_ctx=None, *, uid=<factory>, label=None, tags=<factory>, templ_hash=None, graph, output_stream=<factory>, local_behaviors=<factory>, cursor_id, cursor_history=<factory>, replay_algorithm_id='diff_v1', checkpoint_cadence=1, causality_mode=CausalityMode.CLEAN, causality_break_reason=None, causality_break_step_id=None, call_stack_ids=<factory>, last_redirect=None, redirect_trace=<factory>, reentrant_steps=-1, cursor_steps=-1, choice_steps=-1, user=None, user_id=None, worker_dispatcher=None)[source]¶
Persistent traversal state across player actions.