-
Notifications
You must be signed in to change notification settings - Fork 0
State Machine Workflow
An SM-driven workflow runs as a stage pipeline inside the orchestrator session. Each stage is a fresh LLM transcript bounded by its own allowedTools, completionTool, completionSchema, and turnCap. The SM is the authority over stage order; the orchestrator session provides the active interactor set, consent posture, and tool set that every stage inherits.
- The project's
codeReviewSM is attached to the orchestrator session. - Pipeline:
Plan → Execute → Review. - Stage definitions:
-
Plan:allowedTools: [Read, Grep, Glob],completionTool: submit_plan,turnCap: 20. -
Execute:allowedTools: [Read, Grep, Glob, Edit, Write],completionTool: submit_diff,turnCap: 40. -
Review:allowedTools: [],completionTool: submit_review,turnCap: 10.
-
- Security mode:
ask; bundled TUI active as an interactor.
sequenceDiagram
autonumber
actor User
participant Orchestrator as orchestrator session
participant Core as core
participant StageExec as stage execution
participant LLM
User->>Orchestrator: "/run codeReview refactor auth module"
Orchestrator->>Core: start SM pipeline entry stage
Core->>StageExec: begin Plan Setup
StageExec->>StageExec: resolve allowedTools, populate StageContext
StageExec->>LLM: Init interpolates body against StageContext, sends system prompt
LLM-->>StageExec: propose Edit tool call (out of allowedTools)
Core->>Orchestrator: grantStageTool request (auto-issued)
Orchestrator->>User: approve Edit for this proposal?
User-->>Orchestrator: deny
Orchestrator-->>Core: deny
Core-->>LLM: typed denied-tool-call result
LLM-->>StageExec: propose Grep tool call
StageExec->>Core: in allowedTools → SM-approve (mode gate bypassed)
Core-->>LLM: Grep result
LLM-->>StageExec: call submit_plan with payload
StageExec->>StageExec: parse completionSchema ok
StageExec->>StageExec: Assert ok
StageExec->>Core: Exit with plan result
Core->>StageExec: Next returns Execute sequential
Core->>StageExec: begin Execute Setup
StageExec->>LLM: Init with Execute prompt plus plan
LLM-->>StageExec: call Edit tool
StageExec->>Core: in allowedTools → SM-approve (mode gate bypassed)
Core-->>LLM: Edit result
LLM-->>StageExec: call submit_diff
StageExec->>Core: Exit with diff result
Core->>StageExec: Next returns Review sequential
Core->>StageExec: begin Review
StageExec->>LLM: Init with Review prompt plus diff
LLM-->>StageExec: call submit_review
StageExec->>Core: Exit with review result
Core-->>Orchestrator: pipeline done
Orchestrator-->>User: workflow complete
| Boundary | Who acted | Why it matters |
|---|---|---|
Plan Setup |
Stage execution | Resolved the stage's tool set against the orchestrator's live set; populated StageContext. The body template is interpolated at Init, not here. |
Plan Init |
Stage execution | Interpolated the Plan body template against StageContext and sent the composed system prompt under a fresh transcript. |
Plan Act first LLM turn |
LLM → stage | LLM proposed Edit, which is out of Plan.allowedTools. Core auto-issued a grantStageTool prompt through the orchestrator session's active interactors; the user denied. A typed denied-tool-call result returned on the same transcript; the LLM replanned within the envelope. |
Plan Assert |
Stage execution |
completionSchema parsed the plan payload; semantic check (non-empty steps) would go here. |
Plan Exit → Next |
SM pipeline code | Inspected Plan's StageResult, returned { stages: [Execute], execution: sequential }. |
Execute Act |
Stage execution | Different transcript, wider tool set. Still one orchestrator session, same active interactor set. |
Review Act |
Stage execution | No tools allowed — the stage must synthesise a review from the plan and diff passed in via ctx.upstream. |
Every boundary records an audit event. See Audit Trail.
The trace above shows Plan.allowedTools forcing an out-of-envelope Edit through grantStageTool — the security-mode gate (even yolo) never gets a chance to approve the call on its own, because an out-of-envelope proposal is routed to active interactors regardless of mode. If the user had approved, the minted token would have covered exactly that proposal; a later Edit with different arguments would trigger a fresh prompt. See the safety-critical precedence invariant.
When the LLM proposes a tool call not in the stage's allowedTools, core auto-issues a grantStageTool interaction request — the stage author does not invoke this. Example: during Execute the LLM calls Bash("git status") and Bash is not in Execute.allowedTools; core bundles the proposal identity ({stageExecutionId, attempt, proposalId, tool, argsDigest, argsSummary}) and routes it to the orchestrator session's active interactors. On approve the minted token covers exactly this proposal — a later Bash call with different arguments needs a fresh grant. Consuming a matching token is an SM-approve decision: the security-mode gate is bypassed, guard hooks still run. On deny, defer, or no interactor (headless), a typed denied-tool-call result returns to the LLM on the same transcript and Act continues.
See Interaction Protocol grantStageTool and Stage Executions one-shot tool grants.
-
Does not read LLM text to decide transitions.
Next()inspectsStageResult— the parsed completion payload, the Assert verdict, and SM state — not the transcript. -
Does not gate commands.
/tools,/reload,/model, etc. are orthogonal to the SM. See Command Model. - Does not replace the orchestrator session's interactors. Every user prompt from inside a stage goes through the session's active interactor set.
A pipeline may fan out: Next() returns { stages: [Test, Lint], execution: "parallel", join: Review }. Siblings run concurrently up to the session's parallel stage cap, their Exit outputs aggregate into ctx.upstream, and Review runs as the join. A failure in any sibling strict-cancels the rest and fails the workflow. See Concurrency and Cancellation and Stage Executions fan-out.
With deterministic Next() and deterministic tools, this pipeline is much closer to deterministic than a freeform chat would be. Caveats:
- The LLM is still non-deterministic — given the same stage prompt, it may propose different tools.
- Tools that read the network or live filesystem break determinism if the external state changes.
- Compaction (if fired) introduces non-determinism. Stages have their own transcripts, so the total token budget spreads across stage bodies — keep each stage's budget tight.
See Determinism and Ordering, Ralph.
- Execution Model
- Message Loop
- Concurrency and Cancellation
- Error Model
- Event and Command Ordering
- Event Bus
- Command Model
- Interaction Protocol
- Hook Taxonomy
- Host API
- Extension Lifecycle
- Env Provider
- Prompt Registry
- Resource Registry
- Session Lifecycle
- Session Manifest
- Persistence and Recovery
- Stage Executions
- Subagent Sessions
- Contract Pattern
- Versioning and Compatibility
- Deprecation Policy
- Capability Negotiation
- Dependency Resolution
- Validation Pipeline
- Cardinality and Activation
- Extension State
- Conformance and Testing
- Providers
- Provider Params
- Tools
- Hooks
- UI
- Loggers
- State Machines
- SM Stage Lifecycle
- Stage Definitions
- Commands
- Session Store
- Context Providers
- Settings Shape
- Trust Model
- Project Trust
- Extension Isolation
- Extension Integrity
- LLM Context Isolation
- Secrets Hygiene
- Security Modes
- Tool Approvals
- MCP Trust
- Sandboxing
- Configuration Scopes
- Project Root
- Extension Discovery
- Extension Installation
- Extension Reloading
- Headless and Interactor
- Determinism and Ordering
- Launch Arguments
- Network Policy
- Platform Integration
Tools
UI
Session Stores
Loggers
Providers
Hooks
Context Providers
Commands
- First Run
- Default Chat
- Tool Call Cycle
- Hook Interception
- Guard Deny Reproposal
- State Machine Workflow
- SM Stage Retry
- Hot Model Switch
- Capability Mismatch Switch
- Session Resume
- Session Resume Drift
- Approval and Auth
- Interaction Timeout
- Headless Run
- Parallel Tool Approvals
- Subagent Delegation
- Scope Layering
- Project First-Run Trust
- Reload Mid-Turn
- Compaction Warning
- MCP Remote Tool Call
- MCP Prompt Consume
- MCP Resource Bind
- MCP Reconnect