Skip to content

How Data Flows Through a Workflow

Validibot separates workflow data by where it came from, who named it, and when it becomes available. Whenever you write a rule, configure an input, or connect two steps, you are choosing one of these data concerns rather than reaching into one global dictionary.

If you've ever written p.price > 0 or s.target_eui <= 60 and wondered what the prefixes mean, this page is the complete user-level overview.

The governing rule is simple:

A workflow contains many kinds of data, but only an author-named CEL/JSON value in s.* is a signal.

Validibot tip

You don't have to use the short prefix. p.price and payload.price mean the same thing. The short forms keep expressions readable; the long forms are there when you want to be explicit.

The namespaces at a glance

Short Long What it holds Scope
p. payload. The raw file the submitter sent (JSON, XML, CSV cells, parsed RDF, …) Whole workflow
submission. Metadata and server-stamped facts that live beside the submitted content Whole workflow
c. const. Fixed literals configured by the workflow author Whole workflow
s. signal. The workflow's named vocabulary — values you've mapped or promoted Whole workflow
i. input. This step's inputs — facts the validator sees before it runs One step, before its run
o. output. This step's outputs — values the validator produced after running One step, after its run
steps.<key>.input. / steps.<key>.output. An earlier step's inputs or outputs, addressed by step key Whole workflow

The teaching analogy: think of each step as a function in a program.

  • i.* are the function's parameters.
  • o.* is what the function returns.
  • s.* is module-level state shared across functions.
  • p.* is the raw input the program started with.
  • submission.* is metadata about that input — who, when, what type, how big.
  • c.* is fixed configuration compiled into this workflow version.
  • steps.foo.* is direct access to another function's parameters or return value.

When to reach for each one

p.* — the raw submission. Always present. Whatever the submitter sent, exactly as they sent it. p.price reads the price field of a JSON submission. For XML, attributes appear with an @ prefix (m["@Conductivity"]); see the CEL Expressions page for the XML rules.

c.* — workflow constants. Fixed authoring-time values such as a policy threshold, a list of allowed regions, or an agreed tolerance. Constants are part of the versioned workflow definition and are available as c.<name>/const.<name>. They are not signals because they are not resolved from run data.

s.* — the workflow's vocabulary. Author-named CEL/JSON values. You create them two ways:

  1. Workflow signal mapping — on the workflow's settings page, pick a name like target_eui, point it at a path in the submission, and it's available from the start of the run as s.target_eui.
  2. Promotion from a step — take an input or output of a particular step, click Copy to Signal, and give it a workflow-wide name. It is available as s.<your_name> after that input or output stage completes, so downstream steps can use it.

If you're writing steps.preflight.output.zone_count in more than one assertion, that's the cue to promote it — s.zone_count is stabler and shorter.

i.* — this step's inputs. Values the validator can see at the start of this step, before its main work runs. For an EnergyPlus step this is parser-extracted facts about the IDF (i.zone_count, i.idf_version). For a step with template variables, the resolved variable values land here. i.* is step-local — i.zone_count in one step is unrelated to i.zone_count in another.

o.* — this step's outputs. Values the validator produced after running. For an EnergyPlus step this is the simulation results (o.site_eui_kwh_m2, o.unmet_heating_hours). For a JSON Schema step there are usually no outputs — the validator just says pass or fail.

o.* is step-local and temporally bound: only available in output-stage assertions on the step that produced it. The assertion editor enforces this — when you're editing an input-stage assertion, autocomplete won't offer o.* references.

steps.<step_key>.input.* / steps.<step_key>.output.* — direct access to an earlier step's values by step key. Good for one-off cross-step references. For values you cite often, promote to s.* instead.

submission.* — the submission envelope. Context about the submission that lives beside the file rather than inside it, so it resolves the same way no matter what was uploaded — JSON, XML, CSV, or an RDF .ttl graph. Two flavours:

  • Submitter-provided (treat as untrusted): submission.name, submission.short_description, and the free-form submission.metadata.<key> bag. Whoever launched the run set these.
  • Server-stamped (trustworthy): submission.file_type, submission.size (bytes), submission.uploaded_at (a timestamp). Validibot sets these — a submitter can't forge them.

