ProseScript
Specify required steps and their order inside one render. Contract Markdown declares the interface; ProseScript describes the procedure.
ProseScript
Contract Markdown states requirements and interfaces. ProseScript specifies required steps inside one render when their order or method matters. This optional imperative layer leaves the interface in the surrounding contract.
The mantra is declarative by default, explicit when needed. ProseScript is always secondary to the contract. It never declares what a node depends on or what it produces. That is the contract's job. It only choreographs the steps inside one render.
ProseScript is the intra-node layer. call invokes a function, and
session / agent / resume spawn one-off subagents, all ephemeral and
internal to producing this node's world-model. Cross-node connections are never
made in ProseScript; they are subscriptions that Forme wires from a
responsibility's ### Requires to a producer's ### Maintains.
When to pin, and when not to
Use Contract Markdown to declare requirements and dependencies. Use ProseScript when order, loops, branching, retries, parallelism, or exact call choreography are required. Those steps constrain the permitted approach; they do not replace the requirements for the result.
The Prose VM follows pinned choreography exactly. It does not infer new
parallelism, reorder calls, or add missing calls. That precision is the whole
point: a pinned ### Execution block is a promise that the steps run as
written.
| You want | Reach for |
|---|---|
| A standing truth kept current; the host may choose the graph | Contract Markdown (### Maintains) |
| An exact sequence of calls inside one render | ProseScript in ### Execution |
| Parallel fan-out with a join policy | parallel in ProseScript |
| Retry / backoff on a flaky call | retry / backoff call modifiers |
| A one-off subagent internal to this render | session / agent / resume |
Where ProseScript lives
ProseScript appears on exactly two surfaces. Both are owned by Contract Markdown, so an embedded script must not redeclare caller inputs or public outputs.
| Surface | Scope | Primary call style |
|---|---|---|
### Execution in a *.prose.md | Pinned intra-node choreography | call function-name |
Pattern ### Delegation | Slot interaction rules inside a pattern instance | call slot-name |
A responsibility declares its interface with ### Requires / ### Maintains; a
function declares it with ### Parameters / ### Returns; a pattern's
delegation script draws on ### Slots, ### Config, and the instance
bindings the same way. Inside the script, those declared names are simply in
scope. The reactive interface always belongs to the contract sections.
Embedded ProseScript should never restate them.
A first pinned block
Inside Contract Markdown, a fenced prose block under ### Execution pins the
choreography for one render:
### Execution
```markdown
let findings = call researcher
topic: topic
let report = call writer
findings: findings
return report
```topic comes from the contract's ### Requires (responsibility) or
### Parameters (function). return report hands the result to the enclosing
### Maintains or ### Returns. The VM runs researcher, then writer, in
that order, every time. Nothing is reordered or inferred.
The constructs
ProseScript is a small, complete language. The constructs below cover everything the grammar exposes.
Bindings and values
let creates a mutable binding; const creates an immutable one. Object
results can be destructured.
let draft = call writer
brief: brief
const threshold = "high confidence"
let { findings, sources } = call researcher
topic: topicValues are strings ("short", """multi\nline"""), numbers, booleans, null,
arrays, objects, and references. Object shorthand keeps variable names:
{ findings, sources } means { findings: findings, sources: sources }. String
interpolation uses {name} or {object.property}. Scope is lexical, and loop
variables, block parameters, and catch variables are immutable within their
body.
call
call invokes a Contract Markdown function, a pattern instance or slot, or a
used dependency function. Input bindings are indented name: expression
lines; the result is the target's declared outputs.
let response = call external-api
request: request
retry: 3
backoff: exponentialretry and backoff are call modifiers (backoff may be none, linear, or
exponential). Each call target must resolve to a real function or pattern
slot, or Forme rejects the block.
parallel
Branch-local statements run concurrently and join according to modifiers.
parallel:
let security = call security-reviewer
code: code
let performance = call performance-reviewer
code: code
let report = call synthesizer
context: { security, performance }Modifiers control the join: "all" (default), "first", "any" with
count: N, and on-fail: policies ("fail-fast" default, "continue",
"ignore"). Defaults are ("all", on-fail: "fail-fast").
for, repeat, and loop
Fixed repetition, collection iteration, and open or model-sized loops:
repeat 3 as attempt:
call generator
attempt: attempt
parallel for item in items:
call processor
item: item
loop until all tests pass (max: 5):
let results = call tester
if results include failures:
call fixer
test-results: resultsOpen loop blocks should carry a (max: N) bound (a missing bound is a warning
in source and an error in generated canonical docs). parallel for preserves
input order unless a parallel modifier requests race semantics.
if / elif / else and choice
if runs the first true branch in order. choice lets the VM select exactly
one labeled peer branch by criteria.
if review has critical concerns:
call reviser
review: review
elif review has minor concerns:
call polisher
review: review
else:
call approver
choice best recovery path:
option "retry":
call retryer
option "abort":
throw "No safe recovery path"Conditions are discretion text: natural-language conditions the VM
evaluates. They may be bare, **wrapped**, or ***multi-line***. Prefer
concrete, observable conditions over vague ones.
try / catch / finally and throw
try:
let response = call external-api
request: request
retry: 3
backoff: exponential
catch as err:
call fallback
error: err
finally:
call cleanuptry runs its body; catch handles an unhandled failure; finally always
runs. throw (bare) re-raises the active error inside catch;
throw expression raises a new one. A try needs at least a catch or a
finally.
session, agent, and resume
Pinned blocks can spawn direct subagents when the work is intentionally a
one-off internal to this render, not a reusable function.
agent researcher:
model: sonnet
persist: project
prompt: "Research thoroughly and keep a compact project memory."
skills: ["web-search"]
shape:
self: ["research", "source evaluation"]
prohibited: ["writing source files", "running shell commands"]
let findings = session: researcher
prompt: "Research {topic}"
context: topic
let review = resume: researcher
prompt: "Review the new draft"
context: findingssession "prompt" spawns a one-off subagent; session: agent uses an agent
definition; resume: agent continues a persistent agent with memory. shape is
the ProseScript equivalent of Contract Markdown ### Shape. It expresses
behavioral boundaries, not raw secret or permission values. Host sandbox
permissions remain a host adapter concern.
Inside ### Execution, prefer call function over a direct session when an
equivalent function exists. The VM warns when you skip a real function for an
ad-hoc subagent. Reach for session / agent / resume only when the work is
genuinely an intentional one-off.
Blocks, do, and pipelines
Anonymous do: groups sequential statements; named block definitions are
reusable local choreography invoked with do name(...). Pipelines transform
collections left to right with map, pmap, filter, and reduce.
block review-and-fix(artifact, max_rounds):
let review = call critic
artifact: artifact
if review has critical issues:
return call fixer
artifact: artifact
review: review
return artifact
let result = do review-and-fix(draft, 3)
let summaries = articles
| filter:
call relevance-checker
article: item
| map:
call summarizer
article: itemBlock definitions are collected before execution, so a block may be invoked before its definition. Each invocation gets its own scope, and parameters are immutable within the call.
use: declaring dependencies
use declares an external dependency for pinned choreography. It is processed
before agents, blocks, and statements, and follows the same disk-only resolution
rules as prose run.
use "github.com/openprose/prose/packages/std/evals/inspector" as inspector
let inspection = call inspector
run-path: run_pathuse is valid in a standalone ProseScript block, but not inside embedded
Contract Markdown ProseScript. Declare the dependency in the contract, not in
the render body.
The boundary: ProseScript does not own the interface
This is the single most important rule, restated. ProseScript does not own public interfaces in current OpenProse source:
### Requires(responsibility) or### Parameters(function) declares the variables available to### Execution.### Maintains(responsibility) declares the world-model truth the render must keep current;### Returns(function) declares the value it must produce.returnchooses the execution block's result for the enclosing contract.
Legacy standalone .prose files used input and output declarations. Treat
those as upgrade inputs, not current syntax. Writing input or output
inside current ProseScript is an error. Run prose upgrade --dry-run to migrate.
How a pinned block runs
Embedded ### Execution runs in three phases:
- Parse the lexical structure, blocks, declarations, and statements.
- Validate names, scopes, call targets, inputs and outputs, loop bounds, and
surface-specific restrictions. Forme checks that every
calltarget resolves to a realfunction(local,used, or astd/library function) or a delegated helper the render declares, and that the returned value satisfies the enclosing### Maintains/### Returns. - Execute in source order, using the Prose VM for calls, sessions, state, bindings, retries, and final result publication.
Validation errors block execution; warnings do not, but generated canonical docs should resolve warnings where possible.
Pattern delegation
Pattern ### Delegation describes how bound slots interact. When written as
fenced prose, it is validated as ProseScript with pattern-specific scope: slot
names, config keys, and parent-provided inputs are in scope, and call may
target slots or nested pattern instances.
loop until critic accepts output (max: config.max_rounds):
let output = call worker
task: task
feedback: feedback
let verdict = call critic
output: output
quality-bar: config.quality-bar
if verdict is not accepted:
let feedback = verdict.feedback
return outputPattern files are not directly runnable. A responsibility instantiates the pattern and binds its slots before the delegation runs.
Where to go next
Contracts
The authored surface: Markdown contracts, the five kinds, and the recognized sections that own the interface ProseScript leans on.
Contract authoring
How requirements and required steps work together, and where the agent can choose its approach.
For the complete grammar, validation tables, and execution model, see the ProseScript reference in the loaded skill. When the docs and the skill disagree, trust the skill.
Contracts
Write requirements in Contract Markdown. Learn the five contract kinds and the sections that define inputs, maintained state, required steps, and limits.
Typed Image
A pixel-only diagram is a visual source one rung above Markdown. An intelligent compile resolve reads the picture and emits the contracts, and whether it compiles is the typecheck.