Argument bindings for planned tool calls. A string value inside a
planned call’s args may embed {{steps.N.path}} (data captured from a
prior step) or {{calls.K.path}} (an earlier call in the same step,
zero-based). Extraction feeds strict-save validation; resolution
substitutes live or pinned run data at execution/preview time.
One matcher per SOP trigger source. SopTrigger::behavior is the only place
that names every variant for matching; field specs are generated separately
by the TriggerFields derive, so neither matching nor the authoring
registry hand-lists a source. Add a variant and the compiler forces its arm
here (matching) and the derive supplies its fields.
Wire-edit application: translates a visual editor’s connect/disconnect
gesture into the corresponding StepRouting/StepFailure mutation on a
draft Sop. The graph is a projection; these edits write back to the
source of truth it was projected from.
CAS claim handle. Opaque to the engine; the store validates it. The single
admission primitive A1’s concurrency tick and C’s out-of-band approver share.
One field of a trigger payload a condition can reference. path is the
JSON path suffix after $. (empty for a scalar payload compared directly),
label is the human name, value_type drives the value control, and
options carries the choices when value_type is Enum.
The canonical legend for the SOP canvas: flow-wire roles, pin classes, and
run states. The single authority a surface reads to render a legend and
per-handle/per-wire hover context, so no surface hardcodes these lists.
Canvas geometry carried on every serialized graph so the web canvas reads
placement pitch from the wire instead of a local literal. The values are
fixed by LayoutGeometry::CANONICAL; the struct rides on GraphLayout
only to expose them to non-Rust surfaces.
Grid placement for one node: column = longest flow path from an entry,
row = order of insertion within that column. x/y carry a persisted
canvas coordinate when the node has been dragged; absent otherwise.
The set of payload fields a condition may reference for a given source.
open marks sources whose payload is arbitrary user JSON (MQTT, AMQP,
webhook body, channel message): the surface offers a guided path/op/value
builder plus a sample-payload probe rather than a fixed field list. direct
marks a scalar payload (peripheral signal) compared with the bare op value
form and no path.
An authored tool invocation planned for a step. Args may embed
{{steps.N.path}} / {{calls.K.path}} bindings (see sop::binding)
that resolve against captured run data before dispatch. pinned
carries a sample output (typically lifted from a real run’s
StepToolCall.output_data) so downstream bindings can be authored
and previewed without re-executing the tool.
Injected side-effect adapters for build_sop_engine. Each is optional and
fail-closed when absent: the route falls back to the log-only no-op adapter,
and the forge.comment / llm.generate capabilities report a clear failure
instead of acting. The daemon injects real implementations; CLI / standalone
callers pass SopEngineAdapters::default().
Lightweight projection of a run for list surfaces (Runs page). Carries
just enough to render a row and open the per-run overlay, without the
full step-result payload.
JSON Schema fragment for validating step input/output data.
Stored as a raw serde_json::Value so callers can validate without
pulling in a full JSON Schema library.
One tool invocation captured during a step’s execution. A step may
make any number of calls (including the same tool repeatedly);
index preserves invocation order so authoring surfaces can replay
the sequence and map data between calls.
A single named output port on a switch step. Rules are evaluated top to
bottom; the first whose when guard passes routes the run to goto. A
rule with when unset is the catch-all (n8n’s “unknown”/default port) and
should be ordered last.
Everything an authoring surface needs to offer trigger configuration:
the full ordered source list, the bound (non-channel) sources with their
field shapes, and per-channel availability. Channel is special-cased
into channels because its options depend on live config, not just the
enum; sources still names it so surfaces render the picker from one
backend-walked list instead of reconstructing it.
One editor gesture. role picks which routing field is mutated; port
is required for Switch (index into routing.switch) and ignored
otherwise. Accepted over RPC (sops/wire-draft) and HTTP.
Value-input widget hint for one condition field, so the authoring surface
renders the right control (text box, number, checkbox, enum select) and
quotes string comparands correctly.
Projection constructors for the shared SopGraph type. SopGraph lives
in zeroclaw-sop-graph, so the build logic that needs the runtime’s
Sop/ToolSpec hangs off this extension trait instead of an inherent
impl. Call sites keep using SopGraph::from_sop(..) with the trait in
scope.
Apply one wire edit to a draft SOP, mutating the routing field the edit’s
role maps to. Validates both endpoints first; on Err the SOP is
unchanged. Semantics per role:
Every SopTriggerSource except Channel becomes a bound source whose field
specs come from the source’s own TriggerBehavior; every inbound-capable
ChannelKind becomes a channel row with whatever configured aliases match
it. No per-source field list lives here: the source describes its own fields.
Build a single shared SopEngine + SopAuditLogger pair.
This is the sole construction site for SOP state within a daemon.
Callers receive Arc<Mutex<SopEngine>> and Arc<SopAuditLogger>
handles — never call SopEngine::new or SopAuditLogger::new
directly outside this module.
True when err is the typed ResumeAtCapacity backpressure marker (an
over-cap resume was refused), as opposed to a store fault. Lets a caller in
another module or crate (e.g. resolve_gate, or the gateway resume endpoint)
render it as backpressure (HTTP 503) rather than a fault without depending on
the private struct.
Renumber steps to a contiguous 1..=N sequence (positional order wins)
and remap every internal reference: routing.next, depends_on,
switch goto targets, and on_failure: goto. References to steps that
no longer exist are dropped (goto falls back to Fail). No-op when
step numbers are ambiguous (duplicates), since a remap would guess.
Runs automatically inside save_sop.
Parse procedure steps from SOP.md content.
Expects a ## Steps heading followed by numbered items (1., 2., …).
Each item’s first bold text (**...**) is the step title; the rest is body.
Sub-bullets parse execution hints and dark per-step contract metadata.
Build the registry from live config: collects configured channel aliases
(normalizing - to _ to match ChannelKind wire names) and delegates
to build_registry.
Render steps back to SOP.md markdown, the inverse of parse_steps.
Every contract field (tools, scope, schema, routing, failure policy,
mode) becomes a sub-bullet, so render -> parse is lossless.
Resolve every binding in args, returning a new value. A string that is
exactly one binding resolves to the referenced JSON value (any type);
bindings embedded in longer strings interpolate as text. Unresolvable
references error rather than passing template text to a tool.
Enumerate every run the engine holds (active + retained terminal),
newest first, optionally scoped to one SOP. Errors only if the engine
lock is poisoned. This is the Runs surface’s data source.
Persist an SOP to <sops_dir>/<name>/ as SOP.toml + SOP.md.
Normalizes step numbers first, then rejects the write entirely if
strict validation finds blocking problems; nothing touches disk on
failure.
Spawn a background task that drives a resumed SOP action to its next
blocking or terminal state. Gate-clearing surfaces without an ambient agent
turn (HTTP decide, WS approvals, manual dashboard runs) land here:
ExecuteStep runs through a fresh agent loop under the step’s resolved
agent, DeterministicStep routes through the engine’s headless
deterministic driver, and every other action is already parked or terminal.
Build the tool-spec map an SOP graph projection uses to type step pins.
Keys are tool names; values are the tool’s declared parameters (input
pins) and output (output pin) schema. Derived once from the agent’s
resolved security policy so the pins mirror the exact tools the step can
call, not a hand-authored list.