Skip to content

Graph

zeroth.contracts.graph

Workflow graph contracts.

This package re-exports the most important classes so you can import them directly from zeroth.contracts.graph instead of digging into sub-modules. Graph validation composes runtime checks and therefore lives in :mod:zeroth.runtime.graph_validation; the contract-owned validators are under :mod:zeroth.contracts.graph.validation.

AgentNode

Bases: NodeBase

A graph node that runs an AI agent.

Wraps an AgentNodeData with the shared node fields like contracts and policy bindings.

to_governed_step_spec

to_governed_step_spec() -> GovernedStepSpec

Convert this agent node into a spec the execution engine understands.

AgentNodeData

Bases: BaseModel

Configuration for an AI agent step.

Holds the instruction prompt, which model to use, what tools and memory the agent can access, and other agent-specific settings.

AgentToolBinding

Bases: BaseModel

Exposes an attached executable-unit node as a callable tool.

The attachment itself is a tool-kind edge from the agent to the unit; this binding carries what the model needs to call it — an alias distinct from the node id, a description, and described arguments. All three are author-provided, never derived, so the model never sees internal ids.

parameters_schema

parameters_schema() -> dict[str, Any]

Compile the argument list into a JSON Schema object for tool calling.

Capability

Bases: StrEnum

A specific permission that a node might need to do its job.

Each value represents one kind of action (like reading from the network or writing to the filesystem). Authored tool bindings declare the capabilities they require, and policies use these values to control what nodes are allowed to do; the policy engine republishes the enum from :mod:zeroth.governance.policy.models.

Condition

Bases: BaseModel

A rule that decides whether an edge should be followed.

Conditions are attached to edges and evaluated at runtime to determine which path the execution should take next.

DisplayMetadata

Bases: BaseModel

Human-readable labels and tags shown in the UI for a node or graph.

Edge

Bases: BaseModel

A connection between two nodes in the graph.

Data edges define the flow of execution. They can optionally carry a condition (to branch) and a mapping (to transform data between nodes). Tool edges (kind="tool") attach an executable unit to an agent as a callable tool — they are structural, never traversed as control flow.

EntrypointNode

Bases: NodeBase

The node where a run enters the graph.

Declares the workflow's public input contract and passes the (already ingress-validated) payload through unchanged, leaving an audit record of what entered the workflow.

to_governed_step_spec

to_governed_step_spec() -> GovernedStepSpec

Convert this entrypoint into a spec the execution engine understands.

EntrypointNodeData

Bases: BaseModel

Configuration for the workflow's entrypoint. Deliberately empty.

The entrypoint's contract lives on the node's shared input_contract_ref — it is the workflow's public input contract, pinned into the deployment snapshot and enforced against every submitted run payload.

ExecutableUnitNode

Bases: NodeBase

A graph node that runs a code or script executable unit.

Wraps an ExecutableUnitNodeData with the shared node fields.

to_governed_step_spec

to_governed_step_spec() -> GovernedStepSpec

Convert this executable unit node into a spec the execution engine understands.

ExecutableUnitNodeData

Bases: BaseModel

Configuration for a code/script execution step.

Two authoring paths share this node: a manifest_ref pointing at a registered unit (the medium-code path), or inline_source carrying authored code directly (the Studio code node). Exactly one must be set; inline code always runs as a sandboxed subprocess.

ExecutionSettings

Bases: BaseModel

Safety limits and behavior settings for running a graph.

These settings prevent runaway execution by capping the number of steps, total runtime, and visits per node or edge.

sequential_join_enabled class-attribute instance-attribute

sequential_join_enabled: bool = False

Compatibility field whose authored presence selects the engine mode.

Omitted or explicit True selects the structured-token engine. Explicit False selects the warned legacy runtime. The raw default remains False solely to preserve the published model signature and wire compatibility. In token mode, a convergent node (>1 non-tool inbound control-flow edge that is not a parallel_config fan-in) is dispatched once per iteration after all its inbound edges resolve (delivered or suppressed), with delivered payloads merged via its JoinConfig.

