Skip to content

Execution Model

Z-M-Huang edited this page May 4, 2026 · 4 revisions

Execution Model

How work moves through stud-cli. Turns, stages, concurrency, cancellation, backpressure.

This page is the why and how-they-relate. The stage sequence itself lives in Message Loop.


The unit of work is the turn

A turn is one user-visible pass of the message loop: input comes in, a request is composed and dispatched to a Provider, the response is streamed, tool calls are executed, output is rendered. Turns start and end at clear boundaries.

Boundary What triggers it
Turn start A RECEIVE_INPUT event: user text or an incoming command; or, for an attached SM, a sequential stage execution beginning Setup; or the scheduler spawning a parallel fan-out group (siblings + optional join — one SessionTurnStart for the whole compound turn).
Turn end The RENDER stage completes for the last response of the turn; or (sequential SM turn) the stage's Exit runs; or (parallel SM fan-out turn) the last sibling's Exit runs when no join is declared, or the join stage's Exit runs when one is.

A session is a sequence of turns. See Session Lifecycle.


Turn stages

Each turn walks through a fixed sequence of turn stages. These are the kernel's ABI for the message loop; extensions attach via Hooks.

stateDiagram-v2
    [*] --> RECEIVE_INPUT: turn start
    RECEIVE_INPUT --> COMPOSE_REQUEST: input ready
    COMPOSE_REQUEST --> SEND_REQUEST: request built
    SEND_REQUEST --> STREAM_RESPONSE: dispatched
    STREAM_RESPONSE --> TOOL_CALL: tool emitted
    STREAM_RESPONSE --> RENDER: no tool
    TOOL_CALL --> COMPOSE_REQUEST: continuation
    TOOL_CALL --> RENDER: terminal
    RENDER --> [*]: turn end
Loading

Per-stage responsibilities — names and intent only — are detailed in Message Loop.

Every turn stage has pre and post hook points. Three sub-kinds of hook are allowed: transform, observer, guard. See Hook Taxonomy.

Turn stage ≠ SM stage. The five turn stages above (RECEIVE_INPUTRENDER) are the kernel message-loop stages, unrelated to an SM's Stage pipeline. When an SM is attached, each sequential SM stage execution walks through these turn stages as its own session turn; a parallel fan-out group bundles its siblings (and the optional join) into a single compound session turn. See Stage Executions § Turn boundary and persistence.


Authority during a turn

At each turn stage's boundary core runs, in order:

  1. Pre-stage guards. Any guard hook attached to the stage's pre point votes. A single deny blocks the stage.
  2. Stage body. Core runs the stage. Transform hooks rewrite payloads in order; observers fire.

Post-stage hooks fire only after the stage body completes. If a stage is blocked at step 1, its post hooks do not fire either (there was no body to observe).

Where SM authority lands

The attached SM does not vote on each turn stage boundary. Its authority is concentrated at two places:

  • Turn definition. When an SM stage execution is the turn, the stage's rendered body is the system prompt, the tool manifest is allowedTools plus the injected completion channel, and turnCap bounds how many LLM turns Act may issue. See Stage Executions.
  • Tool-call approval inside TOOL_CALL. The stage's allowedTools (plus any matching grantStageTool token) determines whether a proposed call runs, is refused, or auto-issues an interaction request for a grant. This is the SM precedence axis in Tool Approvals. Consuming a grant token is SM-approve; the security-mode gate is bypassed; guard hooks still run.

Invariant. For gated tool calls, SM authority runs before the mode gate and before guards (guard-deny still wins in any mode). Hooks observe or transform what core allowed to happen; they do not reorder SM authority.


Concurrency pockets

Most of a turn is sequential. Concurrency appears in a few well-defined places.

Parallel tool calls

The provider may emit multiple tool calls in one response. Core dispatches them with bounded parallelism. Each tool call runs its own pre/post hooks and, where applicable, its own approval prompt.

Approval prompts are serialized by core in tool-call order. Each prompt then fans out through the active interactors, and the first response wins — a single UI never faces two approval dialogs at once. See Parallel Tool Approvals flow.

For continuation accounting, that whole batch still belongs to the same continuation round. The round count advances only if core sends the tool results back to the provider and the provider emits another tool-calling response.

Streaming

STREAM_RESPONSE overlaps with downstream consumers through the event bus: tokens are published as they arrive. Tool calls emitted mid-stream are buffered until the provider signals the call is complete, then dispatched to TOOL_CALL.

