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:
- 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 ass.target_eui. - 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-formsubmission.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:
- The submission arrives, making
p.*,submission.*, andc.*available. - Signal mappings resolve, creating the initial
s.*vocabulary. - The next step's bindings and parser populate
i.*. - Input-stage assertions run. The current step has no
o.*values yet. - The validator runs and produces value outputs plus any separate artifacts.
- Output-stage assertions run with both
i.*ando.*available. - Promotions add selected value inputs or outputs to
s.*for downstream use. - 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 optionallys.*.i.*ando.*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.