Skip to content

Latest commit

 

History

History
289 lines (222 loc) · 7.96 KB

File metadata and controls

289 lines (222 loc) · 7.96 KB

Flow contract

An editable flow is UTF-8 TOML. Compilation recursively validates referenced resources and emits an immutable graph interpreted by the installed VIA release.

Complete example

name = "company/change"

[inputs.task]
description = "Decision-complete task."
accepts = ["text", "file"]

[inputs.repository]
description = "Repository context."
accepts = ["directory"]

[limits]
node_attempts = 3
contract_repair_attempts = 2

[nodes.implement]
uses.action = "company/implement"
agent = "company-maker"

[nodes.implement.inputs.task]
from = "$task"

[nodes.implement.inputs.repository]
from = "$repository"

[nodes.implement.outputs.patch]
description = "Candidate patch."
kind = "file"

[nodes.review]
uses.action = "company/review"
agent = "company-reviewer"
needs = ["implement"]
restart_from = "implement"
human_approval = true

[nodes.review.inputs.patch]
from = "implement.patch"

[nodes.review.outputs.review]
description = "Review evidence."
kind = "file"

[nodes.review.limits]
node_attempts = 2
contract_repair_attempts = 1

[outputs.patch]
description = "Approved patch."
from = "implement.patch"
path = "change.patch"

Root fields

Field Contract
name Non-empty logical flow name
limits Required retry budgets
inputs Optional public input contracts
nodes At least one node
outputs Optional public publication bindings

Unknown fields produce one aggregated warning per file and do not block parsing.

Inputs and assets

[inputs.brief]
description = "Approved brief."
accepts = ["text", "file"]

Kinds are text, file, and directory. File and directory bindings must exist inside the project and contain no symlinks. Every declared flow input is required at compile time; undeclared bindings are rejected.

Node inputs are explicit:

[nodes.write.inputs.brief]
from = "$brief"

[nodes.review.inputs.document]
from = "write.document"

$brief references a public flow input. write.document references a declared output of another source node. A node-output reference automatically implies a data dependency. needs may still state the same dependency for readability; the compiler deduplicates it.

Node resources

A node uses exactly one resource.

Action:

[nodes.write]
uses.action = "./actions/write.md"
agent = "writer"

Nested flow:

[nodes.verify]
uses.flow = "company/verify"

[nodes.verify.inputs.change]
from = "write.bundle"
description = "Candidate selected for final verification."

[nodes.verify.outputs.evidence]
description = "Verification evidence used by this flow."
kind = "file"

Process:

[nodes.compile]
uses.run = [
  "via",
  "flow",
  "compile",
  "{inputs.source}/flow.toml",
  "--output",
  "{outputs.graph}",
]

[nodes.compile.inputs.source]
from = "write.bundle"

[nodes.compile.outputs.graph]
description = "Compiled graph."
kind = "file"

Virtual all-approved gate:

[nodes.review_gate]
uses.gate = "all-approved"
needs = ["architecture_review", "code_review"]
restart_from = "implement"

Only action nodes declare agent. Every source node declares the inputs it binds. Action and process nodes declare the outputs they produce:

[nodes.write.outputs.bundle]
description = "Generated source bundle."
kind = "directory"

Output kinds are file or directory. A nested-flow call repeats the callee's public output keys and kinds so the call contract is visible during manual audit. Compilation rejects missing, extra, or mismatched output declarations. Descriptions at the callsite explain the caller's use of each input and output. The callee still owns its public contract.

Executable nodes may declare no outputs. This is normal for coding nodes whose result is a validated worktree side effect rather than a portable asset. Creating a Git commit is also an explicit project policy: if required, model it as a separate node after review or approval. Reusable software flows do not commit implicitly.

An action result may also return safe supplemental artifacts that were not declared. They are retained as evidence and shown to downstream dependencies, but they cannot be referenced by a static node.output binding or published as a public flow output. This keeps the executable contract explicit while allowing useful dynamic diagnostics.

uses.run is an argv array, never a shell expression. A path-like program must be an executable regular file inside the project without symlinks; a bare program is resolved through PATH at execution. Every declared run input and output must appear as an {inputs.<key>} or {outputs.<key>} placeholder. The program element itself cannot contain placeholders.

Dependencies, conditions, and restart

needs = ["prepare"]
restart_from = "prepare"

[nodes.review.if]
node = "prepare"
outcome = "approved"

Dependencies must exist and be acyclic. A condition references a dependency and matches its latest accepted outcome. A false condition makes the node inapplicable without blocking terminal state.

restart_from must identify a strict ancestor. A restart result sends exact review feedback and accepted artifacts back to that target. Unordered restart nodes are rejected when either restart segment can invalidate the other; ordered loops, independent branches, and branches proven mutually exclusive by exact conditions remain valid.

An all-approved gate has at least two action dependencies and no agent, inputs, outputs, conditions, approval, or limits. Its restart_from is a common strict ancestor of every dependency. Dependencies return complete with outcome approved or rejected: all approved completes the gate, while any rejection restarts the target with the latest accepted artifacts from every dependency. A blocked dependency leaves the gate pending and the run blocked.

Set human_approval = true on an action, process, or nested-flow source node when downstream work must wait for explicit user confirmation:

[nodes.review]
uses.action = "company/review"
agent = "company-reviewer"
human_approval = true

Compilation lowers the declaration to an internal checkpoint after all terminals of that source. Nested flows are otherwise fully inlined: they have no independent run, publication root, or runtime boundary. human_approval cannot be combined with if.

Limits

[limits]
node_attempts = 3
contract_repair_attempts = 2
  • node_attempts >= 1: automatic semantic attempts per node.
  • contract_repair_attempts >= 0: result-contract repair operations inside one node attempt.

Node [limits] may override either field. The CLI owns attempt numbers.

Public outputs

[outputs.bundle]
description = "Approved bundle."
from = "write.bundle"
path = "."

from must reference a declared node output. path is a relative canonical destination inside the run's publication root. path = "." publishes the contents of a directory bundle. Runtime validation checks declared key, presence, kind, containment, and symlink safety before accepting an output. Only the root flow's public outputs are published. A nested flow's path values do not create a nested publication root and are ignored after its public output contract has been checked.

Locators

Form Resolution
package/resource `.via/packages/package/{flows
local/resource `.via/local/{flows
./resource, ../resource Relative to the containing flow
absolute path Only inside the project

Namespaced locator segments use letters, digits, _, and -. Relative locators may traverse only while remaining inside the project.

Compilation

via flow validate company/change
via flow compile company/change \
  --input task=file:task.md \
  --input repository=directory:. \
  --output graph.toml

Normal user execution uses via run create --flow ..., which performs compilation internally and stores the graph inside the run. The explicit compile command is for validation, package fixtures, and debugging.