Graph

Bases: BaseModel

The top-level object representing an entire workflow graph.

A graph contains nodes (the steps), edges (the connections), execution settings, and metadata. It also tracks its lifecycle status (draft, published, or archived) and version number.

transition_to

transition_to(status: GraphStatus) -> Graph

Move the graph to a new lifecycle status (e.g. draft -> published).

Returns a new Graph object with the updated status. Raises ValueError if the transition is not allowed.

publish

publish() -> Graph

Mark this graph as published (ready to run).

archive

archive() -> Graph

Mark this graph as archived (no longer active).

to_governed_flow_spec

to_governed_flow_spec() -> GovernedFlowSpec

Convert the entire graph into a GovernedFlowSpec for the execution engine.

This compiles nodes into steps and edges into transitions, producing the format the runtime expects.

GraphStatus

Bases: StrEnum

The lifecycle stage of a graph version.

Graphs start as DRAFT, get PUBLISHED when ready, and can be ARCHIVED when no longer needed.

HttpRequestNode

Bases: NodeBase

A provider-free, read-only resilient HTTP workflow step.

HttpRequestNodeData

Bases: BaseModel

A bounded read-only request to a controlled local/private endpoint.

This first public HTTP-node slice is intentionally GET-only and rejects public hostnames, URL credentials, query strings, and fragments. That keeps authored graphs free of secret-bearing URL material and prevents the node from becoming an unrestricted SSRF primitive. Public Internet calls remain the job of governed connectors until an explicit allowlist contract is designed.

HumanApprovalNode

Bases: NodeBase

A graph node that pauses execution until a human approves.

Wraps a HumanApprovalNodeData with the shared node fields.

to_governed_step_spec

to_governed_step_spec() -> GovernedStepSpec

Convert this approval node into a spec the execution engine understands.

HumanApprovalNodeData

Bases: BaseModel

Configuration for a step that pauses and waits for a human to approve.

Defines what data the approver sees and how they can respond.

IfNode

Bases: NodeBase

A zero-cost decision node with explicit named scalar routes.

IfNodeData

Bases: BaseModel

Configuration for a deterministic scalar decision controller.

LoopNode

Bases: NodeBase

A zero-cost loop controller whose topology is executed by the graph runtime.

LoopNodeData

Bases: BaseModel

Configuration for a deterministic bounded retry loop header.

The first visit always enters the body. Subsequent visits evaluate until; a false result repeats while retries remain, then routes to the explicit limit outcome. max_retries counts additional body executions after the initial attempt.

MCPToolNode

Bases: NodeBase

A graph node exposing one pinned MCP tool as an agent-callable target.

to_governed_step_spec

to_governed_step_spec() -> GovernedStepSpec

Convert this node into a spec the execution engine understands.

MCPToolNodeData

Bases: BaseModel

One MCP tool, pinned at import time.

An MCP server advertises its tools at list_tools() on the day of the run, which is the opposite of what the graph model assumes: publish-time validation, diffing and version pinning all need a contract that exists before the run. Importing freezes one tool's shape here so those mechanisms have something to act on, and schema_hash is what the runtime compares the live server against before it will call anything.

Note what this does NOT change: an MCP call still bypasses the operation boundary, so it stays at-least-once with no replay suppression and no reconciliation. Pinning constrains the tool's shape, not its delivery semantics -- which is why this is its own node type rather than a mode on ExecutableUnitNode, where the weaker guarantee would be invisible.

RetrievalNode

Bases: NodeBase

A graph node that retrieves grounded context from a vector memory connector.

to_governed_step_spec

to_governed_step_spec() -> GovernedStepSpec

Convert this retrieval node into a spec the execution engine understands.

RetrievalNodeData

Bases: BaseModel

Configuration for a retrieval (RAG) step.