submission is long-only. There's no single-letter alias because s is already taken by signals.

Step inputs and outputs are declared ports

The Inputs and Outputs panels describe the contract of a validator step. An entry tells you what the step can consume or produce, its stable name, its data type, and—in the case of an input—how it can be connected.

That declaration is not itself a signal. At runtime, a small input value can appear as i.<name> and a small output value can appear as o.<name>. It only enters s.* if the workflow author explicitly promotes it.

Input bindings provide the wiring. For example, a step input could receive its value from p.project.target, s.target, c.default_target, or an earlier step's output. The input keeps its local i.* identity regardless of where the binding found the value.

Values and artifacts travel differently

Inputs and outputs can be small CEL/JSON values or full artifacts such as an FMU, weather file, report, transformed document, or log.

Value ports can participate in expressions, appear in i.* or o.*, travel through steps.*, and be copied to a signal.

Artifact ports participate in file binding, access control, storage, hashes, retention, and lineage. An artifact is never a signal and cannot be copied into s.*. A validator can still expose small facts about an artifact—such as o.has_report, o.row_count, or o.file_format—and those values can be used in expressions or promoted.

When values become available

The prefixes also express time:

  1. The submission arrives, making p.*, submission.*, and c.* available.
  2. Signal mappings resolve, creating the initial s.* vocabulary.
  3. The next step's bindings and parser populate i.*.
  4. Input-stage assertions run. The current step has no o.* values yet.
  5. The validator runs and produces value outputs plus any separate artifacts.
  6. Output-stage assertions run with both i.* and o.* available.
  7. Promotions add selected value inputs or outputs to s.* for downstream use.
  8. Later steps can address completed values directly through steps.<key>.*.

A mapped signal is available to every step because it exists before execution starts. A promoted signal is not retroactive; earlier steps cannot see a value that has not been produced yet.

When i.* and o.* are empty

A natural question: "Why are i.* and o.* sometimes empty?"

A step populates i.* or o.* only when its validator runs a process that transforms data. Three positions on the spectrum:

  • No process (JSON Schema, XML Schema, Basic) — assertions use p.* and optionally s.*. i.* and o.* are empty.
  • Process produces outputs only (SHACL, THERM) — the validator parses or evaluates the payload and emits results. Assertions primarily use o.*. i.* is empty.
  • Process has discrete input and output stages (EnergyPlus, FMU) — the validator extracts facts from the payload first (i.*), runs its main work, then emits results (o.*). Both stages are meaningful.

If you open a workflow step and the Inputs or Outputs panel is empty, that's intentional — it accurately reflects what the chosen validator does with your data.

Direct step references or signals?

Use a direct reference such as steps.energyplus.output.site_eui_kwh_m2 when the assertion intentionally depends on that particular producer and contract key.

Promote the value to a signal such as s.actual_eui when it represents shared workflow vocabulary, appears in several assertions or steps, or should survive a future change in the producing validator. The direct reference preserves provenance; the signal expresses business meaning.

Choosing the right prefix

  • Raw content supplied for validation: p.*
  • Metadata about that submission: submission.*
  • Fixed workflow policy or threshold: c.*
  • Current step parameter or parsed input fact: i.*
  • Current step result: o.*
  • One-off dependency on a known earlier step: steps.<key>.*
  • Shared author-named business vocabulary: s.*
  • File or file-like input/output: use an artifact port, not a CEL namespace

A special case: row.*

There's one namespace that doesn't appear in the table above because it only exists in one place. Inside a row rule on a Tabular validator step, row.<column> refers to the current row's value for a declared column (row.min_depth <= row.max_depth), and the rule runs once per data row.

row.* isn't available anywhere else, and a row rule may only reference columns declared in the step's schema — typos are caught when you save the step, not on the next run.

Where to next

  • CEL Expressions — the full guide to writing rules over these namespaces, including all supported operators, Validibot helpers, and worked examples.
  • Tabular Validator — the only place row.* exists.
  • Glossary — formal definitions of signal, payload, submission, and friends.
  • Manage Workflows — configure mappings, bindings, assertions, and promotions in the authoring interface.
Spotted a problem on this page? Report it or suggest an edit