24 KiB
PEL/PROGRAM-DAG/1 — DAG Program Scheme for PEL
Status: Approved Owner: Niklas Rydberg Version: 0.3.1 SoT: Yes Last Updated: 2025-11-16 Linked Phase Pack: N/A Tags: [execution, composition]
Document ID: PEL/PROGRAM-DAG/1
Layer: L1 Scheme Profile (on top of PEL/1-CORE)
Depends on (normative):
ASL/1-CORE v0.4.x— Artifact Substrate (value model)PEL/1-CORE v0.3.x— Primitive Execution Layer (core semantics)
Integrates with (informative):
PEL/1-SURF v0.2.x— Store-backed execution surfaceENC/PEL-PROGRAM-DAG/1(planned) — Canonical encoding for DAG programsPEL/TRACE-DAG/1(planned) — Node-level execution trace profileOPREG/PEL1-KERNEL(planned) — Operation registry for DAG opsSUBSTRATE/STACK-OVERVIEW v0.1.x— Kernel / near-core layering
© 2025 Niklas Rydberg.
License
Except where otherwise noted, this document (text and diagrams) is licensed under the Creative Commons Attribution 4.0 International License (CC BY 4.0).
The identifier registries and mapping tables (e.g. TypeTag IDs, HashId assignments, EdgeTypeId tables) are additionally made available under CC0 1.0 Universal (CC0) to enable unrestricted reuse in implementations and derivative specifications.
Code examples in this document are provided under the Apache License 2.0 unless explicitly stated otherwise. Test vectors, where present, are dedicated to the public domain under CC0 1.0.
0. Conventions
RFC 2119 terms (MUST, MUST NOT, SHOULD, MAY, etc.) are normative.
From ASL/1-CORE:
Artifact {
bytes: OctetString
type_tag: optional TypeTag
}
TypeTag {
tag_id: uint32
}
Reference {
hash_id: HashId
digest: OctetString
}
HashId = uint16
From PEL/1-CORE (summary):
SchemeRef = Reference
ExecutionStatus = uint8
ExecutionErrorKind = uint8
ExecutionErrorSummary {
kind: ExecutionErrorKind
status_code: uint32
}
DiagnosticEntry {
code: uint32
message: OctetString
}
ExecutionResultValue {
pel1_version : uint16
status : ExecutionStatus
scheme_ref : SchemeRef
summary : ExecutionErrorSummary
diagnostics : list<DiagnosticEntry>
}
PEL/1-CORE defines the following shared enums:
ExecutionStatus {
OK = 0
SCHEME_UNSUPPORTED= 1
INVALID_PROGRAM = 2
INVALID_INPUTS = 3
RUNTIME_FAILED = 4
}
ExecutionErrorKind {
NONE = 0
SCHEME = 1
PROGRAM = 2
INPUTS = 3
RUNTIME = 4
}
For this scheme:
-
Exec_DAGuses only the followingExecutionStatusvalues:OK INVALID_PROGRAM INVALID_INPUTS RUNTIME_FAILED(
SCHEME_UNSUPPORTEDis never produced byExec_DAG; surfaces likePEL/1-SURFhandle that before calling the scheme.) -
It uses only the following
ExecutionErrorKindvalues:NONE PROGRAM INPUTS RUNTIME
The status ↔ kind mapping for this scheme is:
status = OK -> summary.kind = NONE
status = INVALID_PROGRAM -> summary.kind = PROGRAM
status = INVALID_INPUTS -> summary.kind = INPUTS
status = RUNTIME_FAILED -> summary.kind = RUNTIME
summary.status_code is scheme-defined but MUST be deterministic and respect:
status = OK→status_code = 0status = INVALID_PROGRAM→status_code = 2(canonical core code)status = INVALID_INPUTS→status_code = 3(canonical core code)status = RUNTIME_FAILED→status_codeMUST be non-zero and MUST NOT be2or3(operation/scheme–defined runtime code).
Exact non-zero runtime codes come from operation registries and/or a future error code profile; this spec only constrains ranges and mapping to ExecutionStatus / ExecutionErrorKind.
1. Purpose & Non-Goals
1.1 Purpose
PEL/PROGRAM-DAG/1 defines a concrete PEL scheme where:
-
Programs are acyclic computation graphs (DAGs) of operations (Nodes).
-
Nodes read from:
- external inputs (by index into the
inputslist given toExec_DAG), and - outputs of other Nodes (by
NodeId+output_index).
- external inputs (by index into the
-
Programs designate root outputs as the scheme-level outputs.
This profile provides:
-
A precise program value model:
Program,Node,DagInput,RootRef. -
Structural validity rules for such programs.
-
A canonical topological ordering for deterministic evaluation.
-
A binding of
PEL/1-CORE’sExec_sto a specific function:Exec_DAG : (program: Artifact, inputs: list<Artifact>, params: optional Artifact) -> (outputs: list<Artifact>, result: ExecutionResultValue)
Exec_DAG is pure and store-neutral; store-backed execution is handled by PEL/1-SURF.
1.2 Non-Goals
This scheme does not define:
- Graph/provenance edges (
TGK/1-CORE). - Store semantics (
ASL/1-STORE) or surfaces (PEL/1-SURF). - Concrete binary layouts (
ENC/PEL-PROGRAM-DAG/1). - Trace artifact formats (
PEL/TRACE-DAG/1). - Operation registries (
OPREG/PEL1-KERNEL/ extensions) or specific operation semantics.
Those are separate profiles layered on top of this scheme.
2. Core Model
2.1 OperationId
Operations are identified by a logical OperationId:
OperationId {
name: String // logical text identifier
version: uint32 // monotonic per name
}
Stringhere is a logical UTF-8 string; its concrete encoding into bytes is defined byENC/PEL-PROGRAM-DAG/1.- For a given
(name, version)within a given operation registry, semantics MUST be immutable once published. Any semantic change MUST be expressed as a newversion.
At this scheme level, OperationId is opaque. A registry profile (e.g. OPREG/PEL1-KERNEL) defines a function:
OpSemantics(name, version) :
(inputs: list<Artifact>, params: ParamsValue)
-> Ok(list<Artifact>) | Err(status_code: uint32, diagnostics: list<DiagnosticEntry>)
2.2 Params
Each Node carries params as an opaque parameter blob:
ParamsBytes = OctetString // raw bytes in the Program encoding
ParamsValue = scheme-defined structured value
Requirements:
-
For each operation
(name, version):- There MUST exist a well-defined abstract parameter model (
ParamsValue). - There MUST be exactly one canonical encode/decode between
ParamsValueandParamsBytes.
- There MUST exist a well-defined abstract parameter model (
-
All conformant implementations of that operation MUST:
- decode the same
ParamsBytesinto the sameParamsValue, or - deterministically signal a decode failure.
- decode the same
Static vs dynamic errors (guidance):
- Static param errors (invalid structure, missing mandatory fields, invalid enums, out-of-range constants)
SHOULD be treated as
INVALID_PROGRAM. - Dynamic errors that depend on runtime inputs (e.g. “division by zero” because an input argument is zero, even though the param is syntactically valid) MUST be treated as
RUNTIME_FAILEDand surfaced viastatus_codeanddiagnostics.
This spec requires determinism and the status mapping (§0); it only gives guidance on where to draw the static/dynamic line. Parameter profiles MAY formalize this split more strictly.
The concrete placement of ParamsBytes (embedded in the Program vs separate Artifacts referenced from the Program) is defined by ENC/PEL-PROGRAM-DAG/1.
2.3 NodeId
NodeId = uint32
Constraints:
NodeIdMUST be unique within a singleProgram.- When ordering Nodes,
NodeIdis compared as an unsigned integer (0 < 1 < 2 < … < 2^32 − 1).
2.4 DAG Inputs and Roots
Logical input references inside the Program:
DagInputExternal {
input_index: uint32 // 0-based index into Exec_DAG.inputs
}
DagInputNode {
node_id: NodeId // must exist in Program.nodes
output_index: uint32 // 0-based index into that Node's outputs
}
DagInput =
DagInputExternal
| DagInputNode
Scheme outputs (what Exec_DAG returns as outputs) are designated by:
RootRef {
node_id: NodeId
output_index: uint32
}
RootRef is interpreted like DagInputNode, but only in the final output selection step.
2.5 Node
A Node is a single operation application:
Node {
id: NodeId
op: OperationId
inputs: list<DagInput>
params: ParamsBytes
}
Constraints:
idMUST be unique within the Program.inputsMAY be empty (nullary operations).- All
DagInputNode.node_idMUST refer to someNode.idinProgram.nodes.
Semantics:
-
When evaluated, the Node:
- resolves each
DagInputto an inputArtifact, - decodes
paramsinto aParamsValueaccording toop, - applies the operation’s pure function to the input Artifacts and decoded params,
- yields zero or more output Artifacts.
- resolves each
The number and meaning of outputs per operation are defined in the operation registry, not here.
2.6 Program
A Program is an acyclic graph:
Program {
nodes: list<Node>
roots: list<RootRef>
}
Constraints (structural validity is detailed in §3):
- All
Node.idare unique. - All
DagInputNode.node_idandRootRef.node_idrefer to some Node innodes. - The dependency graph induced by
DagInputNodeis a DAG (no cycles).
2.7 Program Artifact and TypeTag
At the ASL/1 layer:
-
A Program Artifact is an
Artifactsuch that:type_tag = TYPE_TAG_PEL_PROGRAM_DAG_1(symbolic constant), andbytesencode exactly oneProgramvalue underENC/PEL-PROGRAM-DAG/1.
ENC/PEL-PROGRAM-DAG/1 MUST:
- assign a concrete numeric
tag_idforTYPE_TAG_PEL_PROGRAM_DAG_1, - define an injective, deterministic, streaming-friendly encoding for
Program, - ensure that all conformant engines decode/encode Programs identically.
This scheme assumes such an encoding exists and is canonical; it does not define the layout itself.
3. Structural Validity
A Program is structurally valid for this scheme if and only if all of the following hold:
-
Unique NodeIds
-
For all
Node A,Node Binnodes:A.id == B.id ⇒ A and B are the same Node
-
-
Node references are in range
-
For every
DagInputNode{ node_id, ... }in anyNode.inputs:- There exists a
Nodeinnodeswithid = node_id.
- There exists a
-
-
Roots are in range
-
For every
RootRef{ node_id, ... }inroots:- There exists a
Nodeinnodeswithid = node_id.
- There exists a
-
-
Acyclic dependency graph
-
Define a directed graph
G:- vertices: all
NodeIdvalues innodes, - edge from
N.idtoM.idifN.inputscontainsDagInputNode{ node_id = M.id, ... }.
- vertices: all
-
GMUST be acyclic (a DAG). -
Equivalently, there exists at least one topological ordering of Nodes where all dependencies of a Node appear before it.
-
-
OperationId syntax is well-formed
-
Each
Node.opis syntactically well-formed as:- a valid
namestring (logical UTF-8 as perENC/PEL-PROGRAM-DAG/1), and - a
uint32version.
- a valid
-
Whether
(name, version)is defined in the operation registry is an execution-time concern (§5.3).
-
-
Parameter profile is available for each Node
-
For every
Nodeinnodes:- The active operation/parameter registry (e.g.
OPREG/PEL1-KERNEL+ parameter profiles) MUST provide a parameter-decoding function for that Node’sOperationId.
- The active operation/parameter registry (e.g.
-
3.1 Parameter decoding and INVALID_PROGRAM
Program validity depends both on graph structure and on decodability of Node params under the chosen operation registry.
Given:
- a
Programvalue, - an operation registry for this scheme, and
- a set of parameter profiles,
validation MUST include parameter decoding for every Node:
-
For each
NodeinProgram.nodes:- Let
opbe itsOperationId. - Let
params_bytesbe itsParamsBytes. - Let
decode_params_opbe the param-decoding function associated withop.
- Let
-
The engine MUST conceptually invoke
decode_params_op(params_bytes)during Program validation. -
If decoding fails for any Node (including any length, range, or structural violations defined by
decode_params_op):- the Program MUST be classified as structurally invalid for this scheme, and
- any execution attempt MUST yield
status = INVALID_PROGRAM(independent of theinputs).
Implementations MAY:
- eagerly validate all params before any node evaluation, or
- lazily decode on first use of a Node’s params,
as long as the observable semantics is:
For a fixed
programArtifact, either all Node params are decodable (and execution can proceed), or every call toExec_DAGon thatprogramyieldsstatus = INVALID_PROGRAM, regardless ofinputsandparams.
A Program Artifact whose bytes cannot be decoded as a Program under ENC/PEL-PROGRAM-DAG/1 is treated as “not a Program” and MUST also produce status = INVALID_PROGRAM.
4. Canonical Topological Order
4.1 Definition
Given a structurally valid Program:
-
Construct the dependency graph
Gas in §3.4. -
While unplaced Nodes remain:
- Let
Cbe the set of Nodes whose dependencies are already in the order (i.e., allDagInputNodetargetNodeIds are in the placed set). - From
C, choose the Node with smallestNodeId(numeric ascending). - Append that Node to the canonical order.
- Mark it as placed, and iterate.
- Let
The resulting sequence of Nodes is the canonical topological order and is unique given the Program.
4.2 Use
All references in this spec to:
- “Nodes in canonical order”, or
- “evaluating Nodes in order”,
refer to this ordering.
Implementations MAY evaluate Nodes in parallel or with different internal scheduling, but the observable behavior (outputs and ExecutionResultValue) MUST be equivalent to evaluating Nodes sequentially in canonical topological order.
5. Scheme Binding to PEL/1-CORE
5.1 Scheme identity
This scheme is identified by a SchemeRef pointing to a scheme descriptor Artifact, denoted:
SchemeRef_DAG_1 : SchemeRef
The descriptor Artifact (its TypeTag, bytes, and meaning) is defined in a separate descriptor document (e.g. PEL/PROGRAM-DAG-DESC/1). All conformant engines MUST agree on:
- the descriptor Artifact bytes, and
- the resulting
Reference(SchemeRef_DAG_1).
In PEL/1-CORE terms, this scheme binds:
SchemeRef = SchemeRef_DAG_1
Exec_s = Exec_DAG
5.2 Exec_DAG signature
For this scheme, Exec_s is instantiated as:
Exec_DAG(
program: Artifact, // Program Artifact
inputs: list<Artifact>, // external input Artifacts
params: optional Artifact // optional global parameters
) -> (outputs: list<Artifact>, result: ExecutionResultValue)
Interpretation:
-
programMUST be an Artifact withtype_tag = TYPE_TAG_PEL_PROGRAM_DAG_1and decodable as aProgram. Otherwise,status = INVALID_PROGRAM. -
inputsis the list of external inputs referenced byDagInputExternal.input_index. -
params, if present, is a scheme-specific Artifact that MAY:- be interpreted as global configuration, or
- be ignored,
depending on the scheme descriptor.
The scheme MUST be deterministic:
For fixed
program.bytes,inputslist, andparams.bytes, all conformant implementations ofExec_DAGMUST produce identicaloutputsandExecutionResultValue.
5.3 High-level semantics of Exec_DAG
Exec_DAG MUST proceed conceptually in the following steps:
-
Decode and validate Program
-
Attempt to decode
program.bytesas aProgramunderENC/PEL-PROGRAM-DAG/1. -
If decoding fails:
-
outputs = []. -
result:pel1_version = 1 status = INVALID_PROGRAM scheme_ref = SchemeRef_DAG_1 summary.kind = PROGRAM summary.status_code = 2 diagnostics = [ deterministic diagnostic entries ] -
Return.
-
-
If decoding succeeds but the Program violates any structural validity rule (§3):
- Same as above:
status = INVALID_PROGRAM.
- Same as above:
-
-
Resolve operation semantics and validate params
-
For each
Node.opinProgram.nodes, resolve(Node.op.name, Node.op.version)against the operation registry for this scheme. -
If any
OperationIdused in the Program is not defined:-
Treat the Program as invalid:
outputs = [].resultasINVALID_PROGRAM(status_code = 2) with diagnostics indicating unknown operations.
-
Return.
-
-
For each Node:
-
Decode its
ParamsBytesusing the parameter profile for itsOperationId. -
If decoding fails:
-
Treat as
INVALID_PROGRAM:outputs = [].resultasINVALID_PROGRAM(status_code = 2) with appropriate diagnostics.
-
Return.
-
-
Otherwise, conceptually store the decoded
ParamsValuefor use in evaluation.
-
-
-
Evaluate Nodes in canonical order
-
Compute the canonical topological order (§4) of Nodes.
-
Maintain:
node_outputs : NodeId -> list<Artifact> -
For each Node
Nin canonical order:-
Resolve Node inputs
-
For each
DagInputinN.inputs:-
If
DagInputExternal{ input_index }:-
If
input_index >= len(inputs):-
This is an input error:
-
outputs = []. -
result:pel1_version = 1 status = INVALID_INPUTS scheme_ref = SchemeRef_DAG_1 summary.kind = INPUTS summary.status_code = 3 diagnostics = [ deterministic diagnostic for missing input_index ]
-
-
Return.
-
-
Otherwise use
inputs[input_index]as that input Artifact.
-
-
If
DagInputNode{ node_id, output_index }:-
Let
outs = node_outputs[node_id](must exist because canonical order ensures dependencies are evaluated first). -
If
output_index >= len(outs):-
This is a program error (operation output arity mismatch):
outputs = [].resultasINVALID_PROGRAM(status_code = 2) with suitable diagnostics.
-
Return.
-
-
-
-
Collect the resolved input Artifacts into
node_input_artifacts.
-
-
Apply operation semantics
-
Let
params_valuebe the decodedParamsValuefor NodeN. -
Invoke the operation semantics:
Op(N.op)( node_input_artifacts: list<Artifact>, params_value: ParamsValue ) -> Ok(list<Artifact>) | Err(status_code: uint32, diagnostics: list<DiagnosticEntry>) -
Implementation requirements:
Op(N.op)MUST be pure and deterministic.- It MUST NOT perform I/O, consult time, or depend on mutable global state.
-
If
Ok(outputs_for_node):- Set
node_outputs[N.id] = outputs_for_node.
- Set
-
If
Err(status_code, diag):-
This is a runtime failure;
Exec_DAGMUST:-
Set
outputs = [](this scheme is fail-fast; it does not define partial outputs). -
Set
result:pel1_version = 1 status = RUNTIME_FAILED scheme_ref = SchemeRef_DAG_1 summary.kind = RUNTIME summary.status_code = status_code // non-zero, operation-defined diagnostics = diag // or diag plus additional scheme diagnostics
-
-
No further Nodes are evaluated.
-
Return.
-
-
-
-
-
Compute scheme outputs (success case)
-
If all Nodes have been evaluated successfully (no early return):
-
Initialise
outputs = []. -
For each
RootRefinProgram.roots(in order):-
Let
outs = node_outputs[root.node_id]. -
If
root.output_index >= len(outs):-
Treat as
INVALID_PROGRAM:outputs = [].resultas in step 1 with suitable diagnostics.
-
Return.
-
-
Otherwise, append
outs[root.output_index]tooutputs.
-
-
When all roots resolve successfully:
-
outputsis the list of root Artifacts inProgram.rootsorder. -
result:pel1_version = 1 status = OK scheme_ref = SchemeRef_DAG_1 summary.kind = NONE summary.status_code = 0 diagnostics = []
-
-
-
-
Return
- Return
(outputs, result).
- Return
5.4 Determinism guarantees
For fixed:
program.bytes,inputslist (sequence and values),paramspresence andparams.bytes,
and given:
- a fixed canonical encoding profile (
ENC/PEL-PROGRAM-DAG/1), - a fixed operation registry (
OPREG/PEL1-KERNEL+ extension registries) for this scheme,
all conformant implementations of Exec_DAG MUST produce:
- identical logical
outputs(asArtifactvalues), and - identical
ExecutionResultValuepayloads.
No implementation-specific identifiers, timestamps, or environment details may appear in result or in the contents of outputs.
6. Interaction with PEL/1-SURF (Informative)
When this scheme is used with PEL/1-SURF:
-
The surface layer provides
program_ref,input_refs,params_refand a StoreInstance. -
It resolves those references to
program,inputs,paramsviaASL/1-STOREget. -
It invokes
Exec_DAG(program, inputs, params)as defined here. -
It persists:
- each element of
outputsviaput, producingoutput_refs, and - a surface ExecutionResult artifact containing
ExecutionResultValueplus context.
- each element of
Store-resolution failures (ERR_NOT_FOUND, ERR_INTEGRITY, ERR_UNSUPPORTED) are handled by PEL/1-SURF, which maps them to INVALID_PROGRAM / INVALID_INPUTS with store_failure metadata, without calling Exec_DAG.
Exec_DAG only sees Programs and input/param Artifacts that have successfully been loaded from the store.
7. Interaction with Trace Profiles (Informative)
A trace profile such as PEL/TRACE-DAG/1 MAY:
- Capture per-node inputs, outputs, and error states.
- Encode these in a
TraceValueArtifact with its ownTypeTag. - Specify that
Exec_DAG(or the surface) produces a trace Artifact as one of the outputs, and thatPEL/1-SURFexposes itsReferenceastrace_refin surface results.
PEL/PROGRAM-DAG/1 itself does not mandate traces; it only ensures evaluation order and deterministic behavior so that a trace profile can be layered cleanly.
8. Conformance
An implementation is PEL/PROGRAM-DAG/1–conformant if it:
-
Program handling
- Correctly decodes and encodes
Programvalues according to this spec andENC/PEL-PROGRAM-DAG/1. - Checks structural validity as in §3, including parameter decodability.
- Treats structurally invalid or undecodable Programs as
INVALID_PROGRAMinExec_DAG.
- Correctly decodes and encodes
-
Scheme binding
- Recognizes
SchemeRef_DAG_1as the scheme identifier. - Implements
Exec_DAGexactly as in §5 for this scheme. - Ensures that
result.scheme_ref = SchemeRef_DAG_1andresult.pel1_version = 1.
- Recognizes
-
Operation registry integration
-
Uses a registry for
(OperationId -> OpSemantics)where:- each
OpSemanticsis pure and deterministic, - param encodings are canonical and deterministic,
- runtime errors are mapped to non-zero
status_codes and deterministic diagnostics.
- each
-
Treats unknown
OperationIdvalues in a Program asINVALID_PROGRAM.
-
-
Status mapping
-
Produces
ExecutionResultValue.statusonly from:OK,INVALID_PROGRAM,INVALID_INPUTS,RUNTIME_FAILED.
-
Ensures
summary.kindandsummary.status_codefollow the mapping in §0 and §5.3.
-
-
Determinism and purity
-
Satisfies PEL/1-CORE’s determinism contract:
- For fixed inputs, all conformant implementations produce identical
outputsandExecutionResultValue.
- For fixed inputs, all conformant implementations produce identical
-
Does not consult time, randomness, external I/O, or mutable global state as part of
Exec_DAGsemantics.
-
-
Layering
- Does not depend on any particular store, graph, certification, or overlay behavior.
- Treats store resolution, provenance, certification, and overlays as responsibilities of other specs (
PEL/1-SURF,TGK/1-CORE,CIL/1,FER/1,FCT/1).
End of PEL/PROGRAM-DAG/1 v0.3.1 — DAG Program Scheme for PEL
Document History
- 0.3.1 (2025-11-16): Registered as Tier-1 spec and aligned to the Amduat 2.0 substrate baseline.