NoETL DSL Refactoring Specification
Version: March 2026
Status: Development Reference for Refactoring and Validation
1. Purpose
This document defines the NoETL DSL model to use as the target for refactoring and validation.
It is intended to remove ambiguity for agentic AI development and implementation work across:
- server orchestration
- worker execution
- dynamic execution mode
- playbook composition
- reference-only result handling
- scoped state mutation
This document defines the author-facing DSL surface and the required execution semantics behind it.
2. Core Design Principles
2.1 Server and worker responsibilities
The server is the sole authority for:
- workflow node admission
- routing between workflow nodes
- dynamic node synthesis
- execution tree ownership
- replay reconstruction from the event log
Workers are pure executors of step tool bodies. Workers do not synthesize workflow nodes and do not mutate the execution graph.
2.2 Playbook as template
A playbook is a reusable template for execution. Execution data and runtime state determine how the template is traversed.
2.3 Workflow node identity
The workflow list remains a list of named workflow nodes identified by step. The step concept must remain in the runtime model because it is the schedulable, auditable, replayable graph node.
2.4 Tool as executable body
tool remains the executable body of a workflow step.
A step may contain:
- a single primitive tool invocation, or
- a composite tool program expressed as an ordered list of tool items
A composite tool is still one step-level execution unit.
2.5 Unified author-facing data model
The DSL surface uses:
inputoutputset
These replace ambiguous combinations such as:
argsoutcomeresultset_ctxset_iternext.arcs[].args
3. Main DSL Concepts
3.1 workflow
workflow is the orchestration graph seed.
Each item in workflow is a workflow node identified by step.
3.2 step
step is the workflow node identity used by the server for:
- admission
- scheduling
- routing
- synthesis
- replay
3.3 tool
tool is the executable body of the step.
Allowed forms:
Single tool form
tool:
kind: http
input:
url: "{{ workload.api_url }}"
Composite tool form
tool:
- name: init_page
kind: noop
- name: fetch_page
kind: http
- name: paginate
kind: noop
In composite form, tool items may jump, retry, continue, skip, or break according to local policy and runtime state.
3.4 input
input is the author-facing input binding mechanism.
It may be used at:
- step level
- tool level
- between tool items in a composite tool program
input defines the values that a boundary receives.
3.5 output
output is the author-facing observable result object.
output replaces outcome and replaces author-facing use of result.
Use this structure:
output.statusoutput.dataoutput.refoutput.error- optional kind-specific metadata such as
output.http.status
output.data is the returned payload.
output.ref is the reference to externally stored payload.
3.6 set
set is the author-facing scoped mutation mechanism.
set is a first-class field and must not live under spec.
set may be used at:
- step level
- tool item level inside a composite tool program
- between tool items, where local state-machine transitions require updated state before another item executes
This allows a composite tool program to behave like a local state machine while still using the same author-facing assignment model everywhere.
3.7 spec
spec contains execution modifiers and execution policy, such as:
- retry behavior
- timeout
- loop mode
- concurrency
- validation
- storage hints
- policy rules
spec does not contain the primary executable body and does not contain set.
3.8 next
next controls routing between workflow steps.
next may also contain transition-scoped set on individual arcs.
Cross-step data propagation is performed through set, using either:
- step-level
setfor values published regardless of which arc is taken next.arcs[].setfor values written only when a specific transition is selected
4. Unified Input and Output Semantics
4.1 Why input/output are used everywhere
The same concepts are used at every boundary:
- step receives
input - tool receives
input - tool item may define
input - step exposes
output - tool item exposes
output - playbook call receives
inputand returnsoutput
This keeps the DSL consistent for humans and for AI generation.
4.2 Tool item chaining inside composite tool programs
In a composite tool program, each tool item may consume input, produce output, and apply set before control moves to another item.
Example:
tool:
- name: init_page
kind: noop
set:
iter.page: 1
iter.pages_fetched: 0
- name: fetch_page
kind: http
input:
url: "{{ workload.api_url }}/api/v1/patient-records"
params:
patientId: "{{ iter.item }}"
page: "{{ iter.page }}"
spec:
policy:
rules:
- when: "{{ output.status == 'error' and output.http.status == 429 }}"
then: { do: retry, attempts: 30, delay: 1.0 }
- else:
then:
do: continue
set:
iter.has_more: "{{ output.data.meta.page < output.data.meta.totalPages }}"
iter.pages_fetched: "{{ (iter.pages_fetched | int) + 1 }}"
- name: paginate
kind: noop
spec:
policy:
rules:
- when: "{{ iter.has_more | string | lower == 'true' }}"
then:
do: jump
to: fetch_page
set:
iter.page: "{{ (iter.page | int) + 1 }}"
This is valid because input, output, and set are allowed between tool items as part of the same local execution program.
5. Scopes
5.1 workload
workload is immutable execution input.
Use it for external request data and fixed execution parameters.
5.2 ctx
ctx is workflow-scoped mutable shared state.
Use it for values that must survive across steps.
5.3 step
step may be used as step-scoped mutable local state.
It exists only for the current workflow step execution.
5.4 iter
iter is loop-scoped mutable local state.
It is isolated per iteration, especially in parallel mode.
5.5 input
input is boundary-local bound input.
It exists for the current step, tool, or tool item boundary.
5.6 output
output is boundary-local observable result.
It exists for the most recently completed executable boundary.
6. set Semantics
6.1 General rule
set writes values into named scopes.
Preferred flat form:
set:
ctx.token_ref: "{{ output.ref }}"
iter.page: "{{ (iter.page | int) + 1 }}"
step.request_url: "{{ workload.api_url ~ '/v1/items' }}"
6.2 Allowed targets
Allowed set targets:
ctx.*step.*iter.*
Read-only sources include:
workload.*input.*output.*
6.3 Step-level set vs arc-level set
Use step-level set when a value must be published regardless of which next arc is taken.
Example:
set:
ctx.last_ref: "{{ output.ref }}"
ctx.last_status: "{{ output.status }}"
Use next.arcs[].set when the value belongs to a specific transition and must only be written if that arc is selected.
Example:
next:
spec:
mode: exclusive
arcs:
- step: validate
set:
ctx.validation_ref: "{{ output.ref }}"
6.4 Ordering
When both step-level and arc-level assignment exist, execution order is:
- the step or composite tool completes
- step-level
setis applied nextarc conditions are evaluated- the selected arc-level
setis applied - the destination step is scheduled
6.5 Parallel safety
In parallel loops:
iter.*writes are allowed for the current iterationstep.*writes are allowed for the current step executionctx.*writes must follow deterministic merge policy or be explicitly allowed by runtime rules
7. Reference-Only Result Model
7.1 Rule
Large payloads are not carried inline in step events. They are externalized and represented by output.ref.
Downstream full payload access must happen through explicit resolution.
7.2 Reference naming convention in set
When assigning a reference, the target variable name must end with _ref.
Example:
set:
ctx.patient_records_ref: "{{ output.ref }}"
This tells both humans and agents that the value is a locator, not hydrated data.
7.3 Hydrated data assignment
If a step wants to store hydrated payload content:
set:
ctx.patient_records: "{{ output.data }}"
This is distinct from assigning a reference.
7.4 Reference object requirements
A reference object must make retrieval explicit. It must include enough information to determine:
- where the data is stored
- what channel or protocol is needed to retrieve it
- which credential or auth reference is required
- integrity and metadata details
Normalized author-facing shape:
output:
ref:
type: relational | nats | object_store | blob
locator: {}
auth_reference: "keychain/main"
meta:
content_type: "application/json"
bytes: 102400
sha256: "..."
ttl: "24h"
7.5 Resolution rule
A step must not read full business payload fields directly from a reference object.
To access payload content, the step must call a resolver-capable tool.
Example:
- step: resolve_records
input:
records_ref: "{{ ctx.patient_records_ref }}"
tool:
kind: resolve
input:
ref: "{{ input.records_ref }}"
set:
step.records: "{{ output.data }}"
8. Workbook and Playbook Reuse
8.1 Inline tool reuse vs playbook call
Inline composite tool programs are local state machines inside a workflow step.
kind: playbook is a separate execution boundary with:
- its own execution instance
- its own scopes
- its own event log
- returned
outputconsumed by the caller
Because of that, child playbook calls do not overlap with the caller’s tool item namespace.
8.2 Workbook guidance
A workbook-like local reusable unit is only justified if you need an intermediate callable boundary between:
- inline local tool logic
- full child playbook execution
If such a mechanism exists, it must behave as a call boundary, not as macro expansion into the caller’s local item namespace.
9. Validation Rules
toolmay be a mapping or a list.nextperforms routing and may carry transition-scopedset, but must not carry cross-step payload input.input,output, andsetare valid at step level and tool-item level.setmust not be nested underspec._reftargets must receive reference objects.- Non-
_reftargets must not receive unresolved references. output.datais the only author-facing payload field; author-facingresultis disallowed.- Author-facing
outcomeis disallowed and replaced byoutput. argsis disallowed in new authoring and replaced byinput.
10. Migration Summary
Replace:
args->inputoutcome->outputresultpayload access ->output.dataresult_ref->output.refset_ctx/set_iter->setnext.arcs[].args->next.arcs[].setor step-levelset, plus consumerinput
11. Refactoring Goal
The target model is:
workflowsteptoolinputoutputsetspecnext
This model must be used consistently so that AI-generated refactoring and future development have one unambiguous author-facing surface.