Documentation

Why Two Tiers?

Some expressions can be resolved as soon as the node is about to execute, before the upstream outputs are known. Environment variables and secrets fall into this category — they are static configuration data that does not depend on what any upstream node produced.

Other expressions — like @{input:employeeId} or @{output:fetchUser.email} — cannot be resolved until the orchestrator has assembled the input bag from upstream outputs. Trying to resolve them before that point would always produce an empty result.

The two-tier design resolves each field exactly when its source data is available, avoiding both premature resolution (empty results) and deferred resolution (unnecessary overhead).

The Full Pipeline

flowchart LR subgraph T1["Tier 1 — AtConfigLoad"] direction TB CM["3-Layer Config Merge\nExtension → Connector → Element"] T1E["Evaluate AtConfigLoad fields\nenv: secret: context:"] CM --> T1E end subgraph IM["Input Assembly"] direction TB IA["Orchestrator maps upstream\noutputs into element InputData"] end subgraph T2["Tier 2 — AtInputReady"] direction TB T2E["Evaluate AtInputReady fields\ninput: output: var:"] end subgraph V["Validation"] direction TB VL["Required fields present?\nExecutor registered?\nConnector data valid?"] end EXEC(["Node Executor\nAll fields fully resolved"]) T1 --> IM --> T2 --> V --> EXEC style T1 fill:#181d33,stroke:#6c8cff style IM fill:#111425,stroke:#34d399 style T2 fill:#181d33,stroke:#a78bfa style V fill:#181d33,stroke:#fbbf24 style EXEC fill:#162040,stroke:#6c8cff,color:#c8d8ff

Tier 1 — AtConfigLoad

Tier 1 runs during PrepareExecutionAsync, immediately after the 3-layer config merge completes. Only fields whose NodeFieldDescriptor has EvaluationStage == AtConfigLoad are processed here.

What is available at Tier 1

DirectiveAvailableNotes
@{env:KEY}YesProcess environment variables — always available
@{secret:Key}YesVault secrets — resolved via async secret store
@{context:field}YesExecution context (tenantId, processId, threadId)
@{input:key}PartialThread-level InputData (trigger payload) is available; element-level InputData is not yet populated
@{output:nodeKey.field}NoNodeOutputs are not yet available at Tier 1
@{var:key}NoVariable scope is not yet wired to element context

Tier 1 is synchronous with config loading. Its results are written back into the merged config dictionary in-place. If no fields have AtConfigLoad stage, the expression pipeline is skipped entirely for Tier 1 with zero overhead.

Tier 2 — AtInputReady

Tier 2 runs after the orchestrator has populated the element's InputData bag with mapped outputs from upstream nodes. Only fields with EvaluationStage == AtInputReady are processed here.

What is available at Tier 2

DirectiveAvailableNotes
@{env:KEY}YesAlso available in Tier 1, so typically declared AtConfigLoad instead
@{secret:Key}YesAlso available in Tier 1, so typically declared AtConfigLoad instead
@{context:field}YesSame as Tier 1
@{input:key}YesElement-level InputData — upstream outputs mapped into this node's input
@{output:nodeKey.field}YesFull NodeOutputs from all previously completed nodes
@{var:key}YesVariable scope chain (global + any current loop/function scope)

Tier 2 results are also written back into the merged config dictionary. Like Tier 1, if no fields require AtInputReady evaluation, the entire tier is skipped.

EvaluationStage Enum

ValueWhen it runsUse for
AtConfigLoadAfter 3-layer merge, before input mappingStatic configuration: env vars, secrets, execution context
AtInputReadyAfter input bag is populated from upstream outputsRuntime data: input values, upstream outputs, variables
NeverField is never expression-evaluated (raw string only)

What Happens When No Manifest Exists

If a node executor does not implement INodeFieldManifestSource, all fields default to ExpressionPolicy.Default:

This means only @{env:}, @{secret:}, and @{context:} directives will resolve. @{input:}, @{output:}, and @{var:} directives in fields without an AtInputReady declaration will not resolve and will remain as literal text in the config.

Executors that need runtime data in their config fields must declare a field manifest with AtInputReady stage for those fields.

Error Handling

Expression evaluation errors are handled at the field level. A single field failing to resolve does not abort the entire pipeline. The behavior depends on the field's policy:

Tier skipping is free Both tiers check whether any fields exist that require their stage before doing any work. If your executor has no AtInputReady fields (for example, a simple utility node that only uses @{env:}), Tier 2 is entirely skipped without scanning the config dictionary.