Queries a vector memory connector with text taken from the node input and outputs the retrieved chunks for a downstream node (typically an agent) to ground on. Embedding and ranking are owned by the connector.

SubgraphNode

Bases: NodeBase

A graph node that invokes another published graph as a child workflow.

Wraps a SubgraphNodeData with the shared node fields. The child graph is resolved at execution time via the SubgraphResolver.

to_governed_step_spec

to_governed_step_spec() -> GovernedStepSpec

Convert this subgraph node into a spec the execution engine understands.

SubgraphNodeData

Bases: BaseModel

Configuration for a subgraph invocation step.

Specifies which published graph to invoke as a child workflow, how threads are shared, and the maximum nesting depth allowed. Embedded inside SubgraphNode; the runtime subgraph executor consumes it.

graph_ref instance-attribute

graph_ref: str

Name of the published graph to invoke.

version class-attribute instance-attribute

version: int | None = None

Specific deployment version; None means latest active.

thread_participation class-attribute instance-attribute

thread_participation: Literal["inherit", "isolated"] = (
    "inherit"
)

Whether the child run shares the parent's thread or gets its own.

max_depth class-attribute instance-attribute

max_depth: int = Field(default=3, ge=1, le=10)

Maximum recursion depth for nested subgraph invocations.

TemplateMemoryBinding

Bases: BaseModel

Declares which memory connector values to inject into the template memory namespace.

Each binding pulls one value (get mode) or a set of values by prefix (scan mode) from a named connector and exposes them as memory.<as_name> in prompt templates. connector_instance_id must match one of the values in the parent AgentNodeData.memory_refs list.

ToolArgument

Bases: BaseModel

One argument of a tool exposed to an agent.

The description is mandatory: the model only sees the JSON schema built from these entries, so an undescribed argument is unusable in practice.

to_schema_property

to_schema_property() -> dict[str, Any]

Render this argument as a JSON Schema property.

GraphRepository

GraphRepository(
    database: AsyncDatabase,
    validator: GraphValidator | None = None,
    template_reference_index: TemplateReferenceIndexWriter
    | None = None,
)

Persistence layer for versioned graph documents.

validator property

validator: GraphValidator | None

The validator publish enforces with, exposed for read-only preflights.

A caller that wants to tell an author "this graph will publish" must ask the SAME validator publish will ask. Constructing a fresh GraphValidator() instead silently drops whatever the wired one carries -- the contract registry, and the MCP grants resolver whose absence makes graph_validation skip every mcp_tool rule -- so the preflight answers a strictly weaker question than the one it appears to answer.

save async

save(
    graph: Graph,
    *,
    tenant_id: str | None = None,
    workspace_id: str | None = None,
) -> Graph

Insert or update a draft graph version.

WS-B: when tenant_id is supplied it is stamped onto the graph (so the serialized payload and the dedicated tenant_id column agree) before persisting. Omitting it keeps graph.tenant_id (default "default") for internal/code-authored callers.

create async

create(
    graph: Graph,
    *,
    tenant_id: str | None = None,
    workspace_id: str | None = None,
) -> Graph

Create a new graph (alias for save).

get async

get(
    graph_id: str,
    version: int | None = None,
    *,
    tenant_id: str | None = None,
    workspace_id: str | None = None,
) -> Graph | None

Load a graph by ID. Returns the latest version if no version is specified.

WS-B: when tenant_id is supplied, a graph owned by a different tenant is invisible (returns None) so the API can 404 without disclosing existence. None means no tenant filter (internal path).

list async

list(
    *,
    tenant_id: str | None = None,
    workspace_id: str | None = None,
) -> list[Graph]

Return the latest version for each graph id (optionally tenant-scoped).

list_versions async

list_versions(
    graph_id: str,
    *,
    tenant_id: str | None = None,
    workspace_id: str | None = None,
) -> list[Graph]

Return all versions of a specific graph, ordered oldest to newest.

publish async

