Skip to main content

Module document

Module document 

Source
Expand description

The graph document format: the Graph envelope, the six node kinds, the edges that connect them, and the small payload types a node carries.

Everything here is pure data. No type reads the clock, draws randomness, or performs IO. That purity is deliberate: this crate is a leaf that the CLI, the control plane, and (later) a wasm dashboard projection all parse graph documents with, so it must drag in no runtime and no host dependency.

§Two postures, one format

A graph is a CONTROL document, not a data payload. A silently dropped field could drop a gate or an unenforced budget, so parsing is STRICT: every struct and the node enum carry #[serde(deny_unknown_fields)], and a stray key is rejected rather than ignored. That is the opposite posture from the event log, which stays forward-tolerant because it carries recorded data.

The one concession the two share is the additive schema_version discipline (see SCHEMA_VERSION): a graph that was recorded under an older build must still parse and validate under a newer one. Strict in, additive-tolerant out.

§The optional node display name, and why it hashes unlike an agent’s

Every node payload carries an optional name: a short, purely presentational label (“Approve the draft”) an author can hang on a node so a rendered graph reads by intent instead of by id. Bounds mirror the precedent set by the agent definition’s own name (salvor_cli::agent_config::MAX_NAME_LEN): at most 64 CHARACTERS (chars().count(), not bytes), and, when set, not empty or all whitespace. [crate::validate] enforces both, node-precise.

An agent’s name is deliberately excluded from its agent_def_hash: an agent is a long-lived identity that a run keeps replaying under the same hash while an operator relabels it, so a rename must not mint a new identity (see salvor_runtime::Agent::def_hash). A graph document has no such identity to protect — it IS its hash, the whole reason POST /v1/graphs stores it content-addressed. So a node’s name gets NO special treatment: it is an ordinary field on the payload struct, present on the wire exactly when set (skip_serializing_if = "Option::is_none"), and folds into the canonical JSON salvor_engine::graph_hash hashes like any other field. Renaming a node is therefore authoring a new document version, by design, the same way changing a prompt or an over reference is.

Structs§

AgentNode
An agent node: a full agent loop referenced by content hash.
BranchCase
One case of a BranchNode: a name and the condition that selects it.
BranchNode
A branch node: routes on a typed output.
Edge
A directed edge: a typed payload flows from one node to another.
FoldNode
A fold node: bounded iteration that accumulates across passes.
GateNode
A gate node: human approval that suspends the run.
Graph
A graph document: the control document authored once, submitted, hashed into a run, and then frozen. It carries the schema version, the set of nodes (each with a stable string id), and the edges that connect them.
MapNode
A map node: fan-out a sub-run per element of a typed list, with a concurrency cap.
ToolNode
A tool node: one direct tool invocation.

Enums§

BranchCondition
How a BranchCase is selected. Modeled as data; not evaluated in this crate.
FoldBody
The body a FoldNode runs each pass. Adjacently tagged, mirroring MapBody, so adding a third form later stays additive. A node body names an existing node by id (checked for existence during validation); a subgraph body embeds a whole Graph and is deferred exactly as the map’s subgraph body is.
FoldJoin
How a FoldNode folds its passes into the single value it produces.
MapBody
The body a MapNode maps each element through.
Node
One node in a graph: exactly one of the six kinds the runtime knows how to execute.

Constants§

SCHEMA_VERSION
The schema version stamped onto every graph document.