Expressive Language: Node Kinds, Binding, State, Routes, Examples
July 4, 2026 ยท View on GitHub
Continues from README.md: node kinds, binding to Rust,
the state model, routes, policies, comments/strings, safety, examples,
formatting, testkit, and implementation milestones.
Node Kinds
Initial built-in node kinds:
agent
Uses the harness agent loop or one model call depending on config.
Supported fields:
modelsystemprompttoolsroutesretrytimeout
model
Single model invocation. Does not automatically execute tools.
Supported fields:
modelsystempromptroutesretrytimeout
tool_executor
Executes tool calls already present in state.
Supported fields:
toolsnextretrytimeout
router
Routes based on a named route function provided from Rust.
Supported fields:
routesmetadata
subgraph
Calls another compiled graph.
Supported fields:
graphnextroutes
subagent
Calls a registered harness agent as a graph node.
Supported fields:
agentinputnextroutesretrytimeoutsteering
Example:
node research {
kind subagent
agent "researcher"
steering {
parent allow ["add_instruction", "request_status", "cancel"]
human allow ["add_instruction", "pause", "resume", "cancel"]
delivery "safe_boundary"
}
next synthesize
}
Steering policies lower into harness steering policy and graph task policy. They can narrow a child agent's model/tool/runtime limits but cannot grant capabilities absent from the registry or parent run policy.
repl_agent
Runs a registered REPL script or model-driven CodeAct loop through the harness REPL runtime.
Supported fields:
modelscripttoolsroutesretrytimeout
interrupt
Emits a resumable human-in-the-loop interrupt.
Supported fields:
promptoptionsroutesmetadata
join
Waits for named upstream nodes or barrier channels before continuing.
Supported fields:
sourcesnexttimeout
Binding To Rust
The language should not define arbitrary Rust closures. Instead, it should bind to Rust-provided registries:
let workflow = LanguageCompiler::new()
.with_models(models)
.with_tools(tools)
.with_node_templates(templates)
.compile_source("support.rag", source)?;
Registries:
- model registry
- tool registry
- agent registry
- node template registry
- route function registry
- reducer registry
- graph registry for subgraphs
- store registry
- middleware registry
- REPL script registry
When a graph is generated by a REPL session, the session may call the compiler with source text or an AST, but the compiler must use the same registries and policy checks. Generated source can request capabilities only from the allowed set attached to the parent run or registry namespace.
This keeps source files declarative and prevents unsafe dynamic execution.
State Model
Version 1 should keep state Rust-owned. The language can refer to standard channels by convention and bind them to registered reducers:
messagestool_callsstructured_responsemetadataartifactscandidatesusageinterrupts
Example:
channel messages messages
channel candidates append
channel usage aggregate "usage_delta"
channel review overwrite
Future versions may add state schema declarations:
state SupportState {
messages: messages append
customer_id: string overwrite
ticket_id: string? overwrite
}
State schemas should be delayed until reducer-based graph execution exists.
Routes
Routes are named outcomes.
routes {
tool_call -> tools
final -> END
escalate -> human_review
}
Rules:
- route names are unique per node
- route targets must exist or be
END - route names are ASCII identifiers
- a node may use
routesornext, not both ENDis reserved
Future typed route support can generate Rust enums from route declarations.
Policies
Node-level policies:
node agent {
timeout 30s
retry {
max_attempts: 3
backoff: "exponential"
}
}
Graph-level defaults:
graph support_agent {
defaults {
timeout 60s
recursion_limit 50
}
}
Policies lower into graph node policies and harness request policies.
Comments And Strings
Comments:
// line comment
Strings:
system "single line"
prompt """
multi-line prompt
"""
Multi-line strings should preserve content exactly except for one predictable dedent rule.
Safety
The language must be safe to parse and validate from untrusted text.
Safety rules:
- no arbitrary code execution
- no filesystem access from language source
- no network access from language source
- no dynamic provider lookup without registry binding
- no environment variable interpolation in v1
- bounded parser recursion
- bounded source size
Examples
Minimal Model Graph
graph summarize {
start model
node model {
kind model
model "default"
system "Summarize the user request."
next END
}
}
Agent With Tools
graph support_agent {
start agent
node agent {
kind agent
model "default"
system "Resolve support requests using tools when useful."
tools ["lookup_user", "create_ticket"]
routes {
tool_call -> tools
final -> END
}
}
node tools {
kind tool_executor
next agent
}
}
Human Review
graph approval_flow {
start draft
node draft {
kind model
model "default"
system "Draft a response."
next review
}
node review {
kind interrupt
prompt "Approve this response?"
routes {
approved -> send
rejected -> draft
}
}
node send {
kind tool_executor
tools ["send_email"]
next END
}
}
Formatting
Formatter goals:
- stable ordering within declarations
- preserve comments
- normalize indentation to two spaces
- one item per line for lists longer than one entry
- avoid rewriting prompt body content
The formatter can come after parser and diagnostics.
Testkit
language::testkit should include:
- parse snapshot helper
- diagnostic snapshot helper
- compile helper with fake registries
- golden source fixtures
- round-trip formatter tests once formatter exists
Implementation Milestones
L1: Parser Skeleton
- token model
- spans
- lexer
- parser for graph, start, node, next, routes
L2: Diagnostics
- structured diagnostics
- duplicate node validation
- unknown route target validation
L3: Compiler Preview
- compile topology into
GraphBuilder - support
kind model - support
kind agent - bind model names
L4: Tool Binding
- validate tool names
- compile
tool_executor - add agent/tool graph example
L5: Policies And Subgraphs
- parse timeout and retry
- bind subgraphs
- compile node policies
L6: Channels, Commands, And Fanout
- parse channel declarations
- bind reducer registry entries
- lower
commandandsends - compile join/barrier nodes
L7: Agent-Authored Graphs
- compile generated source under parent run policy
- require review gates when policy marks generated graphs as untrusted
- store generated blueprint provenance
- expose graph diff and preview diagnostics
L8: Formatter
- stable formatting
- golden tests