publish(
    graph_id: str,
    version: int | None = None,
    *,
    tenant_id: str | None = None,
    workspace_id: str | None = None,
) -> Graph

Move a draft graph to published status so it can be executed.

Phase 43-02 (D-15): if a GraphValidator is wired, run validate_or_raise BEFORE the DRAFT -> PUBLISHED state transition. A validation failure raises and leaves the graph in DRAFT with its persisted state unchanged.

archive async

archive(
    graph_id: str,
    version: int | None = None,
    *,
    tenant_id: str | None = None,
    workspace_id: str | None = None,
) -> Graph

Archive a graph version so it is no longer active.

clone_published_to_draft async

clone_published_to_draft(
    graph_id: str,
    version: int | None = None,
    *,
    tenant_id: str | None = None,
    workspace_id: str | None = None,
) -> Graph

Create a new draft version by copying a published graph.

This is how you edit a published graph: clone it, modify the draft, then publish the new version.

update_status async

update_status(
    graph_id: str,
    status: GraphStatus,
    version: int | None = None,
    *,
    tenant_id: str | None = None,
    workspace_id: str | None = None,
) -> Graph

Change a graph's lifecycle status (publish, archive, etc.).

get_latest_version async

get_latest_version(
    graph_id: str,
    *,
    tenant_id: str | None = None,
    workspace_id: str | None = None,
) -> int

Return the highest version number for a graph. Raises KeyError if not found.

diff async

diff(
    graph_id: str,
    left_version: int,
    right_version: int,
    *,
    tenant_id: str | None = None,
    workspace_id: str | None = None,
) -> GraphDiff

Compare two versions of the same graph and return what changed.

TokenEngineSnapshot

Bases: BaseModel

One atomic token-engine checkpoint, suitable for exact replay.

Queue and dispatch entries deliberately retain complete envelopes. The snapshot validates those copies against the canonical tokens entry so replay never has to reconstruct payload or provenance from node metadata.

TokenEngineSnapshotState

Bases: StrEnum

Lifecycle state represented by a durable engine snapshot.

CancellationFence

Bases: _FrozenContract

The latest persisted cancellation generation and acknowledgements.

CanonicalTokenOrder

Bases: _FrozenContract

Persisted ordering data used by loop and join reducers.

sort_key

sort_key() -> tuple[object, ...]

Implement the sort key boundary for this component.

DeferredJoinDelivery

Bases: _FrozenContract

A durable edge delivery held until an overlapping join frontier arrives.

DispatchLifecycleState

Bases: StrEnum

Represent dispatch lifecycle values used by the runtime.

ForkChild

Bases: _FrozenContract

Represent fork child state and behavior.

ForkInstance

Bases: _FrozenContract

Represent fork instance state and behavior.

ForkLifecycleState

Bases: StrEnum

Represent fork lifecycle values used by the runtime.

ForkLineageFrame

Bases: _FrozenContract

One root-to-leaf entry in a token's durable fork lineage.

ForkObligation

Bases: _FrozenContract

Represent fork obligation state and behavior.

ForkObligationOutcome

Bases: StrEnum

Represent fork obligation outcome state and behavior.

InFlightDispatch

Bases: _FrozenContract

One durable dispatch claim, including replay and cancellation identity.

IterationContinuationDelivery

Bases: _FrozenContract

Represent iteration continuation delivery state and behavior.

IterationFrame

Bases: _FrozenContract

Represent iteration frame state and behavior.

active_member_token_ids property

active_member_token_ids: tuple[TokenId, ...]

Implement the active member token ids boundary for this component.

IterationFrameState

Bases: StrEnum

Represent iteration frame values used by the runtime.

IterationMember

Bases: _FrozenContract

Represent iteration member state and behavior.

IterationMembership

Bases: _FrozenContract

A token's membership in one frame of a nested loop-owner chain.

IterationMemberState

Bases: StrEnum

Represent iteration member values used by the runtime.

