Overview
Strata is an experimental systems language for explicit authority, typed concurrency, and runtime-visible execution. Mantle is the runtime target for Strata programs.
The source-to-runtime boundary is executable behavior:
flowchart LR
Source[".str source"] --> Check["strata check"]
Check --> Build["strata build"]
Build --> Artifact[".mta artifact"]
Artifact --> Run["mantle run"]
The runnable gates carry real .str programs through checking, Mantle Target
Artifact build, Mantle admission, execution, and runtime observability.
If you are new to Strata, start with Getting Started, then Language Concepts, then the Hello tutorial. Use the Language Reference and Syntax Reference as the source-authoring contract.
The documentation in this book tracks accepted behavior, file identities, runtime boundaries, and the development gates that must stay green as the language and runtime grow.
Reading Paths
For a first working program:
- Read Getting Started.
- Build and run
examples/hello.str. - Read Tutorial: Hello.
- Read Tutorial: Actors And Messages.
For precise source behavior:
- Read Language Reference.
- Read Syntax Reference.
- Check Diagnostics when a command rejects a program.
For runtime and contributor work:
- Read Runtime Traces.
- Read Artifact And Runtime Boundary.
- Read Implementation Architecture.
- Use Development Gates before closing changes.
Getting Started
This guide takes a clean checkout to a checked, built, and executed Strata program.
Prerequisites
Install stable Rust 1.85 or newer with Cargo. The workspace uses Rust Edition 2024. Standard repository gates select stable explicitly; nightly is reserved for fuzz and Miri recipes that opt into it per command.
Useful local tools:
rustup toolchain install stable --profile minimal --component rustfmt,clippy
cargo +stable install just --version 1.50.0 --locked
cargo +stable install mdbook --version 0.5.2 --locked
cargo +stable install mdbook-mermaid --version 0.17.0 --locked
The 1.50.0 value above is the pinned just command runner version, not the
Rust compiler version.
The standard just quality gate runs documentation and metadata checks on every
local platform. Install jq, xmllint, mdbook, and mdbook-mermaid before
using that bundle.
Ubuntu-based environments can install the metadata tools with:
sudo apt-get install jq libxml2-utils
macOS systems with Homebrew can install the metadata tools with:
brew install jq libxml2
Windows systems should install jq and an xmllint provider such as libxml2
with their package manager, or run the full just quality bundle in a WSL
Ubuntu environment. Confirm the tools are on PATH with jq --version,
xmllint --version, mdbook --version, and mdbook-mermaid --version.
Do not set a repository-wide nightly Rust override. Use just install-fuzz-tools
and just install-miri-tools only when running the nightly fuzz or Miri gates.
Build The Binaries
From the repository root:
just build
This builds the Strata CLI and Mantle runtime CLI. The executable filenames are
platform-specific, so the commands below use repository just recipes to run
the right binary on the local platform.
Check A Strata Program
strata check parses and semantically checks source without writing an
artifact.
just strata-check examples/hello.str
Expected result:
strata: checked examples/hello.str (module hello, entry Main)
Build A Mantle Artifact
strata build checks the source and writes a Mantle Target Artifact under
target/strata/ by default.
just strata-build examples/hello.str
Expected result:
strata: built examples/hello.str -> target/strata/hello.mta
Run The Program
Mantle validates and executes the generated artifact:
just mantle-run target/strata/hello.mta
Expected output includes:
hello from Strata
mantle: loaded target/strata/hello.mta
mantle: spawned Main pid=1
mantle: delivered Start to Main
mantle: stopped Main normally
mantle: trace target/strata/hello.observability.jsonl
The trace path is important. It records what Mantle actually validated and executed.
Inspect Local Spawn Authority
For programs that declare local spawn authority, the optional inspection commands show the checked source authority surface and the admitted artifact authority tables without changing the normal build or run path:
just strata-authority-summary examples/actor_emit_spawn_send.str
just strata-build examples/actor_emit_spawn_send.str
just mantle-inspect-authority target/strata/actor_emit_spawn_send.mta
Pass json as the final recipe argument when CI or audit tooling needs
deterministic machine-readable output.
For checked component composition, the source-side report emits the admitted composition graph, admitted binding facts, and endpoint authority edges without changing the Mantle artifact:
just strata-composition-report examples/component_composition_main.str json
just strata-build examples/component_composition_main.str
just strata-composition-build examples/component_composition_main.str
just strata-composition-admit target/strata/component_composition_main.component-composition.json json
just strata-composition-bind-runtime \
target/strata/component_composition_main.component-composition.json \
target/strata/component_composition_main.mta \
target/strata/component_composition_main.deployment-composition.json
mantle run target/strata/component_composition_main.mta \
--composition-binding target/strata/component_composition_main.deployment-composition.json
just strata-target-requirements examples/component_composition_main.str json
just mantle-feature-declaration json
just mantle-admit target/strata/component_composition_main.mta json
Run The Standard Gate
After editing source, examples, runtime behavior, artifacts, or docs, use the central automation:
just quality
For the source-to-runtime acceptance examples only:
just source-to-runtime-gates
For the bounded/property assurance smoke tests:
just bounded-assurance-smoke
For the docs only:
just docs
What To Read Next
Read Language Concepts for the core ideas, then Tutorial: Hello for a guided walkthrough of the smallest accepted program.
Language Concepts
Strata is a source language for explicit state, explicit effects, typed messages, and runtime-observable process execution.
The implementation is intentionally small. The .str source surface declares
source-level records, enums, pure source functions, processes, message types,
state types, and process transitions. Mantle executes the admitted artifact
produced from that source.
Source Versus Runtime
Strata source is where author-visible meaning lives:
- names;
- records;
- enums;
- source functions;
- process declarations;
- message declarations;
- state transition expressions;
- declared effects.
Mantle runtime is where admitted execution lives:
- process instances;
- process references;
- mailboxes;
- loaded typed IDs;
- transition tables;
- runtime events;
- trace output.
The two surfaces are related, but not interchangeable. Source labels are useful for diagnostics and traces. Runtime dispatch uses loaded typed IDs.
flowchart TB
subgraph Source["Strata source surface"]
Records["records and enums"]
Functions["pure functions"]
Processes["process declarations"]
Effects["declared effects"]
end
subgraph Runtime["Mantle runtime surface"]
Instances["process instances"]
Mailboxes["bounded mailboxes"]
Ids["loaded typed IDs"]
Events["runtime events"]
end
Source -->|"checked and lowered"| Runtime
Records -. diagnostics .-> Events
Processes -. trace metadata .-> Events
Ids --> Events
Processes
A Strata program is organized around processes. A process declares:
- a bounded mailbox;
- a state type;
- a message type;
- an
initfunction that creates the initial state; - optional pure process-local functions;
- a
stepfunction that handles messages and returns a transition result.
The entry process is named Main. Mantle starts Main and delivers its first
message variant as the entry message.
Spawning a process creates a runtime process instance and binds it to an immutable typed process reference value:
authority spawn_worker: Cap<Spawn<Worker>>;
let worker: ProcessRef<Worker> = spawn Worker;
The authority declaration is process-local, exact for the target process, and must be used by a local spawn site. The reference is local to the transition that spawned it. Multiple references may target the same process definition, which creates multiple runtime instances.
Messages
A process message type is an enum. Each variant is a message the process can accept.
enum WorkerMsg {
Ping,
Assign(Job),
}
Sends are statically checked against the target process message enum:
send worker Ping;
send worker Assign(Job { phase: Ready });
Payload variants carry one immutable value. Actor step parameter patterns can
bind that payload, and whole-body match msg arms can bind the same payload:
fn step(state: WorkerState, Assign(job: Job)) -> ProcResult<WorkerState> ! [] ~ [] @det {
return Stop(WorkerState { job: job });
}
Source Functions
Strata accepts pure source functions at module level and inside processes. Functions are checked and expanded before lowering; Mantle does not dispatch by function names at runtime.
fn readiness(Warm) -> Readiness ! [] ~ [] @det {
return WarmReady;
}
fn state_for(mode: StartupMode) -> MainState ! [] ~ [] @det {
match mode {
Cold => {
return MainState { readiness: ColdReady };
}
Warm => {
return MainState { readiness: WarmReady };
}
}
}
Source functions are immutable value producers. They perform no statements, use
! [] ~ [] @det, and return whole values. A process-local function can
encapsulate non-message-handling value construction for init, step results,
and send payloads.
Payload-bearing enum constructors are source values too. A function may match
Assigned(job: Job) in a signature pattern or whole-body match, and the payload
binding is an immutable value scoped to that clause or arm.
State
Process state is immutable at the source level. A transition returns a whole replacement state or the supplied state.
return Continue(SawFirst);
return Stop(state);
return Panic(Failed);
Panic(value) is an abnormal terminal result: Mantle records the replacement
state and failure evidence, fails the run, and does not replay the consumed
message.
There is no assignment statement and no field update expression. Record state is constructed as a new whole value:
return Continue(WorkerState { phase: Idle });
Effects
Effects must be visible in the function signature. The effects are:
emit;spawn;send.
The declared effect list must exactly match the effects used by each step
clause. For spawn, the effect list is separate from the typed
Cap<Spawn<Target>> authority descriptor.
fn step(state: MainState, Start) -> ProcResult<MainState> ! [spawn, send] ~ [] @det {
let worker: ProcessRef<Worker> = spawn Worker;
send worker Ping;
return Stop(state);
}
This is deliberately stricter than “allow anything in the list.” A declared but unused effect is rejected.
Effectful local sends can also be bound as immutable typed outcomes:
let sent: Result<Unit,SendError<WorkerMsg>> = send worker Ping;
The outcome is a source-visible value, not an exception path. Mantle commits an
accepted message and returns Ok(Unit). If local delivery fails before
acceptance, the SendError<WorkerMsg> value preserves the original message.
Local spawn outcomes use Result<ProcessRef<TargetProcess>,SpawnError<Unit>>;
accepted spawns return the committed process reference as step-local authority.
Determinism And May-Behaviors
Function signatures include determinism and may-behavior positions:
! [effects] ~ [may_behaviors] @det
Buildable source requires ~ [] @det. The parser recognizes @nondet, but
accepted buildable programs are deterministic.
Execution Shape
The source-to-runtime path is:
flowchart LR
Source[".str source"] --> Parse["parse"]
Parse --> Check["check"]
Check --> Lower["lower"]
Lower --> Artifact[".mta artifact"]
Artifact --> Admit["admit"]
Admit --> Run["run"]
Run --> Trace["trace"]
Each phase has a different responsibility. Parser success alone does not mean a program is accepted. A program must also pass semantic checking, artifact validation, and runtime admission.
Language Reference
This page documents the Strata source surface accepted by the buildable implementation.
It is an authoring reference for .str programs, not a description of
Mantle artifact internals.
Source Surface
| Area | Accepted Surface |
|---|---|
| Source unit | One module name; declaration per file, with optional import module_name; declarations immediately after it. |
| Top-level declarations | protocol, port, component, composition, record, enum, fn, and proc. |
| Classes | Not available. |
| Methods | Not available. |
| Top-level functions | Pure deterministic one-argument source functions with optional immutable source-local bindings. |
| Process functions | init, step, and pure deterministic one-argument process-local functions with optional immutable source-local bindings. |
| Imports | Root-path loading of explicit sibling source-unit imports with deterministic dependency ordering. |
| Boundary declarations | protocol, port, component, and local composition declarations for checked communication contracts. |
| Standard library | Not available. |
| Effects | emit, spawn, and send. |
| Local spawn authority | Process-local authority name: Cap<Spawn<Target>>; declarations for dynamic local process creation. |
| Local port authority | Process-local authority name: Cap<PortConnect<Port>>; declarations for typed boundary sends. |
| Local supervision | Process-local `supervise local one_for_one(max_restarts: N_u32, within_ms: N_u64) { child name: Process = spawn Process as permanent |
| Process references | let worker: ProcessRef<Worker> = spawn Worker;, send worker Ping;, send worker via WorkerPort Ping;, and send reply_to Done; for received typed references. |
| Effect outcomes | Immutable step-local Result bindings for local send and the current local spawn success shape. |
| Scalar values | Fixed-width integer source values U8, U16, U32, U64, I8, I16, I32, and I64 with explicit literal suffixes. |
| Data primitives | Immutable String and Bytes source values constructed only from canonical literals. |
| Collections | Immutable List<T,N> and Map<K,V,N> source values with explicit List[...] and Map[key => value] constructors. |
| Scalar operators | +, -, *, /, %, <, <=, >, >=, and scalar equality over matching integer types only. |
| Primitive equality | Exact String and Bytes equality over matching primitive types; no concatenation, prefix predicates, coercion, or byte-buffer mutation. |
| Boolean predicates | !, &&, and ` |
| Pure conditionals | Expression-only if (condition) { value } else { value } over core Bool; concrete forms fold before lowering, and runtime-bound value forms lower as typed Mantle value templates. |
| Source-local bindings | let name: Type = value_expr; inside pure source function block bodies before the terminal return. |
| Runtime branching | Final-position if (condition) { ... return ...; } else { ... return ...; } and statement-level effect branches in step bodies, lowered to Mantle control flow. |
| Patterns | Constructor patterns, constructor payload bindings, nested constructor and record/list/map payload destructuring in functions, message dispatch, state matches, function return-match expressions, step return-match expressions with optional uniform and selected-arm action prefixes, and _ wildcards. |
| Message payloads | enum WorkerMsg { Assign(Job) }, enum WorkerMsg { Work(ProcessRef<Sink>) }, collection payloads, payload sends, and payload-binding step patterns. |
| Pattern dispatch | Function signature patterns, source function match bodies, function return-match expressions, fieldless enum matches in init, step parameter patterns, wildcard step patterns, one whole-body match msg step form per process, whole-body match state inside message-specific step clauses, and step return-match expressions over concrete enum source bindings. Same-constructor payload-sensitive splits are accepted for source functions, whole-body match msg, step parameter patterns, state-match step clauses, and step return-match expressions only when nested typed predicates are provably disjoint. |
| Transition result | ProcResult<T> with Continue(value), Stop(value), and Panic(value). |
The module declaration names a source unit. Explicit imports connect source
units before checking and lowering and define direct cross-unit scope. They do
not create packages, libraries, re-exports, visibility modifiers, or runtime
boundaries.
Source Unit
A Strata source file starts with a module declaration:
module hello;
After the module declaration, zero or more imports may appear before protocols, ports, components, compositions, records, enums, source functions, and processes. See Tutorial: Hello for the minimal complete source shape.
Imports use the narrow form import module_name;. The CLI resolves
module_name to a sibling module_name.str file from the importing source
unit, then builds a typed source-unit dependency graph. The graph must be
acyclic, module identities must be unique, and unqualified top-level type,
function, and process names must not be ambiguous across reachable source
units. Cross-unit function names and enum constructor names also share one
unqualified callable namespace so flattening cannot turn a transitive source
unit into a later call-site ambiguity. Each source unit may use only its own
declarations and declarations from its direct imports; transitive imports are
not re-exported. Declarations from the reachable graph are checked together in a
deterministic dependency-first order. Mantle receives only the lowered target
artifact; it does not resolve imports or execute by source-unit names.
Protocol, Port, And Component Boundaries
The current boundary form declares typed protocols, ports, and component exports
before lowering. send target via Port Message; proves the target process,
protocol message enum, and process-local Cap<PortConnect<Port>> authority, then
lowers typed boundary IDs into Mantle. Details are in Boundary Contracts.
Components may declare imported ports with component MainComponent exports MainPort imports WorkerPort requires ...;. A local composition is an
implementation-local source admission input for component instances and typed
port binding edges, such as bind main imports WorkerPort -> worker exports WorkerPort;.
The checker admits this local input only when instances name visible components,
each import is bound once, exports belong to exporters, ports share one protocol,
and the binding preserves exact current Cap<PortConnect<Port>> authority.
strata composition build <path.str> renders the Strata-owned checked-subset
strata.checked_component_composition artifact for that checked local
composition graph under target/strata/<stem>.component-composition.json by
default, and strata composition admit <path.json> validates the artifact
fail-closed before it can be used as checked-subset validation evidence. The
artifact is not .mta and is not read directly by Mantle; runtime composition
correlation requires a separate explicit mantle.runtime_composition_binding
artifact from strata composition bind-runtime. Source names inside it
are provenance, diagnostics, and debug metadata only; component-instance IDs,
port-binding IDs,
port IDs, protocol IDs, and authority descriptor IDs carry the checked meaning.
Binding classes outside the current source subset are present as empty
fail-closed arrays rather than inferred facts. The older strata composition-report command remains review evidence, not the artifact.
Every buildable program must declare a Main process. Mantle starts Main and
delivers the first message variant of Main’s message enum as the entry
message.
Identifiers
Identifiers must start with an ASCII letter or _, then contain only ASCII
letters, ASCII digits, or _. The single _ token is reserved for wildcard
patterns and cannot be used as an identifier.
Valid examples:
Main
Worker_1
_InternalState
Invalid examples:
1Main
worker-name
_
_, as, authority, bind, bounded, child, component, composition,
else, emit, enum, exports, fn, for, if, import, imports, in,
instance, let, local, mailbox, match, module, mut, one_for_one,
permanent, port, proc, protocol, record, requires, return,
security, send, spawn, supervise, target, temporary, transient,
type, var, and via are reserved everywhere identifiers are accepted.
ProcResult, ProcessRef, Cap, Spawn, ProtocolBoundary, PortConnect,
ComponentExport, List, Map, String, Bytes, Unit, Option,
Result, SendError, SpawnError, U8, U16, U32, U64, I8, I16,
I32, and I64 are reserved type names because they name built-in transition,
process-reference, capability descriptor, collection, primitive data, effect
outcome, and scalar value types.
Type names beginning with __strata_checked_ are reserved for checked IR and
artifact metadata. Checked process-reference artifact labels under that prefix
are keyed by resolved process IDs, not source process names.
Records
Records define structured state values. A fieldless record uses a semicolon:
record MainState;
A record with fields uses braces and does not take a semicolon after the closing brace:
enum Phase { Idle, Done }
record WorkerState {
phase: Phase,
}
Record fields are immutable. mut and var field declarations are rejected.
Record values use constructor syntax:
WorkerState { phase: Idle }
Fieldless record values are written as the record name:
MainState
Record value fields use :, not =. A braced record value must provide every
declared field exactly once; missing, duplicate, or unknown fields are rejected.
Payload-bearing enum values use constructor syntax with one immutable payload value:
Assigned(Job { phase: Ready })
The checker resolves this form against the expected enum type. If the identifier names a source function instead, it is expanded as a function call; constructor and function names cannot collide silently.
Scalar Integer Values
Scalar integer source values are fixed-width and explicitly typed. The accepted
types are U8, U16, U32, U64, I8, I16, I32, and I64.
Numeric value literals require suffixes:
0_u8
10_u32
-1_i16
Unsuffixed numeric value expressions are rejected. Capacity and mailbox-bound numbers remain separate syntax and do not use scalar literal suffixes.
Scalar arithmetic supports +, -, *, /, and % over matching integer
types only. Ordering supports <, <=, >, and >= over matching integer
types and produces Bool. Equality supports scalar operands with the same
integer type. There is no implicit widening, narrowing, signed/unsigned mixing,
or string coercion.
Fully concrete scalar expressions fold during checking. Overflow, underflow, statically known division by zero, statically known modulo by zero, out-of-range literals, and malformed suffixes fail before lowering. Runtime-bound scalar expressions lower as typed Mantle value templates with scalar type metadata and fail-closed runtime evaluation. They do not lower as source strings, binding names, or function names.
Scalars are ordinary source values when they do not carry authority: they can be
used in source function parameters and returns, source-local bindings, process
local pure functions, record fields, message payloads, list/map values, and
runtime if predicates through Bool.
Built-In Values
The buildable value surface includes Bool, String, Bytes, Unit, Option<T>, Result<T,E>, SendError<M>, and SpawnError<A>.
String and Bytes are reserved immutable data primitives, not dispatch keys or authority carriers. Literals use canonical "..." and b"..." forms with only the escapes documented in the syntax reference; lowering emits typed primitive shapes/templates and lowercase-hex labels such as String(7265616479) and Bytes(010262696e). Payload-bearing enum constructors named String or Bytes are rejected; exact equality requires matching primitive types and adds no dynamic string operations, coercions, structural equality, or authority-in-data.
Bool has the fieldless core values False and True. Bool, False, and
True are always available to Strata source and cannot be redeclared by user
code or reused as source declaration names, including records, record fields,
enums, variants, functions, processes, component instances, process authorities,
supervisor children, or boundary declarations. The checker resolves them to core
typed IDs, lowering emits the canonical Mantle fieldless enum shape, and Mantle
admits and executes only the typed artifact value shape rather than dispatching
on source strings. This core surface does
not introduce a general prelude, standard library, floats, dynamic string
operations, or implicit truthiness.
Unit has the single value Unit. Option<T> has None and Some(T).
Result<T,E> has Ok(T) and Err(E). Local send outcomes use
Result<Unit,SendError<MessageType>>; local spawn outcomes use
Result<ProcessRef<TargetProcess>,SpawnError<Unit>>.
let send_result: Result<Unit,SendError<WorkerMsg>> = send worker Work;
let spawn_result: Result<ProcessRef<Worker>,SpawnError<Unit>> = spawn Worker;
Outcome bindings are immutable and scoped to the step body. They may feed
whole-value state transitions when the checker can admit the finite runtime
outcome values into the process state table. Spawn successes carry process
references, so they remain step-local authority values rather than storable
source state. Outcomes may also drive follow-up effects through immutable if
conditions such as send_result == Ok(Unit) or
spawn_result != Err(Exhausted(Unit)); those tests compare typed built-in
variant shape, not process-reference identity. Top-level pre-state effect
bindings are declaration ordered: an outcome is usable only after its let
statement, and outcome bindings must appear before ordinary non-prefix effects
that would otherwise cross the commit-or-return boundary.
SendError<M> preserves the original message for pre-acceptance failures, including direct ProcessRef<T> payload metadata; that authority remains a send/message boundary value, not ordinary process state. Admitted variants are Full(M), Stopped(M), Crashed(M), and MailboxClosed(M). Mantle produces Full for capacity preflight, Stopped for normally terminated targets when that cause remains observable, Crashed for failed targets, and MailboxClosed for explicit mailbox closure, supervisor-driven shutdown, or indistinguishable closed-state rejection. A denied admitted port_connect policy is an authority failure rather than a mailbox lifecycle cause, so typed-port send outcomes fail closed before source-visible binding on policy denial. Ordinary source-created Panic(...) still records no-replay evidence and fails before a later sender can recover the target as a source-visible outcome. SpawnError<A> admits Denied(A), Exhausted(A), and BackendUnavailable(A). Mantle produces Denied for denied declared spawn authority, Exhausted for exhausted local process capacity, and BackendUnavailable when the local spawn backend is explicitly unavailable before acceptance; none of those failures admits or executes the rejected child.
Bare statement send and spawn remain fail-closed runtime effects: Mantle rejects pre-acceptance delivery and process-limit failures instead of silently dropping them. Outcome-form send returns the typed failure before acceptance and commits only on Ok(Unit). These outcomes add no source-visible mutation, source-name runtime dispatch, hidden retries, silent drops, or implicit authority grants; Mantle executes the lowered typed artifact templates.
Local Spawn Authority
A process that dynamically creates another local process must declare an exact typed spawn capability:
proc Main mailbox bounded(1) {
type State = MainState;
type Msg = MainMsg;
authority spawn_worker: Cap<Spawn<Worker>>;
fn step(state: MainState, Start) -> ProcResult<MainState> ! [spawn] ~ [] @det {
let worker: ProcessRef<Worker> = spawn Worker;
return Stop(state);
}
}
Cap<Spawn<Worker>> authorizes only dynamic local creation of Worker. A
capability for another process, a duplicate or unused descriptor, a malformed
descriptor, a self-targeting capability, or a capability targeting the entry
process is rejected. The ! [spawn] effect remains exact effect usage; it does
not prove spawn authority by itself. After checking,
authority declarations lower to typed authority IDs and spawn-site IDs. Source
names remain syntax, diagnostics, and trace metadata.
Local Port Authority
authority connect_worker: Cap<PortConnect<WorkerPort>>; authorizes only
send ... via WorkerPort from that process. Mismatched, duplicate, or unused
port authorities are rejected; ! [send] is effect usage, not authority proof.
Local Supervision
supervise local one_for_one(max_restarts: 2_u32, within_ms: 1000_u64) { child worker: Worker = spawn Worker as permanent; }
declares static lexical child processes owned by a process, separate from dynamic
Cap<Spawn<Target>> authority. The current surface supports local one_for_one
supervisors only. Child names are unique, restart intensity is explicit, modes
are permanent, transient, or temporary, and child graphs must be acyclic.
send worker Work; resolves worker to typed supervisor and child IDs.
Collections
Lists and maps are immutable source values with explicit numeric capacities. They can be used as source function parameters and return values, record fields, process state types, and message payloads when their element, key, and value types are source value types.
List<Phase,2>[Ready, Done]
Map<Phase,Phase,1>[Ready => Done]
Collection constructors are explicit. Bare [Ready, Done] and { Ready: Done }
forms are not part of the buildable surface. Repeated canonical map keys are
rejected. List and map patterns are exact by default. List rest patterns are
suffix-only:
List[first, second] // exact list length
List[first, ..tail] // first must exist; tail is the unmatched suffix
..tail binds an immutable whole list after the fixed prefix. If the matched
value has type List<T,N> and the pattern lists M fixed prefix elements, the
tail binding has type List<T,N-M>. The actual tail may contain fewer entries
because bounded list values may be shorter than capacity.
A trailing .. marker makes a map pattern a subset pattern over the listed
static keys:
Map[Ready => selected] // exact key set
Map[Ready => selected, ..] // Ready must exist; extra keys are allowed
Map[Ready => selected, ..rest] // rest binds a map without Ready
Map ..rest binds an immutable whole map except the listed static keys. If the
matched value has type Map<K,V,N> and the pattern lists M distinct static
keys, the rest binding has type Map<K,V,N-M>. The actual rest may contain fewer
entries because subset patterns still match maps that omit unlisted keys.
Overlapping exact and subset map patterns are rejected instead of relying on source order or specificity. Subset overlap is capacity-aware: two subset patterns overlap when one bounded map can contain both required key sets. Runtime-bound map value keys must be static source values; dynamic-key dictionaries remain deferred. Scalar keys are allowed when they are static source values. Rest binding does not expose collection iteration, order-dependent dispatch, mutation, dynamic keys, or mutable views.
Collection pattern element and map-value positions may contain nested structural
patterns, such as List[Job { phase }, ..tail] or
Map[Ready => Job { phase }, ..rest]. Map nesting uses only listed static keys.
Record field and map entry order are preserved in authored values, artifact values, labels, and traces. Projection still addresses map entries by key, and the language exposes no source-level map iteration or order-dependent dispatch.
Enums
Enums define named variants:
enum MainMsg {
Start,
}
enum WorkerState {
Idle,
Handled,
}
enum WorkerMsg {
Assign(Job),
Stop,
}
Enums used as process state or message types must declare at least one variant. Duplicate variants are rejected. Payload variants are accepted for process state and message enums. Process state payloads remain immutable whole-state values represented through typed state IDs.
Processes
A process declares a mailbox bound, a state type, a message type, an init
function, and one step clause for each accepted message:
proc Worker mailbox bounded(1) {
type State = WorkerState;
type Msg = WorkerMsg;
fn init() -> WorkerState ! [] ~ [] @det {
return Idle;
}
fn step(state: WorkerState, Ping) -> ProcResult<WorkerState> ! [emit] ~ [] @det {
emit "worker handled Ping";
return Stop(Handled);
}
}
Only the aliases State and Msg are accepted inside a process. Processes may
also declare pure deterministic process-local functions alongside init and
step.
Each message variant must resolve to exactly one step clause, selected by an
explicit constructor pattern, by one wildcard pattern, or by one whole-body
match msg. A message-specific step clause can also dispatch over current
state with a whole-body match state. A process cannot mix parameter-pattern
or state-match step clauses with a match msg step body.
An init function returns one immutable whole state value. It may use a
whole-body match or a pure return match over one fieldless enum constructor
when the checker can select one arm before lowering:
fn init() -> MainState ! [] ~ [] @det {
return match Warm {
Cold => {
return MainState { readiness: ColdReady };
}
Warm => {
return MainState { readiness: WarmReady };
}
};
}
This is not runtime dispatch. The checker proves the fieldless enum scrutinee,
checks exhaustiveness and arm shapes, and emits the selected initial state as the
existing typed state ID. init return match arms must be statement-free and must
return whole state values; nested return matches and payload-binding
materialization into the initial state are rejected.
Function Signatures
The accepted function signature shape is:
fn name(params...) -> ReturnType ! [effects] ~ [may_behaviors] @det {
...
}
Buildable source requires:
| Function | Required Shape |
|---|---|
init | No parameters, returns the process state type, uses ! [] ~ [] @det. |
parameter-pattern step | Parameters exactly state: StateType, MessagePattern, returns ProcResult<StateType>, uses ~ [] @det. |
match step | Parameters exactly state: StateType, msg: MsgType, returns ProcResult<StateType>, uses ~ [] @det, and has a whole-body match msg. |
state-match step | Parameters exactly state: StateType, MessagePattern, returns ProcResult<StateType>, uses ~ [] @det, and has a whole-body match state. |
| source function | One binding parameter or one pattern parameter, returns a source value type, uses ! [] ~ [] @det, and has no runtime statements. |
The parser recognizes @nondet, but buildable source rejects it. The
may-behavior list after ~ must be empty.
Normal source functions are checked before lowering and expanded into the value positions where they are called. They do not become Mantle runtime dispatch entries and cannot perform runtime effects. A process-local function is visible only inside that process. A module function is visible throughout the module. Recursive function call cycles are rejected.
Source function block bodies may introduce sequential immutable source-local values before the terminal return:
fn route(work: Work) -> Phase ! [] ~ [] @det {
let current: Phase = status(work);
let selected: Phase = if (current == Active) { Active } else { Idle };
return selected;
}
Each binding uses let name: Type = value_expr;. The annotated type must be a
declared source value type without process-reference authority: String,
Bytes, a scalar integer type, a record, enum, List<T,N>, or Map<K,V,N>
whose contained types are also source values. A
message enum that carries a direct ProcessRef<T> payload is valid as a message
type, but it is not a pure source value type. The right-hand side is a pure
source value expression and may refer to parameters, pattern bindings, earlier
source-local bindings, source function calls, source-time conditionals,
return-match values, record/list/map constructors, and Bool/equality predicates.
Bindings are immutable and cannot be reassigned.
Source-local bindings may not shadow parameters, pattern bindings, earlier local
bindings, constructors, types, processes, or visible source functions. They
cannot use ProcessRef<T>, spawn, send, emit, runtime for, mutation, or
runtime-only state/payload forms. The checker resolves these names during source
function expansion; lowering emits only typed values, templates, typed IDs, and
validated artifact data. Source-local binding names do not become Mantle dispatch
keys or executable runtime bindings.
Source-function pattern bindings use the same immutable checker namespace. They must not shadow source functions or process-reference bindings introduced by the owning process.
Function signature patterns can author enum dispatch:
fn readiness(Cold) -> Readiness ! [] ~ [] @det {
return ColdReady;
}
fn readiness(Warm) -> Readiness ! [] ~ [] @det {
return WarmReady;
}
fn status(Assigned(job: Job)) -> WorkStatus ! [] ~ [] @det {
return Active(job);
}
A source function signature may also destructure fields from an immutable record value:
fn phase_of(Job { phase }) -> JobPhase ! [] ~ [] @det {
return phase;
}
fn renamed_phase(Job { phase: current }) -> JobPhase ! [] ~ [] @det {
return current;
}
Source function signatures may also dispatch on exact immutable collection shapes:
fn first(List<Phase,2>[phase, _]) -> Phase ! [] ~ [] @det {
return phase;
}
fn lookup(Map<Phase,Phase,1>[Ready => selected]) -> Phase ! [] ~ [] @det {
return selected;
}
Nested function patterns compose constructor payloads, records, and collection element/value projections:
fn routed_phase(Assign(Job { phase })) -> Phase ! [] ~ [] @det {
return phase;
}
fn listed_phase(List<Routed,1>[Assign(Job { phase })]) -> Phase ! [] ~ [] @det {
return phase;
}
A function may also use a whole-body match over its typed binding parameter:
fn readiness_body(mode: StartupMode) -> Readiness ! [] ~ [] @det {
match mode {
Cold => {
return ColdReady;
}
Warm => {
return WarmReady;
}
}
}
Whole-body function matches and function return-match expressions may split one top-level constructor when nested typed enum predicates are provably disjoint. The split remains checker-time source dispatch before lowering:
fn route(packet: Packet) -> Phase ! [] ~ [] @det {
match packet {
Envelope(Assign(Ready)) => {
return Ready;
}
Envelope(Assign(Done)) => {
return Done;
}
}
}
Whole-body function matches may also destructure a concrete record binding:
fn phase_of(job: Job) -> JobPhase ! [] ~ [] @det {
match job {
Job { phase } => {
return phase;
}
}
}
Whole-body function matches can destructure exact list patterns, suffix-only list rest, exact map patterns, and map subset/rest patterns. A wildcard arm may provide fallback:
fn first_or_unknown(items: List<Phase,1>) -> Phase ! [] ~ [] @det {
match items {
List[phase] => {
return phase;
}
_ => {
return Unknown;
}
}
}
Or a block body may return a match over an in-scope source value binding:
fn status(work: Work) -> WorkStatus ! [] ~ [] @det {
return match work {
Empty => {
return Idle;
}
Assigned(job: Job) => {
return Active(job);
}
};
}
The same function return-match form may destructure a concrete record binding:
fn phase_of(job: Job) -> JobPhase ! [] ~ [] @det {
return match job {
Job { phase } => {
return phase;
}
};
}
Collection return matches use the same collection patterns:
fn ready_value(items: Map<Phase,Phase,2>) -> Phase ! [] ~ [] @det {
return match items {
Map[Ready => selected, ..rest] => {
return selected;
}
_ => {
return Unknown;
}
};
}
Enum matches are exhaustive and immutable. Source function whole-body matches and
return-match expressions may repeat a top-level constructor only when nested
typed enum predicates are provably disjoint; identical predicates, unguarded
constructor arms, and unproven overlaps are rejected. Signature groups still keep
one clause per top-level constructor. Record body/return matches use one record
pattern arm for the matched record type. Collection patterns match exact list
length or exact map keys unless they use ..tail, .., or ..rest; _ remains
available as a collection fallback.
Payload-bearing source function patterns and record/list/map destructuring
patterns bind immutable source values. A function call must provide a concrete
enum constructor value for signature-pattern, whole-body match, or function
return-match dispatch. Record and collection destructuring functions require a
concrete value argument after source function expansion. Functions are still
expanded before lowering and do not become runtime dispatch entries.
Pure Conditionals
Source functions and pure value expressions can use a source-time value-level conditional:
fn readiness(flag: Bool) -> Readiness ! [] ~ [] @det {
return if (flag) { WarmReady } else { ColdReady };
}
The equivalent braced pure return form is also supported in source functions. The
condition type is exactly core Bool; user declarations of Bool, True, or
False are rejected. Both expression-form branches are source value expressions
checked against the same expected return, field, state, or payload type. Braced source function return-if branches may contain immutable
source-local bindings before the terminal pure return. Branches cannot perform
emit, send, spawn, runtime for, branch-local process-reference binding,
mutation, or any other runtime statement.
Concrete conditionals are selected during source checking and source function
expansion, so lowering sees only the selected value. Runtime-bound value
conditionals over Bool lower as typed Mantle value templates containing the
condition template and both typed branch value templates. Mantle does not
receive a conditional runtime dispatch entry, a source function name, or a
source-string branch key.
Typed Equality Predicates
The equality surface is == and != over these operand families:
- core
Boolwith fieldlessFalseandTruevalues; - fixed-width scalar integer values with matching scalar types;
- immutable
Stringvalues and immutableBytesvalues with matching primitive types; - fieldless values of the same payload-free enum type;
- built-in
Option,Result,SendError, andSpawnErrorvalues only when one side is a safe built-in variant pattern such asNone,Ok(Unit), orErr(Exhausted(Unit)).
Both operands must have the same checked type. Concrete source operands fold
during checking, so lowering sees only the selected True or False value.
Runtime-dependent operands lower as typed Mantle value templates and Mantle
evaluates them from typed values. Equality does not dispatch through
source names, function names, debug labels, or parser strings.
Structural record/list/map equality, payload enum equality, process-reference equality, direct equality between two spawn outcomes, dynamic string predicates, and matching a preserved message payload by equality are not part of the buildable equality surface.
Boolean Predicate Composition
Bool-producing predicates can be composed with grouping, unary !, binary
&&, and binary ||:
if ((flag == True) && !(status == Done)) {
emit "still active";
} else {
emit "complete";
}
Every composed operand must produce core Bool. The supported operands are
direct Bool values or templates, typed equality predicates,
typed scalar-ordering predicates, and nested Boolean predicate composition.
Fully concrete source predicates fold during checking. Runtime-dependent
predicates lower into typed Mantle value templates; Mantle validates the typed
tree, validates all operands, evaluates it from typed runtime values, and
records the selected branch through the existing branch_selected trace event.
Predicate composition does not add floats, dynamic string operations, structural equality, payload enum equality, process-reference equality, assignment, mutation, or authority.
Runtime branching, runtime iteration, statements, effects, step patterns, state transitions, and limits are documented in Runtime Reference.
Boundary Contracts
Boundary declarations give source-visible communication an explicit typed contract before it reaches Mantle.
protocol WorkerProtocol message WorkerMsg requires Cap<ProtocolBoundary<WorkerProtocol>>;
port WorkerPort protocol WorkerProtocol target Worker requires Cap<PortConnect<WorkerPort>>;
component WorkerComponent exports WorkerPort requires Cap<ComponentExport<WorkerComponent>>;
WorkerMsg must be a named enum. WorkerPort must target a declared process
whose type Msg is WorkerMsg. WorkerComponent exports that port. The
required capability descriptors must point back to the declaration being defined;
mismatched descriptors are rejected.
Components can also declare imported ports and a local composition can bind component instances through typed port edges:
component MainComponent exports MainPort imports WorkerPort requires Cap<ComponentExport<MainComponent>>;
composition AppComposition {
instance main component MainComponent;
instance worker component WorkerComponent;
bind main imports WorkerPort -> worker exports WorkerPort;
}
The checker builds typed component-instance IDs and typed port-binding edges before lowering. A composition is admitted only when every component import is bound exactly once, both instances are declared, the importing component actually imports the port, the exporting component actually exports the port, and the two ports share one protocol. Direct source-unit imports still control which components and ports are visible; transitive-only component or port access is rejected.
A typed boundary send uses the optional via clause:
authority connect_worker: Cap<PortConnect<WorkerPort>>;
fn step(state: MainState, Start) -> ProcResult<MainState> ! [spawn, send] ~ [] @det {
let worker: ProcessRef<Worker> = spawn Worker;
send worker via WorkerPort Work;
return Stop(state);
}
Checking proves all of these facts before lowering:
- the send target is a typed
ProcessRef<Worker>; WorkerPorttargetsWorker;WorkerProtocolusesWorkerMsg;Workis a variant ofWorkerMsg;- the sending process declares and uses exact
Cap<PortConnect<WorkerPort>>authority.
After checking, boundary references are typed IDs. Lowering emits Mantle
protocol, port, component, composition, authority, and action tables with those
IDs. Mantle admits the tables before ArtifactLoaded and runtime dispatch uses
admitted process, message, and port IDs. Protocol, port, component, component
instance, process, and message names are metadata for diagnostics and traces
only.
Invalid boundary shapes fail closed at the earliest layer that can see them. A source program with an undeclared port, mismatched protocol message type, missing port authority, duplicate boundary name, ambiguous direct import, unbound component import, duplicate component import binding, unimported-port binding, protocol-mismatched or port-authority-mismatched composition edge, or reserved descriptor type name fails checking. A hand-authored artifact with mismatched boundary table IDs, unimported-port composition binding, missing port authority, malformed composition edge, port-authority mismatch, or unbound component import fails admission before runtime events begin.
Typed boundary sends emit boundary_send_checked runtime trace events before
mailbox acceptance. Accepted sends continue to message acceptance; admitted
authority-policy denials record boundary_result=denied and stop before target
message side effects. Invalid boundary shapes are still admission diagnostics,
not runtime trace events, because they do not enter runtime dispatch or produce
ArtifactLoaded.
Run the source-to-runtime example with:
just run-example boundary_contracts_main
just run-example component_composition_main
Current implementation limits: the source composition form is a local typed
graph admission input for component instances and port bindings. strata composition build checks that graph and emits the Strata-owned checked-subset
strata.checked_component_composition validation artifact; it is not .mta, is
not Mantle runtime input, and it does not add remote send, distributed
transport, capability binding syntax, deployment manifests, package resolution,
hot upgrade, or generated port stubs. Runtime composition correlation requires
the separate explicit strata composition bind-runtime bridge, which validates
admitted checked composition evidence against a matching .mta and emits
mantle.runtime_composition_binding for mantle run --composition-binding.
Binding classes outside the current source subset are emitted as empty arrays
and fail closed if forged non-empty.
Use just strata-composition-report examples/component_composition_main.str json
for review evidence, and just strata-build examples/component_composition_main.str
plus just strata-composition-build examples/component_composition_main.str plus
just strata-composition-admit target/strata/component_composition_main.component-composition.json json plus just strata-composition-bind-runtime target/strata/component_composition_main.component-composition.json target/strata/component_composition_main.mta target/strata/component_composition_main.deployment-composition.json when CI
needs fail-closed Strata-owned checked-subset validation evidence and the
explicit runtime-correlation binding. Use just strata-target-requirements examples/component_composition_main.str json to inspect the typed Mantle runtime
features Strata requires for the checked program. strata authority-summary ... --format json also reports the checked
component-boundary authority edge summary for the same checked IR. Mantle admits
the lowered .mta by comparing embedded typed requirements against mantle feature-declaration; it does not consume source-side reports or composition
validation artifacts.
Runtime Reference
This page documents Strata source forms that lower to typed Mantle runtime behavior. It continues the authoring reference from Language Reference.
Runtime Feature Declaration And Target Binding
Every lowered .mta artifact carries a typed target_requirements block. For
Strata, the block is derived from checked IR and lowering facts: local
execution, bounded mailboxes, trace support, local spawn/send/effect use,
runtime branches and loops, typed value templates, typed effect outcomes,
boundary tables, and component-composition metadata where those surfaces are
present.
Mantle publishes its own runtime feature declaration for the current local-only
target profile, mantle.local_only.v1:
just mantle-feature-declaration json
Admission compares the artifact identity and typed requirements against that
profile-backed declaration and reports the admitted runtime_profile:
just strata-target-requirements examples/component_composition_main.str json
just mantle-admit target/strata/component_composition_main.mta json
Current implementation limits are runtime implementation limits, not Strata language law. The admitted profile is single-host local runtime execution with
explicit local host sinks, no ambient network authority, no transport authority, no cluster membership, and no admitted remote operation policy. Remote send,
remote spawn, distributed transport, cluster membership, package management, hot upgrade, generated stubs, native output, and serialized execution-plan
bytecode remain unsupported. Mantle may compile an admitted LoadedProgram into internal executable plans for dispatch and value-template evaluation, but
.mta remains the language-neutral boundary. Strata does not emit bytecode or executable template programs. Unsupported required features, malformed
source-language metadata, or invalid loaded template references fail before ArtifactLoaded and runtime side effects. Mantle treats source language as opaque
artifact metadata and never dispatches on source names.
Mantle execution is host-boundary explicit: after artifact admission and loaded runtime-table construction, the runtime core records events, program stdout,
monotonic clock reads, and final flushes through a RuntimeHost. mantle run is the filesystem/stdout host adapter that writes *.observability.jsonl
and program output; in-memory execution uses the same admitted runtime core with in-memory sinks. Program-output trace events are written before stdout so
failed stdout sinks still leave audit evidence; mandatory host sink setup, write, or flush failures fail closed before the CLI prints a successful run report.
Admitted Authority/Effect Binding
Authority/effect facts cross through .authority-effect.json; typed policy
decisions cross through .authority-policy.json; Strata binds both with a
matching .mta into .authority-effect-binding.json. Mantle consumes only
--authority-effect-binding, validating schema/source namespace, .mta
identity, typed descriptors/effects, and the closed policy table before side
effects. Raw checked sidecars are never valid Mantle runtime input.
Runtime Branching
Step bodies can use a final-position runtime if whose condition is a checked
Bool value template, or a statement-level runtime if whose branches run
effects and then continue to the enclosing final return:
fn step(state: WorkerState, Branch(flag: Bool)) -> ProcResult<WorkerState> ! [emit] ~ [] @det {
if (flag == True) {
emit "worker took warm branch";
return Stop(WarmReady);
} else {
emit "worker took cold branch";
return Stop(ColdReady);
}
}
fn step(state: WorkerState, Branch(flag: Bool)) -> ProcResult<WorkerState> ! [emit] ~ [] @det {
if (flag != False) {
emit "worker handled true";
} else {
emit "worker handled false";
}
return Continue(state);
}
Statement-level runtime branches also support guard/no-op shapes:
fn step(state: WorkerState, Check(flag: Bool)) -> ProcResult<WorkerState> ! [emit] ~ [] @det {
if (flag == True) {
emit "guard saw true";
}
if (!(flag == False)) {
emit "guard enabled";
} else {
}
if (flag == True) {
} else {
emit "guard saw false";
}
return Continue(state);
}
This is ordinary runtime branching. If the condition depends on the received
payload or current state payload, Strata lowers the checked condition, branch
actions, and any branch next states into the Mantle artifact. Mantle validates
the typed condition, validates both branches, executes only the selected branch,
and records branch_selected trace events with a stable artifact
branch_path. Branch effects must be declared by the step effect list.
Statement-level runtime branches may contain one direct nested statement-level
runtime branch action; deeper direct branch nesting is rejected at source
checking, artifact admission, and loaded-runtime admission. Runtime branch
statement prefixes cannot bind source-local values or process references.
Branches may contain bounded
runtime for statements, including effect-only loop prefixes before the
terminal return in final-position runtime branches. Each loop body remains the
same runtime-loop body surface. Final-position branch prefixes may also
contain one direct statement-level runtime if action; deeper direct
branch-action nesting remains rejected. A final-position runtime branch may end
in one direct nested final-position runtime if, which lowers as a bounded
nested NextState::IfElse; third-level terminal runtime branches remain
rejected at source checking, artifact admission, and loaded-runtime admission.
An omitted statement-level else lowers as an explicit empty branch. Empty
statement-level branches perform no effects, no state change, no authority
acquisition, and no hidden work; branch selection is still observable through
branch_selected. One branch may be empty when the sibling branch contains at
least one action. Both branches empty are rejected at source checking,
artifact admission, and loaded-runtime admission. Final-position runtime
branches must return the same step result from both branches. Statement-level
runtime branches cannot return; state changes still occur only through the
enclosing immutable whole-value Continue, Stop, or Panic return.
Scalar ordering, scalar equality, and checked scalar arithmetic are supported over matching fixed-width integer types. Exact String and Bytes equality is supported over matching primitive types.
Runtime-bound scalar/primitive predicates and pure value conditionals lower as typed Mantle templates; Mantle admission and runtime evaluation validate those templates in admitted value-template positions.
Unbounded loops, Mantle-side import resolution, floats, dynamic string operations, structural collection comparison, and a standard library remain outside the buildable runtime surface.
Runtime Iteration
Step bodies can use a bounded runtime for loop over an immutable typed list
binding:
authority spawn_worker: Cap<Spawn<Worker>>;
fn step(state: BatchState, Batch(items: List<Bool,2>)) -> ProcResult<BatchState> ! [spawn, send] ~ [] @det {
let worker: ProcessRef<Worker> = spawn Worker;
for item in items {
send worker Branch(item);
}
return Stop(state);
}
This is ordinary runtime iteration. The collection source must be an identifier
binding that checks as List<Element,N> and remains runtime-bound, such as a
message payload binding. Strata lowers the checked collection template, typed
loop element ID, immutable element binding, maximum item count, and body actions
into the Mantle artifact. Mantle validates the loop structure and body,
executes the body once per runtime element in collection order, enforces the
collection length and runtime fuel limits, and records loop_started,
loop_iteration, and loop_completed trace events.
The loop element is immutable and may be used only as a typed value template in
the loop body. A loop item may also use a record pattern over the element type,
for example for Job { phase: routed_phase } in jobs { ... }. That binds an
immutable projected field value for the loop body; lowering emits a typed record
field projection from the active loop element, not an executable source binding
name. Loop bodies may use statement-level runtime if over the active loop
element, a projected loop-element field, or another checked Bool template,
including the same one-sided no-op branch shapes supported outside loops. Mantle
selects the branch during execution and records branch_selected inside the loop
trace with the active loop element ID and iteration index. Bounded loops may also
appear inside statement-level runtime branches, allowing a guard to select or
skip a whole loop. If the selected branch is empty, Mantle records only the outer
branch_selected event and emits no loop events. If the selected branch contains
the loop, trace order is branch_selected, loop_started, ordered
loop_iteration body effects, and loop_completed.
The loop body is intentionally narrow: no nested loops, no
spawn, no return, no assignment, and no process-reference element type. A
loop body may contain the statement-level runtime if / no-op shape,
including one direct nested statement-level runtime branch and including when
the loop is itself inside a selected guard branch. Deeper direct branch nesting
remains rejected. Send targets may be process references bound before the loop
or direct ProcessRef<T> payloads received by the current step.
Declare every body effect in the enclosing step effect list.
Statements
The accepted statements are:
emit "text";
let worker: ProcessRef<Worker> = spawn Worker;
send worker Ping;
if (flag == True) { emit "true"; } else { emit "false"; }
if (flag == True) { emit "true"; }
if (flag == True) {} else { emit "false"; }
for item in items { send worker Branch(item); }
for item in items { if ((item != False) && !(item == False)) { send worker Branch(item); } }
for Job { phase: routed_phase } in jobs { send worker AssignPhase(routed_phase); }
return Stop(state);
return Continue(next_state);
return Panic(failed_state);
emit records and prints an output literal. Output literals must be non-empty,
must not contain control characters, and do not support string escapes.
spawn starts another declared process and returns an immutable typed process
reference. The reference binding is local to the transition and must be
typed as ProcessRef<TargetProcess>.
send queues a message through a process reference spawned in the same
transition or through a received ProcessRef<T> payload binding. The message
must be accepted by the reference target’s process message enum. Static
validation rejects
self-spawn, spawning the already-started entry process, duplicate
process-reference binding in one transition, sends before the reference is
bound, mailbox overflow, and messages left unhandled after a target stops
normally.
Payload messages use the variant constructor at the send site:
send worker Assign(Job { phase: Ready });
The payload value is checked against the target message variant’s payload type. Unit message variants reject payload arguments, and payload variants require one payload argument.
Process references can be payloads when the message variant declares a typed reference:
enum WorkerMsg { Work(ProcessRef<Sink>) }
send worker Work(sink);
The received reference is immutable and can be used as a send target:
fn step(state: WorkerState, Work(reply_to: ProcessRef<Sink>)) -> ProcResult<WorkerState> ! [send] ~ [] @det {
send reply_to Done;
return Stop(state);
}
Runtime dispatch uses the transported runtime process ID and checked target process ID. Source names remain diagnostics and trace metadata.
Received references can also be used as send targets inside statement-level branches and bounded loop bodies. This does not make process references general source values: they remain direct message payload authority, and lowering emits typed received-payload send targets rather than source binding names.
Current process-reference boundaries:
| Surface | Current status |
|---|---|
let worker: ProcessRef<Worker> = spawn Worker; | Supported as an immutable transition-local binding when the process has exact Cap<Spawn<Worker>> authority. The checker resolves the target process before lowering. |
send worker Ping; | Supported for a process reference spawned earlier in the same transition. Lowering emits a process-reference table ID. |
enum WorkerMsg { Work(ProcessRef<Sink>) } | Supported only as a direct message payload type. |
send worker Work(sink); and send reply_to Done; | Supported for direct process-reference payload forwarding. Mantle routes by checked target process ID and runtime process ID. |
Multiple ProcessRef<Worker> bindings to one process definition | Supported. Each spawn creates a separate runtime process instance; transported refs stay bound to their runtime PID and never retarget to another instance. |
| Process references directly in record fields or collection element/key/value types | Rejected. Process references are runtime authority, not general immutable data values. |
| Process references nested inside record, enum, list, map, or next-state payload templates | Rejected. A process reference must be the direct payload of a message that declares ProcessRef<T>. |
Message enums that carry direct ProcessRef<T> payloads as pure source values | Rejected. They are message authority surfaces, not source-local computation values. |
| Process references in process state values or next-state templates | Rejected. Process states remain immutable source values without embedded runtime authority. |
| Sending by process definition names, source strings, registries, dynamic worker pools, stale process-reference retargeting, remote supervision | Not supported. Lexical local supervisor child sends use admitted supervisor/child IDs, not strings. |
Patterns are source-level syntax for typed value decomposition. The current
runnable surface supports constructor patterns, constructor payload bindings,
nested constructor and record/list/map payload destructuring, function return-match
expressions, and wildcards. Normal source functions may match concrete enum values
or destructure concrete record/list/map values, init may use one whole-body
match over a fieldless enum constructor to select the initial state, and actor
message dispatch may use one whole-body match over the typed message parameter:
fn step(state: WorkerState, msg: WorkerMsg) -> ProcResult<WorkerState> ! [emit] ~ [] @det {
match msg {
First => {
emit "worker matched First";
return Continue(SawFirst);
}
Second => {
emit "worker matched Second";
return Stop(Done);
}
}
}
Step match is an authoring form for the same semantics as step parameter
patterns, including typed payload bindings and nested record/list/map payload
destructuring. Whole-body match msg arms and step parameter patterns may split
one top-level message constructor by exact nested typed payload predicates when
those predicates are provably disjoint over discovered concrete payload cases.
Checking resolves each arm into typed message-keyed transitions, typed payload
guards when needed, and typed projection templates before lowering. Mantle still
dispatches by typed message IDs, current state IDs when a transition is
state-specific, payload type IDs, exact typed payload identity, and loaded
template structure, not by source strings. In this buildable step subset the
match scrutinee must be the typed message parameter, and match arms are
block-delimited without comma separators.
A message-specific step may instead match the current state parameter when
the process state type is an enum:
fn step(state: WorkerState, Complete) -> ProcResult<WorkerState> ! [emit] ~ [] @det {
match state {
Idle => {
emit "worker had no job";
return Stop(Idle);
}
Working(job: Job) => {
emit "worker completed job";
return Stop(Done(job));
}
Done(job: Job) => {
emit "worker already done";
return Stop(Done(job));
}
}
}
State-match arms resolve against the declared process state enum and are exhaustive over its variants. Payload-bearing state variants may bind the whole payload with the declared payload type or destructure a concrete record/list/map payload. Each binding is immutable and local to that transition arm. Lowering emits typed Mantle transitions keyed by typed message ID plus checked current state ID, and runtime selection fails closed if the current state is not in the loaded state table.
An init match is checked against the enum that owns the scrutinee constructor.
It must be exhaustive, duplicate-free, and statement-free; each arm returns an
immutable whole state value. Payload-bearing enum variants can be covered by
explicit patterns or _, but init arms cannot materialize payload bindings in
the returned state because the initial state lowers to one static state ID.
Effects
The ! [...] effect list is exact source-level effect usage for each step
clause. It must exactly match the clause actions. For a
match msg, match state, or step return match step, the one effect list
applies to every generated transition. Match-body arms must use exactly those
effects. A return-match uniform prefix lowers the same actions onto each
selected transition, and selected-arm prefixes must still leave every generated
transition with exactly the declared effects. Missing, duplicate, and unused
declared effects are rejected before lowering.
Dynamic local process creation also requires a process-local typed authority
declaration such as authority spawn_worker: Cap<Spawn<Worker>>;. The
! [spawn] effect proves that the transition uses spawning; it does not prove
which process may be spawned.
Lexical local supervisor children are a separate authority path. Mantle starts
static children declared by supervise local one_for_one(...) in declaration
order, stops them in reverse declaration order when the owning process stops,
and routes send child_name Message; through admitted supervisor and child IDs.
Source checking, artifact validation, and loaded-runtime admission reject cyclic
local supervisor child graphs before startup.
Restarted children receive fresh runtime process IDs and rerun init; Mantle
does not replay accepted messages, effects, or previous state after a crash.
permanent children restart after normal stop or panic, transient children
restart after panic only, and temporary children are not restarted. Restart
intensity is enforced per supervisor plan. If restart intensity, runtime process
capacity, or the default same-monotonic-tick throttle denies a restart, Mantle
fails the supervisor scope and reports the scope failure in the runtime trace.
Forced child-tree stops are reported with supervisor-specific stop reasons, not
as source-level normal termination.
| Effect | Statement |
|---|---|
emit | emit "text"; |
spawn | let worker: ProcessRef<Worker> = spawn Worker; |
send | send worker Ping; or send reply_to Done; |
init cannot perform statements in the buildable surface and therefore uses an
empty effect list.
Authority Inspection
Authority inspection is an optional tooling surface. It does not run during normal check, build, or run commands, and it does not write a report file.
The source-side command checks Strata source and prints the checked process authorities and spawn-site classifications before lowering:
just strata-authority-summary examples/actor_emit_spawn_send.str
just strata-authority-summary examples/actor_emit_spawn_send.str json
The artifact-side command admits a Mantle Target Artifact and prints the artifact authority and spawn-site tables without executing the program:
just strata-build examples/actor_emit_spawn_send.str
just mantle-inspect-authority target/strata/actor_emit_spawn_send.mta
just mantle-inspect-authority target/strata/actor_emit_spawn_send.mta json
Text output is intended for local review. JSON output is deterministic and uses typed process IDs, authority IDs, capability descriptors, spawn-site IDs, supervisor IDs, supervisor child IDs, child modes, and restart-intensity fields so CI and audit tooling can compare the source-side and artifact-side authority surfaces. Process, authority, and child labels are included only as metadata.
Composition admission reporting and component-composition artifact admission stay Strata-side. Runtime correlation requires a separate explicit binding step:
just strata-composition-report examples/component_composition_main.str
just strata-composition-report examples/component_composition_main.str json
just strata-build examples/component_composition_main.str
just strata-composition-build examples/component_composition_main.str
just strata-composition-admit target/strata/component_composition_main.component-composition.json json
just strata-composition-bind-runtime \
target/strata/component_composition_main.component-composition.json \
target/strata/component_composition_main.mta \
target/strata/component_composition_main.deployment-composition.json
mantle run target/strata/component_composition_main.mta \
--composition-binding target/strata/component_composition_main.deployment-composition.json
The report is emitted from checked Strata composition facts and a diagnostic
FNV-1a source fingerprint. It records typed component-instance IDs, typed
component import/export port obligations, typed port-binding IDs, admitted
binding results, empty unsatisfied imports for admitted compositions, component
export authority surfaces, endpoint port authority requirements, and
cross-component authority edges. The component-composition artifact is the
durable JSON form of that source-side checked-subset validation evidence and
self-identifies as strata.checked_component_composition. It is not .mta and
is not read by Mantle. strata composition bind-runtime validates an admitted
checked composition artifact against a matching .mta and emits the separate
mantle.runtime_composition_binding JSON artifact. Mantle accepts that binding
only when supplied with --composition-binding; without it, Mantle executes the
.mta normally and emits no composition/deployment correlation fields.
just strata-target-requirements examples/actor_ping.str
just strata-target-requirements examples/actor_ping.str json
just mantle-feature-declaration
just mantle-feature-declaration json
strata target-requirements checks and lowers source, then prints the runtime
feature IDs embedded in the generated artifact. mantle feature-declaration
prints the current mantle.feature_declaration.v6 metadata: artifact format,
schema version, source-language metadata policy, local containment and mailbox
models, conservative message-observation fields, allocation fields,
validity-window defaults, component/spawn observability fields, supported
runtime features, and unsupported implementation limits. Mantle admission
derives the minimum feature set required by the .mta tables and requires the
embedded target requirements to cover that set. The reports do not execute
source names or alter runtime state.
Step Patterns
A step parameter pattern handles one message constructor:
fn step(state: MainState, Start) -> ProcResult<MainState> ! [emit] ~ [] @det {
emit "hello from Strata";
return Stop(state);
}
Payload constructors can bind the received payload in a step parameter pattern
or a whole-body match msg arm:
fn step(state: WorkerState, Assign(job: Job)) -> ProcResult<WorkerState> ! [] ~ [] @det {
return Stop(WorkerState { job: job });
}
fn step(state: WorkerState, msg: WorkerMsg) -> ProcResult<WorkerState> ! [] ~ [] @det {
match msg {
Assign(job: Job) => {
return Stop(WorkerState { job: job });
}
}
}
The binding is immutable and local to that transition. It can be used where a
value of the bound payload type is expected, including whole-value state
returns, record fields, downstream message payload sends, and send targets when
the payload type is ProcessRef<T>. Payload bindings cannot shadow the state
parameter, process declarations, type names, or value constructors.
Process-reference bindings in the same transition cannot shadow a payload
binding.
Record, list, and map payloads can also be destructured directly in step
parameter patterns, match msg arms, and match state arms:
fn step(state: WorkerState, Assign(Job { phase })) -> ProcResult<WorkerState> ! [] ~ [] @det {
return Continue(WorkerState { seen: phase });
}
fn step(state: WorkerState, Items(List[phase, ..tail])) -> ProcResult<WorkerState> ! [] ~ [] @det {
return Continue(WorkerState { seen: phase });
}
fn step(state: WorkerState, Lookup(Map[Ready => phase, ..rest])) -> ProcResult<WorkerState> ! [] ~ [] @det {
return Continue(WorkerState { seen: phase });
}
fn step(state: WorkerState, Envelope(Assign(Job { phase }))) -> ProcResult<WorkerState> ! [] ~ [] @det {
return Continue(WorkerState { seen: phase });
}
fn step(state: WorkerState, Holding(List[Job { phase }, ..tail])) -> ProcResult<WorkerState> ! [] ~ [] @det {
return Continue(WorkerState { tail: tail });
}
These bindings are immutable projections of the concrete payload value. A
constructor payload, record field, list element, list rest, map value, or map
rest can be used in whole-value state returns and downstream payloads, but
process references still remain valid only as direct message payload bindings.
Fieldless nested enum constructors such as Envelope(Assign(Ready)) are
accepted as typed shape predicates; they do not introduce bindings.
Shape-only collection payload patterns such as Items(List[_]),
Lookup(Map[Ready => _]), or Lookup(Map[..]) are rejected;
use the constructor pattern without destructuring when the payload is ignored.
Multiple parameter-pattern or state-match step clauses may share one
top-level message constructor only when exact nested typed payload predicates
are provably disjoint. For state-match clauses, each accepted payload case
expands across the checked current-state cases from the match state body, and
lowering emits typed transitions keyed by message ID, current state ID, and exact
typed payload guard. A wildcard step pattern may cover discovered concrete
payload cases not matched by explicit payload-sensitive step-signature or
state-match clauses. The fallback lowers to exact typed payload-guarded
transitions for those discovered cases; it is not an open-ended runtime
catch-all for future payload values. State-match fallback cases additionally
expand across the checked current-state cases from the fallback match state
body before lowering.
If a process accepts more than one message, it can declare explicit clauses for specific constructors and one wildcard clause for the remaining variants:
fn step(state: WorkerState, First) -> ProcResult<WorkerState> ! [emit] ~ [] @det {
emit "worker handled First";
return Continue(SawFirst);
}
fn step(state: WorkerState, _) -> ProcResult<WorkerState> ! [emit] ~ [] @det {
emit "worker handled Second";
return Stop(Done);
}
Every accepted message variant, or every discovered concrete payload case inside a payload-sensitive split, must resolve to exactly one generated transition. Explicit constructor clauses handle their named variants. One wildcard clause may cover variants that do not have explicit clauses. When payload-sensitive step-signature or state-match splitting is active for a variant, the same wildcard may cover only discovered concrete payload cases not matched by explicit payload predicates. Duplicate explicit clauses, overlapping payload predicates, missing coverage, duplicate wildcard clauses, missing variant coverage, and unreachable wildcard clauses are rejected. Parameter patterns are compile-time dispatch only: Mantle dequeues one message at a time and dispatches by typed message ID, current state ID when a transition is state-specific, and exact typed payload identity when a payload guard exists. Payload-bearing variants keep one stable typed message case, and their immutable values travel in runtime message envelopes.
State Transitions
step returns ProcResult<StateType>:
return Continue(next_state);
return Stop(final_state);
return Panic(failed_state);
Continue(value) replaces the process state and keeps the process running.
Stop(value) replaces the process state and terminates the process normally.
Panic(value) replaces the process state, marks the process failed, records
failure trace evidence, and fails the run without replaying the consumed
message.
Passing the state parameter preserves the supplied state:
return Stop(state);
Passing a record value, enum variant, list, or map creates an explicit whole-value state replacement:
return Continue(WorkerState { phase: Idle });
return Continue(Working(Job { phase: Ready }));
return Continue(List<Phase,2>[Ready, Done]);
return Continue(Map<Phase,Phase,1>[Ready => Done]);
return Stop(Handled);
return Panic(Failed);
A step body may also use return match over a concrete enum source value
binding when the checker can reduce the match to one typed transition before
lowering. Statements before the return match are a uniform action prefix; the
checker lowers that same typed action list onto every generated transition.
The selected arm may then perform its own typed action-block prefix,
including emit, direct in-scope send, statement-level runtime if, and
bounded runtime for actions, before the terminal whole-value result:
fn step(state: WorkerState, Envelope(Assign(phase: Phase))) -> ProcResult<WorkerState> ! [emit] ~ [] @det {
emit "return-match prefix";
return match phase {
Ready => {
emit "ready arm prefix";
return Continue(SawReady);
}
Done => {
emit "done arm prefix";
return Stop(Done);
}
};
}
This form is not runtime dispatch. The checker requires an immutable enum source
binding with a concrete value proven during step clause or state-match expansion,
checks every arm as a ProcResult<StateType>, selects the matching arm, and
lowers the selected arm to the existing typed transition shape. Uniform prefix
effects occur before the selected arm prefix in source program order and remain
committed runtime actions. Selected arm prefixes use the same
statement-level action sequencing as a step body: local emit, in-scope direct
send, statement-level runtime if, and bounded runtime for over checked
runtime List<T,N> bindings. The checker still validates every arm template
fail-closed before selected actions lower as typed Mantle artifact actions.
Arm-local spawn, process-reference binding, nested runtime for statements,
final-position runtime if, nested return matches, matching state, matching
non-enum values, and dynamic payload catch-all dispatch are rejected.
Typed Effect Outcomes
Local send and spawn operations can bind typed immutable outcomes in step
bodies:
let spawn_result: Result<ProcessRef<Worker>,SpawnError<Unit>> = spawn Worker;
let send_result: Result<Unit,SendError<WorkerMsg>> = send worker Work;
return Stop(MainState { sent: send_result });
These bindings lower to typed Mantle effect-outcome IDs and typed value templates. Source binding names are not runtime dispatch keys. When an outcome template is used as a next-state value, the checker admits the finite outcome states required by that transition before artifact execution. Spawn outcome successes carry process references, so they are kept as step-local authority values and are not admitted as ordinary source state.
The source checker enforces declaration order for outcome bindings. An outcome is visible only after its binding statement, and outcome bindings must precede ordinary effect statements. Process-reference spawns in that prefix are executed before state resolution so outcome sends can target them. Process-reference spawns after ordinary effects execute in ordinary source order and cannot be used by later outcome bindings.
For local send outcomes, Mantle checks target process, lifecycle mailbox state, and mailbox capacity before accepting the message. Accepted sends commit and return Ok(Unit); pre-acceptance failures return Err(Full(message)), Err(Stopped(message)), Err(Crashed(message)), or Err(MailboxClosed(message)) with the original message preserved. Stopped is produced for normally terminated targets when that cause remains observable. MailboxClosed is reserved for explicit mailbox closure, supervisor-driven shutdown, or indistinguishable closed-state rejection. A denied admitted port_connect policy is a typed authority failure rather than a mailbox lifecycle cause; both bare typed-port sends and typed-port send outcomes fail closed as runtime errors before delivery after recording boundary_send_checked denial evidence. Ordinary source-created Panic(...) still records no-replay evidence and fails before a later sender can recover the target; direct ProcessRef<T> message metadata remains send/message-boundary authority and does not become storable source state.
For local spawn outcomes, accepted spawns commit the new process and return Ok(process_ref) with a typed ProcessRef<TargetProcess> payload. If process authority is denied by the admitted runtime policy before acceptance, Mantle returns Err(Denied(Unit)); if process capacity is exhausted before acceptance, Mantle returns Err(Exhausted(Unit)); if Mantle has no available local spawn backend for this run, it returns Err(BackendUnavailable(Unit)) before acceptance. Bare statement send and spawn still fail closed: pre-acceptance failure is reported as a runtime error instead of being silently dropped.
The mantle run CLI accepts --max-runtime-processes N for bounded local execution and rejects missing, zero, non-integer, and overflowed values before execution. With --max-runtime-processes 1, the exhausted-spawn example observes Err(Exhausted(Unit)) after the entry process consumes capacity; no Worker is admitted or executed. The CLI also accepts --disable-local-spawn-backend for bounded local backend-unavailable testing; outcome-form spawn returns Err(BackendUnavailable(Unit)), while bare spawn and lexical supervisor child startup fail closed before binding or admitting a child process.
Mantle records each typed outcome binding as effect_outcome_bound trace
evidence with the numeric outcome_id, outcome action, target process ID, and
closed result category. The event proves whether a source-visible outcome came
from accepted runtime work, denied spawn authority, exhausted process capacity,
or a pre-acceptance send failure without using source binding names as runtime
dispatch keys.
Outcome values can also drive follow-up effects through ordinary immutable branching:
if (spawn_result != Err(Exhausted(Unit))) {
emit "spawn accepted";
} else {
emit "spawn rejected";
}
if (send_result == Ok(Unit)) {
emit "send accepted";
} else {
emit "send not accepted";
}
These conditions branch on a typed built-in variant pattern. They do not add
structural equality for preserved message payloads, and they do not compare
process-reference identities from Ok(process_ref) spawn outcomes.
examples/effect_outcome_mailbox_full.str, examples/effect_outcome_stopped_target.str, examples/process_ref_stale_lifecycle.str, and examples/local_supervision_inactive_crashed_send_outcome.str exercise source-to-runtime pre-acceptance send failure outcomes; the stale lifecycle gate keeps a newer same-definition worker running while a transported old PID returns Err(Stopped(message)) instead of retargeting. The denied-, exhausted-, and backend-unavailable spawn examples check/build the same typed local spawn outcome shape, then run Mantle with denied admission, an exhausted process limit, or a disabled local spawn backend. Each rejected spawn outcome records typed trace evidence and admits no child process.
Current pattern-matching closure boundaries:
| Surface | Current status |
|---|---|
step return match after uniform pre-return effects | Supported. Uniform prefix actions lower identically onto each selected typed transition. |
step return match arm-local emit / in-scope direct send prefixes | Supported only after source-time concrete arm selection; selected arm actions lower as typed transition actions before the terminal result. |
step return match arm-local statement-level runtime if prefix | Supported after source-time concrete arm selection. Branches use typed action templates and obey the same statement-level runtime branch nesting bound as ordinary step actions. |
step return match arm-local bounded runtime for prefix | Supported over checked runtime List<T,N> bindings. Loop bodies use typed action templates, may include statement-level runtime branches, and still reject nested runtime for. |
step return match arm-local bounded runtime for with a loop-body runtime if | Supported under the ordinary statement-level branch nesting bound. Branch conditions and branch actions lower through typed loop-element templates inside the selected arm. |
step return match arm-local statement-level runtime if containing bounded runtime for | Supported for branch-local loops over checked runtime List<T,N> bindings. Branch-local loops stay selected inside the concrete arm and lower as typed branch actions, not source-label dispatch. |
step return match state | Rejected. Use whole-body match state, then match a concrete state-payload binding when one is proven. |
init match over payload-bearing arm constructors | Supported only when every arm returns a state value that does not materialize payload bindings. |
init return match over payload-bearing constructors | Rejected. init return match selects a static initial state from a fieldless enum scrutinee. |
| Init arms materializing payload bindings in the initial state | Rejected. The initial state lowers to one static typed state ID. |
Shape-only collection predicates such as Items(List[_]), Lookup(Map[Ready => _]), or Lookup(Map[..]) | Rejected. Bind at least one immutable projected value, or match only the enclosing constructor when the payload is ignored. |
| Dynamic-key map matching | Rejected. Map pattern keys are static source values. |
Arbitrary/general match expressions outside supported function, init, step, match msg, and match state forms | Future language semantics, not part of the current buildable surface. |
State changes are immutable whole-value transitions. There is no assignment statement, no source-visible field mutation, and no payload rewrite inside an existing process state.
Limits
The buildable language surface enforces bounded sizes:
| Limit | Value |
|---|---|
| Source bytes | 1 MiB |
| Identifier bytes | 128 |
| Distinct checked artifact types | 4096 |
| Output literal bytes | 16 KiB |
| Processes | 256 |
| State values per process | 1024 |
| Message variants per process | 1024 |
| Static process-reference bindings per process definition | 4096 |
| Distinct output literals | 4096 |
| Actions per process | 4096 |
| Mailbox bound | 65,536 |
| Type nesting | 32 |
| Value nesting | 32 |
These limits are part of the artifact and runtime boundary.
Syntax Reference
This page gives a compact grammar-style view of the accepted source syntax. The Language Reference explains the same surface in prose.
The notation is informal:
- quoted text is literal syntax;
?means optional;*means zero or more;+means one or more;|means choice.
Source File
source_file =
module_decl import_decl* top_level_decl*
module_decl =
"module" ident ";"
import_decl =
"import" ident ";"
top_level_decl =
record_decl
| enum_decl
| protocol_decl
| port_decl
| component_decl
| composition_decl
| function
| process_decl
Records
record_decl =
"record" ident ";"
| "record" ident "{" record_field ("," record_field)* ","? "}"
record_field =
ident ":" type_ref
Fieldless records use the semicolon form. Braced records must declare at least one field.
Imports must appear immediately after the module declaration and before any top-level declaration. The current import form admits one source-unit module identifier and resolves it from the importing source unit’s directory; aliases, wildcards, re-exports, packages, and path strings are not accepted.
Protocols, Ports, And Components
protocol_decl =
"protocol" ident "message" ident "requires" type_ref ";"
port_decl =
"port" ident "protocol" ident "target" ident "requires" type_ref ";"
component_decl =
"component" ident "exports" ident component_imports? "requires" type_ref ";"
component_imports =
"imports" ident ("," ident)*
composition_decl =
"composition" ident "{" composition_item* "}"
composition_item =
component_instance
| port_binding
component_instance =
"instance" ident "component" ident ";"
port_binding =
"bind" ident "imports" ident "->" ident "exports" ident ";"
A protocol names one enum message type, a port binds that protocol to one
target process, and a component exports one port with zero or more imported
ports. A composition declares local component instances and port binding edges
from an imported port on one instance to an exported port on another instance.
This is the current implementation’s local source admission input. strata composition build emits Strata-owned checked-subset evidence
(strata.checked_component_composition), not .mta or direct Mantle input.
Runtime correlation requires a separate explicit mantle.runtime_composition_binding
artifact from strata composition bind-runtime. Source names are metadata only;
typed component-instance, import/export port, port-binding, port, protocol, and
authority descriptor IDs carry meaning. Unsupported binding classes stay empty fail-closed arrays; strata composition-report remains Strata-owned review evidence.
The required authority annotations must be exact:
Cap<ProtocolBoundary<ProtocolName>>, Cap<PortConnect<PortName>>, and
Cap<ComponentExport<ComponentName>>. See
Boundary Contracts for the checked and lowered
contract. Capability bindings, remote bindings, package manifests, and port
stubs are not part of this implementation surface.
Enums
enum_decl =
"enum" ident "{" enum_variant_list? "}"
enum_variant_list =
enum_variant ("," enum_variant)* ","?
enum_variant =
ident
| ident "(" type_ref ")"
Enums used as process state or message types must have at least one variant. Payload variants are accepted for process state and message enums. State enum payload constructors create immutable whole-state values.
Processes
process_decl =
"proc" ident "mailbox" "bounded" "(" number ")" "{"
process_member*
"}"
process_member =
state_alias
| message_alias
| authority_decl
| supervisor_decl
| init_function
| step_function
| source_function
state_alias =
"type" "State" "=" type_ref ";"
message_alias =
"type" "Msg" "=" type_ref ";"
authority_decl =
"authority" ident ":" process_authority_type ";"
process_authority_type =
"Cap" "<" "Spawn" "<" ident ">" ">"
| "Cap" "<" "PortConnect" "<" ident ">" ">"
supervisor_decl =
"supervise" "local" "one_for_one" "(" "max_restarts" ":" number "_u32" "," "within_ms" ":" number "_u64" ")" "{" supervisor_child+ "}"
supervisor_child =
"child" ident ":" ident "=" "spawn" ident "as" ("permanent" | "transient" | "temporary") ";"
The aliases, authority declarations, supervisor declarations, and functions may
appear in any order. State, Msg, and init must each appear exactly once.
Cap<Spawn<ProcessName>> and Cap<PortConnect<PortName>> authorities must
target declared objects and be used by matching local actions. Non-init/step
functions are process-local source functions. A local supervisor declares static
lexical children and does not grant dynamic spawn authority.
Each concrete message case must resolve to one generated transition through an
explicit constructor pattern, one wildcard pattern, one match msg body, or a
state-match step for a constructor or wildcard message pattern. Parameter-pattern,
state-match, and whole-body match msg dispatch may split same-constructor
clauses by exact typed payload guard when nested predicates are disjoint over
discovered payload cases. A process cannot mix parameter-pattern/state-match
forms with a match msg step body. Other process members are rejected.
Functions
function =
"fn" ident "(" params? ")" "->" type_ref
"!" effect_list
"~" ident_list
determinism
function_body
params =
function_param ("," function_param)* ","?
function_param =
param_binding
| pattern
param_binding =
ident ":" type_ref
pattern =
ident
| ident "(" constructor_payload_pattern ")"
| ident "{" record_pattern_fields "}"
| "List" list_type_args? "[" list_pattern_items? "]"
| "Map" map_type_args? "[" map_pattern_entries? "]"
| "_"
constructor_payload_pattern =
ident ":" type_ref
| ident
| ident "(" constructor_payload_pattern ")"
| ident "{" record_pattern_fields "}"
| "List" list_type_args? "[" list_pattern_items? "]"
| "Map" map_type_args? "[" map_pattern_entries? "]"
| "_"
record_pattern_fields =
record_pattern_field ("," record_pattern_field)* ","?
record_pattern_field =
ident
| ident ":" ident
list_type_args =
"<" type_ref "," number ">"
map_type_args =
"<" type_ref "," type_ref "," number ">"
list_pattern_items =
collection_pattern_binding ("," collection_pattern_binding)* ("," ".." ident ","? | ","?)
map_pattern_entries =
".." ident? ","?
| value_expr "=>" collection_pattern_binding
("," value_expr "=>" collection_pattern_binding)*
("," ".." ident? ","? | ","?)
collection_pattern_binding =
nested_collection_pattern
| ident
| "_"
nested_collection_pattern =
ident "(" constructor_payload_pattern ")"
| ident "{" record_pattern_fields "}"
| "List" list_type_args? "[" list_pattern_items? "]"
| "Map" map_type_args? "[" map_pattern_entries? "]"
effect_list =
"[" (effect ("," effect)* ","?)? "]"
effect =
"emit" | "spawn" | "send"
ident_list =
"[" (ident ("," ident)* ","?)? "]"
determinism =
"@det" | "@nondet"
Collection pattern bindings that begin with an identifier are nested patterns
only when the identifier is followed by ( or {, or when List/Map is
followed by optional type arguments and [. Otherwise the identifier is an
immutable binding name.
Buildable source accepts bodies for init, step, module functions, and
process-local functions. It requires deterministic functions and empty
may-behavior lists. Normal source functions are pure: they use ! [], allow only
source value expressions, immutable source-local bindings, and pure braced
return branches, perform no runtime statements, and are expanded before
lowering.
Function Bodies
function_body =
";"
| "{" block_body "}"
| "{" match_body "}"
block_body =
block_statement* (return_statement | return_if_else)
block_statement =
source_function_statement
| runtime_statement
match_body =
"match" ident "{" match_arm+ "}"
match_arm =
pattern "=>" "{" block_body "}"
Patterns are source-level binding and decomposition syntax. Strata supports
constructor patterns, constructor payload bindings/destructuring, record
destructuring patterns, list/map collection patterns, and _ wildcards.
Buildable semantic consumers are normal source function signatures and match
bodies, function return-match expressions, fieldless enum init matches,
actor step dispatch, message-specific match state step bodies, and step
return-match expressions with optional uniform action prefixes. Record/list/map
destructuring patterns are accepted in those payload-capable positions when the
payload has the matching type. These forms may split a top-level constructor by
disjoint exact nested typed payload predicates.
Source function calls still expand before lowering; enum pattern dispatch
requires a concrete enum constructor value and record/list/map destructuring
requires a concrete value.
Buildable source requires bodies. init uses no parameters. Each
parameter-pattern step uses state: StateType followed by one message
constructor or wildcard pattern:
parameter_pattern_step_function =
"fn" "step" "(" "state" ":" type_ref ","
(ident | ident "(" constructor_payload_pattern ")" | "_") ")"
"->" "ProcResult" "<" type_ref ">"
"!" effect_list "~" "[]" "@det"
"{" block_body "}"
The first type_ref names the process state type. The message pattern is a
constructor, payload constructor, or _. Patterns bind immutable transition
locals, may destructure concrete records/lists/maps and nested constructors, and
use fieldless nested enum constructors as typed shape predicates. List rest is
suffix-only after at least one fixed element. Map patterns are exact unless they
end in .. or ..rest; ..rest binds the remaining immutable map.
Multiple parameter-pattern step clauses may share one top-level message
constructor only when exact nested typed payload predicates are provably
disjoint. A wildcard fallback may cover only discovered concrete payload cases
not matched by explicit payload-sensitive clauses. The fallback lowers to exact
typed payload-guarded transitions for those discovered cases; it is not a
runtime catch-all for future payload values.
A match step uses a typed message parameter and a whole-body
match over that parameter:
match_step_function =
"fn" "step" "(" "state" ":" type_ref "," ident ":" type_ref ")"
"->" "ProcResult" "<" type_ref ">"
"!" effect_list "~" "[]" "@det"
"{" match_body "}"
Each match arm uses constructor or wildcard pattern syntax. Constructor payload patterns may bind or destructure nested constructor, record, list, and map payloads. Arms that share one top-level message constructor may split by exact typed payload guard when their nested predicates are provably disjoint. The match scrutinee must be the typed message parameter in the current buildable step subset. Match arms are block-delimited and do not use comma separators. The step effect list applies to every generated transition, so each arm must use exactly the declared effects.
A state-match step uses the normal state parameter plus a message constructor
or wildcard pattern, then uses a whole-body match state:
state_match_step_function =
"fn" "step" "(" "state" ":" type_ref ","
(ident | ident "(" constructor_payload_pattern ")" | "_") ")"
"->" "ProcResult" "<" type_ref ">"
"!" effect_list "~" "[]" "@det"
"{" "match" "state" "{" match_arm+ "}" "}"
State-match arms resolve against the declared process state enum. Payload
variants may bind the whole payload or destructure concrete record/list/map and
nested constructor payloads. Bindings are immutable and transition-local. Each
generated transition is keyed by message ID and checked current state ID, with an
exact typed payload guard when payload-sensitive clauses split. State changes
still occur only by returning a whole value through Continue(...), Stop(...),
or Panic(...).
In a block-bodied step, the return expression may be a match over a
transition-local enum source binding whose concrete value is already proven by
step clause or state-match expansion. Any statements before the return match are
a uniform action prefix for every selected lowered transition:
step_return_match =
"return" "match" ident "{" match_arm+ "}" ";"
Every arm body may use local emit, in-scope direct send, statement-level
runtime if, and bounded runtime for before the terminal result. The checker
validates each arm fail-closed, selects the concrete arm before lowering, and
emits the same typed transition metadata as a direct step return. Arm-local
spawn, process-reference binding, nested runtime for, final-position runtime
if, nested return matches, catch-all dispatch, source-string selectors, and
source-function or init arm statements remain rejected.
In a pure block-bodied init, the return expression may be a match over one
fieldless enum constructor:
init_return_match =
"return" "match" ident "{" match_arm+ "}" ";"
Every arm body must be statement-free and must return one whole state value. The checker selects the concrete arm before lowering and emits the selected initial state as the existing typed state ID. This syntax does not allow effect statements before the return match, nested return matches in arms, dynamic dispatch, or source-string selectors.
A normal source function is a module-level function or a process-local function
whose name is not init or step:
source_function =
"fn" ident "(" (param_binding | pattern) ")"
"->" type_ref
"!" "[]" "~" "[]" "@det"
("{" block_body "}" | "{" match_body "}")
Function block bodies must not contain effect statements, process-reference bindings, sends, or runtime loops. They may contain zero or more immutable source-local value bindings before the terminal return:
source_local_binding =
"let" ident ":" type_ref "=" value_expr ";"
The binding type must be a source value type without process-reference
authority, and the value expression must be pure. Message enums that carry
direct ProcessRef<T> payloads are message authority surfaces, not
source-local value types. Source-local bindings are distinct from
ProcessRef<T> spawn bindings and are rejected in init, step,
statement-level runtime branches, or runtime loop bodies.
Function match bodies match the function’s typed binding parameter. A function
block may also return a match over an in-scope source value binding, or use
braced pure return branches:
fn readiness(flag: Bool) -> Readiness ! [] ~ [] @det {
if (flag) {
return WarmReady;
} else {
return ColdReady;
}
}
The braced form is pure control flow for returned values. Each branch may
contain immutable source-local bindings before its terminal return. Concrete
conditions select one branch during source function expansion. Function
return-match arms are exhaustive, duplicate-free, immutable, and still expand
before lowering.
Function calls and payload-bearing enum values share the same surface syntax:
call_or_payload_constructor =
ident "(" value_expr ")"
The checker resolves that form against the expected type. A declared enum
constructor becomes an immutable enum value; a declared function is expanded in
init, step result values, and send payload values. Recursive function call
cycles are rejected.
Statements
source_function_statement =
source_local_binding
runtime_statement =
emit_statement
| process_ref_statement
| spawn_outcome_statement
| send_statement
| send_outcome_statement
| if_statement
| for_statement
source_local_binding =
"let" ident ":" type_ref "=" value_expr ";"
emit_statement =
"emit" string_literal ";"
process_ref_statement =
"let" ident ":" process_ref_type "=" "spawn" ident ";"
process_ref_type =
"ProcessRef" "<" ident ">"
spawn_outcome_statement =
"let" ident ":" "Result" "<" process_ref_type "," "SpawnError" "<" "Unit" ">" ">"
"=" "spawn" ident ";"
send_statement =
"send" ident ("via" ident)? ident payload_arg? ";"
send_outcome_statement =
"let" ident ":" "Result" "<" "Unit" "," "SendError" "<" type_ref ">" ">"
"=" "send" ident ("via" ident)? ident payload_arg? ";"
payload_arg =
"(" value_expr ")"
if_statement =
"if" "(" value_expr ")" "{" branch_statement* "}"
("else" "{" branch_statement* "}")?
branch_statement =
emit_statement
| send_statement
| nested_if_statement
| for_statement
nested_if_statement =
"if" "(" value_expr ")" "{" nested_branch_statement* "}"
("else" "{" nested_branch_statement* "}")?
nested_branch_statement =
emit_statement
| send_statement
| for_statement
for_statement =
"for" for_item "in" ident "{" loop_statement* "}"
loop_statement =
emit_statement
| send_statement
| if_statement
for_item =
ident
| ident "{" record_pattern_fields "}"
return_statement =
"return" return_expr ";"
return_if_else =
"if" "(" value_expr ")" "{" block_body "}"
"else" "{" block_body "}"
Statement admission is contextual. Pure source function blocks admit only
source_function_statement; init and step runtime blocks admit
runtime_statement. Runtime branches and loop bodies use the narrower
branch_statement, nested_branch_statement, and loop_statement sets above.
The identifier in process_ref_statement names an immutable process reference
value. The identifier after spawn is the process definition name. The
ProcessRef<T> annotation must name the same process definition.
spawn_outcome_statement binds
Result<ProcessRef<TargetProcess>,SpawnError<Unit>> as an immutable step-local
value. The Ok branch carries the committed process reference; it is authority
data and is not valid source state. A top-level outcome binding is visible only
to later statements and return values.
The first send identifier is a local process reference or direct
ProcessRef<T> payload binding. Optional via PortName must name a declared
port matching the target process and message type, with a used local
Cap<PortConnect<PortName>> authority. Payload variants require one payload
value; unit variants reject payload values.
send_outcome_statement binds a typed local send result. The annotation must be
Result<Unit,SendError<TargetMessageType>> so pre-acceptance failure variants
can preserve the original message value. Top-level send and spawn outcome
bindings must precede ordinary non-prefix effect statements in the same step
body. A top-level process-reference spawn can remain in that pre-state prefix
so later outcome sends can target the spawned process reference.
The for collection source is an identifier binding with runtime-bound
List<T,N> type. The element item is an immutable element binding or record
pattern over the element type. Loop bodies support statement-level runtime if,
but reject nested loops, return, spawn, branch-local source or process
reference bindings, and branch nesting beyond one direct nested layer.
Types
type_ref =
ident
| ident "<" type_arg ("," type_arg)* ","? ">"
type_arg =
type_ref
| number
Checking accepts ProcResult<StateType> for step, ProcessRef<ProcessName> for direct process authority surfaces, Cap<Spawn<ProcessName>> and Cap<PortConnect<PortName>> in process authorities, and List<T,N> / Map<K,V,N> over source value types.
String, Bytes, Unit, Option<T>, Result<T,E>, SendError<M>, and SpawnError<A> are built-in value shapes for primitive data, explicit domain failure, and typed effect outcomes.
Values
return_expr =
value_expr
| match_body
value_expr =
value_or_expr
value_or_expr =
value_and_expr
| value_or_expr "||" value_and_expr
value_and_expr =
value_equality_expr
| value_and_expr "&&" value_equality_expr
value_equality_expr =
value_ordering_expr
| value_ordering_expr equality_op value_ordering_expr
equality_op =
"=="
| "!="
value_ordering_expr =
value_additive_expr
| value_additive_expr ordering_op value_additive_expr
ordering_op =
"<"
| "<="
| ">"
| ">="
value_additive_expr =
value_multiplicative_expr
| value_additive_expr additive_op value_multiplicative_expr
additive_op =
"+"
| "-"
value_multiplicative_expr =
value_unary_expr
| value_multiplicative_expr multiplicative_op value_unary_expr
multiplicative_op =
"*"
| "/"
| "%"
value_unary_expr =
value_primary_expr
| "!" value_unary_expr
| "-" suffixed_integer_literal
value_primary_expr =
ident
| string_literal
| bytes_literal
| suffixed_integer_literal
| ident "(" value_expr ")"
| ident "{" record_value_field ("," record_value_field)* ","? "}"
| "List" list_type_args? "[" value_expr_list? "]"
| "Map" map_type_args? "[" map_value_entries? "]"
| "(" value_expr ")"
| "if" "(" value_expr ")" "{" value_expr "}" "else" "{" value_expr "}"
record_value_field =
ident ":" value_expr
value_expr_list =
value_expr ("," value_expr)* ","?
map_value_entries =
value_expr "=>" value_expr
("," value_expr "=>" value_expr)* ","?
suffixed_integer_literal =
number scalar_suffix
string_literal = '"' (string_literal_char | string_escape)* '"'
bytes_literal = 'b"' (bytes_literal_char | bytes_escape)* '"'
string_literal_char = printable character except '"' or '\\'
bytes_literal_char = printable ASCII byte except '"' or '\\'
string_escape = '\\"' | '\\\\' | '\\n' | '\\r' | '\\t' | '\\u{' hex{1,6} '}'
bytes_escape = '\\"' | '\\\\' | '\\n' | '\\r' | '\\t' | '\\x' hex hex
hex = ASCII hex digit
scalar_suffix =
"_u8" | "_u16" | "_u32" | "_u64"
| "_i8" | "_i16" | "_i32" | "_i64"
Parenthesized value expressions group any value expression without changing its
type.
ident(value) is a function call when ident names a visible source function and
a payload-bearing enum value when it names a constructor of the expected enum type.
List and map constructors are explicit. Optional type and capacity arguments are accepted for readability; the checker still validates each value against the expected bounded source value type.
Typed equality predicates are deliberately narrow. left == right and left != right require operands with the same checked type: Bool, String, Bytes, a scalar integer type, or a payload-free enum.
Fully concrete source equality folds during checking. Runtime-bound equality lowers as a typed Mantle value template; operands are not runtime dispatch strings.
Structural record/list/map equality, dynamic string predicates, process-reference equality, and payload enum equality remain unsupported.
Scalar literals require explicit suffixes in value positions. Arithmetic uses
+, -, *, /, and %; ordering uses <, <=, >, and >=. Scalar
operators require matching integer types and perform checked arithmetic. Fully
concrete overflow, underflow, division by zero, and modulo by zero fail during
checking. Runtime-bound scalar operators lower as typed Mantle value templates
and fail closed during runtime evaluation.
Boolean predicate composition is also narrow. !, &&, and || are supported
only over Bool values, typed equality or scalar-ordering predicates, or nested composed
predicates. ! binds tighter than &&, and && binds tighter than ||; use
parentheses for explicit grouping. Fully concrete predicates fold during
checking. Runtime-bound predicates lower as typed Mantle value templates over
typed Bool-producing operands, not as source strings or function names.
Pure conditionals require core Bool, whose fieldless False and True values
are always available and cannot be redeclared. Both branches are value
expressions checked against the same expected type. Concrete conditions select one branch before lowering.
Runtime-bound expression-form conditionals lower as typed Mantle value
templates. Expression branch bodies cannot contain statements or effects.
Final-position return_if_else is runtime control flow in step bodies. The
condition must produce core Bool, but it may depend on received
payload or current-state payload bindings. Each branch is a block body with its
own statements and terminal return. Branch statement prefixes are limited to
emit, send, bounded for actions, and one direct statement-level
if_statement action. A bounded for prefix keeps the ordinary
loop-body surface, including the loop-body branch action. Strata lowers
the checked condition, branch action prefixes, and branch next states to Mantle
control flow; Mantle executes only the selected branch and traces the branch
choice. Deeper direct branch-action nesting remains rejected at source checking,
artifact admission, and loaded-runtime admission.
Statement-level if_statement is runtime control flow for effects before the
enclosing return. Branches may contain emit, send, and bounded for
statements, plus one direct nested statement-level if_statement. The else
branch may be omitted, and one branch body may be empty when the sibling branch
has at least one effect statement, nested branch action, or bounded-loop action;
both branches empty are rejected. An omitted
else lowers as an explicit empty branch in the typed Mantle artifact. Branches
cannot bind source-local values or process references, return, contain nested
loops, or exceed the one direct nested branch-action layer. Inside for, the
condition may use the immutable loop element binding or an immutable field
projected from a loop-element record pattern; lowering emits typed Mantle
templates over the loop element ID, not the source binding name. Loop-body
branch bodies follow the same direct nested branch-action bound.
init returns a state value or a pure return match that the checker reduces
to one state value before lowering. step returns Continue(value),
Stop(value), Panic(value), a final-position runtime if, or a
return match that the checker reduces to one of those result forms before
lowering while preserving any uniform action prefix as typed transition actions.
A final-position runtime if branch may end in one direct nested final-position
runtime if; third-level terminal runtime branches remain rejected.
Literals
The literal surface is intentionally narrow:
- decimal numbers are accepted for mailbox bounds;
- suffixed integer literals are accepted for scalar source values;
- string literals are accepted as immutable
Stringvalues and foremittext, with only\",\\,\n,\r,\t, and valid one-to-six-digit\u{HEX}escapes; - bytes literals are accepted as immutable
Bytesvalues, with printable ASCII plus\",\\,\n,\r,\t, and\xNNescapes; - newline and carriage return characters are not allowed raw inside string or bytes literals.
Identifiers
ident =
(ASCII letter | "_") (ASCII letter | ASCII digit | "_")*
_, as, authority, bind, bounded, child, component, composition,
else, emit, enum, exports, fn, for, if, import, imports, in,
instance, let, local, mailbox, match, module, mut, one_for_one,
permanent, port, proc, protocol, record, requires, return,
security, send, spawn, supervise, target, temporary, transient,
type, var, and via are reserved everywhere identifiers are accepted. The
single _ token is reserved for wildcard patterns.
Bool, ProcResult, ProcessRef, Cap, Spawn, ProtocolBoundary,
PortConnect, ComponentExport, List, Map, Unit, Option, Result,
SendError, SpawnError, String, Bytes, U8, U16, U32, U64, I8, I16, I32, and I64 are reserved type names because they name built-in truth,
transition, process-reference, capability descriptor, collection, primitive data, effect outcome, and scalar value types.
Tutorial: Hello
This tutorial walks through the smallest Strata program accepted by the source-to-runtime gate.
The complete source is examples/hello.str.
Module
module hello;
The module declaration names the source unit. This example does not import another source unit and does not create a package boundary.
State And Message Types
record MainState;
enum MainMsg {
Start,
}
MainState is a fieldless record. It gives the process a concrete state type
without carrying data.
MainMsg declares the messages accepted by Main. Start is the first variant,
so it is the entry message delivered to Main when Mantle starts the program.
Main Process
proc Main mailbox bounded(1) {
type State = MainState;
type Msg = MainMsg;
...
}
Every runnable program needs a Main process. The mailbox bound limits how many
messages can wait in the process mailbox.
The State alias points to the process state type. The Msg alias points to
the process message enum.
Initial State
fn init() -> MainState ! [] ~ [] @det {
return MainState;
}
init takes no parameters and returns the initial state. It uses no effects,
has no may-behaviors, and is deterministic.
Step Function
fn step(state: MainState, Start) -> ProcResult<MainState> ! [emit] ~ [] @det {
emit "hello from Strata";
return Stop(state);
}
step receives the process state and handles the Start message named in its
parameter pattern.
The body emits text and then returns Stop(state). Passing state preserves
the supplied state while stopping normally.
The effect list is [emit] because the body uses exactly one effect.
Run It
just run-example hello
The program prints:
hello from Strata
Mantle also writes:
target/strata/hello.observability.jsonl
That trace should contain artifact_loaded, process_spawned,
message_accepted, message_dequeued, program_output, and
process_stopped events.
Common Edits
If you remove emit "hello from Strata";, also change the effect list from
[emit] to []. Declaring an unused effect is rejected.
If you add a second message variant to MainMsg, add a step clause for that
variant or use one _ clause for variants without explicit clauses. Each
accepted message must resolve to exactly one clause.
Tutorial: Actors And Messages
This tutorial introduces multiple processes, message sends, and message-keyed state transitions.
Read Tutorial: Hello first if the basic Main process shape is unfamiliar.
Process Roles
examples/actor_ping.str has two processes:
Main, the entry process;Worker, a process spawned byMain.
Main starts the worker, sends Ping, and stops. Worker receives Ping,
emits output, transitions to Handled, and stops.
Worker Message Type
enum WorkerMsg {
Ping,
}
This says Worker accepts one message: Ping.
Main can send that message after spawning Worker:
proc Main mailbox bounded(1) {
type State = MainState;
type Msg = MainMsg;
authority spawn_worker: Cap<Spawn<Worker>>;
fn step(state: MainState, Start) -> ProcResult<MainState> ! [spawn, send] ~ [] @det {
let worker: ProcessRef<Worker> = spawn Worker;
send worker Ping;
return Stop(state);
}
}
worker is an immutable process reference for the spawned runtime instance. The
order matters in this source surface. Sending through a reference before it is
bound is
rejected.
Worker State Type
enum WorkerState {
Idle,
Handled,
}
Worker starts in Idle:
fn init() -> WorkerState ! [] ~ [] @det {
return Idle;
}
After handling Ping, it stops in Handled:
fn step(state: WorkerState, Ping) -> ProcResult<WorkerState> ! [emit] ~ [] @det {
emit "worker handled Ping";
return Stop(Handled);
}
The transition is a whole state replacement. Handled is the new state value.
Multiple Messages
examples/actor_sequence.str extends the pattern with two worker messages:
enum WorkerMsg {
First,
Second,
}
When a process accepts multiple messages, each message must resolve to one
step clause. Explicit patterns handle named variants, and _ handles the
remaining accepted variants:
fn step(state: WorkerState, First) -> ProcResult<WorkerState> ! [emit] ~ [] @det {
emit "worker handled First";
return Continue(SawFirst);
}
fn step(state: WorkerState, _) -> ProcResult<WorkerState> ! [emit] ~ [] @det {
emit "worker handled Second";
return Stop(Done);
}
Missing coverage, duplicate explicit patterns, duplicate wildcard patterns, and wildcards that cannot cover any remaining message are rejected.
Continue Versus Stop
Continue(SawFirst) means:
- replace the worker state with
SawFirst; - keep the worker running;
- allow later queued messages to be handled.
Stop(Done) means:
- replace the worker state with
Done; - terminate the worker normally.
The runtime trace records both steps with message IDs and state IDs.
Message Payloads
examples/actor_payloads.str adds a typed payload to a worker message:
record Job {
phase: JobPhase,
}
enum WorkerMsg {
Assign(Job),
}
The sender constructs one immutable payload value:
send worker Assign(Job { phase: Ready });
The receiver binds that value in a step parameter pattern:
fn step(state: WorkerState, Assign(job: Job)) -> ProcResult<WorkerState> ! [emit] ~ [] @det {
emit "worker assigned job";
return Stop(WorkerState { job: job });
}
The payload binding is immutable and local to the transition. Mantle executes
typed message IDs; payload values travel in runtime message envelopes
and appear in traces as payload metadata on the stable Assign message label.
Process references can also travel as typed payloads:
enum WorkerMsg {
Work(ProcessRef<Sink>),
}
send worker Work(sink);
The receiver can use the immutable payload binding as a send target:
fn step(state: WorkerState, Work(reply_to: ProcessRef<Sink>)) -> ProcResult<WorkerState> ! [send] ~ [] @det {
send reply_to Done;
return Stop(state);
}
Mantle routes that send by the transported runtime process ID and admitted target process ID. The source binding name is trace and diagnostic metadata, not the runtime dispatch key.
Multiple Instances
examples/actor_instances.str spawns two runtime instances of the same process
definition:
let first: ProcessRef<Worker> = spawn Worker;
let second: ProcessRef<Worker> = spawn Worker;
send first Ping;
send second Ping;
first and second are separate process references. Mantle assigns each
spawned worker a different runtime pid, and the trace records both messages
and both worker steps with the same process definition ID but different process
instance IDs.
Run The Actor Examples
just build
just run-example actor_ping
just run-example actor_sequence
just run-example actor_match
just run-example actor_instances
just run-example actor_payloads
just run-example actor_reply
For actor_sequence, the trace should show Worker dequeuing First, stepping
with Continue, then later dequeuing Second and stepping with Stop.
actor_match should show the same typed transition behavior from the
whole-body match msg authoring form.
Runtime Traces
Mantle writes runtime traces as line-delimited JSON. Each line is one runtime event.
By default, running:
just mantle-run target/strata/actor_sequence.mta
writes:
target/strata/actor_sequence.observability.jsonl
The trace is evidence that Mantle admitted and executed the artifact. It is not a substitute for running the source-to-runtime gate.
Trace Schema
Every event includes Mantle-owned trace schema metadata:
trace_schema, currentlymantle-runtime-observability;trace_schema_version, currently1.
These fields describe the JSONL observability contract only. They do not
identify a .mta artifact schema, carry executable bindings, select runtime
behavior, or move runtime semantics into Strata.
When mantle run receives an admitted explicit runtime composition binding with
--composition-binding, each trace event also carries deployment_id and
composition_id, and events associated with a mapped runtime process carry
component_instance_id. The current binding schema uses singleton
deployment_id=0; it is a stable correlation namespace, not a unique
deployment allocator. These fields are observability correlation only. They
are emitted from the validated mantle.runtime_composition_binding artifact,
not from source names and not from .component-composition.json. Deployment,
composition, and component-instance trace fields appear only when that binding is
supplied; absent binding input means Mantle fabricates no deployment,
composition, or component-instance IDs. Trace validation requires deployment and
composition identity to stay stable for the whole bound trace. Once a mapped
process emits component_instance_id, later events for the same process_id
must keep the same component-instance correlation; adding it after an earlier
uncorrelated event for that process or omitting it later fails closed.
Binding admission requires every runtime process to map to one checked
component instance, so process-scoped events in bound traces carry
component_instance_id. Non-process-scoped events carry only the bound
deployment_id/composition_id namespace. These fields are observability-only
and are never used for dispatch or source-name lookup.
Mantle validates traces with a read-only schema check when repository tests ask
for trace evidence. Validation checks event kind support, required fields,
string-vs-number field shapes, exact per-event field sets, payload and loop
context field grouping, strict unsigned integer syntax, numeric branch-path
segments that match Mantle’s emitted branch-path encoding, schema identity,
artifact_loaded first/no-repeat ordering, closed runtime enum value domains,
u32 artifact typed-ID width, u16 branch-path segment width, branch-path length,
artifact process-ID bounds from process_count, Mantle-contiguous spawned PID
sequencing, non-entry spawn parent evidence, runtime PID-to-process-ID
correlation, optional deployment/composition/component-instance correlation
field grouping plus whole-trace identity stability, bound component-instance
table bounds, one-to-one process/component-instance correlation,
supervisor-child slot causality for child starts and restart decisions,
restart-window numeric bounds and decision coupling, and process lifecycle
causality boundaries after terminal stop/fail events. It never
dispatches from trace data and never turns labels, source names, or debug
strings into runtime inputs.
The validator fails closed under explicit byte, event-count, and runtime-process limits. The default validation limits match Mantle’s default 8 MiB trace-byte budget, cap validation at 100,000 events, and cap spawned runtime process correlation at 10,000 process IDs; callers that expose trace validation outside the repository gates can pass caller-specific positive limits. Limits bound validation work only. They do not make trace JSON executable, trusted source semantics, or an artifact boundary.
Identity Fields
Trace events include both labels and numeric IDs.
Labels are for reading:
process;message;state;text.
IDs are for stable runtime identity:
pid, the runtime process instance ID;process_id, the admitted process definition ID;message_id, the admitted message case ID;state_id, the admitted typed state value ID;payload_type_id, the admitted artifact type ID when a payload is present;outcome_id, the admitted effect-outcome binding ID for a pre-state outcome action;output_id, the admitted output literal ID.
Do not treat labels as runtime dispatch keys. Runtime execution uses admitted typed IDs and typed state value identities.
When multiple runtime instances are spawned from one process definition, the
instances share process_id and label metadata but have different pid values.
examples/actor_instances.str exercises that shape.
Event Types
| Event | Meaning |
|---|---|
artifact_loaded | Mantle admits an artifact and loads its entry metadata. |
process_spawned | Mantle creates a runtime process instance. |
spawn_authority_checked | Mantle checked an admitted spawn-site authority and optional authority-policy decision before spawn acceptance. |
boundary_send_checked | Mantle checked a typed-port boundary send authority before mailbox acceptance. |
effect_outcome_bound | Mantle bound a typed effect outcome and recorded its source-visible result category. |
message_accepted | Mantle accepts a message into a process mailbox. |
message_dequeued | A process dequeued a message for handling. |
branch_selected | Mantle selected a typed runtime control-flow branch. |
loop_started | Mantle started a bounded runtime collection loop. |
loop_iteration | Mantle started one ordered runtime loop body iteration. |
loop_completed | Mantle completed a bounded runtime collection loop. |
process_stepped | A transition ran for a message. |
state_updated | A process state changes to another admitted state value. |
program_output | A process emitted declared output. |
process_stopped | A process stopped normally. |
process_failed | A process failed abnormally after a consumed message. |
supervisor_child_started | A supervisor started a declared lexical child. |
supervisor_restart_decision | A supervisor accepted, denied, or skipped a child restart. |
Artifact Loaded
Example shape:
{"event":"artifact_loaded","format":"mantle-target-artifact","schema_version":"6","source_language":"strata","module":"actor_sequence","entry_process_id":0,"entry_process":"Main","entry_message_id":0,"process_count":2,"trace_schema":"mantle-runtime-observability","trace_schema_version":1}
Important fields:
formatandschema_versionidentify the artifact schema;source_languageidentifies the frontend that produced the artifact;entry_process_idandentry_message_ididentify the runtime entrypoint.
Process Spawned
Example shape:
{"event":"process_spawned","pid":2,"process_id":1,"process":"Worker","state_id":0,"state":"Waiting","mailbox_bound":2,"spawned_by_pid":1,"trace_schema":"mantle-runtime-observability","trace_schema_version":1}
pid is the runtime process instance. process_id is the admitted process
definition. spawned_by_pid is present when another process spawned this one.
When present, the spawning PID must still be live; a stopped or failed process
cannot create a later runtime process.
Spawn Authority Checked
Example shape:
{"event":"spawn_authority_checked","pid":1,"process_id":0,"process":"Main","target_process_id":1,"spawn_site_id":0,"authority_id":0,"authority_policy_decision_id":0,"spawn_kind":"dynamic_local","authority_result":"accepted","trace_schema":"mantle-runtime-observability","trace_schema_version":1}
spawn_site_id and authority_id are admitted typed table IDs.
authority_policy_decision_id is the admitted typed policy row, or null for
runs without an authority/effect binding. spawn_kind currently records
dynamic_local. authority_result is accepted or denied; a denied spawn
outcome returns Err(Denied(Unit)) before the target process is accepted.
Boundary Send Checked
Example shape:
{"event":"boundary_send_checked","pid":1,"process_id":0,"process":"Main","port_id":0,"port":"WorkerPort","protocol_id":0,"protocol":"WorkerProtocol","authority_id":1,"authority_policy_decision_id":1,"target_process_id":1,"target_process":"Worker","message_id":0,"message":"Work","boundary_result":"accepted","trace_schema":"mantle-runtime-observability","trace_schema_version":1}
port_id, protocol_id, authority_id, target_process_id, and
message_id are admitted typed IDs. authority_policy_decision_id is the
admitted typed policy row, or null for runs without an authority/effect
binding. Labels are diagnostics and trace metadata only. A boundary_result of
accepted is emitted only for typed-port sends that proceed on the same runtime
path as mailbox acceptance; a policy denied result is emitted before mailbox
acceptance and before target-side message side effects. A typed send outcome
that returns Full, Stopped, Crashed, or MailboxClosed before mailbox
acceptance does not emit an accepted boundary event; a denied typed-port send
outcome fails closed before any effect_outcome_bound event. Invalid boundary shapes
still fail artifact or loaded-program admission before runtime dispatch.
Effect Outcome Bound
Example shapes:
{"event":"effect_outcome_bound","pid":1,"process_id":0,"process":"Main","outcome_id":0,"action":"spawn","target_process_id":1,"spawn_site_id":0,"outcome_result":"backend_unavailable","trace_schema":"mantle-runtime-observability","trace_schema_version":1}
{"event":"effect_outcome_bound","pid":1,"process_id":0,"process":"Main","outcome_id":1,"action":"send","target_process_id":1,"message_id":0,"outcome_result":"mailbox_closed","trace_schema":"mantle-runtime-observability","trace_schema_version":1}
outcome_id, target_process_id, spawn_site_id, message_id, and
port_id are admitted typed IDs. action is spawn or send.
outcome_result is a closed Mantle result category: ok, denied,
exhausted, or backend_unavailable for spawn outcomes, and ok, full,
stopped, crashed, or mailbox_closed for send outcomes. Authority-policy
denial of a typed-port send outcome is not represented as a send outcome result;
it fails closed before source-visible binding. Spawn outcome events require spawn_site_id and do not carry a
message or port ID. Send outcome events require message_id and may carry
port_id when the outcome action was tied to an admitted typed port.
This event records the Mantle-owned cause of the immutable source-visible
Result value. It does not add executable source bindings, does not dispatch
from labels, and does not turn trace JSON into a Strata or .mta input.
Message Accepted And Dequeued
Example shape:
{"event":"message_accepted","pid":2,"process_id":1,"process":"Worker","message_id":0,"message":"First","queue_depth":1,"sender_pid":1,"trace_schema":"mantle-runtime-observability","trace_schema_version":1}
{"event":"message_dequeued","pid":2,"process_id":1,"process":"Worker","message_id":0,"message":"First","queue_depth":0,"trace_schema":"mantle-runtime-observability","trace_schema_version":1}
message_accepted records mailbox admission. message_dequeued records the
message selected for the next transition. When sender_pid is present, it
must reference a live runtime process at the admission event; payload process
references remain typed evidence and are not executable dispatch handles.
Payload-bearing messages keep the stable admitted message label and add
payload_type_id plus payload fields:
{"event":"message_accepted","pid":2,"process_id":1,"process":"Worker","message_id":0,"message":"Assign","payload_type_id":2,"payload":"Job{phase:Ready}","queue_depth":1,"sender_pid":1,"trace_schema":"mantle-runtime-observability","trace_schema_version":1}
Runtime dispatch always uses the numeric message_id; labels are trace
metadata. For transitions without payload guards, payload values are recorded
only as trace data. Payload-sensitive dispatch, when present, uses the admitted
payload type ID and exact typed payload value identity, not source strings or
debug labels. Payload type identity is the numeric ID from the admitted artifact
type table, not a source type string.
For state-specific transitions produced by match state, runtime dispatch
uses the current admitted state_id together with the admitted message_id.
The trace continues to record the resulting state ID and label; labels are not
used to select the transition.
When a payload is a transported process reference, the trace also includes the admitted target process ID and runtime process ID:
{"event":"message_accepted","pid":2,"process_id":1,"process":"Worker","message_id":0,"message":"Work","payload_type_id":2,"payload":"type2#3","payload_process_id":2,"payload_pid":3,"queue_depth":1,"sender_pid":1,"trace_schema":"mantle-runtime-observability","trace_schema_version":1}
Branch Selected
Example shape:
{"event":"branch_selected","pid":2,"process_id":1,"process":"Worker","message_id":0,"message":"Branch","branch":"then","scope":"action","branch_path":[0],"condition_type_id":0,"condition":"True","trace_schema":"mantle-runtime-observability","trace_schema_version":1}
branch is then or else. scope is action for action-level branch
execution, including statement-level branches and branch action prefixes lowered
from final-position runtime if, and next_state for branch selection used to
resolve the transition result state. branch_path is a stable numeric path
assigned from the admitted runtime artifact structure; it is trace identity, not
source syntax. Its segments must be values the Mantle runtime encoder can emit:
action indexes, selected branch action indexes, selected loop-body action
indexes, or selected next-state branch sentinels. condition_type_id is the
admitted artifact type ID for the checked Bool condition, and condition is
trace metadata for the evaluated value. Runtime branch selection uses the
admitted typed condition, not source strings, function names, or debug labels.
For composed predicates, this metadata is still only the final evaluated Bool
value; the branch path remains the trace identity for the admitted artifact node.
Statement-level branches record this event even when the selected branch is an
admitted no-op branch with no actions.
When a selected statement-level branch contains the single admitted nested
runtime branch action, the nested branch records its own branch_selected event
with a child branch_path derived from the admitted outer branch path and
selected branch action index.
When a statement-level branch guards a bounded runtime loop, the outer
branch_selected event is recorded before any loop events. If the selected
branch is empty, no loop_started, loop_iteration, or loop_completed event
is emitted for that branch.
When a branch runs inside a bounded runtime loop body, branch_selected appears
after that iteration’s loop_iteration event and before the selected branch
effects. The event also includes loop_element_id and loop_index so the
selected branch is tied directly to the active immutable loop element binding.
The condition may be the evaluated active loop element value.
Runtime Loop Events
Example shape:
{"event":"loop_started","pid":2,"process_id":1,"process":"BatchWorker","message_id":0,"message":"Batch","element_id":0,"collection_type_id":0,"max_items":2,"item_count":2,"trace_schema":"mantle-runtime-observability","trace_schema_version":1}
{"event":"loop_iteration","pid":2,"process_id":1,"process":"BatchWorker","message_id":0,"message":"Batch","element_id":0,"index":0,"element_type_id":1,"element":"True","trace_schema":"mantle-runtime-observability","trace_schema_version":1}
{"event":"loop_completed","pid":2,"process_id":1,"process":"BatchWorker","message_id":0,"message":"Batch","element_id":0,"iteration_count":2,"trace_schema":"mantle-runtime-observability","trace_schema_version":1}
element_id, collection_type_id, and element_type_id are admitted artifact
IDs. index, item_count, and iteration_count record the bounded runtime
execution. element is trace metadata for the evaluated element value. Runtime
loop execution uses the admitted typed collection template and loop element ID,
not the source binding name.
Process Stepped
Example shape:
{"event":"process_stepped","pid":2,"process_id":1,"process":"Worker","message_id":0,"message":"First","result":"Continue","state_id":1,"state":"SawFirst","trace_schema":"mantle-runtime-observability","trace_schema_version":1}
result is Continue, Stop, or Panic. state_id and state are the
transition target state. Payload-bearing steps include the same
payload_type_id and payload fields as mailbox events.
State Updated
Example shape:
{"event":"state_updated","pid":2,"process_id":1,"process":"Worker","from_state_id":0,"from":"Waiting","to_state_id":1,"to":"SawFirst","trace_schema":"mantle-runtime-observability","trace_schema_version":1}
State updates are whole-value replacements. The trace records the previous and
new admitted state values, including payload-bearing enum state values such as
Working(Job{phase:Ready}).
Program Output
Example shape:
{"event":"program_output","pid":2,"process_id":1,"process":"Worker","stream":"stdout","output_id":0,"text":"worker handled First","trace_schema":"mantle-runtime-observability","trace_schema_version":1}
output_id identifies the admitted output literal. text is the readable
output.
Process Stopped
Example shape:
{"event":"process_stopped","pid":2,"process_id":1,"process":"Worker","reason":"normal","trace_schema":"mantle-runtime-observability","trace_schema_version":1}
The stop reason is normal for source-level Stop(...) termination,
supervisor_shutdown when a running supervisor stops its child tree during
orderly shutdown, and supervisor_failure when a failed supervisor or failed
supervised process forces its child tree to stop.
Process Failed
Example shape:
{"event":"process_failed","pid":2,"process_id":1,"process":"Worker","state_id":1,"state":"Failed","reason":"panic","trace_schema":"mantle-runtime-observability","trace_schema_version":1}
Panic(value) records a process_stepped event with result:"Panic", then a
process_failed event. The dequeued message is already consumed and is not
replayed. The failure reason is panic. Supervisor-scope failures also use
process_failed; current supervisor failure reasons are
supervisor_restart_intensity_exceeded,
supervisor_restart_capacity_exceeded, and supervisor_restart_throttled.
These process_failed.reason values are detailed failure-class metadata. For
supervisor restart causality, every process_failed terminal event has the
supervisor exit class panic.
Supervisor Lifecycle Events
Example shapes:
{"event":"supervisor_child_started","supervisor_pid":1,"supervisor_process_id":0,"supervisor_process":"Main","supervisor_id":0,"child_id":0,"child":"worker","child_pid":2,"child_process_id":1,"child_process":"Worker","spawn_site_id":0,"spawn_kind":"lexical_supervisor_child","trace_schema":"mantle-runtime-observability","trace_schema_version":1}
{"event":"supervisor_restart_decision","supervisor_pid":1,"supervisor_process_id":0,"supervisor_process":"Main","supervisor_id":0,"child_id":0,"child":"worker","child_pid":2,"child_process_id":1,"child_process":"Worker","reason":"panic","decision":"restarted","restart_time_ms":0,"restart_window_count":1,"restart_window_limit":3,"restart_window_ms":1000,"new_child_pid":3,"trace_schema":"mantle-runtime-observability","trace_schema_version":1}
supervisor_id, child_id, and spawn_site_id are admitted typed IDs.
spawn_kind records the lexical supervisor-child classification.
child, supervisor_process, and child_process are trace labels only.
restart_time_ms is the sampled monotonic runtime time for restart-window
decisions. restarted and denied decisions carry numeric restart time;
not_restarted records null because no restart-window sample is needed. The
restart window fields record the observed count and configured limit/window
after stale entries are pruned. The configured limit and window duration must be
greater than zero, and the observed count must not exceed the configured limit.
restarted records a nonzero observed count; not_restarted records zero
because no restart was attempted. Only a restarted decision carries a numeric
new_child_pid; not_restarted and denied decisions set new_child_pid to
null. Supervisor lifecycle events must be caused by a live supervisor PID.
Child-start events require a live child PID that was spawned by the supervisor.
Restart decisions require prior supervisor_child_started evidence for the
same typed supervisor/child slot, require child_pid to be that slot’s current
child, require a prior process_stopped/process_failed event whose terminal
exit class matches the restart decision reason (process_stopped normal
matches normal; any process_failed matches panic), and for restarted
decisions require a distinct live new_child_pid spawned by the supervisor. A
denied restart fails the supervisor scope when intensity, capacity, or the
default same-monotonic-tick throttle prevents a restart.
Runtime supervision uses admitted supervisor tables and typed child IDs, not
labels.
Diagnostics
Strata diagnostics are intended to reject invalid source close to the layer that can explain it. Parser errors describe source shape. Checker errors describe semantic rules. Runtime errors describe validated execution failures.
Reading A Diagnostic
Run:
just strata-check examples/hello.str
If checking fails, fix the first reported error first. Later errors may be a result of the first invalid shape.
Common Artifact And Runtime Errors
| Diagnostic Contains | Likely Cause | Fix |
|---|---|---|
unsupported artifact schema version | The .mta does not use the single schema version admitted by this Mantle build. | Rebuild the source with the current strata build command. |
target requirements source_language ... does not match artifact source_language | A malformed artifact declares conflicting source-language metadata. | Rebuild the artifact; do not edit .mta target requirement fields by hand. |
artifact field source_language must be an identifier | A malformed artifact has invalid source-language metadata. | Rebuild the artifact; Mantle treats source language as opaque artifact metadata and does not dispatch on source names. |
target requirements do not declare required runtime feature ... | The artifact uses a typed runtime construct that is missing from its target-requirement block. | Rebuild the artifact from checked source; Mantle derives required features from decoded .mta tables and fails closed on underdeclared requirements. |
target runtime feature ... is not supported by this Mantle runtime | The artifact requires a runtime feature outside the current Mantle declaration, such as remote spawn/send or distributed transport. | Use only currently implemented local runtime features or run on a Mantle runtime that declares the required feature. |
target requirement runtime features must be sorted / duplicate target requirement runtime feature | A malformed artifact target-requirement block is not canonical. | Rebuild the artifact from checked source; Mantle fails closed on malformed requirement fields. |
missing --max-runtime-processes value / invalid --max-runtime-processes value / --max-runtime-processes must be greater than zero | mantle run received a missing, non-integer, overflowed, or zero runtime process limit. | Pass a positive integer, for example mantle run target/strata/effect_outcome_spawn_exhausted.mta --max-runtime-processes 1. |
local spawn backend unavailable | A local process-creation path ran while the local spawn backend is disabled for this Mantle run, such as bare spawn or lexical supervisor child startup. | Use outcome-form let result: Result<ProcessRef<T>,SpawnError<Unit>> = spawn T; when source should handle Err(BackendUnavailable(Unit)); supervisor child startup and bare spawn fail closed, or run without --disable-local-spawn-backend. |
field "schema_version_major" must be schema version ... / field "schema_version_minor" must be schema version ... | A .component-composition.json artifact uses a schema version this Strata build does not admit. | Rebuild the artifact with just strata-composition-build <path.str> and avoid hand-editing schema fields. |
component composition artifact admission rejected | strata composition admit validated a well-formed artifact whose global admission result is rejected. | Treat the artifact as failed checked-subset validation evidence; rebuild from checked source or inspect the rejection fields before retrying the gate. |
admitted component composition artifact has unsatisfied_imports / both bound and unsatisfied | A composition artifact was edited or generated inconsistently: it claims global admission while carrying unsatisfied imports, or it marks one declared import as both bound and unsatisfied. | Rebuild from checked source, or emit a rejected artifact with each unbound declared import listed once as unsatisfied evidence. |
omits binding or unsatisfied-import evidence | A composition artifact declares a component import but does not include either one binding or one unsatisfied-import entry for it. | Rebuild from checked source; admitted artifacts must bind every declared import exactly once, while rejected artifacts must explain unbound imports explicitly. |
more than once | A composition artifact duplicates binding or unsatisfied-import evidence for the same importer instance and imported port. | Keep exactly one binding for each admitted declared import, or exactly one unsatisfied-import entry for each rejected unbound import. |
references unknown component instance id / must reference typed id | A composition artifact binding, authority descriptor, or authority-flow edge uses a missing, non-canonical, or internally inconsistent typed reference. | Rebuild from checked source; source names are metadata only and cannot replace typed component-instance, port-binding, port, protocol, or authority IDs. |
runtime composition binding requires an admitted checked composition artifact | strata composition bind-runtime was asked to bind rejected or non-admitted checked composition evidence to a .mta. | Rebuild from checked source, run strata composition admit, and bind only globally admitted checked composition evidence. |
runtime composition binding source fingerprint does not match artifact source hash / mantle_artifact_source_hash_fnv1a64 | A runtime binding or checked composition artifact does not match the .mta it is being bound to or run with. | Rebuild the .mta, .component-composition.json, and .deployment-composition.json from the same checked source; do not hand-edit identity fields. |
field "schema_id" must be "mantle.runtime_composition_binding" / composition_schema_id / source_fingerprint_algorithm / artifact_kind / deployment_id / admission_result | Mantle received a malformed or wrong-kind deployment-composition binding. | Supply the output from strata composition bind-runtime; .component-composition.json is not Mantle runtime input. |
component_instance_id ... is duplicated / process_id ... is duplicated / port_binding_id ... does not match runtime artifact | A runtime binding tries to forge, duplicate, or retarget typed composition-to-process correlation. | Rebuild the binding from the admitted checked composition artifact and matching .mta; Mantle validates typed IDs before execution. |
runtime trace component_instance_id requires a process-scoped event | Trace evidence attached component-instance correlation to an event without one primary process_id. | Regenerate the trace from Mantle; deployment and composition IDs can appear on artifact-level events, but component-instance IDs are only process-scoped observability fields. |
runtime trace deployment_id must be 0 | Trace evidence uses a deployment namespace outside the current singleton runtime binding schema. | Regenerate the trace from Mantle; v1 runtime composition bindings use deployment_id=0 only. |
component_instance_id ... is outside runtime trace composition component table / component_instance_id ... is already correlated with process_id | Bound trace evidence claims an impossible component-instance/process correlation for the loaded process table. | Regenerate the trace from Mantle with the matching .deployment-composition.json; each bound process maps to exactly one checked component instance and each component instance maps to one process. |
runtime trace composition context changed after artifact_loaded / component_instance_id changed for process_id / omitted established component_instance_id | Trace evidence changed or dropped composition correlation after Mantle established an admitted runtime binding context. | Regenerate the trace from Mantle; deployment/composition IDs are stable for the whole bound run and component-instance IDs are stable for each mapped process. |
missing --composition-binding value / duplicate --composition-binding | mantle run received an incomplete or conflicting composition binding flag. | Pass at most one explicit binding path, for example mantle run target/strata/component_composition_main.mta --composition-binding target/strata/component_composition_main.deployment-composition.json. |
authority/effect artifact admission rejected / field "policy_inputs" is not implemented / references unknown message id / references unknown state id / references unknown protocol id / references unknown port id / references unknown component id / references unknown supervisor id / does not match spawn_site_id / component_import_port_count ... does not match declared count | strata authority-effects admit validated a malformed or unsupported checked authority/effect fact artifact. | Rebuild with strata authority-effects build <path.str>; unsupported future policy sections must stay empty until admitted, transition IDs must reference declared per-process state/message facts, lexical supervisor-child spawn sites must match checked supervisor-child proof facts, authority descriptors must reference the artifact’s declared protocol/port/component table counts, and component import-authority lists must match their declared import_port_count. |
authority policy artifact / decision count ... does not match checked authority count / descriptor does not match checked authority/effect facts / unsupported authority policy decision / unknown field "process" | strata authority-effects policy admit validated a malformed, stale, spoofed, or non-closed typed policy decision artifact. | Rebuild with strata authority-effects policy build <authority-effect.json>; every checked process authority must have exactly one typed decision, descriptors must match, and source labels/debug names cannot authorize behavior. |
runtime authority/effect binding source fingerprint does not match artifact source hash / mantle_artifact_source_hash_fnv1a64 | A checked authority/effect artifact, typed authority policy artifact, or runtime binding does not match the .mta it is being bound to or run with. | Rebuild the .mta, .authority-effect.json, .authority-policy.json, and .authority-effect-binding.json from the same checked source; do not hand-edit identity fields. |
descriptor does not match runtime artifact / spawn_site_id ... does not match runtime artifact / transition_id ... effects do not match runtime artifact | A runtime authority/effect binding tries to forge, widen, strip, or retarget typed authority, spawn-site, or exact-effect facts. | Rebuild the binding from the admitted .authority-effect.json and matching .mta; Mantle validates typed IDs and descriptors before execution. |
field "schema_id" must be "mantle.runtime_authority_effect_binding" / authority_effect_schema_id / authority_policy_schema_id / policy_decisions | Mantle received a malformed, wrong-kind, unsupported-version, or unsupported-policy authority/effect binding. | Supply the output from strata authority-effects bind-runtime; raw .authority-effect.json and .authority-policy.json files are not Mantle runtime input. |
missing --authority-effect-binding value / duplicate --authority-effect-binding | mantle run received an incomplete or conflicting authority/effect binding flag. | Pass at most one explicit binding path, for example mantle run target/strata/effect_outcome_spawn_denied.mta --authority-effect-binding target/strata/effect_outcome_spawn_denied.authority-effect-binding.json. |
--deny-spawn-authority cannot be combined with --authority-effect-binding | mantle run was given both a direct runtime spawn-denial flag and an authority/effect binding policy. | Encode the policy in .authority-policy.json with strata authority-effects policy build --deny-spawn-authority, bind it with strata authority-effects bind-runtime, then pass only --authority-effect-binding to Mantle. |
boundary send authority denied | A typed authority policy denied the process-local port_connect authority used for a boundary send. | Admit a policy with an admit decision for that typed process/authority ID, or change the source authority/effect facts and rebuild all artifacts. |
Common Source Errors
| Diagnostic Contains | Likely Cause | Fix |
|---|---|---|
expected protocol, port, component, composition, record, enum, function, or proc declaration | A top-level item is not accepted. | Use protocol, port, component, composition, record, enum, fn, or proc after module and any imports. |
imports must appear before top-level declarations | An import appears after a record, enum, function, or process declaration. | Move all import module_name; declarations immediately after module name;. |
imports require checking from a root source path | A source string with imports was passed to the single-source checker. | Run strata check <root.str> or use the source-program API so imports can be resolved. |
source ... could not be resolved | An imported sibling .str file is missing or cannot be read. | Add the imported module_name.str file next to the importing source unit or fix the import name. |
source path ... is not a regular file | A root source or imported source path is a directory, symlink, FIFO, or other non-regular file. | Use a regular .str file for each source unit. |
resolved outside source directory | An import resolved through a symlink or filesystem indirection outside the importing source unit’s directory. | Keep imported source units as regular sibling .str files. |
source loading is unsupported on this target | Source loading was requested on a target without secure source file identity support. | Use a Unix-family target or Windows for source loading. |
artifact I/O requires secure file identity support | Mantle artifact read/write was requested on a target without secure path identity support. | Use a Unix-family target or Windows for artifact IO. |
import cycle ... is not supported | Source units import each other cyclically. | Break the cycle; this surface only accepts an acyclic dependency graph. |
duplicate module identity ... | Two reachable source units declare the same module name. | Give each reachable source unit a unique module identity. |
ambiguous imported ... name ... | Reachable source units declare the same unqualified type, function, process, protocol, port, component, or cross-unit callable name. | Rename one declaration; aliases, re-exports, and qualified references are not part of this surface. |
references ... without importing ... | A source unit uses a type, enum variant, pure function, process, protocol, port, component, or composition edge from another reachable source unit without directly importing that unit. | Add an explicit import module_name; to the source unit that uses the declaration. |
composition ... instance ... component ... import port ... is not bound | A component instance imports a port that no composition binding satisfies. | Add one bind importer imports Port -> exporter exports Port; edge for that instance import. |
composition ... binds instance ... imported port ... more than once | A composition tries to bind the same imported port on one instance more than once. | Keep exactly one binding per imported (instance, port) pair. |
composition ... cannot bind imported port ... to exported port ... because their protocols differ | A composition edge connects ports that do not share one protocol. | Bind the import to a component export whose port uses the same protocol. |
composition ... cannot bind imported port ... to exported port ... because their port authorities differ | A composition edge would connect distinct Cap<PortConnect<...>> authority descriptors. | Bind the import to the exact exported port authority it requires; source composition does not widen port authority descriptors. |
composition ... instance ... component ... does not import port ... | A binding names an imported port that is not declared by the importing component. | Add the port to that component’s imports list or bind the correct imported port. |
composition ... instance ... component ... does not export port ... | A binding names an exported port that is not exported by the exporting component. | Bind to the component’s declared export port. |
entry process Main is not declared | The program has no Main process. | Add proc Main .... |
root source unit ... must declare entry process Main | The loaded root source unit imports a module that may contain Main, but the root itself does not declare the entry process. | Declare proc Main ... in the root source file passed to strata check or strata build. |
type name ... is reserved | A source record or enum tries to use a built-in transition, capability, collection, primitive such as String or Bytes, outcome, or scalar type name. | Rename the source type. |
uses reserved prefix __strata_checked_ | A source type name collides with internal checked type metadata. | Rename the source type without the reserved prefix. |
checked type_count exceeds Mantle artifact limit | The checked program needs more distinct artifact types than the Mantle artifact limit allows. | Reduce the number of distinct state, message, payload, and process-reference types. |
process ... must declare type State | A process is missing its state alias. | Add type State = StateType;. |
process ... must declare type Msg | A process is missing its message alias. | Add type Msg = MessageEnum;. |
init must declare no parameters | init has parameters. | Use fn init() -> StateType .... |
init body must not perform statements | init uses emit, spawn, or send. | Return only the initial state. |
match scrutinee ... fieldless enum variant | An init whole-body match or init return match tries to match a non-constructor name or a payload-bearing constructor. | Match one fieldless enum constructor in init. |
init return match ... | An init return match is non-exhaustive, overlaps an earlier arm, has an unreachable wildcard, or nests another return match in an arm. | Cover each variant once or use one reachable _, and return one whole state value from each arm. |
init return match arm cannot use payload binding ... in returned state | An init return match arm tries to materialize an arm payload binding into the initial state. | Return a whole state value that does not depend on match-arm payload bindings. |
step must declare state parameter and message pattern | step has the wrong parameter count. | Use state: StateType, MessageConstructor or state: StateType, _. |
step second parameter must be a message constructor pattern or wildcard pattern | The second step parameter is a typed binding instead of a message pattern. | Replace msg: MsgType with a message constructor or _, or use a whole-body match msg. |
step returns ..., expected ProcResult<...> | step return type is wrong. | Return ProcResult<StateType>. |
step return match scrutinee ... must be a concrete enum source value binding | A step return match tries to match state, a non-enum value, or a value that is not an immutable source binding. | Match a transition-local enum payload or state-payload binding whose concrete value is already proven by the checker, or use whole-body match msg / match state. |
step return match scrutinee ... requires a discovered concrete message payload case | A step return match tries to match a message payload binding from an unguarded transition. | Use a payload-sensitive pattern that lowers to exact discovered payload guards, or move the dispatch to whole-body match msg. |
step return match arm cannot bind process reference ... | A step return match arm tries to acquire arm-local authority with spawn. | Bind process references before return match; selected arm prefixes may use emit, in-scope direct send, statement-level runtime if, and bounded runtime for actions. |
step return match arm cannot perform final-position runtime if | A step return match arm tries to use terminal runtime branching. | Move runtime branching outside the return-match arm, or split the behavior into explicit step clauses / whole-body dispatch. |
step return match arm nested return match is not supported | A step return match arm tries to nest another return match. | Keep the selected arm as typed action statements followed by one terminal Continue(...), Stop(...), or Panic(...). |
step return match arm must return Stop..., Continue..., or Panic... | A step return match arm returns a bare state value or another unsupported result form. | Return one whole state value inside Continue(...), Stop(...), or Panic(...) from every arm. |
step body must return Stop..., Continue..., or Panic... | A step returns a bare state value or an unsupported result form. | Return one whole state value inside Continue(...), Stop(...), or Panic(...). |
step may-behaviors must be empty | The ~ [...] list is not empty. | Use ~ []. |
step must be deterministic | step uses @nondet. | Use @det. |
uses effect ... but does not declare it | The body performs emit, spawn, or send without matching effect usage. | Add the exact used effect to ! [...] or remove the statement. |
declares effect ... but does not use it | The effect list is wider than the body. | Remove the unused effect. |
declares duplicate effect | The effect list repeats one effect. | Keep each effect at most once. |
send outcome binding must have type Result<Unit,SendError<...>> | A local send outcome annotation does not preserve the target process message type. | Annotate the binding as Result<Unit,SendError<TargetMsg>>. |
spawn outcome binding must have type Result<ProcessRef<...>,SpawnError<Unit>> | A local spawn outcome annotation does not return the committed process reference on success. | Annotate the binding as Result<ProcessRef<TargetProcess>,SpawnError<Unit>>. |
effect outcome binding ... conflicts with ... | An outcome binding reuses a step state parameter, process reference, process declaration, or source value name. | Give the immutable outcome a fresh step-local name. |
effect outcome binding ... is used before it is bound | A statement references an outcome before the outcome let statement. | Move the outcome binding before the first use. |
effect outcome binding ... must appear before ordinary effect statements | A later outcome binding would be executed before an earlier non-prefix effect at runtime. | Bind local effect outcomes before ordinary emit, send, if, or for statements. A top-level process-reference spawn may stay in the pre-state prefix so later outcome sends can target it. |
effect outcome binding ... cannot be used as a next-state value because type ... has non-finite payload values | A next state tries to store an outcome whose possible payload values cannot be finitely admitted into the process state table. | Store only finite outcome value shapes in state, or handle the effect without placing that outcome in state. |
effect outcome binding ... would expand next-state candidates to ... | Multiple finite outcome bindings would exceed the admitted process state-value limit. | Store fewer independent outcome fields, branch before storing, or keep the outcome outside process state. |
effect outcome id ... appears after ordinary effects | A Mantle artifact tries to bind an effect outcome after an ordinary effect boundary. | Emit outcome actions in the pre-state prefix before ordinary effects. |
process reference outcome must remain step-local | A Mantle artifact or loaded runtime template tries to use a process-reference-carrying outcome as ordinary state or payload data. | Branch on a typed built-in outcome variant, or keep the process reference as step-local authority. |
for loop collection must be an identifier binding | A runtime for loop tries to iterate a literal or computed value. | Iterate an immutable runtime collection binding such as a typed message payload binding. |
for loop collection ... must have type List<T,N> | A runtime for loop source is not a typed list collection. | Use a binding whose type is List<Element,N>. |
for loop collection must be a runtime list binding | A for loop source is static source data rather than runtime data. | Pass the list as a typed runtime payload or state-derived runtime binding. |
for loop element binding ... cannot have process reference type | A runtime for loop tries to iterate process-reference values as ordinary loop data. | Keep ProcessRef<T> as direct message authority and do not place it in loop collections. |
for loop record pattern ... cannot match ... | A runtime loop record pattern names the wrong record type or tries to destructure a non-record element. | Match the actual record element type, or use a plain immutable loop element binding. |
record pattern ... must bind at least one field | A source record pattern, including a loop-element record pattern, has no fields. | Bind at least one immutable field or use a plain immutable binding. |
for loop record pattern ... binds field ... more than once | A runtime loop record pattern repeats one record field. | Bind each projected field at most once. |
for loop record pattern ... has no field ... | A runtime loop record pattern names a field outside the loop element record. | Bind a declared field from the record element. |
loop element binding ... is declared more than once | A loop-element record pattern maps multiple fields to the same local binding. | Use one distinct immutable binding name per projected field. |
loop element binding ... cannot have process reference type | A loop-element record pattern projects a ProcessRef<T> field as ordinary data. | Keep process references out of records, lists, maps, state, and projected loop data. |
loop element binding ... conflicts ... | A loop element or projected field binding reuses a reserved name, source binding, process reference, process declaration, type, or constructor. | Choose a distinct immutable binding name for the loop body. |
for loop body cannot bind process reference | A loop body tries to create new authority with spawn. | Bind process references before the loop and keep the loop body to checked linear effects. |
nested for loops are not supported | A loop body contains another loop. | Flatten the runtime payload shape or split the behavior into separate steps. |
assignment statements are not supported | Source code uses assignment-style mutation. | Bind immutable values through declarations or return a whole replacement state value. |
statement-level if branches must not return | An effect-only runtime branch tries to terminate the step. | Put the final return after the statement-level branch, or use final-position runtime if when each branch must return. |
statement-level if branches cannot bind local values or process references | A runtime branch tries to introduce branch-local source computation or authority. | Use source-local bindings only in pure source functions, bind process references before the branch, and keep runtime branches to checked branch effects. |
statement-level if action nesting exceeds maximum depth | Direct statement-level runtime branch nesting goes beyond the single supported nested layer. | Keep direct branch actions to an outer branch plus one nested branch. |
statement-level if branches cannot both be empty | A statement-level runtime branch has no actions on either side. | Put an action, such as emit, send, or a bounded for loop, in one branch. Omitted else and explicit else {} are allowed only when the other branch has work. |
runtime if branch cannot bind process references | A checked runtime branch tries to introduce branch-local authority. | Bind process references before the branch and keep branch bodies to declared effects. |
runtime if action nesting exceeds maximum depth | A checked runtime branch action exceeds the direct nesting bound. | Keep runtime branch actions to an outer branch plus one nested branch. |
next_state runtime if nesting exceeds maximum depth | Source, checked IR, artifact admission, or loaded-runtime admission sees a third terminal runtime branch. | Keep terminal next-state runtime branching to an outer final-position branch plus one direct nested final-position branch. |
runtime if action branches cannot both be empty | A decoded or constructed artifact tries to validate a runtime branch action with no actions in either branch. | Keep no-op branches explicit in the typed artifact, but ensure the sibling branch has at least one action. |
Source Function Errors
| Diagnostic Contains | Likely Cause | Fix |
|---|---|---|
function ... conflicts with a declared type or value constructor | A source function name collides with a type or enum constructor. | Choose a distinct function name. |
function ... must declare exactly one parameter | A normal source function uses an arity outside the current buildable call form. | Use one typed binding parameter or one pattern parameter clause. |
function ... must use a declared record, enum, scalar, primitive, list, or map type without process-reference authority | A source function parameter or return type names something outside the source value type set, including an enum that carries ProcessRef<T> authority. | Use String, Bytes, a declared record or enum type, a scalar integer type, or List<T,N> / Map<K,V,N> over source value types that do not contain process references. |
function ... must not declare effects / function ... must not perform statements | A normal source function tries to perform runtime behavior. | Keep normal functions pure; perform emit, spawn, and send only in step. |
function ... may-behaviors must be empty / function ... must be deterministic | A normal source function is not in the deterministic buildable subset. | Use ~ [] @det. |
source function parameter ... conflicts ... | A source function binding parameter reuses a process-reference binding, type, constructor, process, or source function name. | Choose a distinct immutable parameter name. |
source-local binding ... conflicts ... | A pure source function binding reuses a parameter, pattern binding, prior local binding, process-reference binding, type, constructor, process, or source function name. | Choose a distinct immutable binding name. |
function ... source-local binding ... must use a declared record, enum, scalar, primitive, list, or map type without process-reference authority | A source-local binding annotation is not a source value type, such as ProcessRef<T>, an enum that carries ProcessRef<T>, or an unknown type. | Bind only String, Bytes, records, enums, scalar integer types, List<T,N>, or Map<K,V,N> over source value types that do not contain process references. |
source-local binding ... value must produce ... | A source-local binding right-hand side is unknown, impure, or does not match the annotated type. | Use a pure source value expression with the exact annotated source value type. |
function ... is not declared | A value expression calls an unknown function. | Declare a module function or process-local function with that name. |
function ... returns ..., expected ... | The function return type does not match the value position where it is called. | Call a function returning the expected type or change the annotation. |
source function call cycle ... is not supported | Source function calls are recursive, but pure functions are expanded before lowering and have no recursion model. | Remove the cycle; pass whole values through non-recursive functions. |
... conflicts with core Bool type / ... conflicts with core Bool value constructor | A user declaration tries to define Bool, True, or False, which are Strata core built-ins. | Rename the user declaration; Bool, True, and False are always available and cannot be redeclared. |
payload-bearing variant ... collides with reserved primitive value label | An enum constructor with a payload is named String or Bytes, which would collide with canonical Mantle primitive data labels such as String(7265616479). | Rename the payload-bearing enum constructor so primitive artifact values remain unambiguous typed data. |
if condition must have type Bool | A source conditional condition resolves to a non-Bool source value. | Return or pass the core True or False value, or produce Bool through equality, scalar ordering, or Boolean predicate composition. |
if then branch must produce ... / if else branch must produce ... | A conditional branch does not match the expected source value type. | Return the same source value type from both branches. |
if branches are pure value expressions and must not perform statements | A conditional branch contains emit, let, send, or return. | Keep branch bodies to one source value expression and move effects to supported step forms. |
source function ... return-if ... branch must not perform statements | A braced source-function return branch tries to perform emit, send, spawn, a runtime loop, or another runtime statement before returning. | Keep source functions pure; use only immutable source-local bindings before the terminal pure return. |
equality operands must have the same type | A == or != expression compares values of different checked types. | Compare two Bool values, two String values, two Bytes values, two scalar values of the same integer type, or two fieldless values from the same enum. |
equality operands must be Bool, String, Bytes, scalar values, or fieldless enum values | A == or != operand is outside the equality surface. | Use Bool, matching String or Bytes values, matching scalar integer values, or a payload-free enum value; records, lists, maps, process references, and payload-bearing enum values are not equality operands. |
String literal exceeds maximum primitive data length / Bytes literal exceeds maximum primitive data length | A primitive literal would exceed the bounded source/artifact data budget for one immutable value. | Keep literal data within the primitive data limit or model larger data outside the current source contract surface. |
unsupported string escape / unicode string escape ... | A string literal uses a non-canonical or malformed escape sequence. | Use \", \\, \n, \r, \t, or \u{HEX} with a valid Unicode scalar value. |
unsupported bytes escape / bytes escape ... must use two hex digits / bytes literal raw data must be printable ASCII or escaped | A bytes literal uses non-byte Unicode data or a malformed byte escape. | Use printable ASCII bytes or canonical escapes such as \x00 through \xff. |
numeric value literals require an explicit scalar suffix / unsupported scalar literal suffix ... | A numeric value expression is unsuffixed or uses a suffix outside the admitted scalar set. | Use suffixes such as _u32 or _i64; mailbox bounds and collection capacities remain unsuffixed syntax. |
scalar literal ... is outside ... range | A scalar literal cannot fit in its declared fixed-width integer type. | Use a value in range or choose a wider explicit scalar type. |
scalar operands must have the same type / scalar literal ... has type ..., expected ... | A scalar operator mixes widths or signedness. | Use matching scalar types; explicit casts and inference are not part of the buildable surface. |
scalar arithmetic result ... is outside ... range | Concrete scalar arithmetic overflows or underflows the target type. | Keep the computation in range or choose a wider explicit scalar type. |
scalar division by zero / scalar modulo by zero | A concrete or runtime scalar operation divides or takes modulo by zero. | Guard the zero case before the operation or use a non-zero divisor. |
process-reference equality is not supported | A == or != expression compares process-reference authority. | Keep process references as explicit authority handles; do not branch on reference identity. |
list and map equality are not supported | A == or != expression tries to compare a collection type. | Compare a core Bool or payload-free enum predicate instead. |
record equality is not supported | A == or != expression tries to compare a record value. | Compare a core Bool or payload-free enum predicate instead. |
equality type ... must not declare payload-bearing enum variants | A == or != expression targets payload data instead of a safe built-in outcome variant pattern. | Use a payload-free enum, compare a safe pattern such as Ok(Unit) or Err(Exhausted(Unit)), or use an explicit function/match shape rather than payload equality. |
requires one operand to be a safe built-in variant pattern | A == or != expression compares two runtime-dependent Option, Result, SendError, or SpawnError values directly. | Compare the runtime value against a safe variant pattern such as Ok(Unit), None, or Err(Full(Work)); do not compare two payload-carrying runtime values structurally. |
boolean ! operand must produce Bool | A ! predicate operand resolves to a non-Bool value. | Apply ! only to Bool, typed equality, scalar ordering, or nested Boolean predicate expressions. |
left operand of && must produce Bool / `right operand of | must produce Bool` | |
boolean predicate expression produces Bool, expected ... | A composed predicate is used where a non-Bool value is required. | Use predicate composition only in Bool positions such as conditions or Bool fields. |
parenthesized value operand must produce ... | A parenthesized expression does not match the expected source value type. | Keep the grouping expression typed to the surrounding value position. |
function ... declares duplicate pattern for variant ... | More than one source function clause handles the same constructor. | Keep one clause per constructor. |
function ... must handle variant ... | A source function signature pattern group or match body is non-exhaustive. | Add the missing constructor clause/arm or one _ fallback. |
function ... wildcard pattern is unreachable | Explicit source function clauses already cover every variant. | Remove the wildcard clause or remove the explicit clauses it should cover. |
pattern ... overlaps an earlier pattern for the same typed payload shape | A function match or return-match repeats a constructor with an identical, unguarded, or not-provably-disjoint nested predicate. | Keep one unguarded constructor arm, or split the constructor only by disjoint nested enum predicates. |
match has no matching pattern for ... / return match has no matching pattern for ... | A function call reached a concrete nested payload shape not covered by the function match arms. | Add a disjoint nested predicate arm for that shape or add one _ fallback where fallback behavior is intended. |
record pattern ... has no field ... | A source function record pattern names a field outside the matched record. | Bind a declared field from the record. |
record pattern ... binds field ... more than once | A source function record pattern repeats one field. | Bind each record field at most once. |
record pattern binding ... is declared more than once | A source function record pattern binds two fields to the same local name. | Use one distinct immutable binding name per field. |
record pattern binding ... conflicts ... | A source function record pattern binding reuses a reserved, process, process-reference binding, source function, type, or constructor name. | Choose a distinct immutable binding name. |
requires a concrete record value argument | A record destructuring function or function match is trying to destructure a value that is not concrete after source function expansion. | Pass a concrete record value into the function or match a source binding that resolves to one. |
requires a concrete list value argument / requires a concrete map value argument | A collection destructuring function or function match is trying to destructure a value that is not concrete after source function expansion. | Pass a concrete List[...] or Map[...] value into the function, or match a source binding that resolves to one. |
map pattern duplicates key ... / map value ... duplicates key ... | A map pattern or map value repeats the same canonical key. | Keep each map key once. |
declares overlapping collection patterns ... | Exact and rest/subset collection patterns could match the same concrete value. | Make list rest or map subset patterns disjoint, use a single exact pattern, or add a wildcard fallback for the non-overlapping remainder. |
list rest pattern must declare at least one prefix element | A list rest pattern used List[..tail], which would bind the original list without proving any element is present. | List at least one fixed-position element before ..tail. |
list rest binding cannot be a wildcard | A list rest pattern used .._, which would look like a binding while intentionally discarding the suffix. | Bind the suffix with ..tail, or use an exact list pattern when no suffix value is needed. |
subset map pattern must declare at least one key | A subset map pattern used Map[..], which is equivalent to a map-specific catchall and binds nothing. | Use _ for catchall behavior or list at least one static key before ... |
map rest pattern must declare at least one key | A rest-binding map pattern used Map[..rest], which would bind the original map without proving any key is present. | List at least one static key before ..rest. |
map rest binding cannot be a wildcard | A map rest pattern used .._, which would look like a binding while intentionally discarding the remainder. | Use .. to ignore the remainder or ..rest to bind it. |
map payload pattern keys must be static source values / map pattern keys must be static source values | A map payload or function pattern tries to derive a key from a runtime binding such as current state or a payload value. | Use static source keys; model dynamic-key dictionaries separately once key-set IFC semantics exist. |
map value type ... keys must be static source values | A runtime-bound map value tries to derive a map key from a payload or state binding. | Use static source keys; model dynamic-key dictionaries separately once key-set IFC semantics exist. |
collection pattern binding ... conflicts ... | A list or map function pattern binding reuses an existing source value binding, process-reference binding, source function, or declared value name. | Choose a distinct immutable binding name. |
list payload pattern must bind at least one value / map payload pattern must bind at least one value | A constructor payload pattern tries to use a collection shape test without binding any projected value. | Bind at least one immutable element/value, or use the message constructor without payload destructuring when the payload can be ignored. |
match record pattern ... must declare exactly one arm | A source function whole-body match over a record tries to use enum-style multi-arm dispatch. | Use one record destructuring arm for the matched record type. |
match over record ... cannot use a wildcard pattern | A source function whole-body match over a record tries to use _. | Use the record destructuring pattern for the matched record type. |
match record pattern binding ... conflicts ... | A source function whole-body record match binding reuses an existing source value binding, process-reference binding, or source function name. | Choose a distinct immutable binding name. |
return match scrutinee ... must be a source value binding | A function return-match tries to match a name that is not an in-scope immutable source value. | Match the function parameter or a payload binding introduced by an enclosing source match. |
return match must handle variant ... | A function return-match is non-exhaustive. | Add the missing constructor arm or one _ fallback. |
return match record pattern ... must declare exactly one arm | A function return-match over a record tries to use enum-style multi-arm dispatch. | Use one record destructuring arm for the matched record type. |
return match record pattern binding ... conflicts ... | A function return-match record binding reuses an existing source value binding, process-reference binding, or source function name. | Choose a distinct immutable binding name. |
payload ... has type ..., expected ... | A source function or step payload binding annotation does not match the constructor payload type. | Use the declared payload type. |
match payload binding ... conflicts ... | A source function match arm reuses a parameter, process-reference binding, or source function name for a payload binding. | Use a distinct immutable payload binding name. |
value ... is not a variant of enum ... | A payload-constructor expression names a constructor outside the expected enum. | Use a constructor from the expected enum or call a declared function. |
enum variant ... requires a payload / does not accept a payload | A payload-bearing constructor was used as a fieldless value, or a fieldless constructor was called with a payload. | Match the constructor’s declared payload shape. |
Message Handling Errors
| Diagnostic Contains | Likely Cause | Fix |
|---|---|---|
step pattern message ... is not accepted | A step pattern names a message constructor outside the process message enum. | Use a declared message constructor. |
duplicate step pattern for message | A message variant has more than one explicit step clause. | Keep one explicit clause per variant. |
duplicate wildcard step pattern | More than one step clause uses _. | Keep one wildcard clause. |
wildcard step pattern is unreachable | Explicit clauses already cover every accepted message variant or every discovered concrete payload case the wildcard could cover. | Remove the wildcard clause or remove an explicit clause that it should cover. |
must declare step pattern for message | A message variant is not covered by an explicit or wildcard step clause. | Add a step clause for the missing message or add one _ clause. |
declares a wildcard step pattern with a payload-sensitive state match step pattern / declares payload-sensitive step pattern ... with a state match wildcard step pattern | A payload-sensitive state-match split is mixed with a non-state-match wildcard fallback, or a state-match wildcard fallback is mixed with a non-state-match payload-sensitive clause. | Use state-match bodies for both the explicit payload-sensitive clauses and the wildcard fallback, use supported step-signature or match msg fallback where that surface is intended, or remove the wildcard or payload predicate. |
step pattern ... overlaps an earlier pattern / state match step pattern ... overlaps an earlier pattern | Two step patterns can match the same message payload shape. | Keep one unguarded clause or make the nested payload predicates exact and disjoint. |
must declare step pattern for message ... payload ... | A payload-sensitive step split omits a discovered concrete payload case. | Add an explicit step clause for the missing payload case; on step-signature, state-match, or match msg surfaces, one _ fallback may cover discovered remainder cases when the fallback shape is supported. |
step pattern ... has no discovered payload case | A step payload predicate does not correspond to a discovered concrete payload case. | Use a concrete payload case that the checker can discover from sends and constructors. |
payload-sensitive step pattern for message ... has no discovered payload case for wildcard fallback / payload-sensitive state match step pattern for message ... has no discovered payload case for wildcard fallback | A wildcard fallback is paired with payload-sensitive step signatures or state-match clauses, but the checker did not discover any concrete payload case for the wildcard to lower. | Send or construct the concrete payload case before it is handled, remove the fallback, or use explicit discovered payload cases. |
match body must be the whole function body | A match msg appears after another statement or has trailing body statements. | Use one whole-body match msg form or step parameter patterns. |
match expressions are only supported ... | A general match is used inside a value expression such as a result constructor argument. | Use a supported whole-body match, return match, step parameter pattern, or function return-match form. |
match step must declare a typed message parameter | A match step uses a parameter pattern instead of msg: MsgType. | Use fn step(state: StateType, msg: MsgType). |
match scrutinee ... must be the step message parameter | The match scrutinee is not the typed message parameter. | Match the declared message parameter, usually match msg. |
state match step must use a match body | A match state step was parsed in a non-match body shape. | Make match state { ... } the whole step body. |
state match pattern ... requires a payload binding | A payload-bearing state variant is matched without binding its payload. | Write the arm as Variant(name: PayloadType). |
state match pattern ... does not carry a payload | A fieldless state variant was matched with a payload binding. | Remove the binding from the fieldless variant arm. |
state match payload ... has type ..., expected ... | A state payload binding annotation does not match the state variant payload type. | Use the declared state variant payload type. |
state payload binding ... conflicts with message payload binding | A match state arm reuses the enclosing message payload binding name. | Give the state payload binding its own transition-local name. |
message parameter ... has type ..., expected ... | The typed message parameter is not the process Msg type. | Use the process message type in the second parameter. |
cannot mix match step bodies with step parameter patterns | One process mixes match msg dispatch with parameter-pattern or state-match dispatch. | Use either parameter-pattern/state-match clauses or one match msg body for the process. |
sends message ... not accepted by ... | The target process message enum has no such variant. | Send a declared target message variant. |
message ... requires a payload | A send omits the payload for a payload variant. | Pass one value with send worker Variant(value);. |
message ... does not accept a payload | A send passes a payload to a unit variant. | Remove the payload argument or send a payload variant. |
payload type ... must be a named record, enum, list, map, or process reference type | A payload variant uses an unsupported applied/generic type. | Declare a named record or enum type, use List<T,N> / Map<K,V,N> over source values, or use ProcessRef<TargetProcess>. |
payload type ... must declare exactly one target process | A direct ProcessRef payload declaration has the wrong arity or a const argument. | Declare process-reference payloads as ProcessRef<TargetProcess>. |
step pattern payload ... has type ... expected ... | A step payload binding annotation does not match the variant payload type. | Use the declared payload type in the parameter pattern. |
payload binding ... conflicts / process reference ... conflicts with payload binding | A local immutable binding shadows state, a process, a type, a value constructor, or another local binding in the same transition. | Use distinct immutable binding names. |
payload has type ..., expected ... | A runtime envelope or artifact payload template carries the wrong value type. | Match the payload value type to the target message variant. |
payload ... exceeds maximum length | A payload value label is too large for the artifact or runtime trace boundary. | Use a smaller payload value or split the payload into smaller fields/messages. |
payload ... is not a bound process reference | A ProcessRef<T> payload send uses a value that is not a process reference. | Pass an immutable process reference binding or received ProcessRef<T> payload. |
contains a process reference; process references must be direct message payloads | A record field or collection type tries to make process references general source values. | Keep ProcessRef<T> as the direct payload type of a message variant, then forward that received reference directly. |
process references must be direct message payloads / process reference template must be a direct message payload | A process reference payload is nested inside a record, enum, collection, or next-state template. | Send ProcessRef<T> only as the direct payload of a message that declares ProcessRef<T>. |
Match Errors
| Diagnostic Contains | Likely Cause | Fix |
|---|---|---|
match scrutinee ... is not a fieldless enum variant | An init match uses a scrutinee that is not a fieldless enum constructor. | Match a declared fieldless enum constructor. |
match pattern ... is not a variant of enum ... | A match arm names a constructor outside the scrutinee enum. | Use a constructor from the matched enum. |
init match must handle variant | An init match is non-exhaustive. | Add an arm for the missing variant or one _ arm. |
init match declares duplicate pattern | More than one arm handles the same constructor. | Keep one arm per constructor. |
init match wildcard pattern is unreachable | Explicit arms already cover the matched enum. | Remove the wildcard arm or remove the explicit arms it should cover. |
init match pattern ... does not carry a payload | A fieldless constructor pattern tries to bind a payload. | Remove the binding. |
init match arm cannot use payload binding ... in returned state | An init match arm tries to materialize a payload binding even though init matches lower to a static initial state. | Return a concrete whole state value from each init match arm. |
State Errors
| Diagnostic Contains | Likely Cause | Fix |
|---|---|---|
value ... is not a variant of enum ... | A returned enum value does not belong to the expected enum. | Return a variant from the process state enum. |
record constructor ... does not match expected record ... | A record value constructor does not match the expected state type. | Construct the expected record type. |
record value fields use ':' | A record value used assignment syntax. | Use field: value, not field = value. |
process reference payloads are not valid state values / process reference templates are not valid next-state values | A state value or next-state template tries to embed runtime process authority. | Keep process references in direct message payloads; process states must be immutable data values. |
state value state conflicts | A state enum variant is named state. | Rename the variant. |
current state payload template requires a payload-bearing state | An artifact or checked transition uses a state-payload template without a payload-bearing current state guard. | Ensure the transition is keyed by a checked payload-bearing state value. |
current_state id ... is not a valid state value / is not a loaded state value | An artifact transition references a current state outside the loaded state table. | Emit only typed state IDs from lowering; reject or regenerate invalid artifacts. |
Process And Mailbox Errors
| Diagnostic Contains | Likely Cause | Fix |
|---|---|---|
spawns itself | A process tries to spawn itself. | Spawn another declared process. |
authority type must be Cap<Spawn<ProcessName>> or Cap<PortConnect<PortName>> | A process authority declaration uses a malformed capability wrapper. | Declare local spawn authority as authority name: Cap<Spawn<TargetProcess>>; or local port-send authority as authority name: Cap<PortConnect<PortName>>;. |
authority descriptor must be Spawn<ProcessName> or PortConnect<PortName> | A Cap<...> process authority declaration does not contain a supported process-local descriptor. | Use Cap<Spawn<TargetProcess>> for dynamic local spawn or Cap<PortConnect<PortName>> for typed boundary sends. |
protocol ... authority must be Cap<ProtocolBoundary<...>> / port ... authority must be Cap<PortConnect<...>> / component ... authority must be Cap<ComponentExport<...>> | A boundary declaration has the wrong capability descriptor or target name. | Use the exact authority shape for the declaration being defined. |
port ... targets process ... with message type ..., expected protocol ... message type ... | A port binds a protocol to a process whose Msg enum does not match. | Point the port at a process with the protocol message enum or declare a protocol for that target process. |
port ... is not declared | A send ... via ... names an undeclared port. | Declare the port before checking the send, or remove the via contract. |
send via port ... requires authority Cap<PortConnect<...>> / send through port id ... requires authority port_connect | A typed boundary send lacks the exact process-local port authority. | Add a used authority name: Cap<PortConnect<PortName>>; declaration, or remove the typed-port send. |
sends through port id ... targeting process id ..., expected ... | A Mantle artifact send references a port table ID whose admitted target process does not match the send target. | Fix the frontend lowering or the artifact table; Mantle will not reinterpret source names at runtime. |
spawn authority target must be a process name | The spawn descriptor target is not a bare process name. | Target a declared process directly, for example Cap<Spawn<Worker>>. |
spawn authority targets entry process | A process authority declaration tries to create the already-started entry process. | Target a non-entry worker process. |
spawn target ... requires authority Cap<Spawn<...>> | A step uses local dynamic spawn without exact authority for that target process. | Add a process-local authority declaration for the exact target, or remove the spawn. |
duplicates spawn authority descriptor | A process declares the same spawn capability more than once. | Keep one declaration per exact target process. |
declares unused spawn authority / declares unused port authority | A process declares authority that no local spawn site or typed-port send uses. | Remove the unused declaration or add the corresponding checked use. |
declares duplicate supervisor child | A local supervisor declares the same lexical child name more than once in one owner process. | Give each child a unique name. |
local supervisor graph contains cycle | Static lexical supervisor declarations contain an indirect child cycle. | Make each local supervisor child tree acyclic. |
restart intensity ... must be greater than zero | A supervisor plan omits a positive restart count or restart window. | Use explicit positive max_restarts: N_u32 and within_ms: N_u64 values. |
lexical supervisor child spawn site ... carries dynamic authority | A Mantle artifact tries to use dynamic spawn authority for a lexical child site. | Emit lexical supervisor child sites with supervisor and child IDs only. |
supervisor child ... spawn site targets | A supervisor plan and its lexical spawn site disagree on the child target. | Keep the child declaration target, spawn target, and spawn-site target identical. |
conflicts with a process declaration | A process reference uses the same name as a process definition. | Use a distinct reference name. |
undeclared process reference or supervisor child | A send references a name that is never spawned in the process and is not a lexical supervisor child. | Add a matching let worker: ProcessRef<Worker> = spawn Worker; statement, or target a declared lexical supervisor child. |
send target ... is not a process reference payload | A send target names a payload binding whose type is not ProcessRef<T>. | Send through a process reference binding or a received ProcessRef<T> payload. |
unbound process reference | A transition sends through a reference before it is bound. | Spawn the reference before sending through it. |
duplicates process reference id | A transition binds the same reference twice. | Use two distinct references or bind once. |
mailbox would exceed bound | A send would overflow the target mailbox. | Increase the mailbox bound or send fewer messages before the target runs. |
would retain ... unhandled message | A process can stop while messages remain in its mailbox. | Continue until queued messages are handled or avoid queuing them. |
mailbox_bound must be no greater than | The mailbox bound exceeds the validated limit. | Lower the bound. |
Runtime Errors
Runtime errors are emitted by Mantle after artifact admission starts. Common
causes include invalid artifacts, blocked trace paths, mailbox exhaustion,
explicit Panic(...) transition results, trace size exhaustion, and dispatch
budget exhaustion.
Use the source gate first:
just strata-check path/to/program.str
just strata-build path/to/program.str
Then run Mantle:
just mantle-run target/strata/program.mta
If source checking passes but Mantle rejects an artifact, inspect the artifact and runtime boundary docs before changing runtime behavior.
Examples
Runnable Strata examples live under examples/.
Read them in this order:
hello.strfor the minimum source-to-runtime program.actor_ping.strfor spawning, sending, and a single worker transition.actor_sequence.strfor multiple messages and message-keyed transitions.actor_match.strfor whole-body match authoring that checks into typed message-keyed transitions.init_match.strfor whole-body match authoring ininit.init_return_match.strfor pure init return-match expressions.function_match.strfor module functions, process-local functions, and pattern matching outside actor dispatch.function_payload_match.strfor payload-bearing enum construction and matching in normal source functions.function_if_else.strfor pure value-level conditionals selected before lowering. See alsofunction_local_bindings.strfor sequential immutable source-local computation in normal source functions.function_collection_match.strfor immutable list/map source values and collection patterns in normal source functions.function_return_match.strfor function return-match expressions.process_return_match.strfor process step return-match expressions with uniform effect prefixes.process_return_match_arm_prefix.strfor selected step return-match arm action prefixes.process_return_match_arm_runtime_if_prefix.strfor selected step return-match arm-local runtime branch prefixes.process_return_match_arm_for_prefix.strfor selected step return-match arm-local bounded runtime loop prefixes.process_return_match_arm_for_if_prefix.strfor selected step return-match arm-local bounded runtime loop prefixes with loop-body runtime branch actions.process_return_match_arm_if_for_prefix.strfor selected step return-match arm-local runtime branch prefixes with bounded runtime loop branch actions.process_return_match_arm_action_block.strfor selected step return-match arms using ordinary action-block sequencing with multiple runtime branches and bounded loops.function_record_pattern.strfor source function record destructuring patterns.function_record_return_match.strfor function return-match record destructuring.function_record_body_match.strfor whole-body function match record destructuring.state_payload_enum.strfor payload-bearing process state enum transitions.collection_state.strfor immutable collection state and payload-dependent collection next-state templates.state_payload_match.strfor matching immutable current process state payloads.actor_instances.strfor multiple runtime instances of one process definition.actor_payloads.strfor typed message payloads and immutable payload bindings in actor step parameter patterns.runtime_if_else.strfor Mantle-backed runtime branching over a message payload.runtime_scalar_priority.strfor typed scalar payload computation, runtime-bound value conditionals, and Mantle-backed scalar branch selection.source_contract_data_primitives.strfor immutableStringandBytesdata in records, messages, functions, collections, exact equality, and Mantle typed primitive runtime values.runtime_payload_projection_if.strfor Mantle-backed runtime branching over a projected field from an immutable received record payload.runtime_payload_projection_next_state.strfor Mantle-backed runtime next-state branching over a projected field from an immutable received record payload.runtime_state_payload_projection_if.strfor Mantle-backed runtime branching over a projected field from an immutable current-state record payload.runtime_state_payload_projection_next_state.strfor Mantle-backed runtime next-state branching over a projected field from an immutable current-state record payload.runtime_nested_if_actions.strfor one bounded layer of nested statement-level runtime branch actions.runtime_final_if_guarded_loop.strfor bounded loop action prefixes inside final-position runtime branches.runtime_final_if_nested_if_actions.strfor one direct nested statement-level runtime branch action inside final-position runtime branches.runtime_final_if_nested_terminal_if.strfor one direct nested terminal final-position runtime branch inside final-position runtime branches.runtime_guard_noop.strfor omittedelseand explicit no-op runtime branch behavior.runtime_for_each.strfor Mantle-backed bounded runtime iteration over a typed list payload.runtime_for_each_empty.strfor the zero-iteration runtime collection case.runtime_for_each_if.strfor Mantle-backed runtime branch selection inside bounded loop bodies.runtime_for_each_nested_if_actions.strfor one bounded nested runtime branch inside a bounded loop-body branch.runtime_guarded_for_each.strfor guarding a whole bounded runtime loop.runtime_guarded_ref_loop.strfor routing a guarded bounded loop through a received direct process reference.runtime_guarded_ref_loop_jobs.strfor routing ordinary immutableJobvalues through a guarded loop and received direct process reference.runtime_loop_element_projection.strfor projecting immutable record fields from guarded runtime loop elements.actor_payload_match.strfor the same payload binding through a whole-bodymatch msg.actor_payload_split_match.strfor payload-sensitive same-message splitting inside a whole-bodymatch msg.actor_payload_split_signature.strfor payload-sensitive same-message splitting across step parameter patterns.actor_payload_split_signature_wildcard.strfor payload-sensitive step-signature wildcard fallback over discovered concrete payload cases.actor_payload_state_match_split.strfor payload-sensitive same-message splitting across state-match step clauses.actor_payload_state_match_wildcard.strfor payload-sensitive state-match wildcard fallback over discovered concrete payload cases.nested_patterns.strfor nested immutable constructor, record, list, and map payload destructuring.actor_reply.strfor transporting typed process references through message payloads.process_ref_stale_lifecycle.strfor a transported old runtime PID returningErr(Stopped(message))without retargeting to a newer worker.actor_emit_spawn_send.strfor one transition with declared emit, spawn, and send authority.imports_main.str, withimports_types.strandimports_worker.str, for source-unit imports checked and lowered from one root source path.boundary_contracts_main.str, withboundary_contracts_worker.str, for typed protocol, port, and component boundary contracts.component_composition_main.str, withcomponent_composition_worker.str, for checked local component-instance and port-binding composition admission.effect_outcomes.strfor immutable typed local send/spawn outcomes, commit-or-return state evidence, and the typed send-error contract shape.effect_outcome_mailbox_full.strfor a source-visibleFullsend outcome.effect_outcome_stopped_target.strfor a source-visibleStoppedsend outcome after a normally terminated target.effect_outcome_crashed_target.strfor the fail-closed boundary where a source-createdPanic(...)prevents a later observer from recovering the crash as a source-visible send outcome.effect_outcome_spawn_denied.strfor source-visible local spawn authority denial before process acceptance.effect_outcome_spawn_exhausted.strfor source-visible local spawn capacity exhaustion before process acceptance under--max-runtime-processes 1.effect_outcome_spawn_backend_unavailable.strfor source-visible local spawn backend unavailability before process acceptance under--disable-local-spawn-backend.actor_panic_no_replay.strfor fail-closed actor failure and no replay after message dequeue.local_supervision_restart.str,local_supervision_permanent_stop.str,local_supervision_temporary.str,local_supervision_transient_restart.str,local_supervision_transient.str, andlocal_supervision_inactive_send_outcome.strfor localone_for_onesupervision, lexical child sends, restart modes, stopped-child send outcomes, and restart observability.local_supervision_inactive_crashed_send_outcome.strfor source-visibleErr(Crashed(message))when an inactive temporary supervised child failed before the send was accepted.
Detailed notes are grouped by topic:
- Basic examples
- Function and return-match examples
- Runtime and actor examples
- Import, boundary, and failure examples
Basic Examples
These examples cover the first source-to-runtime programs and initial process dispatch forms.
Hello
examples/hello.str is the first source-to-runtime gate. It checks,
builds, runs, emits hello from Strata, and records an observability trace.
just build
just run-example hello
Key source ideas:
Mainis the entry process.MainMsg.Startis the entry message.emitis declared in thestepeffect list.Stop(state)terminates normally without changing state.
Actor Ping
examples/actor_ping.str is the first actor/runtime gate. It spawns a worker,
sends a message, handles that message, updates state, terminates normally, and
records the runtime trace.
just run-example actor_ping
Key source ideas:
Maindeclaresauthority spawn_worker: Cap<Spawn<Worker>>;, then useslet worker: ProcessRef<Worker> = spawn Worker;beforesend worker Ping;.WorkerMsg.Pingis checked againstWorker’s message type.WorkerreplacesIdlewithHandled.- Both processes stop normally.
Actor Sequence
examples/actor_sequence.str exercises message-keyed process transitions. The
worker handles First, returns a whole replacement state with Continue(...),
then handles Second through the wildcard clause and returns a whole
replacement state with Stop(...).
just run-example actor_sequence
Key source ideas:
WorkerMsghas two variants, and each variant resolves to exactly onestepclause.- The explicit
Firstclause handlesFirst;_covers the remaining accepted message variants. Continue(SawFirst)keeps the worker alive for the next queued message.Stop(Done)terminates the worker normally.
The runtime trace records process, message, state, and output IDs alongside labels so that behavior can be checked without treating labels as executable bindings.
Actor Match
examples/actor_match.str exercises the whole-body match msg
authoring form. The checker resolves each arm into the same typed transition
table used by step parameter patterns.
just run-example actor_match
Key source ideas:
fn step(state: WorkerState, msg: WorkerMsg)declares a typed message parameter.match msgmust be the whole function body.- Each arm returns a whole replacement state through
Continue(...)orStop(...). - The generated Mantle artifact still dispatches by typed message IDs.
Init Match
examples/init_match.str exercises a non-step whole-body match in init. The
checker resolves the fieldless enum scrutinee, proves the arms are exhaustive,
and selects the typed initial state before lowering.
just run-example init_match
Key source ideas:
match Warmis checked againstStartupMode.- Both
ColdandWarmarms return immutable wholeMainStaterecord values. - The Mantle trace starts
MaininMainState{readiness:WarmReady}, proving the selected initial state reached runtime admission.
Init Return Match
examples/init_return_match.str exercises a pure return match expression in
init. The checker resolves the fieldless enum scrutinee, proves the arms are
exhaustive, selects one whole initial state value, and lowers that state through
the existing typed artifact state table.
just run-example init_return_match
Key source ideas:
return match Warm { ... };is accepted only becauseWarmis a fieldless enum constructor.- Each arm is statement-free and returns one immutable whole
MainStatevalue. - Mantle receives the selected typed initial state ID; it does not dispatch on the source match arm names.
Function And Return-Match Examples
These examples cover pure source functions, immutable source-local computation, source patterns, and checked return-match forms.
Function Match
examples/function_match.str exercises normal source functions outside actor
dispatch. It uses module-level functions and process-local functions, including
signature-pattern dispatch and a whole-body match function.
just run-example function_match
Key source ideas:
readiness_sig(Cold)andreadiness_sig(Warm)are module-level function clauses selected by typed signature patterns.readiness_body(mode: StartupMode)uses a whole-bodymatch modeoutside an actorstep.MainandWorkerdeclare process-local functions for state construction.send worker Assign(ready_job(Ready))proves function calls are expanded for message payload discovery and lowering.- Mantle sees typed state IDs, message IDs, and payload templates, not source function dispatch names.
Function Payload Match
examples/function_payload_match.str extends normal source functions to
payload-bearing enum values. It constructs source-visible enum payload values,
matches them through signature patterns and whole-body function matches, and
lowers a received actor payload through a process-local function into an enum
payload state template.
just run-example function_payload_match
Key source ideas:
Assigned(Job { phase: Ready })is resolved as a typed enum value, not a function call.status_sig(Assigned(job: Job))binds the enum payload immutably in a normal source function signature pattern.status_body(work: Work)matches the typed function parameter and binds the payload inside the selected arm.state_for(Assigned(job))proves a process-local function can wrap a received immutable payload into a source enum value before lowering.
Function If Else
examples/function_if_else.str uses pure source-time conditionals in normal
source functions. The checker resolves the core Bool condition and selects one
immutable branch before lowering. The example includes
both expression-form return if (...) { value } else { value }; and braced pure
return branches.
just run-example function_if_else
Key source ideas:
Bool,False, andTrueare core built-ins for conditionals and cannot be redeclared.if (flag) { WarmReady } else { ColdReady }is a pure value expression, not a statement block.if (flag) { return WarmReady; } else { return ColdReady; }is an equivalent pure source-function return branch shape, not a runtime branch.- Both branches are checked against the same expected type.
- Mantle receives selected typed state values, not a conditional branch key or source function dispatch name.
Function Local Bindings
examples/function_local_bindings.str uses sequential immutable bindings in
normal source functions and process-local pure functions. The checker resolves
each typed source-local value before lowering.
just run-example function_local_bindings
Key source ideas:
let current_local: Phase = status(work);binds an immutable source value, not a runtime action.- Later source-local bindings can use earlier bindings in source function calls, pure conditionals, scalar arithmetic and ordering, record/list/map constructors, and equality predicates.
- Braced pure return-if branches can introduce branch-local immutable values before their terminal return.
- Function return-match arms can use source-local bindings before their terminal pure return.
- Process-local pure functions use the same source-local binding rules.
- Runtime-bound pure value
ifexpressions overBoollower as typed Mantle value templates, not source strings or source function dispatch names. - Message enums that carry direct
ProcessRef<T>payloads remain authority surfaces and are rejected as source-local binding types. - Mantle receives typed values and templates, not source-local binding names or source function dispatch names.
Function Collection Match
examples/function_collection_match.str uses immutable List<T,N> and
Map<K,V,N> source values in normal source functions.
just run-example function_collection_match
Key source ideas:
List<Phase,2>[Ready, Done]andMap<Phase,Phase,2>[Ready => Done, Unknown => Unknown]are typed, bounded immutable collection values.fn first(List<Phase,2>[phase, _])dispatches on exact list length and binds one immutable element.- Function body and return matches can use exact list patterns, list rest patterns
such as
List[_, ..tail], exact map patterns, and subset map patterns such asMap[Ready => selected, ..rest]with_fallback arms. - Function expansion leaves Mantle with a resolved
MainStatevalue, not source function dispatch names.
Function Return Match
examples/function_return_match.str uses a function return match expression to
select an immutable result from an in-scope source value binding.
just run-example function_return_match
Key source ideas:
return match work { ... };is checked as a pure function return expression.- The selected arm binds enum payloads immutably before function expansion.
- Mantle receives the resolved
MainState{status:Active(Job{phase:Ready})}state value, not source function dispatch.
Process Return Match
examples/process_return_match.str uses a process step return match over a
concrete enum payload binding after a uniform emit prefix.
just run-example process_return_match
Key source ideas:
Envelope(Assign(phase: Phase))binds an immutable enum payload whose concrete value is already proven by payload-sensitive dispatch.emit "process return match uniform prefix";lowers as the same typed action prefix on every selected transition.return match phase { ... };is checked and reduced to a typedContinue(...)orStop(...)transition before lowering.- Mantle executes the emitted typed transition IDs and payload guards; it does not dispatch on source strings or function names.
Process Return Match Arm Prefix
examples/process_return_match_arm_prefix.str extends step return match with
selected arm-local emit and typed send prefixes before the terminal result.
just run-example process_return_match_arm_prefix
Key source ideas:
- The checker still selects one concrete return-match arm before lowering.
- The uniform prefix lowers onto every selected transition before the arm-local prefix.
- Only the selected arm’s typed actions lower onto that transition; the arm-local
sendprefixes also seed the receiver’s payload-sensitive transitions before lowering. - Arm labels and source binding names are not executable runtime dispatch keys.
- Arm-local
spawn, process-reference binding, final-position runtimeif, nested return matches, and mutable source state changes remain rejected.
Process Return Match Arm Runtime If Prefix
examples/process_return_match_arm_runtime_if_prefix.str extends selected
step return match arm prefixes with statement-level runtime if actions.
just run-example process_return_match_arm_runtime_if_prefix
Key source ideas:
- The checker still source-selects one concrete return-match arm before lowering.
- The checked arm-local runtime branch lowers as a typed Mantle action inside
that selected arm prefix, after any uniform prefix actions and before the
terminal
Continue(...)orStop(...). - This example keeps branch bodies action-only with local
emitand in-scope directsend;process_return_match_arm_if_for_prefix.stralso covers bounded runtimeforactions inside selected runtime branches. - Branch-local
spawn, process-reference binding, branch returns, final-position runtimeif, and runtime branch nesting beyond the artifact bound remain rejected. - Mantle selects the runtime branch from typed artifact templates; source arm labels and source binding names are not executable dispatch keys.
Process Return Match Arm For Prefix
examples/process_return_match_arm_for_prefix.str extends selected
step return match arm prefixes with bounded runtime for over a
checked runtime List<T,N> binding.
just run-example process_return_match_arm_for_prefix
Key source ideas:
- The checker still source-selects one concrete return-match arm before lowering.
- The selected arm loop lowers as a typed Mantle
for_eachaction after any uniform and arm-local checked action prefixes, before the terminalContinue(...)orStop(...). - Loop collections must be checked runtime
List<T,N>bindings. Loop element projections lower as typed templates, not source-name dispatch. - Loop bodies remain action-only: local
emitand in-scope directsendactions are supported, whilespawn, process-reference binding, nested loops, and branch-local process-reference binding remain rejected.
Process Return Match Arm For-If Prefix
examples/process_return_match_arm_for_if_prefix.str extends selected
step return match arm-local bounded loops with loop-body runtime if actions.
just run-example process_return_match_arm_for_if_prefix
Key source ideas:
- The checker still source-selects one concrete return-match arm before lowering.
- The selected arm loop lowers as a typed Mantle
for_eachaction, and the direct loop-body runtime branch lowers as a typed Mantle branch action inside that loop body. - The branch condition and branch sends project from typed immutable loop element bindings, not source aliases or debug strings.
- Loop-body branches remain action-only: local
emitand in-scope directsendactions are supported, while branch returns, branch-localspawn, process-reference binding, nested runtimefor, and runtime branch nesting beyond the artifact bound remain rejected.
Process Return Match Arm If-For Prefix
examples/process_return_match_arm_if_for_prefix.str extends selected
step return match arm-local runtime branches with bounded runtime for actions
inside branches.
just run-example process_return_match_arm_if_for_prefix
Key source ideas:
- The checker still source-selects one concrete return-match arm before lowering.
- The selected arm runtime branch lowers as a typed Mantle branch action, and a
selected branch may contain bounded typed
for_eachactions. - Loop collections remain checked runtime
List<T,N>bindings, and loop element projections lower through typed templates rather than source aliases. - Branch-local loops remain action-only and bounded: local
emit, in-scope directsend, and loop-body runtime branch actions are accepted; branch returns, branch-localspawn, process-reference binding, nested runtimefor, and runtime branch nesting beyond the artifact bound remain rejected.
Process Return Match Arm Action Block
examples/process_return_match_arm_action_block.str exercises selected
step return match arms as ordinary typed step action blocks before their
terminal result.
just run-example process_return_match_arm_action_block
Key source ideas:
- Each selected arm contains multiple statement-level runtime
ifactions and multiple sibling bounded runtimeforactions in source order. - Nested statement-level runtime branches are supported only up to the shared artifact/runtime branch-action bound.
- Runtime loops remain bounded over checked
List<T,N>bindings, and nested runtime loops remain rejected. - Arm-local
spawn, branch-local process-reference binding, final-position runtimeif, nested return matches, mutation, and source-string dispatch remain rejected.
Function Record Pattern
examples/function_record_pattern.str destructures an immutable record value in
a normal source function signature.
just run-example function_record_pattern
Key source ideas:
fn phase_of(Job { phase })binds thephasefield as an immutable source value.field: bindingmay rename a record field binding in the function signature.- The function expands before lowering; Mantle receives the resolved
MainState{phase:Ready}state value.
Function Record Return Match
examples/function_record_return_match.str destructures an immutable record value
inside a function return match expression.
just run-example function_record_return_match
Key source ideas:
return match job { Job { phase } => { return phase; } };binds thephasefield as an immutable source value inside the selected arm.- Record return-match destructuring requires the scrutinee to be an in-scope source binding with a concrete record value.
- The function expands before lowering; Mantle receives the resolved
MainState{phase:Ready}state value.
Function Record Body Match
examples/function_record_body_match.str destructures an immutable record value
inside a whole-body source function match.
just run-example function_record_body_match
Key source ideas:
match job { Job { phase } => { return phase; } }binds thephasefield as an immutable source value inside the selected arm.- Record body-match destructuring requires one record pattern arm over the function parameter’s concrete record value.
- The function expands before lowering; Mantle receives the resolved
MainState{phase:Ready}state value.
Runtime And Actor Examples
These examples cover runtime state transitions, branching, loops, payload dispatch, process references, and failure behavior.
Runtime Scalar Priority
examples/runtime_scalar_priority.str sends a typed U32 payload to a worker,
computes an immutable scalar priority in process-local pure functions, and lets
Mantle select the runtime branch and the runtime-bound priority value from
typed scalar templates.
just run-example runtime_scalar_priority
Key source ideas:
Assign(U32)is a typed scalar message payload, not a string payload.compute_leveluses immutableU32source-local bindings and checked scalar arithmetic.compute_level(weight) >= 10_u32lowers into typed Mantle scalar templates;high_priority(weight)is the pure Bool function used by the runtime branch.classify(weight)returnsif (urgent) { High } else { Normal }; whenurgentdepends on the message payload, lowering emits a typed Mantle value-if template for the selected state field.- The selected branch emits
scalar priority high, proving Mantle executed the scalar branch path.
State Payload Enum
examples/state_payload_enum.str shows payload-bearing process state enum
values. It starts a worker in fieldless Idle, receives an immutable Job
payload, and transitions to Working(Job { phase: Ready }).
just run-example state_payload_enum
Key source ideas:
enum WorkerState { Idle, Working(Job) }declares a payload-bearing state variant.return Stop(Working(job));constructs a whole replacement state from an immutable received payload.- Mantle receives a next-state value template and resolves the resulting
Working(Job{phase:Ready})only because it is present in the typed state table.
Collection State
examples/collection_state.str shows immutable List<Phase,1> and
Map<Phase,Phase,2> process states and lowers received payloads, including list
rest and subset map payload projections, into collection next-state templates.
just run-example collection_state
Key source ideas:
type State = List<Phase,1>;andtype State = Map<Phase,Phase,2>;make worker states collection values rather than records or enums.return Stop(tail);andreturn Stop(Map<Phase,Phase,2>[Ready => next, Done => Ready]);create new immutable whole collection states from received payload bindings.- Mantle receives typed list-rest and map value templates and evaluates them during runtime execution.
State Payload Match
examples/state_payload_match.str matches the current process state as typed
immutable data. The worker first enters Working(Job { phase: Ready }); a later
Complete message dispatches over the current state and binds job inside the
selected state arm.
just run-example state_payload_match
Key source ideas:
fn step(state: WorkerState, Complete)can use a whole-bodymatch state.Working(job: Job)binds the state enum payload immutably for that transition arm only.return Stop(Done(job));returns a whole replacement state; it does not mutate the current state in place.- The Mantle artifact carries state-specific transitions keyed by typed message ID plus checked current state ID, and the payload-derived next state uses a typed current-state payload template.
Actor Instances
examples/actor_instances.str proves process references and instance-aware sends.
Main spawns the Worker process definition twice, binds each runtime instance
to a different process reference, and sends Ping through both references.
just run-example actor_instances
Key source ideas:
let first: ProcessRef<Worker> = spawn Worker;andlet second: ProcessRef<Worker> = spawn Worker;create two runtime worker instances.send first Ping;andsend second Ping;dispatch by reference, not by process definition label.- The runtime trace records two different
pidvalues with the sameprocess_idforWorker.
Actor Payloads
examples/actor_payloads.str sends a typed payload from Main to Worker.
Worker binds that payload with a step parameter pattern and returns a whole
replacement state containing the immutable payload value.
just run-example actor_payloads
Key source ideas:
enum WorkerMsg { Assign(Job) }declares a payload-bearing message variant.send worker Assign(Job { phase: Ready });sends one checked payload value.Assign(job: Job)binds the received payload as an immutable step-local value.WorkerState { job: job }constructs the next state as a whole value.
Component Composition
examples/component_composition_main.str imports
examples/component_composition_worker.str, declares a MainComponent that
imports WorkerPort, and admits a local AppComposition binding the import to
the worker component export. The binding is checked as a typed graph edge before
lowering; Mantle admits typed component-instance and port IDs as
metadata/admission data, while runtime send dispatch still uses loaded typed
process, message, and port IDs.
strata check examples/component_composition_main.str
strata build examples/component_composition_main.str
strata composition build examples/component_composition_main.str
strata composition admit target/strata/component_composition_main.component-composition.json --format json
strata composition bind-runtime target/strata/component_composition_main.component-composition.json target/strata/component_composition_main.mta --output target/strata/component_composition_main.deployment-composition.json
mantle run target/strata/component_composition_main.mta --composition-binding target/strata/component_composition_main.deployment-composition.json
The component-composition.json output is Strata-owned checked-subset evidence (strata.checked_component_composition), not .mta or Mantle runtime input.
strata composition bind-runtime emits the separate deployment-composition.json
binding (mantle.runtime_composition_binding), which Mantle admits only with
--composition-binding and a matching .mta. Source names remain metadata;
typed IDs carry binding, authority-flow, and trace correlation. Unsupported binding classes stay empty fail-closed arrays.
Typed Effect Outcomes
examples/effect_outcomes.str binds local spawn and send results as immutable
Result values, branches on typed outcome variant patterns, and stores the
finite send result in the next state as whole-value data.
just run-example effect_outcomes
Key source ideas:
let spawn_result: Result<ProcessRef<Worker>,SpawnError<Unit>> = spawn Worker;returns the committed process reference on accepted local spawn.let send_result: Result<Unit,SendError<WorkerMsg>> = send worker Work;returnsOk(Unit)after Mantle accepts the message, or typedSendError<WorkerMsg>before acceptance.- Outcome branches such as
spawn_result != Err(Exhausted(Unit))andsend_result == Ok(Unit)use immutable typed variants, not process-reference identity comparisons. - The next state stores
send_result;spawn_resultstays step-local because success carries process-reference authority rather than ordinary source state. - The artifact uses typed outcome IDs and value templates, not source binding names, for runtime meaning.
effect_outcome_mailbox_full,effect_outcome_stopped_target, andeffect_outcome_crashed_targetcover pre-acceptance capacity failure, normally stopped targets, and source-created panic fail-closed behavior.process_ref_stale_lifecycleproves a transported old runtime PID returnsErr(Stopped(message))without retargeting to a newer same-definition worker.effect_outcome_spawn_deniedcovers denied admitted spawn authority before aWorkerinstance is accepted.effect_outcome_spawn_exhaustedcovers process-capacity exhaustion before aWorkeris accepted under--max-runtime-processes 1; the exhausted branch emits without admitting or executing the child. The runtime trace recordseffect_outcome_boundwithoutcome_result:"exhausted".effect_outcome_spawn_backend_unavailablecovers a disabled local spawn backend under--disable-local-spawn-backend; the backend-unavailable branch emits without binding a process reference, admitting a child, or dispatching from source names.- The local supervision examples cover lexical child sends, explicit restart
intensity, child modes, stopped- and crashed-child send outcomes, and restart
traces without replaying consumed messages; crashed-child send outcomes
record
effect_outcome_boundwithoutcome_result:"crashed".
Runtime If Else
examples/runtime_if_else.str branches inside Worker.step over a received
Bool payload. The branch is not selected by Strata during checking; it lowers
as typed Mantle control flow and executes when each worker handles its message.
just run-example runtime_if_else
Key source ideas:
Branch(True)andBranch(False)send two different runtime payloads.if (flag == True) { ... } else { ... }lowers to Mantle branch control flow through a typed equality template.- Each branch emits its own declared output and returns a whole immutable state.
- The runtime trace records
branch_selectedfor boththenandelsepaths.
Runtime Payload Projection If
examples/runtime_payload_projection_if.str branches inside Worker.step over a
field destructured from a received Job record payload. The source binding is
immutable step-local syntax; the runtime branch lowers through a typed Mantle
record-field projection over the received payload.
just run-example runtime_payload_projection_if
Key source ideas:
Assign(Job { phase: assigned_phase })destructures the received record payload without introducing mutation.if (assigned_phase == Ready) { ... } else { ... }lowers as typed Mantle branch control throughReceivedPayload<Job>,RecordField("phase"), and aPhaseequality predicate.- Only the ready payload emits output; the done payload takes the empty branch.
Runtime Payload Projection Next State
examples/runtime_payload_projection_next_state.str branches inside
Worker.step over a field destructured from a received Job record payload.
The branch controls the final next-state result, and the state change remains a
whole immutable value returned through Continue.
just run-example runtime_payload_projection_next_state
Key source ideas:
Assign(Job { phase: assigned_phase })is source syntax for an immutable payload destructuring binding.- The final-position
if (assigned_phase == Ready) { ... } else { ... }lowers to MantleNextState::IfElsethroughReceivedPayload<Job>,RecordField("phase"), and aPhaseequality predicate. - Both branches return whole
WorkerStateenum values; the artifact does not use the source alias as an executable runtime path.
Runtime State Payload Projection If
examples/runtime_state_payload_projection_if.str stores immutable Job
records in process state, later destructures the current state payload, and uses
the projected field to select a statement-level runtime branch action inside
Mantle.
just run-example runtime_state_payload_projection_if
Key source ideas:
Assign(job: Job)stores the whole received job asHolding(job).Holding(Job { phase: held_phase })destructures the immutable current-state payload for the selectedDecidetransition arm.- The statement-level
if (held_phase == Ready) { ... } else { ... }lowers to MantleArtifactAction::IfElsethroughCurrentStatePayload<Job>,RecordField("phase"), and aPhaseequality predicate. - Both branches emit from typed runtime action selection, then return
Continue(state)as a whole-value continuation; the artifact does not use the source alias as an executable runtime path.
Runtime State Payload Projection Next State
examples/runtime_state_payload_projection_next_state.str stores immutable
Job records in process state, later destructures the current state payload,
and uses the projected field to choose a final next state inside Mantle.
just run-example runtime_state_payload_projection_next_state
Key source ideas:
Assign(job: Job)stores the whole received job asHolding(job).Holding(Job { phase: held_phase })destructures the immutable current-state payload for the selectedDecidetransition arm.- The final-position
if (held_phase == Ready) { ... } else { ... }lowers to MantleNextState::IfElsethroughCurrentStatePayload<Job>,RecordField("phase"), and aPhaseequality predicate. - Both branches return whole
WorkerStateenum values; the artifact does not use the source alias as an executable runtime path.
Runtime Nested If Actions
examples/runtime_nested_if_actions.str sends immutable CheckFlags record
payloads to three workers and uses one nested statement-level runtime branch to
choose effect-only action output inside Mantle.
just run-example runtime_nested_if_actions
Key source ideas:
Check(CheckFlags { outer_flag: primary, inner_flag: secondary })destructures the received record payload into immutable source bindings.- The outer and inner
ifconditions lower through typedRecordField(ReceivedPayload<CheckFlags>, ...)Bool equality templates. - The nested branch is an typed Mantle action with a stable nested
branch_path; source aliases such asprimaryandsecondaryare not executable dispatch paths. - State remains a whole-value
Continue(state)after the effect-only nested branch action; branch-localspawn, process-reference binding,return, nested loops, and deeper direct branch nesting remain rejected.
Runtime Final If Guarded Loop
examples/runtime_final_if_guarded_loop.str sends immutable CheckFlags record
payloads to three workers and uses a bounded loop action prefix inside a
final-position runtime branch before returning the whole-value step result.
just run-example runtime_final_if_guarded_loop
Key source ideas:
- The final-position
if (enabled == True) { ... } else { ... }lowers both branch action prefixes and branch next states through typed Mantle control flow. - The selected enabled branch records
branch_selected, runs the bounded loop, and then returnsContinue(state). - The selected disabled branch emits
worker disabled, returns the same whole-value step result shape, and does not execute loop actions. - Loop element access lowers through the typed loop element ID; source aliases
such as
itemare not executable runtime dispatch paths. - Branch-local
spawn, branch-local process-reference binding, nested loops, deeper direct branch-action nesting, and mutable state updates remain rejected.
Runtime Final If Nested If Actions
examples/runtime_final_if_nested_if_actions.str sends immutable CheckFlags
record payloads to four workers and uses one direct nested statement-level
runtime branch action as an action prefix inside each selected final-position
runtime branch. Each outer final-position branch still ends in the same
whole-value Continue(state) result.
just run-example runtime_final_if_nested_if_actions
Key source ideas:
- The final-position
if (outer == True) { ... } else { ... }lowers both branch action prefixes and branch next states through typed Mantle control flow. - The direct nested
if (inner == True) { ... } else { ... }in each selected final-position branch lowers as a typed MantleArtifactAction::IfElse. - Nested branch sends use the declared
reporter: ProcessRef<Reporter>binding created before the final-position branch and typedReceivedPayloadrecord field templates for the immutableinner_flagvalue. - Runtime execution records the final-position branch selection, nested branch selection, selected emit/send effects, and final whole-value result behavior in order.
- Third-level runtime branch actions, nested loops, branch-local
spawn, branch-local process-reference binding, statement-branchreturn, and mutable state updates remain rejected.
Runtime Final If Nested Terminal If
examples/runtime_final_if_nested_terminal_if.str sends immutable CheckFlags
record payloads to four workers and uses one direct nested final-position
runtime branch as the terminal return inside each selected final-position
runtime branch. Each nested leaf returns a whole-value Continue(...) state.
just run-example runtime_final_if_nested_terminal_if
Key source ideas:
- The outer
if (outer == True) { ... } else { ... }lowers to a typed MantleNextState::IfElse. - The terminal nested
if (inner == True) { ... } else { ... }in each selected branch lowers to a direct nested typed MantleNextState::IfElse. - Branch conditions lower through typed
ReceivedPayloadrecord-field templates for immutableouter_flagandinner_flag, not source aliases. - Runtime execution records outer and nested
next_statebranch selections, selected output effects, and the final whole-value state transition in order. - Third-level terminal runtime branches, deeper branch actions, nested loops,
branch-local
spawn, branch-local process-reference binding, statement-branchreturn, and mutable state updates remain rejected.
Runtime Guard Noop
examples/runtime_guard_noop.str shows statement-level runtime branches where
one selected branch intentionally performs no effects. The conditions are still
checked Bool predicates and lower into typed Mantle branch actions.
just run-example runtime_guard_noop
Key source ideas:
if (flag == True) { ... }lowers an omittedelseas an explicit empty Mantle branch.else {}is an explicit no-op branch;{}on one side is allowed when the sibling branch has an declared effect.- Empty selected branches do not emit, send, acquire authority, or change state,
but they still record
branch_selected. - Both branches empty are rejected before runtime execution.
Runtime For Each
examples/runtime_for_each.str iterates inside BatchWorker.step over a
received List<Bool,2> payload. The loop is not unrolled or selected by Strata
during checking; it lowers as typed Mantle loop control flow and executes once
per runtime element.
just run-example runtime_for_each
examples/runtime_for_each_empty.str uses the same shape with
List<Bool,0>[] and proves that Mantle records loop start/completion without
executing the body.
just run-example runtime_for_each_empty
Key source ideas:
for item in items { ... }requiresitemsto be a typed runtime list binding.itemis an immutable per-iteration value binding lowered to a typed loop element ID.- The loop body sends
Branch(item)in collection order. - The runtime trace records
loop_started, oneloop_iterationper item, andloop_completed.
examples/runtime_for_each_if.str extends the same runtime loop with
statement-level branch control inside the loop body.
just run-example runtime_for_each_if
Key source ideas:
if ((item != False) && !(item == False)) { ... } else { ... }runs in Mantle for each loop iteration through typed Boolean predicate templates.- The branch condition uses the typed loop element ID; branch payloads remain typed loop element values.
- One branch may be empty when the sibling branch has declared effects. Branches can emit and send, and may contain one direct nested statement-level branch. They cannot return, spawn, loop, or nest more deeply.
- The runtime trace records
loop_iteration,branch_selected, branch effects, andloop_completedin deterministic collection order.
examples/runtime_for_each_nested_if_actions.str projects immutable fields from
each CheckFlags loop element and selects one direct nested statement-level
runtime branch inside the selected outer loop-body branch.
just run-example runtime_for_each_nested_if_actions
Key source ideas:
- The loop item record pattern binds immutable
outerandinnerfield values. - Lowering emits typed
RecordField(LoopElement(...), "...")templates for the outer condition, nested condition, and send payload. - Runtime execution records the loop iteration, outer branch selection, nested branch selection, and selected branch effects in order.
- Third-level runtime branch actions, nested loops, branch-local
spawn, and branch-local process-reference binding remain rejected.
examples/runtime_guarded_for_each.str guards a whole bounded runtime loop with
a statement-level branch.
just run-example runtime_guarded_for_each
Key source ideas:
if (enabled == True) { for item in items { ... } } else {}lowers as a typed Mantle branch whose selectedthenbranch contains a bounded loop.- The disabled selected branch records
branch_selectedbut emits no loop events and performs no branch-local work. - The enabled selected branch records
branch_selected, thenloop_started, orderedloop_iterationbody effects, andloop_completed. - The guarded branch and loop body keep the same restrictions: no nested loops,
no
spawn, noreturn, no assignment, and no process-reference loop element type. Statement-level branch nesting still cannot exceed the single nested layer.
examples/runtime_guarded_ref_loop.str routes that guarded-loop send through a
direct ProcessRef<Worker> received as the current message payload.
just run-example runtime_guarded_ref_loop
Key source ideas:
BatchWorkerstores only value data in state. The worker reference remains a direct message payload onRoute(ProcessRef<Worker>).- The selected enabled branch sends
Branch(item)through the received reference from inside the guarded loop body. - The disabled branch records only
branch_selected; it emits no loop events and performs no branch-local or loop-local authority acquisition. - Lowering emits a typed received-payload send target. Runtime dispatch uses the transported runtime process ID plus checked target process ID, not the source payload binding name.
Runtime Guarded Ref Loop Jobs
examples/runtime_guarded_ref_loop_jobs.str keeps the same received direct
ProcessRef<Worker> routing shape, but the guarded loop iterates over
ordinary immutable Job record values.
just run-example runtime_guarded_ref_loop_jobs
Key source ideas:
- The guard remains a
Boolpredicate over currentBatchRequeststate. - The loop collection is
List<Job,2>, andJobis plain value data. - The worker reference remains direct message authority on
Route(ProcessRef<Worker>); it is not stored in state or nested inside the job list. - Lowering emits the
jobsprojection as a current-state value template, the loop element asJob, and the send target as a typed received-payload process reference.
Runtime Loop Element Projection
examples/runtime_loop_element_projection.str projects immutable Job.phase
data from each loop element, branches on the typed Phase, and sends only the
Ready phase through the received direct ProcessRef<Worker>.
just run-example runtime_loop_element_projection
Key source ideas:
- The loop item uses a record pattern,
Job { phase: routed_phase }, to bind an immutable field value inside the loop body. - Lowering emits
RecordField(LoopElement(...), "phase")typed templates for the inner branch condition and send payload. - Runtime execution uses typed loop element IDs, type IDs, and received process-ref payload targets. The source binding alias is not executable dispatch metadata.
Actor Payload Match
examples/actor_payload_match.str proves the same immutable payload binding
works from a whole-body match msg arm, not only from a step signature.
just run-example actor_payload_match
Key source ideas:
fn step(state: WorkerState, msg: WorkerMsg)binds the message parameter.match msgdispatches through the same typed pattern validation used by signature patterns.Assign(job: Job)binds the received payload immutably inside the match arm.- Runtime still dispatches by typed message IDs and payload type IDs.
Actor Payload Split Match
examples/actor_payload_split_match.str proves that one top-level message
variant can be split inside a whole-body match msg by disjoint nested typed
payload predicates.
just run-example actor_payload_split_match
Key source ideas:
Envelope(Assign(Ready))andEnvelope(Assign(Done))share the same top-level message constructor and differ only by nested payload identity.- The checker accepts the split only because the nested typed predicates are provably disjoint over discovered concrete payload cases.
- Lowering emits exact typed payload guards in Mantle transition records.
- Runtime dispatch uses typed message IDs, current state IDs when applicable, and exact typed payload identity, not source strings or debug labels.
Actor Payload Split Signature
examples/actor_payload_split_signature.str proves the same payload-sensitive
same-message split through step parameter patterns rather than a whole-body
match msg.
just run-example actor_payload_split_signature
Key source ideas:
- Multiple
fn step(state, Envelope(Assign(...)))clauses can share one top-level message constructor when the nested typed predicates are provably disjoint. Envelope(Assign(Ready))andEnvelope(Assign(Done))lower to two typed payload-guarded Mantle transitions for the same typed message ID.- Mantle selects the transition by typed message ID, current state ID when applicable, and exact typed payload identity.
actor_payload_split_match.strexercises the equivalent whole-bodymatch msgauthoring form.
Actor Payload Split Signature Wildcard
examples/actor_payload_split_signature_wildcard.str proves that a
payload-sensitive step-signature split can use _ as fallback for discovered
concrete payload cases not handled by explicit nested predicates.
just run-example actor_payload_split_signature_wildcard
Key source ideas:
Envelope(Assign(Ready))handles the explicitly guarded payload case._handles the discoveredEnvelope(Assign(Done))case, and lowering emits a typed payload guard forAssign(Done)rather than an open runtime catch-all.- The fallback remains bounded to discovered concrete payload cases discovered by checking.
- Runtime dispatch still uses typed message IDs and exact typed payload identity, not source strings or debug labels.
Actor Payload State-Match Split
examples/actor_payload_state_match_split.str proves that state-match step
clauses can share one top-level message constructor by disjoint nested typed
payload predicates.
just run-example actor_payload_state_match_split
Key source ideas:
Envelope(Assign(Ready))andEnvelope(Assign(Done))are explicit, discovered payload cases for the same message constructor.- Each payload case expands across the checked
Idle,SawReady, andDonecurrent-state cases frommatch state. - Lowering emits typed Mantle transitions keyed by message ID, current state ID, and exact typed payload guard.
- State changes remain immutable whole-value returns through
Continue(...)orStop(...); runtime dispatch does not use source strings or debug labels.
Actor Payload State-Match Wildcard
examples/actor_payload_state_match_wildcard.str proves that a
payload-sensitive state-match split can use _ as fallback for discovered
concrete payload cases not handled by explicit state-match clauses.
just run-example actor_payload_state_match_wildcard
Key source ideas:
Envelope(Assign(Ready))handles the explicitly guarded payload case.- The wildcard state-match step handles the discovered
Envelope(Assign(Done))case; lowering emitsAssign(Done)as an exact typed payload guard rather than an unguarded payload catch-all. - Each explicit and fallback payload case expands across the checked
Idle,SawReady, andDonecurrent-state cases from itsmatch statebody. - State changes remain immutable whole-value returns through
Continue(...)orStop(...); runtime dispatch does not use source strings or debug labels.
Nested Patterns
examples/nested_patterns.str composes immutable destructuring across
constructor payloads, records, list elements/rest, and map values/rest.
just run-example nested_patterns
Key source ideas:
AssignEnvelope(Assign(Job { phase }))binds a nested record field from a constructor payload through typed projection paths.HoldEnvelope(Hold(List[Job { phase }, ..tail]))binds an immutable list suffix whole value, not a mutable view.LookupEnvelope(Lookup(Map[Ready => Job { phase }, ..rest]))keeps map matching on static keys while binding nested values and an immutable rest map.- Lowering emits typed Mantle value templates; runtime execution does not use source strings as executable references.
Actor Reply References
examples/actor_reply.str passes a ProcessRef<Sink> as a typed immutable
message payload. Worker receives that reference and sends Done through it.
just run-example actor_reply
Key source ideas:
enum WorkerMsg { Work(ProcessRef<Sink>) }declares a typed reference payload.send worker Work(sink);transports the spawnedSinkreference toWorker.Work(reply_to: ProcessRef<Sink>)binds the received reference immutably.send reply_to Done;routes by the transported runtime process ID and checked target process ID, not by source labels.
Actor Emit Spawn Send
examples/actor_emit_spawn_send.str combines emit, spawn, and send in
one checked transition. Main declares each effect and exact
Cap<Spawn<Worker>> authority, spawns Worker, sends Ping through the typed
process reference, and stops with a whole replacement state.
just run-example actor_emit_spawn_send
Key source ideas:
authority spawn_worker: Cap<Spawn<Worker>>;declares the exact local spawn capability.fn step(...) -> ProcResult<MainState> ! [emit, spawn, send]declares the exact effects used by the body.let worker: ProcessRef<Worker> = spawn Worker;creates a typed process reference.send worker Ping;dispatches by the checked process-reference target and message ID after lowering, not by source text.return Stop(MainState { phase: Done });preserves immutable whole-state transition semantics.
Import, Boundary, And Failure Examples
This page covers runnable examples where source-unit visibility, boundary contracts, or fail-closed runtime behavior are the primary evidence.
Source-Unit Imports
examples/imports_main.str imports imports_types.str and imports_worker.str.
The root owns Main, the type unit owns Job, Phase, and complete, and the
worker unit owns Worker. Strata resolves that graph before checking and
lowering.
just run-example imports_main
The graph is checked in deterministic dependency-first order, direct imports
authorize visible declarations, and Mantle runs target/strata/imports_main.mta
without loading .str files or resolving import names at runtime.
Boundary Contracts
examples/boundary_contracts_main.str runs the checked boundary surface in
Boundary Contracts:
Run it with just run-example boundary_contracts_main.
Actor Panic No Replay
examples/actor_panic_no_replay.str shows an abnormal transition that consumes
one queued Ping, returns Panic(Failed), records failure evidence, and does
not replay the consumed message.
just run-example actor_panic_no_replay
The final command is expected to return non-zero. The trace should show two
accepted Ping messages, one message_dequeued, one process_stepped with
result:"Panic", and one process_failed event.
File Types
This repository uses these first-class file identities:
.strfor Strata source..mtafor Mantle Target Artifacts..component-composition.jsonfor Strata-owned checked-subset component-composition validation artifacts generated undertarget/strata/by default..authority-effect.jsonfor Strata-owned checked authority/effect fact artifacts generated undertarget/strata/by default..authority-policy.jsonfor Strata-owned typed authority policy decision artifacts generated undertarget/strata/by default from admitted authority/effect facts..deployment-composition.jsonfor explicit runtime composition binding artifacts generated undertarget/strata/by default when a checked composition artifact is bound to a matching.mta..authority-effect-binding.jsonfor explicit Mantle runtime authority/effect binding artifacts generated undertarget/strata/by default when checked authority/effect facts are bound to a matching.mta.
.str
.str files are Strata source files. They are the user-authored program
surface and should be UTF-8 text with LF line endings.
A root .str file may import sibling source units with import module_name;.
Those imports are resolved by Strata before checking and lowering. They do not
make .mta files import-aware, and Mantle does not load .str files or
resolve Strata imports at runtime.
Expected MIME type:
text/x-strata
.component-composition.json
.component-composition.json files are Strata checked-subset
component-composition validation artifacts. They are generated by strata composition build <path.str> and validated by strata composition admit <path.json>. They are not .mta, not Mantle runtime inputs, not runtime
composition bindings, and not executable dispatch data.
The artifact self-identifies internally with
schema_id=strata.checked_component_composition, schema_version_major=1,
schema_version_minor=0, artifact_kind=checked_component_composition,
hash_alg=fnv1a64-diagnostic, and source_language=strata. It records source
provenance, a composition ID, component instances with import/export port
obligations, empty arrays for binding classes the current source subset cannot
express, port bindings, binding admission results, unsatisfied imports,
cross-component authority-flow edges, nullable policy/diagnostic hash slots, an
empty extensions map, and a global admission result. Admitted artifacts must bind
every declared component import exactly once, have empty unsatisfied_imports,
no rejected binding results, and no rejection evidence. Rejected artifacts must
carry bounded rejection evidence and must list any unbound declared import once.
strata composition admit also requires source_fingerprint to be the
canonical 16-character lowercase hexadecimal value for the declared
source_fingerprint_algorithm. It may inspect rejected artifacts, but as a
checked-subset validation gate it exits non-zero unless the artifact is globally
admitted.
Source names in the JSON are metadata for diagnostics, provenance, and review. Executable meaning is carried by typed component-instance IDs, port-binding IDs, component IDs, port IDs, protocol IDs, and validated authority descriptors. A hand-edited artifact may rename metadata labels without changing typed admission, but one that drops typed IDs, strips binding evidence, duplicates an import binding, references unknown instances, makes an authority descriptor inconsistent with its typed ID, uses an unsupported schema, or marks unsatisfied imports as admitted fails closed. The admission command validates the artifact schema and internal typed-ID consistency; it is not a source re-check or tamperproof attestation for a coherently rewritten JSON file.
.authority-effect.json
.authority-effect.json files are Strata-owned checked authority/effect fact
artifacts. They are generated by strata authority-effects build <path.str> and
validated by strata authority-effects admit <path.json>. They are not .mta,
not Mantle runtime inputs, not policy grants, and not executable dispatch data.
The artifact self-identifies internally with
schema_id=strata.checked_authority_effects, schema_version_major=1,
schema_version_minor=0, artifact_kind=checked_authority_effects,
hash_alg=fnv1a64-diagnostic, and source_language=strata. It records source
provenance, each checked process by typed process ID, per-process
state/message counts, checked protocol/port/component table counts,
process-local authority IDs with exact authority descriptors, typed spawn-site
IDs, supervisor-child spawn proof facts for lexical supervisor spawn sites,
typed transition IDs with exact message, current-state, and
declared-effect IDs, typed component authority surfaces with declared
import-port counts, nullable
policy/diagnostic hash slots, an empty policy_inputs array, an empty
extensions map, and admission_result=admitted.
Source names and labels in the JSON are metadata for diagnostics, provenance,
and review. Runtime-affecting meaning is carried by typed process IDs,
authority IDs, spawn-site IDs, transition IDs, component IDs, port IDs, protocol
IDs, and exact effect IDs. Admission rejects malformed schema identity,
unsupported versions, noncanonical typed-ID ordering, duplicate exact effects,
unknown referenced process/authority/message/state/protocol/port/component IDs,
lexical supervisor-child spawn facts without matching supervisor/child
backlinks, declared table-count/import-count mismatches, inconsistent
component/port authority descriptors, noncanonical source fingerprints,
non-empty unsupported policy inputs, and non-empty extensions. It validates the
checked fact artifact shape; it does not re-check source and does not make
hand-edited JSON a trust or
integrity attestation. The fnv1a64-diagnostic fingerprint is provenance
correlation metadata, not a cryptographic authenticity proof.
.deployment-composition.json
.deployment-composition.json files are explicit runtime composition binding
artifacts. They are generated by strata composition bind-runtime <component-composition.json> <artifact.mta> --output <path.json> after the
checked composition artifact admits and the .mta has been built from matching
source. They self-identify with
schema_id=mantle.runtime_composition_binding, schema_version_major=1,
schema_version_minor=0, artifact_kind=runtime_composition_binding,
source_language=strata, matching .mta format/schema/module/source-hash
fields, the exact strata.checked_component_composition checked-composition
schema, matching checked-composition version/composition ID fields,
canonical component-instance-to-runtime-process correlations, canonical port
binding IDs, singleton deployment_id=0 correlation namespace,
admission_result=admitted, and an empty extensions map.
This binding artifact is not Strata source, not .mta, not a package manifest,
and not executable dispatch data. It is the only file Mantle accepts for runtime
composition correlation, and only when supplied explicitly with mantle run <artifact.mta> --composition-binding <deployment-composition.json>. Mantle does
not read .component-composition.json, does not infer composition identity from
source names, and does not verify Strata composition safety itself. If no binding
is supplied, Mantle emits no deployment, composition, or component-instance trace
correlation fields.
Mantle validates runtime bindings fail-closed before execution: malformed schema
or version fields, non-admitted bindings, mismatched checked-composition schema,
mismatched source fingerprint or .mta identity, mismatched component instance
or port-binding IDs, duplicate component instance IDs, duplicate process
correlations, and forged non-empty unsupported fields are rejected before
ArtifactLoaded or host-visible runtime side effects.
Labels and source names remain diagnostics/provenance metadata; typed IDs and
loaded runtime process IDs carry meaning.
.authority-policy.json
.authority-policy.json files are Strata-owned typed authority policy decision
artifacts. They are generated by strata authority-effects policy build <authority-effect.json> and validated by strata authority-effects policy admit <authority-policy.json> <authority-effect.json>. They are not .mta,
not raw runtime input, not a policy DSL, and not source re-check evidence.
The artifact self-identifies with
schema_id=strata.authority_policy_decisions,
schema_version_major=1, schema_version_minor=0,
artifact_kind=authority_policy_decisions, matching source identity fields,
matching checked authority/effect schema identity/version,
admission_result=admitted, and an empty extensions map. Its decisions
array is a closed, canonical table over the checked process-authority set. Each
entry carries only typed decision_id, process_id, authority_id, an exact
descriptor, and a decision of admit or deny.
Admission fails closed when the policy is malformed, missing a checked authority, has duplicate or out-of-order typed IDs, references unknown process/authority IDs, mismatches a descriptor, carries source-label spoofing fields, mismatches the checked facts’ source identity, uses unsupported decisions, or has non-empty extensions. Source names and debug labels are not accepted as authority lookup keys.
.authority-effect-binding.json
.authority-effect-binding.json files are explicit Mantle runtime
authority/effect binding artifacts. They are generated by strata authority-effects bind-runtime <authority-effect.json> <authority-policy.json> <artifact.mta> --output <path.json> after the checked authority/effect artifact
and authority policy artifact admit and the .mta has been built from matching
source. They self-identify with
schema_id=mantle.runtime_authority_effect_binding,
schema_version_major=1, schema_version_minor=0,
artifact_kind=runtime_authority_effect_binding, singleton
deployment_id=0, matching .mta format/schema/module/source-hash fields, the
matching checked authority/effect schema identity/version, matching authority
policy schema identity/version, canonical typed process authority, spawn-site,
transition-effect, component-surface facts, canonical typed policy_decisions,
admission_result=admitted, and an empty extensions map.
This binding artifact is not Strata source, not .mta, not a source report,
and not executable dispatch data. It is the only authority/effect sidecar
Mantle accepts, and only when supplied explicitly with mantle run <artifact.mta> --authority-effect-binding <authority-effect-binding.json>.
Mantle validates the binding against the loaded .mta before ArtifactLoaded
or host-visible runtime side effects. Forged source fingerprints, mismatched
.mta identity, altered authority descriptors, altered spawn-site facts,
altered exact-effect facts, missing or conflicting policy decisions,
unsupported policy values, source-label injection, noncanonical typed IDs, and
non-empty unsupported fields fail closed.
The binding intentionally strips Strata source labels and debug names from
runtime-authoritative structures. Policy dispatch is by typed process and
authority IDs only: admitted decisions can accept or deny dynamic spawn
authority and boundary port-connect authority while preserving typed trace
evidence. Mantle does not read .authority-effect.json or
.authority-policy.json directly at runtime and does not infer authority from
source labels, debug names, report text, or command-line widening flags.
.mta
.mta files are Mantle Target Artifacts. They are executable inputs to Mantle,
not Strata source and not proof or evidence artifacts.
The extension is intentionally language-neutral. Strata can emit .mta; other
frontends can emit .mta too. Mantle must decide whether an artifact is
admissible from its internal header and validation data, not from the filename.
Minimum artifact identity fields:
format=mantle-target-artifact
schema_version=6
source_language=strata
target_requirements.source_language=strata
target_requirements.feature_count=...
target_requirements.feature.0=bounded_mailbox
The schema version identifies the admitted .mta encoding shape. It is not a
Strata language release, a migration counter, or a stability guarantee. In the
current greenfield implementation, 6 is the single admitted artifact schema
baseline. Unsupported schema versions are rejected; artifact producers must emit
the admitted schema.
The target_requirements.* block is a typed frontend-to-runtime binding
surface. Strata emits it from checked IR and lowering facts; Mantle admits it
only when the artifact format and schema version match the admitted artifact
baseline, source_language is valid metadata and matches
target_requirements.source_language, and every required runtime feature is
supported by the current Mantle runtime feature declaration. The current Mantle
runtime declaration treats source language as opaque artifact metadata rather
than an executable dispatch key or language whitelist. Requirement entries are
canonical runtime feature IDs such as
local_execution, bounded_mailbox, local_send, local_spawn,
typed_boundary_tables, and component_composition_metadata. They are not
source names and are never executable dispatch strings.
Executable references, type identity, and state transitions inside .mta use
validated table IDs and typed transition forms. Process transition records are
encoded by transition index and carry a message ID field, an optional
current_state ID guard, an optional exact typed payload guard, and exact
effect usage for their actions.
Validation requires each message to have either a transition base with no
current-state guard or one transition base for every admitted current-state ID.
Within each message/current-state base, validation admits either one
payload-unguarded transition or a payload-specific set where every transition
carries an exact typed payload guard. Payload-specific sets enumerate admitted
concrete payload cases; they do not encode source-payload algebra. Runtime
selection indexes the admitted transition table by typed message ID, typed
current state ID when a state guard is present, and exact typed payload identity
when a payload guard is present.
Artifact type identity is carried by a Mantle type table. Process
state_type_id, message_type_id, message payload_type_id, payload template
type_id, state value type_id, and received-payload send-target
target_payload_type_id fields refer to that table by numeric TypeId. Type
labels are metadata only; Mantle does not parse source type strings such as
process-reference spellings to decide runtime behavior.
State value tables carry a type ID, typed value identity, ordered value label, and optional typed payload metadata for each admitted state. The label must match the rendering of the typed value, preserving record field and map entry order from the admitted value. Mantle admission and runtime next-state resolution use the type ID and value identity. State-match payload templates use the admitted current state’s typed payload metadata. Preserved order is visible in labels and traces, but runtime projection uses admitted typed IDs: record fields use record-field IDs into the admitted record shape, and map projections use typed static keys. Labels remain trace and diagnostic metadata, not runtime dispatch keys.
Process references are encoded as per-process reference tables. A spawn action binds a process-reference ID to a runtime process instance for the transition. A send action targets either a process-reference ID or a received typed process-reference payload plus a message ID. Reference debug names remain metadata; runtime delivery uses admitted IDs and runtime process instance IDs.
Local spawn authority is encoded as per-process authority tables and spawn-site
tables. A spawn action references a spawn-site ID, which references an authority
ID with a typed Spawn descriptor for the same target process. Authority names
remain metadata; admission and runtime checks use typed IDs and descriptors.
Protocol, port, component, and composition boundary data is encoded as typed tables. Component imports and composition bindings use port and component-instance IDs; names remain metadata for diagnostics and traces. Mantle validates complete composition binding and protocol compatibility before runtime execution, but it does not resolve Strata component names, import names, or source strings at runtime.
Local supervisor children are encoded as per-process supervisor plans with typed supervisor IDs, child IDs, child modes, restart intensity, and lexical supervisor-child spawn-site classifications. A lexical child spawn site carries supervisor and child IDs instead of a dynamic authority ID.
Message variants may carry an optional payload type ID. Send actions may carry an immutable payload value template, and Mantle delivers the evaluated value in a runtime message envelope. Process-reference payloads carry the admitted target process ID and runtime process ID. Message dispatch uses admitted typed IDs and, for payload-specific transitions, admitted typed payload identity, not payload text, source strings, or debug labels.
Each transition’s action_count is bounded during decode before allocation.
Validation also caps the aggregate action count across all transitions for a
process as an admitted process resource budget.
Names are retained for diagnostics, traces, and metadata. Mantle execution must load and run resolved IDs rather than dispatching by source text.
Generated .mta files should normally remain under target/. Checked-in
.mta files are allowed only as explicitly labeled fixtures or specimens and
must not be used as a substitute for a successful strata build and
mantle run.
Expected MIME type:
application/vnd.mantle.target-artifact
Source-To-Runtime Gates
A runtime-bearing milestone is not complete until the command a user would run succeeds. Documentation and generated artifacts do not replace executable source-to-runtime behavior.
The normal gate shape is:
flowchart LR
Source[".str source"]
Check["strata check"]
Build["strata build"]
Artifact[".mta artifact"]
Run["mantle run"]
Trace["observability trace"]
Source --> Check --> Build --> Artifact --> Run --> Trace
For fail-closed runtime behavior, the source must check and build, and
mantle run must fail only after Mantle validates the artifact and emits trace
evidence for the failure. Source-level rejection gates are different: they must
fail before build/lowering and must not leave a target artifact behind.
Canonical Gates
The canonical user-facing gate is:
just source-to-runtime-gates
The split entrypoints are available when only one side of the gate is needed:
just source-to-runtime-success-gates
just source-to-runtime-failure-gates
The maintained command list belongs in the executable gate definitions:
Justfileowns the commands a user or CI runner executes.crates/strata-mantle-acceptance/tests/source_to_runtime_gates.rsis the root integration harness, with focused gate families undercrates/strata-mantle-acceptance/tests/source_to_runtime_gates/.docs/src/examples.mdowns the curated list of runnable examples. The grouped example pages document the behavior each example demonstrates.
This page intentionally does not enumerate every runtime gate. When a new user-visible language or runtime behavior needs source-to-runtime proof, update the executable gate and document the example in the grouped example pages.
The language surface proof substrate checks that the agreed current-surface proof domains map to declared features and that those features point at the required parser, checker, lowering, boundary, artifact, runtime, diagnostics, example, test, fuzz, and bounded/property evidence classes where those classes apply. It also checks that each proof domain declares the proof obligations implied by those features. That substrate supports implementation claims; it does not replace this executable gate shape.
Representative Commands
The minimum success gate checks, builds, runs, and traces hello.str:
just run-example hello
A richer state/payload gate follows the same shape:
just run-example state_payload_match
The source-unit import gate starts at a root source file and proves that Strata, not Mantle, resolves the reachable dependency graph before lowering:
just run-example imports_main
The source contract data primitive gate proves immutable String and Bytes
values survive source checking, lowering, Mantle artifact admission, runtime
execution, and observability as typed primitive data rather than string dispatch:
just run-example source_contract_data_primitives
BDD scenario: Given immutable String and Bytes state/message values in a
checked .str program, when Strata builds the typed .mta and Mantle runs it,
then the trace and output show typed primitive data survived checking, lowering,
admission, runtime branch evaluation, and process termination without source
string dispatch.
The typed boundary contract gate proves protocol, port, component, and
process-local PortConnect declarations check before lowering and run through
Mantle-admitted typed IDs:
just run-example boundary_contracts_main
The component composition gate proves source-unit reachability plus typed component import binding before lowering, then runs the admitted artifact without runtime source-name resolution:
strata check examples/component_composition_main.str
strata build examples/component_composition_main.str
strata composition build examples/component_composition_main.str
strata composition admit target/strata/component_composition_main.component-composition.json --format json
strata composition bind-runtime \
target/strata/component_composition_main.component-composition.json \
target/strata/component_composition_main.mta \
--output target/strata/component_composition_main.deployment-composition.json
mantle run target/strata/component_composition_main.mta \
--composition-binding target/strata/component_composition_main.deployment-composition.json
strata target-requirements examples/component_composition_main.str --format json
mantle feature-declaration --format json
mantle admit target/strata/component_composition_main.mta --format json
This target-binding gate keeps the boundary explicit. Strata reports the typed
runtime features required by the checked program, emits and admits the
Strata-owned strata.checked_component_composition JSON artifact as
checked-subset component-composition validation evidence, and then emits an
explicit mantle.runtime_composition_binding deployment binding for the matching
.mta. Mantle separately reports the typed features it currently supports,
admits the .mta requirements before execution, and emits deployment,
composition, and component-instance trace correlation only when the explicit
binding is supplied. The checked composition artifact is not Mantle input;
Mantle does not infer source composition, verify Strata composition safety, or
dispatch from source names.
Source imports, component names, authority labels, composition artifacts,
deployment bindings, and report data remain diagnostics, provenance, review
records, admission evidence, or observability correlation evidence; they are not
runtime dispatch inputs.
The authority/effect admission-binding gate proves checked authority and exact
effect facts cross the Strata/Mantle boundary as admitted typed artifacts and a
matching runtime binding. The dynamic-local branch exercises
effect_outcome_spawn_denied:
strata check examples/effect_outcome_spawn_denied.str
strata build examples/effect_outcome_spawn_denied.str
strata authority-effects build examples/effect_outcome_spawn_denied.str
strata authority-effects admit target/strata/effect_outcome_spawn_denied.authority-effect.json --format json
strata authority-effects policy build \
target/strata/effect_outcome_spawn_denied.authority-effect.json \
--output target/strata/effect_outcome_spawn_denied.authority-policy.json
strata authority-effects policy admit \
target/strata/effect_outcome_spawn_denied.authority-policy.json \
target/strata/effect_outcome_spawn_denied.authority-effect.json \
--format json
strata authority-effects bind-runtime \
target/strata/effect_outcome_spawn_denied.authority-effect.json \
target/strata/effect_outcome_spawn_denied.authority-policy.json \
target/strata/effect_outcome_spawn_denied.mta \
--output target/strata/effect_outcome_spawn_denied.authority-effect-binding.json
mantle run target/strata/effect_outcome_spawn_denied.mta \
--authority-effect-binding target/strata/effect_outcome_spawn_denied.authority-effect-binding.json
strata authority-effects policy build \
target/strata/effect_outcome_spawn_denied.authority-effect.json \
--deny-spawn-authority \
--output target/strata/effect_outcome_spawn_denied.authority-deny-policy.json
strata authority-effects policy admit \
target/strata/effect_outcome_spawn_denied.authority-deny-policy.json \
target/strata/effect_outcome_spawn_denied.authority-effect.json \
--format json
strata authority-effects bind-runtime \
target/strata/effect_outcome_spawn_denied.authority-effect.json \
target/strata/effect_outcome_spawn_denied.authority-deny-policy.json \
target/strata/effect_outcome_spawn_denied.mta \
--output target/strata/effect_outcome_spawn_denied.authority-effect-deny-binding.json
mantle run target/strata/effect_outcome_spawn_denied.mta \
--authority-effect-binding target/strata/effect_outcome_spawn_denied.authority-effect-deny-binding.json
The same gate also exercises the lexical supervisor-child branch:
strata check examples/local_supervision_restart.str
strata build examples/local_supervision_restart.str
strata authority-effects build examples/local_supervision_restart.str
strata authority-effects admit target/strata/local_supervision_restart.authority-effect.json --format json
strata authority-effects policy build \
target/strata/local_supervision_restart.authority-effect.json \
--output target/strata/local_supervision_restart.authority-policy.json
strata authority-effects policy admit \
target/strata/local_supervision_restart.authority-policy.json \
target/strata/local_supervision_restart.authority-effect.json \
--format json
strata authority-effects bind-runtime \
target/strata/local_supervision_restart.authority-effect.json \
target/strata/local_supervision_restart.authority-policy.json \
target/strata/local_supervision_restart.mta \
--output target/strata/local_supervision_restart.authority-effect-binding.json
mantle run target/strata/local_supervision_restart.mta \
--authority-effect-binding target/strata/local_supervision_restart.authority-effect-binding.json
The same gate also exercises component authority surfaces with both explicit runtime sidecars supplied to Mantle:
strata check examples/component_composition_main.str
strata build examples/component_composition_main.str
strata composition build examples/component_composition_main.str
strata composition admit target/strata/component_composition_main.component-composition.json --format json
strata composition bind-runtime \
target/strata/component_composition_main.component-composition.json \
target/strata/component_composition_main.mta \
--output target/strata/component_composition_main.deployment-composition.json
strata authority-effects build examples/component_composition_main.str
strata authority-effects admit target/strata/component_composition_main.authority-effect.json --format json
strata authority-effects policy build \
target/strata/component_composition_main.authority-effect.json \
--output target/strata/component_composition_main.authority-policy.json
strata authority-effects policy admit \
target/strata/component_composition_main.authority-policy.json \
target/strata/component_composition_main.authority-effect.json \
--format json
strata authority-effects bind-runtime \
target/strata/component_composition_main.authority-effect.json \
target/strata/component_composition_main.authority-policy.json \
target/strata/component_composition_main.mta \
--output target/strata/component_composition_main.authority-effect-binding.json
mantle run target/strata/component_composition_main.mta \
--composition-binding target/strata/component_composition_main.deployment-composition.json \
--authority-effect-binding target/strata/component_composition_main.authority-effect-binding.json
This gate keeps the ownership split explicit. Strata emits and admits
strata.checked_authority_effects facts from checked IR; Strata emits and admits
the closed strata.authority_policy_decisions table over typed process
authority IDs; the runtime binding then correlates those facts and decisions
with a matching .mta. Mantle consumes only
mantle.runtime_authority_effect_binding, only when supplied through
--authority-effect-binding, and validates the binding before ArtifactLoaded
or runtime side effects. Dynamic spawn and boundary port-connect decisions are
enforced by typed IDs and traced with authority_policy_decision_id. Source
labels, authority names, report text, and debug names remain
diagnostics/provenance metadata and cannot retarget authority, policy, or effect
facts.
An immutable source computation gate proves sequential source-local bindings are resolved before lowering while Mantle executes only typed artifact data:
just run-example function_local_bindings
A scalar computation gate proves typed scalar payloads, immutable process-local scalar functions, runtime scalar predicates, runtime-bound value conditionals, and Mantle execution of typed scalar templates:
just run-example runtime_scalar_priority
A selected return-match arm-prefix gate proves source-selected dispatch before Mantle executes typed runtime arm actions:
just run-example process_return_match_arm_runtime_if_prefix
just run-example process_return_match_arm_for_prefix
just run-example process_return_match_arm_for_if_prefix
just run-example process_return_match_arm_if_for_prefix
A runtime control-flow gate checks, builds, validates, runs, and traces typed Mantle execution:
just run-example runtime_guard_noop
just run-example runtime_scalar_priority
just run-example runtime_nested_if_actions
just run-example runtime_final_if_guarded_loop
just run-example runtime_final_if_nested_if_actions
just run-example runtime_final_if_nested_terminal_if
just run-example runtime_for_each
just run-example runtime_for_each_empty
just run-example runtime_for_each_if
just run-example runtime_for_each_nested_if_actions
just run-example runtime_guarded_for_each
just run-example runtime_guarded_ref_loop
just run-example runtime_guarded_ref_loop_jobs
just run-example runtime_loop_element_projection
just run-example effect_outcomes
just run-example effect_outcome_mailbox_full
just run-example effect_outcome_stopped_target
just run-example process_ref_stale_lifecycle
# Default-limit acceptance only; the exhausted/backend-unavailable branches use dedicated gates below.
just run-example effect_outcome_spawn_exhausted
just run-example effect_outcome_spawn_backend_unavailable
just run-example local_supervision_restart
just run-example local_supervision_permanent_stop
just run-example local_supervision_temporary
just run-example local_supervision_transient_restart
just run-example local_supervision_transient
just run-example local_supervision_inactive_send_outcome
just run-example local_supervision_inactive_crashed_send_outcome
just process-ref-lifecycle-gates runs process_ref_stale_lifecycle plus the
trace-level assertion that a transported old runtime PID stops instead of
retargeting to a newer same-definition worker.
run-example uses default runtime limits and policies. For
effect_outcome_spawn_exhausted and
effect_outcome_spawn_backend_unavailable, it proves the checked source still
runs with available local spawn capacity/backend; the source-visible failure
branches use the dedicated commands below.
The local spawn authority denial gate uses the same check/build path and then runs Mantle with a direct denial runtime limit. The authority/effect binding gate above is the product path for typed admitted spawn-authority policy:
just strata-check examples/effect_outcome_spawn_denied.str
just strata-build examples/effect_outcome_spawn_denied.str
just mantle-run-deny-spawn-authority target/strata/effect_outcome_spawn_denied.mta
The local spawn capacity exhaustion gate uses the same check/build path and then runs Mantle with a deliberately exhausted process limit:
just strata-check examples/effect_outcome_spawn_exhausted.str
just strata-build examples/effect_outcome_spawn_exhausted.str
just mantle-run-max-runtime-processes target/strata/effect_outcome_spawn_exhausted.mta 1
The local spawn backend-unavailable gate uses the same check/build path and then runs Mantle with the bounded local backend disabled before acceptance:
just strata-check examples/effect_outcome_spawn_backend_unavailable.str
just strata-build examples/effect_outcome_spawn_backend_unavailable.str
just mantle-run-disable-local-spawn-backend target/strata/effect_outcome_spawn_backend_unavailable.mta
The exhausted-spawn, backend-unavailable-spawn, stopped-target send,
stale-process-ref send, and inactive-crashed-child send gates also assert
effect_outcome_bound trace events. The denied-port-policy send-outcome gate
instead proves fail-closed authority denial before source-visible outcome
binding or delivery; runtime-unit and trace-validation coverage separately
exercise closed-mailbox runtime causes. These trace events carry the numeric
outcome_id, the spawn or send action kind, and the closed
outcome_result category so the branch result is attributable to Mantle runtime
cause rather than inferred only from output text. For stale process references,
the proof combines the received payload_pid, the later stopped send outcome,
and the absence of delivery to the newer runtime PID.
A source rejection gate must fail during checking and must not create a target artifact:
just strata-check examples/failures/effect_authority_missing.str
just strata-check examples/failures/source_local_binding_process_ref_carrier_enum.str
just strata-check examples/failures/scalar_overflow.str
just strata-check examples/failures/scalar_type_mismatch.str
just strata-check examples/failures/scalar_divide_by_zero.str
just strata-check examples/failures/scalar_runtime_divide_by_zero.str
just strata-check examples/failures/scalar_runtime_modulo_by_zero.str
just strata-check examples/failures/scalar_unsuffixed_literal.str
A runtime fail-closed gate checks and builds successfully, then returns non-zero from Mantle after writing trace evidence:
just run-example actor_panic_no_replay
Each successful mantle run command must validate the generated .mta, execute it,
and emit an observability trace under target/strata/. Expected-failure gates
must return non-zero with source diagnostics or runtime failure evidence at the
layer being tested.
Acceptance tests that read trace evidence validate the Mantle-owned JSONL trace
schema before trusting the trace summary. That validation checks schema identity,
exact per-event field sets, required fields, typed ID field shapes, grouped
payload/loop fields, artifact_loaded first/no-repeat ordering,
Mantle-contiguous spawned PID sequencing, u32 artifact typed-ID width, u16
branch path segments and bounded path length, non-entry spawn parent evidence,
runtime PID-to-process-ID correlation, supervisor-child restart causality, and
restart-window numeric bounds/coupling only; it does not make trace JSON an
artifact boundary or a source semantics input.
When adding a new user-visible language or runtime behavior, add or update an example that follows this shape. A passing unit test is useful, but it does not replace a runnable source-to-runtime command when the behavior is user-facing.
Language Surface Proof Substrate
The current language surface proof substrate is an executable, machine-readable
model of the implemented Strata and Mantle surface. It maps the agreed proof
domains for the current language surface to declared feature entries, maps those
entries to typed proof obligations, and maps those obligations to the evidence
classes required for that feature: parser coverage, checker/static validation,
checked IR and lowering coverage, Strata/Mantle boundary preservation, Mantle
artifact admission, runtime execution, diagnostics, examples, positive and
negative tests, source-to-runtime gates, fuzz seeds, and bounded or property
coverage where that evidence applies.
Typed scalar values and value operators are part of the runtime-bearing
surface: their source syntax, checked folding, typed lowering, Mantle artifact
admission, typed value-if templates, runtime evaluation, and fail-closed
diagnostics are all recorded in the inventory.
Source contract data primitives are recorded as a runtime-bearing typed data
surface: immutable String and Bytes literal syntax, exact primitive equality,
checked primitive value shapes, lowercase-hex artifact labels, Mantle artifact
admission, runtime typed-value execution, observability evidence, source-to-
runtime gates, fuzz seeds, bounded/property coverage, performance-smoke
profiles, diagnostics, and docs are tracked together. Source strings, labels,
and primitive payload bytes remain data or metadata and never become executable
runtime dispatch inputs.
Typed effect outcomes are also runtime-bearing: source-visible send/spawn
Result bindings, checked outcome templates, Mantle artifact admission, runtime
commit-or-return behavior, spawn success process-reference evidence,
source-to-runtime success plus Full pre-acceptance capacity,
stopped-target Stopped, runtime-unit MailboxClosed supervisor-closure,
inactive-child Crashed, denied/exhausted/backend-unavailable spawn failure
evidence, parse/check/lower, artifact-decode,
and runtime-from-source fuzz seeds, bounded
state-admission evidence, and diagnostics are recorded together.
Local spawn authority is recorded as a runtime-bearing boundary feature:
process-local Cap<Spawn<Target>> declarations, exact checked target matching,
typed authority IDs, typed spawn-site IDs, Mantle artifact and loaded-runtime
admission, overbroad-authority rejection, spawn_authority_checked trace
evidence, Err(Denied(Unit)) runtime denial before acceptance,
source-to-runtime gates, fuzz seeds, bounded authority-model evidence,
diagnostics, and docs are tracked together.
Authority policy decisions are recorded as a runtime-bearing Strata/Mantle
boundary feature: checked authority IDs, spawn-site IDs, transition
message/current-state/effect IDs, component authority surfaces with declared
import-port counts, and exact effect facts are rendered as the Strata-owned
strata.checked_authority_effects artifact, admitted before policy admission,
covered by a closed strata.authority_policy_decisions typed decision table,
correlated with the matching .mta, and rendered as the Mantle-facing
mantle.runtime_authority_effect_binding artifact. Mantle admits the runtime
binding by typed IDs and descriptor/effect equivalence before execution. The
dynamic-local branch applies admitted policy decisions before spawn side effects
and records typed spawn_authority_checked evidence; boundary sends apply the
typed port_connect decision before message acceptance; both bare sends and
typed send outcomes fail closed on denial without binding a source-visible
mailbox outcome while recording boundary_send_checked evidence with
authority_policy_decision_id; the
lexical supervisor-child branch records typed supervisor_child_started and
supervisor_restart_decision evidence instead of inventing dynamic authority.
Source labels, debug names, diagnostics, and report text remain metadata; they
cannot retarget authorities, spawn sites, ports, components, effects, or policy
decisions. source_path is slash-normalized provenance metadata, and the
checked schema namespace is frontend-owned as
<source_language>.checked_authority_effects. Mantle rejects raw
.authority-effect.json and .authority-policy.json files supplied as runtime
bindings before trace creation. Source-to-runtime gates for admitted and denied
dynamic-local spawn authority, denied boundary port authority, denied
port-authority send outcomes, lexical supervisor-child spawning, and component
authority surfaces; forged-binding negative tests; authority/effect,
authority-policy, dynamic-local, lexical supervisor-child, and component
runtime-binding fuzz seeds; performance-smoke profiles; diagnostics; and docs
are tracked together.
Local supervision is recorded as a runtime-bearing boundary feature: local
one_for_one supervisor declarations, lexical child IDs, child restart modes,
explicit restart intensity, lexical spawn-site classification, Mantle artifact
and loaded-runtime admission, acyclic child-graph validation, restart trace
evidence, no message replay after panic, source-to-runtime gates, fuzz seeds,
diagnostics, and docs are tracked together.
Source-unit imports are recorded as a Strata-owned composition feature:
import module_name; syntax, root source loading, typed source-unit IDs, an
acyclic dependency graph, deterministic dependency-first checking, duplicate and
ambiguous-name rejection, direct-import symbol validation, source-to-runtime
execution from a multi-file program, fuzz seeds, diagnostics, and docs are
tracked together. Mantle import resolution is not part of the surface; module
names in artifacts remain metadata.
Typed protocol, port, and component boundaries are recorded as a
runtime-bearing boundary feature: source declarations, optional send ... via Port syntax, process-local Cap<PortConnect<Port>> authority, checked
protocol/port/component IDs, direct-import validation, deterministic lowering
into Mantle boundary tables, artifact and loaded-runtime admission,
accepted boundary_send_checked trace evidence, fail-closed admission
diagnostics for denied boundary shapes, source-to-runtime execution, fuzz seeds,
bounded deterministic lowering evidence, diagnostics, and docs are tracked
together. Mantle does not resolve protocol, port, component, or import names at
runtime; those names remain metadata.
Checked local component port-binding composition admission is recorded as a
runtime-bearing boundary feature: component import lists, local composition
declarations, implementation-local source admission input, typed
component-instance IDs, typed port-binding edges, direct-import validation,
duplicate and unbound import rejection, unimported-port binding rejection,
protocol mismatch rejection, deterministic graph lowering, Mantle
composition-table admission, Strata-owned composition admission report emission,
Strata-owned strata.checked_component_composition artifact build/admit
coverage, source-to-runtime execution, fuzz seeds, performance-smoke evidence,
diagnostics, and docs are tracked together. The report and the durable artifact
record the diagnostic FNV-1a source fingerprint, typed component-instance IDs,
component import/export port obligations, typed port-binding IDs, admitted
binding results, empty unsatisfied imports for admitted compositions, endpoint
port authority requirements, and cross-component authority edges for
review/admission. The artifact is emitted as
target/strata/<stem>.component-composition.json, self-identifies with schema
ID/version/source-language fields, and fails closed when typed IDs are missing,
duplicated, malformed, stripped of required binding evidence, internally
inconsistent with authority descriptors, or inconsistent with admission results.
It validates the artifact schema and internal typed-ID consistency; it is not a
source re-check, not a tamperproof attestation for a coherently rewritten JSON
file, and not directly executable Mantle input. strata composition bind-runtime
adds the explicit deployment-admission bridge by validating an admitted checked
composition artifact against a matching .mta and rendering the separate
mantle.runtime_composition_binding JSON artifact. That binding must match the
.mta module, source fingerprint, composition ID, component instances, runtime
process IDs, and port bindings before Mantle can use it for observability
correlation. The checked composition artifact remains Strata-owned and not
.mta; checked IR lowering, Mantle artifact admission, and explicit runtime
binding admission remain separate evidence classes. Mantle admits typed
composition metadata inside .mta but does not resolve source component names,
source-unit imports, source strings, Strata composition artifacts, or report data
at runtime.
Runtime feature declaration and target binding are recorded as a
runtime-bearing boundary feature: Strata derives canonical typed target
requirements from checked IR and lowering facts, embeds them in the .mta,
renders them through strata target-requirements, Mantle renders its own typed
mantle.feature_declaration.v6 declaration, and Mantle admission compares the
artifact’s format, schema version, source language, declared runtime features,
and artifact-derived minimum required features before execution. The runtime
declaration includes conservative message-observation fields and explicit
validity-window defaults without claiming distributed transport support.
Mismatched or malformed source-language metadata, unsupported runtime
features, underdeclared requirements, malformed requirement fields, remote
send/spawn, and distributed transport remain fail-closed implementation limits.
The evidence includes unit, negative, bounded determinism, source-to-runtime,
acceptance, fuzz-seed, performance-smoke, docs, and assurance matrix coverage;
it does not claim theorem-prover coverage.
Remote/distributed runtime capabilities are recorded as a future/non-admitted
boundary: Strata lowering must not emit remote_send, remote_spawn, or
distributed_transport, and Mantle’s mantle.local_only.v1 target profile must
reject forged artifacts requiring those canonical feature IDs before admission,
ArtifactLoaded, host sinks, or runtime side effects. The focused remote /
distributed boundary gate checks current .str entry examples against the
local-only profile and separately forges remote/distributed artifact
requirements to prove fail-closed admission. This preserves the local-runtime
boundary until a real distributed Mantle design exists.
Mantle executable-plan construction is recorded as an internal runtime
implementation feature: Mantle validates a loaded .mta, builds typed
dispatch/action tables and a Mantle-owned executable template program from the
admitted LoadedProgram, and executes from those pre-resolved plan references.
The evidence covers plan construction, deterministic transition ordering, typed
action tables, executable next-state/send/for-each template refs,
source-name/debug-label retargeting attempts, invalid loaded-template reference
rejection before ArtifactLoaded, runtime execution equivalence,
source-to-runtime gates, performance-smoke coverage, and docs. This evidence
does not add a serialized bytecode format, a Strata lowering target, new source
semantics, new fuzz decoder seeds, or new unsafe indexing assumptions; it
remains bounded machine-checked assurance.
Runtime trace observability is recorded as a Mantle-owned runtime feature:
runtime events are strongly typed before rendering, JSONL output carries the
mantle-runtime-observability schema ID and version, and the validator checks
required fields, exact per-event field sets, grouped payload/loop fields, typed
ID fields, strict unsigned integer syntax, u16 branch path segments plus
bounded path length, closed runtime enum value domains, u32 artifact typed-ID
width, artifact process-ID bounds, artifact_loaded first/no-repeat ordering,
Mantle-contiguous spawned PID sequencing, non-entry spawn parent evidence,
runtime PID-to-process-ID correlation, optional runtime-composition correlation
field grouping plus whole-trace composition identity stability, supervisor-child
restart causality, and restart-window numeric bounds/coupling using borrowed
line scans over already-read trace text.
Deployment, composition, and component-instance trace fields appear only when
Mantle was explicitly launched with an admitted mantle.runtime_composition_binding
artifact; absent binding input leaves those fields absent and forbids fabricated
IDs.
Validation is capped by explicit trace-byte, event-count, and runtime-process
limits before trace evidence is used by a gate.
The evidence includes unit and negative schema tests, source-to-runtime trace
validation, fuzz seeds for malformed traces, bounded rendered-all-event
contract evidence for metadata-vs-typed-ID separation, JSONL performance-smoke
coverage, docs, and assurance matrix coverage. No new unsafe code, arena
indexing assumption, or unchecked correlation path is introduced by the trace
validator, so this slice does not add a new Miri-specific target beyond the
existing just miri-ci disposition. The trace contract remains observability
evidence only; it is not an artifact format, Strata lowering target, source
semantic surface, or executable dispatch input.
Run it with:
just language-surface-assurance
The machine-readable inventory is rooted in:
crates/strata-mantle-acceptance/tests/language_surface_assurance.rs
The proof-domain declarations and feature declarations are split by surface under:
crates/strata-mantle-acceptance/tests/language_surface_assurance/
The gate fails when a declared proof domain is missing, when a declared language
surface feature is not included in a proof domain, when a domain omits a proof
obligation implied by its features, when an obligation has no supporting
evidence class, when a feature is missing one of its required evidence classes,
when an evidence path disappears, or when the recorded marker no longer exists
in the cited file. Source-to-runtime evidence must point at the active
Justfile check/build/run commands for the cited source example. The inventory
also fails if an evidence class is incompatible with the feature’s declared
surface layer, so source-only, checker/lowering, artifact-admission, runtime,
docs/example, and future/non-admitted classifications remain distinct.
This gate is the current-language-surface proof substrate for implementation claims. It is not a full theorem-prover proof of every semantic rule, and it does not replace runnable source-to-runtime behavior:
flowchart LR
Source[".str source"] --> Check["strata check"]
Check --> Build["strata build"]
Build --> Run["mantle run"]
Run --> Trace["trace"]
Runtime-bearing features still need source-to-runtime evidence. Trace labels, source names, and documentation text remain metadata and diagnostics surfaces; executable behavior must cross the Strata/Mantle boundary as checked IR, typed IDs, typed value templates, and admitted Mantle artifacts. Record field projection is represented as admitted record-field IDs; source field names remain labels, diagnostics, and trace metadata.
Artifact And Runtime Boundary
Strata owns source syntax, diagnostics, semantic checking, checked IR, and source-visible meaning. Lowering owns conversion from checked Strata IR into Mantle Target Artifacts. Mantle owns artifact admission, runtime execution, process and mailbox state, host boundaries, and observability.
This separation keeps names, metadata, and runtime identity from collapsing into one surface. Source names are useful for diagnostics and traces, but executable runtime dispatch must use loaded typed IDs.
flowchart LR
Source["Strata source names"]
Checked["checked symbols and IDs"]
Artifact["artifact typed tables"]
Runtime["loaded runtime IDs"]
Trace["trace labels"]
Source --> Checked --> Artifact --> Runtime
Source -. metadata .-> Trace
Artifact -. labels .-> Trace
Runtime --> Trace
Mantle crates are structurally language-neutral. They may carry source_language
as opaque artifact metadata, but they must not own Strata source constants,
Strata output-directory defaults, .str examples, or source-to-runtime gates.
Strata-owned defaults live in crates/strata; cross-boundary gates live in
crates/strata-mantle-acceptance.
Artifact type identity is structural in Mantle. Lowering emits a Mantle type
table and artifact records refer to entries by TypeId; Mantle admission and
runtime execution do not parse source type spellings or ProcessRef<...> text
to decide behavior. Type labels remain diagnostics and trace metadata only.
Artifact source_hash_fnv1a64 is a non-authoritative diagnostic fingerprint for
correlating a lowered artifact with source text during local inspection. It is
not an integrity, provenance, authority, or trust decision input; artifact
admission must rely on explicit format/schema validation and typed structures.
Target requirements are checked/lowered facts embedded in the artifact. Strata
emits target_requirements.source_language and a sorted set of canonical
runtime feature IDs such as bounded_mailbox, local_execution, local_send,
local_spawn, emit_effect, and typed_boundary_tables. Mantle compares those
requirements with its mantle.local_only.v1 runtime target profile before
ArtifactLoaded or executing runtime side effects; an unsupported feature fails
closed with a diagnostic such as target runtime feature remote_spawn is not supported.
Admission
Mantle admits artifacts through validation, not filename trust. Before execution, the artifact decoder and validator check:
- artifact magic, format, schema version, and source language;
- typed target requirements, including target source language and canonical runtime feature IDs;
- bounded process, message, state, output, transition, and action counts;
- bounded type table entries and type-kind targets;
- unique process debug names;
- unique typed state value identities per process;
- unique process reference names per process;
- unique typed authority descriptors, referenced authority IDs, and spawn-site table entries per process;
- typed protocol, port, and component boundary tables, including exact required-authority descriptors and port target process/message compatibility;
- typed component composition tables, including declared component instances, imported port bindings, protocol compatibility, and complete binding of every component import;
- either one unguarded transition per accepted message or one state-specific transition for each admitted state value;
- exact transition effect usage for emitted, spawned, and sent actions;
- transition references to known messages, state values, type IDs, process references, authority IDs, spawn-site IDs, outputs, and process IDs.
Decode-time bounds must happen before allocation when counts come from the artifact body.
Mantle also publishes a typed mantle.feature_declaration.v6 runtime feature
declaration. The current declaration names the artifact format and schema
version it admits, its opaque source-language metadata policy, local runtime
feature support, target-profile identity, host/network/transport/cluster
authority policy, non-progress containment class, message-count mailbox model,
conservative message-observation support, allocation model, component/spawn
observability support, validity-window defaults, backend identity, and explicit
implementation limits such as remote send, remote spawn, and distributed
transport. Admission derives the minimum runtime feature set required by the
decoded .mta tables and requires the artifact’s typed target_requirements
block to cover that set before ArtifactLoaded or any runtime side effect.
Unsupported schema versions, unsupported required features, underdeclared
requirements, unsorted or duplicate requirement entries, mismatched
source-language metadata, and malformed requirement fields fail closed.
Strata owns requirement derivation. It derives requirements from checked IR and lowering facts such as local execution, bounded mailboxes, emitted effects, local spawn/send, typed effect outcomes, scalar value templates, runtime branching, bounded runtime loops, typed boundary tables, and component composition metadata. Mantle owns only the declaration and comparison; it does not infer Strata source composition, imports, source names, authority widening, or deployment safety.
Internal Executable Plan
After a .mta decodes into a LoadedProgram, Mantle validates loaded admission
and then constructs an internal executable plan for runtime dispatch. The plan
pre-resolves transition dispatch, action blocks, process-reference targets,
spawn-site authority references, branch bodies, loop bodies, and value-template
program references into typed runtime structures. Runtime value templates are
compiled into Mantle-owned ExecutableTemplateProgram entries and runtime
actions/next-state plans carry ExecutableValueTemplate references rather than
walking recursive loaded artifact templates on the hot path. Process and message
labels may be borrowed for reports and traces, but they are not executable
dispatch keys.
The executable plan and executable template program are not serialized bytecode,
not Strata lowering targets, and not replacements for artifact validation.
Constructing them cannot grant authority or bypass feature, artifact, or
loaded-program admission. Invalid loaded references fail before ArtifactLoaded
and before host-visible runtime side effects. Source names, process debug names,
message labels, template labels, and other display strings remain diagnostics
and trace metadata; runtime dispatch and template execution use admitted typed
IDs and executable-plan references. Canonical type, variant, and field labels
are materialized only when Mantle constructs the bounded RuntimeValue
representation for payload/state data or trace/report rendering; executable
template plans carry typed IDs, not string selectors.
Host Path Handling
Artifact and trace paths are validated before host IO. On Unix targets, Mantle
opens artifact and trace paths with descriptor-relative parent traversal and
O_NOFOLLOW so symlink parents and final symlink leaves fail closed at open
time. On Windows targets, Mantle opens final artifact and trace paths with
reparse-point traversal disabled, rejects reparse-point path components, and
validates the opened handle against the canonical path with stable Windows file
metadata. Other targets fail closed unless they provide equivalent secure path
support.
Execution
Mantle executes from the admitted executable plan. Before emitting
ArtifactLoaded or executing runtime side effects, Mantle validates loaded
entry metadata, state tables, transition state targets and templates, outputs,
record field projections, and all plan-resolved action references. Record field
projection and record construction templates carry typed record-field IDs into
admitted record type shapes; record field names remain metadata for value
labels, diagnostics, and traces. Process references, sends, payload templates,
and transition effect usage are validated as typed IDs or admitted templates
before execution.
Loaded authority tables and spawn-site tables are validated before any runtime
side effect. A spawn action references a typed spawn-site ID; that site
references a typed authority ID whose descriptor must be an exact local
Spawn capability for the same target process. A typed boundary send through a
loaded PortId requires an exact process-local PortConnect authority for that
same port. Loaded authorities that are not referenced by any spawn site or
typed-port send are rejected as overbroad. Authority debug names and source
labels are metadata, not dispatch inputs.
A dequeued message selects the transition by typed message ID, and by admitted
current state ID when the transition table is state-specific. Dynamic next-state
templates resolve to an admitted state ID by typed state value identity, not by
display label text.
Typed boundary sends carry an optional admitted PortId. Mantle validates that
the port targets the same process as the send, that the port protocol message
type matches the target process message enum, and that the process authority
table admits the same port ID. Runtime dispatch still uses the loaded process
and message IDs; protocol, port, and component names are trace metadata only.
Invalid or denied boundary shapes fail artifact or loaded-program admission
before ArtifactLoaded; accepted typed boundary sends emit
boundary_send_checked during runtime dispatch.
Component composition metadata carries component-instance IDs and port IDs. It is admitted as a typed graph: every instance must point at a component table entry, every imported port edge must target a declared instance, the importing component must declare that imported port, the exporting component must export the bound port, protocols must match, and every component import on every instance must be bound. Runtime dispatch does not look up component names or source import names; the composition graph is metadata/admission data for the already lowered typed IDs. The checked composition admission report is a Strata-owned inspection surface derived before Mantle execution. It records the diagnostic FNV-1a source fingerprint, typed component-instance IDs, typed port-binding IDs, admitted binding results, empty unsatisfied imports for admitted compositions, and endpoint authority edges for review, but it is not an executable artifact and Mantle does not read it.
The component-composition validation artifact is also Strata-owned and is emitted
by strata composition build as
target/strata/<stem>.component-composition.json by default. It self-identifies
as the checked-subset strata.checked_component_composition schema version 1.0
with hash_alg=fnv1a64-diagnostic, carries source provenance as metadata whose
diagnostic source fingerprint must have the declared canonical lowercase
hexadecimal shape, and admits or rejects the checked local composition graph with
typed component-instance, component import/export port, port-binding, port,
protocol, and authority descriptor IDs. It is deliberately not .mta and Mantle
does not read it as runtime input. Binding classes that the current source subset
cannot express are present as empty arrays and fail closed if forged non-empty,
keeping the Strata-owned source evidence boundary explicit without inventing
capability, interface, runtime-feature, archive-format, crypto-policy,
policy-hash, or diagnostic-set facts.
strata composition bind-runtime is the explicit deployment-admission bridge
from admitted checked composition evidence to Mantle observability correlation.
It validates that the checked artifact is globally admitted and matches a
specific .mta source language, module, diagnostic source hash, composition ID,
component-instance table, runtime process correlation, and port-binding table,
then emits target/strata/<stem>.deployment-composition.json with
schema_id=mantle.runtime_composition_binding and
composition_schema_id=strata.checked_component_composition. Mantle accepts that
artifact only when the operator supplies mantle run <artifact.mta> --composition-binding <deployment-composition.json>, validates the exact
binding and checked-composition schema identities again before ArtifactLoaded,
and uses it only to render singleton deployment_id=0,
composition_id, and optional component_instance_id trace correlation.
Without the binding argument, Mantle runs the .mta without composition
correlation fields; it does not infer composition identity from source names,
does not verify Strata composition safety itself, and does not dispatch from
composition metadata.
The authority/effect fact artifact is a separate Strata-owned boundary surface
emitted by strata authority-effects build as
target/strata/<stem>.authority-effect.json by default. It self-identifies as
the checked strata.checked_authority_effects schema version 1.0 with
hash_alg=fnv1a64-diagnostic, records checked process IDs, process-local
state/message counts, checked protocol/port/component table counts, authority
IDs, spawn-site IDs, transition IDs, transition message/current-state
references, exact effect IDs, supervisor-child spawn proof facts for lexical
supervisor spawn sites, component/port authority-surface IDs, declared
import-port counts, and source provenance metadata, and fails closed when typed
IDs are noncanonical, duplicated, outside declared table counts, unknown,
internally inconsistent, missing a supervisor-child backlink, or paired with
unsupported non-empty future fields. It is not .mta, not runtime input, and
not a policy grant. Its source_path field is slash-normalized diagnostic
metadata for review/provenance only; neither Strata lowering nor Mantle
execution uses that path as an executable binding.
strata authority-effects policy build turns admitted checked authority/effect
facts into the separate strata.authority_policy_decisions artifact. Policy
admission validates a closed, canonical decision table over typed
process-authority IDs and exact descriptors. Missing, duplicated, out-of-order,
unknown, unsupported, label-spoofed, stale, or descriptor-mismatched decisions
fail closed; source names and debug labels are metadata only.
strata authority-effects bind-runtime is the explicit runtime-admission bridge
from admitted checked authority/effect facts plus an admitted typed authority
policy artifact to Mantle policy input and observability evidence. It validates
the admitted .authority-effect.json and .authority-policy.json against each
other and against a specific .mta source language, module, source hash,
process table, authority table, spawn-site table, transition effect table, and
component authority-surface table, then emits
target/strata/<stem>.authority-effect-binding.json with
schema_id=mantle.runtime_authority_effect_binding. The checked authority/effect
schema name is a frontend-owned namespace, <source_language>.checked_authority_effects;
Strata emits strata.checked_authority_effects, and Mantle validates a binding
language-neutrally against the loaded artifact’s source_language plus that
suffix rather than hardcoding Strata ownership. Mantle accepts the runtime binding
only when the operator supplies mantle run <artifact.mta> --authority-effect-binding <authority-effect-binding.json>, validates the
binding, checked-fact schema identity, and authority-policy schema identity again
before ArtifactLoaded, and uses admitted policy_decisions only to accept or
deny already-declared runtime-enforceable authorities by typed process and
authority IDs. Dynamic spawn and boundary port-connect decisions are enforced
before runtime side effects and recorded with authority_policy_decision_id.
Source labels, debug names, report text, and authority names are metadata and
cannot grant, widen, strip, or retarget runtime authority.
Transition effect metadata is admitted with the artifact, loaded as runtime
effect usage, and must exactly match the action effects that execute.
Runtime if conditions are admitted as typed Bool value templates. Strata
lowers core Bool to the canonical Mantle fieldless enum/value IDs, and Mantle
validates the typed artifact shape instead of dispatching through source names.
Mantle validates both branch bodies before execution, executes only the selected
branch, admits one direct nested runtime branch action layer, rejects deeper
direct branch nesting, rejects branch-local process-reference binding, and
records branch selection in the runtime trace. Runtime branch bodies
may contain admitted bounded loop actions; Mantle validates loop bodies before
execution and still rejects nested loops. Equality conditions are admitted as
typed value templates over Bool or payload-free enum operands; Mantle
evaluates admitted typed values, not source strings or debug labels.
Boolean predicate composition is admitted as a typed Bool value-template tree
built from !, &&, ||, direct Bool templates, and typed equality templates.
The action set covers:
- emitting declared output;
- spawning a declared process through a process reference and admitted spawn authority;
- binding typed spawn and send effect outcomes in the pre-state action prefix;
- sending a declared message through a bound process reference;
- selecting a typed runtime branch over admitted action blocks;
- iterating over an admitted bounded list template with a typed active loop element binding.
The runtime fails closed on invalid sends, unbound process references, duplicate process-reference bindings, mailbox exhaustion, runtime process instance budget exhaustion, dispatch budget exhaustion, emitted-output budget exhaustion, and trace budget exhaustion.
Observability
Runtime traces are line-delimited JSON. They include labels for readability and
numeric IDs for process, message, state, payload type, and output identity. A
trace is evidence of runtime execution, not a substitute for running the
source-to-runtime gate.
Every emitted event also carries the Mantle-owned trace schema ID
mantle-runtime-observability and schema version 1. The schema identifies the
observability contract only; it is not a .mta schema, serialized bytecode,
source semantic surface, retargeting input, or dispatch table. Trace validators
may check required fields, typed ID field shapes, artifact_loaded
first/no-repeat ordering, Mantle-contiguous spawned PID sequencing, and runtime
PID-to-process-ID correlation. Repository validation rejects fields outside the
per-event trace contract and checks grouped payload/loop fields, strict unsigned
integer syntax, u32 artifact typed-ID width, u16 branch-path segment width,
branch-path length, renderer-valid branch-path segment encoding, non-entry
spawn parent evidence, closed runtime enum value domains, process lifecycle
causality boundaries after terminal stop/fail events, and artifact process-ID
bounds. Lifecycle causality validation requires active parent/sender/supervisor
PIDs for events that imply runtime action. Supervisor restart validation also
requires prior child-start evidence for the same typed supervisor/child slot, a
terminal event for the current child PID, and a distinct active replacement PID
spawned by the supervisor for restarted decisions. It also rejects impossible
restart-window evidence such as zero limits/windows, observed counts above the
configured limit, restarted decisions with zero count, and non-restarted
decisions with nonzero count, while still allowing typed evidence to reference
the terminated child when reporting a supervisor decision.
Validators apply explicit byte, event, and runtime-process limits, remain
read-only, and never turn labels, source names, process names, message labels,
or debug strings into executable meaning.
mantle inspect-authority is a read-only inspection command for admitted
artifacts. It validates the .mta through the same artifact reader used before
execution, then prints the typed authority and spawn-site tables. It does not
dispatch by source names, execute runtime actions, or generate a mandatory
report; JSON output is available only when explicitly requested by the caller.
The source-side composition report is emitted by strata composition-report.
The durable checked-subset source-side component-composition artifact is emitted
by strata composition build and validated by strata composition admit. Both
are separate from mantle inspect-authority, canonical deployment-composition
artifacts, and .mta admission.
Target binding inspection is split along the same boundary:
strata target-requirements <path.str>checks and lowers the source, then renders the typed requirements Strata would place in the.mta.mantle feature-declarationrenders the current Mantle runtime feature declaration.mantle admit <path.mta>decodes and validates the.mta, compares typed target requirements against the Mantle declaration, and returns before executing runtime behavior.
Implementation Architecture
This page maps the source-to-runtime implementation for contributors. It is not required for writing simple Strata programs.
Crate Layout
| Path | Responsibility |
|---|---|
crates/strata | Strata CLI, parser, AST, checker, checked IR, and lowering. |
crates/mantle-artifact | Mantle Target Artifact encode, decode, validation, limits, and typed artifact IDs. |
crates/mantle-runtime | Mantle admission, internal executable planning, runtime process state, mailboxes, dispatch, output, and traces. |
crates/strata-mantle-acceptance | Workspace-owned .str to .mta to Mantle execution acceptance gates and boundary ownership checks. |
examples | Runnable Strata programs used by source-to-runtime gates. |
fuzz | Fuzz targets for parser/checker/lowering, artifact decode, and runtime admission paths. |
tools | Editor and MIME metadata. |
Source Path
flowchart LR
Source["source text"]
Lexer["lexer"]
Parser["parser"]
Ast["AST"]
Checker["semantic checker"]
Checked["checked IR"]
Lowering["lowering"]
Artifact["Mantle Target Artifact"]
Source --> Lexer --> Parser --> Ast --> Checker --> Checked --> Lowering --> Artifact
The parser accepts source shape. The checker assigns source-visible meaning, resolves names, validates process/message/state rules, and produces checked IR. It resolves process reference names and source type references into checked IDs, including checked type identities for value and process-reference payloads. Lowering converts checked IR into Mantle artifact tables by mapping checked process, message, state, output, and type IDs to artifact IDs.
Runtime Path
flowchart LR
Artifact["artifact text"]
Decode["decode"]
Validate["validate"]
Load["load typed runtime tables"]
Plan["build executable plan"]
Spawn["spawn Main"]
Deliver["deliver entry message"]
Dispatch["dispatch by message ID"]
Execute["execute typed actions"]
Host["explicit runtime host"]
Trace["JSONL trace sink"]
Output["program output sink"]
Artifact --> Decode --> Validate --> Load --> Plan --> Spawn --> Deliver --> Dispatch --> Execute --> Host
Host --> Trace
Host --> Output
Mantle must validate artifacts before execution. Runtime dispatch uses loaded
typed IDs, not source strings. Payload and state type identity is admitted
through Mantle type-table IDs; source type labels are metadata only. The
executable plan is an internal Mantle structure built from the admitted
LoadedProgram; it pre-resolves typed dispatch, action, and executable value
template references but is not serialized bytecode and is not emitted by Strata
lowering. Strata still emits typed .mta value templates at the boundary;
Mantle owns only the internal executable template program derived from admitted
artifact data. Runtime execution emits events, program output, monotonic time
queries, and final flushes through an explicit RuntimeHost; the CLI is a
filesystem/stdout host adapter over that admitted core, while in-memory callers
use the same runtime path with in-memory sinks.
Important Boundaries
Strata owns:
- source syntax;
- diagnostics;
- AST;
- semantic checking;
- checked IR;
- source-visible meaning.
Lowering owns:
- conversion from checked Strata IR into Mantle artifact records;
- mapping checked process, message, state, type, and output IDs to artifact IDs.
Mantle owns:
- artifact decoding and validation;
- admitted runtime tables;
- process instances and mailboxes;
- action execution;
- host-mediated runtime events, output, clocks, and traces.
Workspace source-to-runtime acceptance gates own the cross-boundary proof that a Strata program can be checked, lowered, admitted by Mantle, executed, and traced.
flowchart LR
subgraph Strata["Strata frontend"]
Syntax["source syntax"]
Check["semantic checking"]
Ir["checked IR"]
end
subgraph Lower["Lowering boundary"]
Map["typed ID mapping"]
Mta["Mantle Target Artifact"]
end
subgraph Mantle["Mantle runtime"]
Admit["artifact admission"]
Tables["loaded typed tables"]
Plan["internal executable plan"]
Exec["runtime execution"]
Obs["observability"]
end
Syntax --> Check --> Ir --> Map --> Mta --> Admit --> Tables --> Plan --> Exec --> Obs
Do not move source-only assumptions into Mantle as trusted runtime behavior. Do not make Mantle dispatch through labels that exist only for diagnostics or trace readability. Do not move Strata source constants, default output paths, examples, or source-to-runtime gates into Mantle-owned crates.
Adding A Language Feature
A source-facing feature usually needs changes across several layers:
- parser;
- AST;
- checker;
- checked IR;
- lowering;
- artifact schema or validation, when runtime representation changes;
- runtime execution, when behavior changes;
- diagnostics;
- examples;
- docs;
- positive tests;
- negative tests;
- source-to-runtime gates, when user-visible execution changes.
Parser acceptance alone is not enough. If another layer can construct or admit the same invalid state, that layer needs its own validation.
Existing Source-To-Runtime Gates
The runnable examples are:
examples/hello.str;examples/actor_ping.str;examples/actor_sequence.str;examples/actor_match.str;examples/init_match.str;examples/init_return_match.str;examples/function_match.str;examples/function_payload_match.str;examples/function_if_else.str;examples/function_collection_match.str;examples/function_return_match.str;examples/process_return_match.str;examples/process_return_match_arm_prefix.str;examples/process_return_match_arm_runtime_if_prefix.str;examples/process_return_match_arm_for_prefix.str;examples/process_return_match_arm_for_if_prefix.str;examples/process_return_match_arm_if_for_prefix.str;examples/function_record_pattern.str;examples/function_record_return_match.str;examples/function_record_body_match.str;examples/state_payload_enum.str;examples/collection_state.str;examples/state_payload_match.str;examples/actor_instances.str;examples/actor_payloads.str;examples/runtime_if_else.str;examples/runtime_payload_projection_if.str;examples/runtime_payload_projection_next_state.str;examples/runtime_state_payload_projection_if.str;examples/runtime_state_payload_projection_next_state.str;examples/runtime_nested_if_actions.str;examples/runtime_final_if_guarded_loop.str;examples/runtime_final_if_nested_if_actions.str;examples/runtime_final_if_nested_terminal_if.str;examples/runtime_guard_noop.str;examples/runtime_for_each.str;examples/runtime_for_each_empty.str;examples/runtime_for_each_if.str;examples/runtime_for_each_nested_if_actions.str;examples/runtime_guarded_for_each.str;examples/runtime_guarded_ref_loop.str;examples/runtime_guarded_ref_loop_jobs.str;examples/runtime_loop_element_projection.str;examples/actor_payload_match.str;examples/actor_payload_split_match.str;examples/actor_payload_split_signature.str;examples/actor_payload_split_signature_wildcard.str;examples/actor_payload_state_match_split.str;examples/actor_payload_state_match_wildcard.str;examples/nested_patterns.str;examples/actor_reply.str;examples/actor_emit_spawn_send.str;examples/effect_outcome_spawn_denied.str;examples/actor_panic_no_replay.str.
The integration tests rooted at
crates/strata-mantle-acceptance/tests/source_to_runtime_gates.rs, with focused
gate families under
crates/strata-mantle-acceptance/tests/source_to_runtime_gates/, mirror the same
source check, artifact build, and runtime execution sequence:
just run-example hello
The same pattern applies to the actor examples. Fail-closed examples check and
build, but their runtime gate expects mantle run to return non-zero after
emitting trace evidence.
Closure Rule
A change that affects source syntax, artifact schema, runtime behavior, diagnostics, examples, or gates should update this book and pass:
just quality
Docs explain the contract; runnable gates prove the behavior.
Development Gates
Repository automation is centralized in Justfile. GitHub Actions and
lefthook delegate to the same recipes used locally.
The standard local verification bundle is:
just quality
This bundle selects stable Rust explicitly through the Justfile. The workspace
requires Rust 1.85 or newer for Edition 2024. The bundle also runs just toolchain-policy-check, which rejects repository instructions or workflow
steps that switch the whole checkout to nightly Rust. Nightly-only tools must be
invoked per command with +nightly.
The quality bundle includes just cfg-check, which typechecks the workspace for
representative Linux, macOS, and Windows Rust targets through the repository
recipe. This keeps platform-specific and test-only #[cfg] paths covered by
real compiler evidence without changing source visibility or suppressing
rust-analyzer’s weak inactive-code hints. If a target is missing locally,
install the reported target with rustup target add --toolchain stable ....
Windows cfg-check coverage includes the Windows source-loading, artifact IO,
and trace path implementations that use reparse-point rejection and stable
opened-file metadata checks.
Runtime host/sink boundary changes must prove both the in-memory host and the CLI filesystem/stdout host adapter. Focused coverage should include trace sink, program-output sink, trace-first program-output audit evidence, and flush failure paths, plus a CLI assertion that mandatory host failures return before any successful run report is printed.
Run the source-to-runtime gates after changes that affect syntax, checking, lowering, artifacts, runtime behavior, diagnostics, examples, or acceptance criteria.
just source-to-runtime-gates
The success gates include the typed target-binding path for the component
composition example: strata target-requirements, mantle feature-declaration, mantle admit, and mantle run. That path proves
requirement extraction, mantle.feature_declaration.v6 rendering,
requirements-vs-declaration admission before runtime execution, and Mantle’s
artifact-derived check that declared requirements cover the decoded runtime
feature surface. just remote-distributed-boundary-gates is part of the broader
source-to-runtime gate and keeps the local-only runtime target profile explicit:
checked Strata entry examples must not lower remote/distributed target
requirements, and forged artifacts requiring remote send, remote spawn, or
distributed transport must fail before runtime admission.
They also include authority/effect admission-binding paths for dynamic local
spawn authority in examples/effect_outcome_spawn_denied.str and lexical
supervisor-child spawning in examples/local_supervision_restart.str: strata authority-effects build, strata authority-effects admit, strata authority-effects policy build, strata authority-effects policy admit,
strata authority-effects bind-runtime, and
mantle run --authority-effect-binding. Those paths prove checked
authority/effect facts and closed typed policy decisions are emitted as
separate Strata-owned artifacts, admitted before binding, correlated with the
matching .mta, and consumed by Mantle as typed runtime
policy/observability input rather than source labels or report text.
Trace-reading acceptance gates also validate the Mantle-owned JSONL trace
schema before using trace evidence. That validation is limited to observability
metadata, exact per-event field sets, required field shapes, grouped
payload/loop fields, artifact_loaded first/no-repeat ordering,
Mantle-contiguous spawned PID sequencing, u32 artifact typed-ID width, u16
branch path segments and bounded path length, non-entry spawn parent evidence,
runtime PID-to-process-ID correlation, supervisor-child restart causality, and
restart-window numeric bounds/coupling; executable behavior still comes from
checked IR, admitted .mta tables, and Mantle runtime dispatch.
Language Surface Proof Substrate
just language-surface-assurance runs the executable current-language-surface
proof substrate. The inventory maps agreed proof domains to declared
Strata/Mantle features, maps those features to typed proof obligations, maps
those obligations to required evidence classes, and verifies that each evidence
pointer still names live repository content. Source-to-runtime evidence must
name active executable check/build/run gate coverage. This gate does not add
language behavior and does not replace .str source-to-runtime execution.
just language-surface-assurance
Bounded Assurance
just bounded-assurance-smoke runs the focused bounded assurance surface for
the current bounded language surfaces. It covers immutable source-local binding
chains, bounded scalar expression trees, binding-expanded scalar equivalents,
scalar values in records, lists, and maps, and pure source value/return if
selection. It also covers selected step return match
action blocks. The gate compares checked IR shapes with
lowered Mantle artifact shapes, validates the artifact boundary, verifies typed
send IDs, checks terminal Continue / Stop / Panic lowering, runs an
explicit smaller bounded runtime execution generator through Mantle, and runs
bypass-mutation rejection tests for malformed artifact equivalents.
This is machine-checked bounded exhaustiveness for the named surface. It is not a theorem-prover proof of the whole language surface.
Continuous Integration
The standard CI workflow installs just and calls just ci-rust on Linux,
macOS, and Windows. The Linux quality job calls just ci-quality, which runs
formatting, native and cross-target checks, tests, clippy, performance smoke
checks, build, tool metadata validation, toolchain policy validation, mdBook,
the language surface proof substrate, source-to-runtime gates, and diff hygiene.
The docs tool install path pins both mdbook and the Mermaid preprocessor, so
Mermaid diagrams are built by the same just docs command locally, in quality
CI, and in the Pages workflow.
CI uses GitHub-owned, SHA-pinned checkout and cache actions. The cache stores Cargo registry/git data and per-job build target directories. It does not cache installed executable tools directly; tool installs remain version-pinned and reuse the cached Cargo target directory where possible.
For local Linux CI parity through act:
just ci-local
Focused Component-Composition Artifact Gate
Run the focused Strata-owned composition artifact gate when changing checked composition artifact generation or admission:
just composition-artifact-gates
It checks and builds examples/component_composition_main.str, writes the
default target/strata/component_composition_main.component-composition.json
checked-subset validation artifact, validates it with strata composition admit --format json, binds that admitted evidence to the matching .mta through
strata composition bind-runtime, and runs Mantle with the resulting
target/strata/component_composition_main.deployment-composition.json. This
proves the explicit Strata/deployment-to-Mantle observability bridge while still
keeping .component-composition.json out of Mantle runtime input.
Focused Authority/Effect Artifact Gate
Run the focused authority/effect artifact gate when changing checked authority/effect rendering, admission, runtime binding, or Mantle authority policy admission:
just authority-effect-artifact-gates
It checks and builds examples/effect_outcome_spawn_denied.str, writes the
default target/strata/effect_outcome_spawn_denied.authority-effect.json
checked authority/effect artifact, validates it with
strata authority-effects admit --format json, builds and admits
target/strata/effect_outcome_spawn_denied.authority-policy.json, binds the
admitted facts and typed policy decisions to the matching .mta, and runs
Mantle with the accepted-policy
target/strata/effect_outcome_spawn_denied.authority-effect-binding.json. It
then builds an explicit deny-spawn
target/strata/effect_outcome_spawn_denied.authority-deny-policy.json, admits
it, binds it, and runs Mantle with
target/strata/effect_outcome_spawn_denied.authority-effect-deny-binding.json.
Next, it repeats the build/admit/bind/run chain for
examples/local_supervision_restart.str so lexical supervisor-child facts are
proven through the same explicit runtime binding bridge. Finally, it checks and
builds examples/component_composition_main.str, emits and admits both the
component-composition, authority/effect, and authority-policy artifacts, binds both runtime
sidecars, and runs Mantle with both explicit bindings. This proves the explicit
Strata-to-Mantle authority/effect bridge across dynamic-local authority, lexical
supervisor-child facts, and component/port authority surfaces while keeping
source labels, debug names, report text, and the Strata-owned
.authority-effect.json artifact non-executable inside Mantle.
Performance Smoke
just performance-smoke runs stable source-to-runtime resource smoke profiles
over examples/collection_state.str, the multi-file source-unit import example,
the typed boundary-contract example, the checked component-composition example,
and the local supervision restart example. It measures repeated Strata checking
and lowering for collection state, source-unit imports, boundary contracts, and
component composition; repeated checked composition report rendering,
Strata-owned composition artifact build/rendering, Strata-owned composition
artifact admission/validation, target requirement rendering from a prechecked
composition input, and repeated Mantle runs with an explicit runtime composition
binding; authority/effect artifact build/rendering, authority/effect artifact
admission, authority-policy artifact build/rendering and admission,
authority/effect runtime-binding admission for the denied-spawn and component
authority-surface examples, and repeated Mantle runs with the explicit
authority/effect binding for the denied-spawn example; Mantle admission
comparison through the runtime profiles; Mantle
artifact encode/decode; repeated Mantle in-memory execution of the
collection-state and boundary-contract artifacts; repeated Mantle JSONL-trace
execution of the collection-state artifact; and repeated Mantle in-memory
execution of the supervision artifact.
The Mantle runtime profiles exercise artifact admission, loaded-program
validation, internal executable-plan construction, typed dispatch, and host
execution together; they do not benchmark a serialized bytecode path.
The smoke output reports wall time, allocation/deallocation counts, allocated
and deallocated bytes, interval-relative live-byte metrics, process CPU time
when the platform exposes it, and resident memory. Allocation metrics come from
a test-only global allocator wrapper around std::alloc::System; the wrapper
does not change allocation policy and only records atomic counters. The live
metrics are relative to the start of each measured profile: net live-byte delta
is the signed end-of-interval live byte change, and peak live over interval start
is the highest live byte count above that start baseline. The net live-byte
delta budget is an upper bound; negative deltas remain valid. Linux CI reports
process CPU from /proc/self/stat and current/peak RSS through /proc; macOS
and BSD local runs report current RSS through ps. RSS budgets are enforced
against current RSS for each measured profile; process-lifetime peak RSS is
reported as context when available. Unlike allocation metrics, ordinary smoke
RSS is whole-test-process resident memory, so use it as a budget signal rather
than per-profile attribution.
The gate uses enforced resource ceilings with CI headroom for scheduler noise. It is meant to catch meaningful regressions in compilation, runtime, CPU, or memory paths without treating every small local timing fluctuation as a failure.
Reviewed reference values and enforced budget ceilings live in
benchmarks/performance-smoke.baseline. Local and CI runs print current
measurements to their logs; git tracks reviewed baseline changes, not every
noisy raw run. The baseline file uses strict key=value entries with
nanosecond, KiB, byte, and count units.
For RSS attribution, use the fresh-process comparison harness instead of the
ordinary smoke run. just performance-rss-compare <base-worktree> <current-worktree> installs a temporary probe test into both worktrees, runs
one named profile per test process, repeats each profile, and reports median,
minimum, maximum, and p90 current-RSS deltas in KiB. This keeps profile-order,
test-binary, and allocator-retained-page effects separate from the ordinary
multi-profile smoke gate.
The default RSS comparison profiles cover collection-state checking/lowering,
collection-state in-memory runtime execution, and boundary-contract in-memory
runtime execution. To attribute local-supervision RSS, pass
local_supervision_restart.in_memory_runtime explicitly after the two
worktrees; both worktrees must contain examples/local_supervision_restart.str.
For review-grade memory evidence, use just performance-memory-review <base-worktree> <current-worktree>. It runs both the fresh-process test-profile
RSS comparison and the product CLI footprint comparison. Treat allocator
metrics as the strict code-path signal. Treat RSS as a repeated median/p90
comparison signal because operating systems and allocators can keep freed pages
resident outside the measured source path.
For product-footprint attribution, use just performance-cli-footprint-compare <base-worktree> <current-worktree>. It builds the release Strata and Mantle CLI
binaries in both worktrees, compares release binary file sizes, then repeats
fresh-process product commands for strata check, strata build, and
mantle run over examples/collection_state.str under a minimal environment.
The reported CLI RSS values come from the operating system process for each
command; they are closer to product footprint than test-harness RSS, but they
still are not per-Mantle-actor memory. The harness also writes metadata with the
compared worktrees, HEADs, platform, run count, and clean-environment policy.
For Linux /proc-based memory evidence from a macOS checkout, run the same
review through local act with two mounted worktrees:
just performance-memory-review-act /tmp/strata-base .
The act recipe uses the performance-memory workflow and mounts the base and
current worktrees into an Ubuntu container. It is an optional review tool, not a
replacement for just quality. The same workflow can also be started manually
in GitHub with a base ref when local worktree mounts are not available.
Useful local command:
just performance-smoke
Useful RSS comparison command:
just performance-rss-compare /tmp/strata-base .
Useful product CLI footprint command:
just performance-cli-footprint-compare /tmp/strata-base .
Useful full memory review command:
just performance-memory-review /tmp/strata-base .
Fuzzing
The fuzz harnesses live under fuzz/ and run with cargo-fuzz on nightly Rust.
They cover these boundaries:
- parsing, checking, and lowering arbitrary UTF-8 source;
- parsing, checking, and lowering delimited multi-source Strata source programs;
- admitting Strata-owned checked component-composition artifacts;
- admitting Strata-owned checked authority/effect artifacts;
- decoding and re-encoding arbitrary UTF-8 artifact text;
- running valid lowered artifacts through the in-memory runtime host;
- admitting Mantle runtime composition bindings;
- admitting Mantle runtime authority/effect bindings;
- validating Mantle-owned runtime trace JSONL without trusting trace strings as executable meaning.
Committed seed corpora under fuzz/seeds/ keep the smoke runs exercising valid
collection, template, and source-to-runtime examples even before mutation finds
those forms from random input. The smoke recipe copies those seeds into ignored
fuzz/corpus/ directories before running cargo-fuzz, so mutation output does
not touch tracked seed fixtures.
Useful local commands:
just install-fuzz-tools
just fuzz-ci
Miri
Miri runs on nightly Rust. The Miri gate is a smoke suite focused on pure or
in-memory paths rather than filesystem-specific CLI behavior. It includes
targeted immutable source-local binding check/lower coverage for the source
resolution path, composition_artifact coverage for the pure
component-composition artifact renderer/admission validator, representative
authority/effect artifact render/admit and closest component-import negative
coverage, and focused Mantle runtime composition- and authority/effect-binding
admission checks, including component authority-surface and negative
forged-field authority/effect-binding checks. Filesystem-specific CLI behavior
remains covered by unit and acceptance tests instead.
Useful local commands:
just install-miri-tools
just miri-ci
Every change that affects user-facing syntax, artifact schema, runtime behavior,
diagnostics, examples, or acceptance gates should update this book and pass
mdbook build docs.