Hook ordering

Hooks within a stage run in ordering-manifest order (see Extension Discovery). Transform hooks pass their output to the next transform hook's input. Observer hooks run concurrently with each other and must not mutate state observable to others. Guard hooks run in order and short-circuit on first deny.


Cancellation

Cancellation propagates downward. Three scopes:

Scope Cancels Triggered by
Session All active turns in the session Session close, fatal error, user quit
Turn The current turn and every stage/tool inside it User interrupt (Ctrl-C), guard deny, provider failure. turnCap is not a turn-cancel trigger — it ends Act with capHit: true on a normal Act-ending branch (see Message Loop — Continuation loop).
Tool One tool call; the rest of the parallel group continues User denies one approval; hook guard denies one tool; tool-level timeout

A cancelled scope fires cancel via the Host API cancellation token that scope created. Downstream extensions are responsible for observing the token and winding down cooperatively.

See Concurrency and Cancellation for the detailed propagation rules and retry semantics.


Backpressure

The LLM produces tokens faster than downstream consumers can render, log, or persist. Core applies backpressure at three points:

  1. Event bus. Publishers write into a bounded queue per subscriber. A slow subscriber sees its queue fill; it drops observer events (never authoritative messages) and emits a diagnostic. See Event Bus.
  2. Interaction protocol. Requests block the requester until a response arrives. There is no timeout by default; an SM may enforce one. See Interaction Protocol.
  3. Tool execution. Tools emit progress through observer events; core does not drain these faster than the slowest observer. Tool bodies that block the process block the turn — this is a design choice, not a bug. Heavy tools should use the Host API's cancellation token and yield.

Reentrancy

stud-cli is single-session-at-a-time in v1. Inside a session:

  • Turns do not overlap. A new turn cannot start until the current turn's RENDER has fired (non-SM turn) or the current stage execution / fan-out group has finished its SessionTurnEnd (SM-driven turn).
  • Parallel fan-out runs as one compound turn. When an attached SM's Next() returns execution: "parallel", every sibling plus the optional join run under a single SessionTurnStart / SessionTurnEnd pair. Individual sibling Exits do not fire session turn events. See Stage Executions § Turn boundary and persistence.
  • Commands can run concurrently with a turn — they act on the main session but do not gate turns, and the turn does not gate them. See Command Model. Commands that mutate session state use Host API session accessors which serialize mutations.
  • Hot reload holds at stage boundaries. A reload request waits for the current stage (or current compound fan-out turn, when one is active) to complete, swaps the extension set, and revises the session manifest. Some categories cannot reload mid-turn; see Extension Reloading.

Relationship to other surfaces

Surface Role in the execution model
State Machines Author the pipeline. A sequential SM stage execution is itself a turn; a parallel fan-out group (siblings + optional join) runs as a single compound turn. See Stage Executions § Turn boundary and persistence. Authoritative for tool-call approval via the stage's allowedTools + grantStageTool tokens.
Hooks Turn-stage-scoped interception: transform (rewrite), observer (witness), guard (vote).
Commands Orthogonal to turns. Act on the main session via Host API session accessors.
Event Bus Projection channel. Tokens, stage transitions, audit events. Never authoritative.
Interaction Protocol Typed request/response where core blocks until an active interactor answers.
Providers Own the SEND_REQUEST and STREAM_RESPONSE bodies on behalf of core.
Tools Run inside the TOOL_CALL stage. Local or remote (MCP).

Determinism

Stage order is deterministic. Hook order within a stage is deterministic (ordering manifest). Approval-prompt order is deterministic (tool-call emission order).

Non-deterministic sources visible in the execution model:

  • Sampling inside the provider.
  • Retries on provider or tool failure.
  • Compaction (if the turn crosses the compaction threshold). See Compaction and Memory.
  • User approvals (wall-clock timing, user decision).
  • Clock and time.

A State Machine aiming for replay-level determinism should structure its session to avoid these surfaces or handle them explicitly. See Determinism and Ordering for the full list.

Introduction

Reading

Core runtime

Contracts

Category contracts

Context

Security

Runtime behavior

Operations

Providers (bundled)

Integrations

Reference extensions

Tools

UI

Session Stores

Loggers

Providers

Hooks

Context Providers

Commands

Case studies

Flows

Maintainers

Clone this wiki locally