JoinInstance

Bases: _FrozenContract

Represent join instance state and behavior.

delivered_obligation_count property

delivered_obligation_count: int

Implement the delivered obligation count boundary for this component.

JoinLifecycleState

Bases: StrEnum

Represent join lifecycle values used by the runtime.

JoinObligation

Bases: _FrozenContract

Represent join obligation state and behavior.

JoinObligationOutcome

Bases: StrEnum

Represent join obligation outcome state and behavior.

LoopEnclosingOwner

Bases: _FrozenContract

The durable outer owner resumed when a loop instance settles.

LoopExit

Bases: _FrozenContract

Represent loop exit state and behavior.

LoopExitRecord

Bases: _FrozenContract

Represent loop exit record state and behavior.

LoopExitResolutionOutcome

Bases: StrEnum

Represent loop exit resolution outcome state and behavior.

LoopInstance

Bases: _FrozenContract

Durable loop state with bounded, scope-local token allocation metadata.

next_token_ordinal is the next never-used ordinal. A runtime derives a deterministic token ID from loop_instance_id plus that ordinal, then advances the cursor atomically. This standalone snapshot validates the exclusive upper bound against currently durable identities; monotonicity between revisions is a transition/CAS invariant.

LoopLifecycleState

Bases: StrEnum

Represent loop lifecycle values used by the runtime.

OperationIdentity

Bases: _FrozenContract

One logical side-effecting operation, addressable across replays.

This is the value threaded to executable units and agent tools instead of leaving them to reconstruct identity from run.metadata.

dedupe_supported property

dedupe_supported: bool

Whether a repeat can be collapsed rather than merely re-attempted.

PayloadDelivery

Bases: _FrozenContract

A wrapper that distinguishes a delivered JSON null from no delivery.

ProvenanceFrame

Bases: _FrozenContract

Represent provenance frame state and behavior.

SchedulingState

Bases: StrEnum

A token's one exclusive scheduler location.

SideEffectSupport

Bases: StrEnum

What a side-effecting integration can guarantee about a repeated call.

The runtime cannot make a remote system idempotent. What it can do is record which guarantee actually applies, so a duplicate that survives is a known residual rather than a silent one.

IDEMPOTENT class-attribute instance-attribute

IDEMPOTENT = 'idempotent'

The target accepts the operation key and collapses repeats itself.

OUTCOME_QUERYABLE class-attribute instance-attribute

OUTCOME_QUERYABLE = 'outcome_queryable'

The target cannot dedupe, but can be asked what a prior call did.

AT_LEAST_ONCE class-attribute instance-attribute

AT_LEAST_ONCE = 'at_least_once'

Neither. A retry may apply the effect twice; this is the default.

TokenEnvelope

Bases: _FrozenContract

The complete, replayable state carried by one control-flow token.

TokenLifecycleState

Bases: StrEnum

Durable lifecycle independent of structured-scope ownership.

derive_operation_key

derive_operation_key(
    *,
    run_id: str,
    idempotency_key: str,
    target_ref: str,
    call_ordinal: int = 0,
) -> str

Derive the stable key identifying one logical side-effecting operation.

The material is exactly the four things that make an operation logically distinct. Two exclusions are load-bearing and deliberate:

attempt is excluded so a transport retry, a token retry and a recovered worker all reproduce the same key -- that reproduction is what lets the receipt store recognise the repeat at all.

dispatch_id is excluded because recover_dispatch re-issues a dispatch for an unchanged logical operation; including it would fork the identity at precisely the moment the guarantee is needed most.

operation_identity

operation_identity(
    *,
    run_id: str,
    dispatch_id: str,
    idempotency_key: str,
    attempt: int,
    target_ref: str,
    call_ordinal: int = 0,
    support: SideEffectSupport = SideEffectSupport.AT_LEAST_ONCE,
) -> OperationIdentity

Build an :class:OperationIdentity with its derived key.