diff --git a/tier1/enc-pel-program-dag-1.md b/tier1/enc-pel-program-dag-1.md new file mode 100644 index 0000000..74bf79c --- /dev/null +++ b/tier1/enc-pel-program-dag-1.md @@ -0,0 +1,734 @@ +# ENC/PEL-PROGRAM-DAG/1 — Canonical Encoding for DAG Programs + +Status: Approved +Owner: Niklas Rydberg +Version: 0.2.0 +SoT: Yes +Last Updated: 2025-11-16 +Linked Phase Pack: N/A +Tags: [binary-minimalism, deterministic] + + + +**Document ID:** `ENC/PEL-PROGRAM-DAG/1` +**Profile ID:** `PEL_ENC_PROGRAM_DAG_V1 = 0x0101` +**Layer:** Scheme Encoding Profile (on top of ASL/1-CORE + PEL/PROGRAM-DAG/1) + +**Depends on (normative):** + +* `ASL/1-CORE v0.4.x` — value model (`Artifact`, `TypeTag`, `Reference`, integers, `OctetString`) +* `ENC/ASL1-CORE v1.0.3` — canonical encoding conventions (integers, `OctetString`, streaming constraints) +* `PEL/PROGRAM-DAG/1 v0.3.1` — DAG Program scheme (`Program` / `Node` model, semantics, canonical topological order) + +**Integrates with (informative):** + +* `PEL/1-CORE v0.3.x` — primitive execution layer (`Exec_s` / `Exec_DAG`) +* `PEL/1-SURF v0.2.x` — store-backed execution surface +* `HASH/ASL1 v0.2.4` — reference formation over canonical encodings +* TypeTag registry (for `TYPE_TAG_PEL_PROGRAM_DAG_1`) +* Operation registries (e.g. `OPREG/PEL1-KERNEL` and param profiles) + +> **Note:** The Profile ID `PEL_ENC_PROGRAM_DAG_V1` is a configuration label. +> It is **not** embedded in the encoded Program bytes. Selection of this encoding profile is done by context (scheme descriptor, store or engine configuration), not per value. + +© 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. Overview + +`ENC/PEL-PROGRAM-DAG/1` defines the **canonical binary encoding** of the `Program` structure defined in `PEL/PROGRAM-DAG/1`: + +```text +Program { + nodes: list + roots: list +} +```` + +and its sub-structures: + +* `OperationId` +* `DagInputExternal`, `DagInputNode`, `DagInput` +* `Node` +* `RootRef` + +This encoding: + +* is **injective** with respect to the logical `Program` model — distinct Programs → distinct byte strings under this profile, +* is **stable and deterministic** across implementations and time, +* is **streaming-friendly** — encoders and decoders can operate in a single forward pass, +* fixes a **canonical topological ordering** for `nodes` (matching the scheme spec). + +The result is used as the payload (`Artifact.bytes`) of Program Artifacts for the `PEL/PROGRAM-DAG/1` scheme, with: + +```text +Artifact.type_tag = TYPE_TAG_PEL_PROGRAM_DAG_1 +Artifact.bytes = ProgramBytes +``` + +Identity of Program Artifacts is then derived via `HASH/ASL1` over `ArtifactBytes` as usual. + +--- + +## 1. Scope & Layering + +### 1.1 Purpose + +This specification defines: + +* The concrete binary layout of: + + * `ProgramBytes` + * `NodeBytes` + * `DagInputBytes` + * `RootRefBytes` + * `OperationIdBytes` + +* Canonicalization rules: + + * Node ordering (canonical topological order), + * fixed field ordering, + * integer widths and encodings. + +It does **not** define: + +* The logical semantics of Programs (DAG evaluation, error statuses, etc.) — those are in `PEL/PROGRAM-DAG/1` and `PEL/1-CORE`. +* The ASL/1 `Artifact` or `Reference` layouts — those are in `ASL/1-CORE` and `ENC/ASL1-CORE`. +* How Programs are used in store-backed execution — that belongs to `PEL/1-SURF`. + +### 1.2 Layering constraints + +In line with `SUBSTRATE/STACK-OVERVIEW`: + +* `ENC/PEL-PROGRAM-DAG/1` is a **scheme-specific encoding profile**. + +* It MUST NOT redefine: + + * `Artifact`, `TypeTag`, `Reference`, or `HashId` (from `ASL/1-CORE`), + * the logical `Program` / `Node` / `DagInput` / `RootRef` model (from `PEL/PROGRAM-DAG/1`). + +* It is **storage-neutral** and **policy-neutral**. + +* It defines exactly one canonical encoding for `Program` values for this scheme under the profile ID `PEL_ENC_PROGRAM_DAG_V1`. + +--- + +## 2. Conventions + +RFC 2119 terms (**MUST**, **SHOULD**, **MAY**, etc.) are normative. + +### 2.1 Integer encodings + +All multi-byte integers are encoded as **big-endian** (network byte order), as in `ENC/ASL1-CORE`: + +* `u8` — 1 byte +* `u16` — 2 bytes +* `u32` — 4 bytes +* `u64` — 8 bytes + +Only **fixed-width** integers are used in this specification. + +### 2.2 Utf8String + +This specification defines a canonical `Utf8String` encoding: + +```text +Utf8String = length (u32) || bytes[0..length-1] +``` + +* `length` is the number of bytes of UTF-8 data. +* `length` MAY be zero. +* Decoders MUST validate that the byte sequence is well-formed UTF-8. +* There is no padding or terminator. + +All strings in this profile (`OperationId.name`) are encoded as `Utf8String`. + +### 2.3 Parameter bytes + +For operation parameters, this profile uses a compact `ParamsBytes` encoding: + +```text +ParamsBytes = length (u32) || bytes[0..length-1] +``` + +* `bytes` is an opaque blob whose interpretation is defined per operation in the operation registry. +* `length` MAY be zero (empty params). + +> This differs from the general `OctetString` encoding (which uses `u64` length and is defined in `ENC/ASL1-CORE`). +> Using `u32` length here is acceptable because this structure lives *inside* the Program Artifact payload, not as an ASL/1 top-level value. + +#### 2.3.1 Parameter profiles and canonicality + +When a `Program` is interpreted under a concrete operation registry + parameter profile set: + +* For each operation `(name, version)`: + + * There MUST be a well-defined abstract parameter model (`ParamsValue`). + * There MUST be exactly one canonical encode/decode pair: + + ```text + encode_params_op : ParamsValue -> ParamsBytes + decode_params_op : ParamsBytes -> ParamsValue | error + ``` + +* All conformant implementations of that operation MUST: + + * decode `ParamsBytes` into the same `ParamsValue`, or + * deterministically detect decoding failures. + +Kernel parameter profiles (e.g. `OPREG/PEL1-KERNEL-PARAMS/1`) MUST ensure: + +* `encode_params_op` followed by `decode_params_op` round-trips exactly. +* Any `ParamsBytes` that fails `decode_params_op` MUST be treated as a **program-level validation error** (`INVALID_PROGRAM`) under `PEL/PROGRAM-DAG/1`, not as a runtime failure. + +### 2.4 Lists + +A list of values of some type `T` is encoded as: + +```text +List = count (u32) || element_0 || element_1 || ... || element_{count-1} +``` + +* `count` is the number of elements (MAY be zero). +* Elements are encoded in order, using the canonical encoding of `T`. + +### 2.5 Encoding version field + +`ProgramBytes` includes a `program_version (u16)` field: + +* In this profile, `program_version` MUST be `1`. +* Any future incompatible change to the layout of `ProgramBytes` under the same profile ID MUST be reflected by a new `program_version` value (and corresponding decoder support). +* Adding fields in a backward-compatible, strictly-append-only way SHOULD be done via a new encoding profile rather than overloading `program_version = 1`. + +Decoders MUST reject any `program_version` they do not implement. + +--- + +## 3. Logical Model Reference + +For convenience, the logical types from `PEL/PROGRAM-DAG/1` are restated informally (normative source remains that document): + +```text +OperationId { + name: string + version: uint32 +} + +DagInputExternal { + input_index: uint32 +} + +DagInputNode { + node_id: NodeId // uint32 + output_index: uint32 +} + +DagInput = + DagInputExternal + | DagInputNode + +Node { + id: NodeId // uint32 + op: OperationId + inputs: list + params: Params // abstract; serialized as ParamsBytes in this profile +} + +RootRef { + node_id: NodeId // uint32 + output_index: uint32 +} + +Program { + nodes: list + roots: list +} + +NodeId = uint32 +``` + +`PEL/PROGRAM-DAG/1` further defines: + +* Structural validity rules (unique NodeIds, acyclicity, etc.). +* Canonical topological order of Nodes. + +This encoding profile assumes the logical model and validity rules as given there. It **does not** re-check them at the encoding-layer; that is scheme-level responsibility. + +--- + +## 4. Program Encoding + +### 4.1 Program header and overall layout + +The canonical encoding of a `Program` value is: + +```text +ProgramBytes :: + program_version (u16) + node_count (u32) + nodes (NodeBytes[0..node_count-1]) + root_count (u32) + roots (RootRefBytes[0..root_count-1]) +``` + +Constraints: + +* `program_version` MUST currently be `1`. + + Decoders: + + * MUST accept `program_version = 1`. + * MUST reject any other value as an unsupported encoding version. + +* `node_count` and `root_count` are the number of elements in the corresponding lists. + +* `nodes` MUST be encoded in the **canonical topological order** defined in `PEL/PROGRAM-DAG/1 §4`. Encoders MUST perform this ordering; decoders MAY rely on it but are not required to re-check DAG properties. + +* `roots` MUST be encoded in the same order as the logical `Program.roots` list. + +### 4.2 Node encoding + +Each `Node` is encoded as: + +```text +NodeBytes :: + node_id (u32) + op_name (Utf8String) + op_version (u32) + input_count (u32) + inputs (DagInputBytes[0..input_count-1]) + params_len (u32) + params_bytes (byte[0..params_len-1]) +``` + +Field meanings: + +1. `node_id (u32)` + + * Encodes `Node.id`. + * MUST be unique across all Nodes in a Program (scheme-level requirement). + +2. `op_name (Utf8String)` + + * Encodes `OperationId.name` as UTF-8. + +3. `op_version (u32)` + + * Encodes `OperationId.version`. + +4. `input_count (u32)` + + * Number of input references consumed by this Node. + +5. `inputs` + + * Exactly `input_count` entries, each encoded as `DagInputBytes` (see §4.3) in order. + +6. `params_len (u32)` and `params_bytes` + + * `params_len` = length of the operation-specific parameter blob. + * `params_bytes` is an opaque blob whose interpretation is defined by the operation’s parameter profile. + * A `params_len` of `0` encodes an empty parameter blob. + +> **Injectivity requirement (Node)** +> For a fixed interpretation of `ParamsBytes` and `OperationId`, distinct logical `Node` values (differing in any field) MUST produce distinct `NodeBytes`. +> Given `NodeBytes` and the corresponding operation registry, a canonical decoder MUST reconstruct exactly the same logical `Node`. + +### 4.3 DagInput encoding + +Each `DagInput` is encoded as a tagged union: + +```text +DagInputBytes :: + kind (u8) + payload(...) +``` + +Where `kind` is: + +```text +0x00 => DagInputExternal +0x01 => DagInputNode +``` + +#### 4.3.1 DagInputExternal + +For: + +```text +DagInputExternal { + input_index: uint32 +} +``` + +Encoding: + +```text +kind = 0x00 +input_index (u32) +``` + +`input_index` is the 0-based index into the `inputs` list passed to `Exec_DAG`. + +#### 4.3.2 DagInputNode + +For: + +```text +DagInputNode { + node_id: NodeId // uint32 + output_index: uint32 +} +``` + +Encoding: + +```text +kind = 0x01 +node_id (u32) +output_index (u32) +``` + +`node_id` MUST refer to some `Node.id` in the same Program (scheme-level validity). +`output_index` is the 0-based index into that Node’s output list. + +#### 4.3.3 Decoder behavior + +Decoders MUST: + +* Treat any `kind` value other than `0x00` or `0x01` as an encoding error (invalid `DagInput`). +* For `kind = 0x00`, read exactly one `u32` as `input_index`. +* For `kind = 0x01`, read exactly two `u32` values (`node_id`, `output_index`). +* Reject truncated encodings (insufficient bytes for the payload). + +Structural validity of indices (e.g., `node_id` existence, output arity) is enforced at the scheme level, not the encoding layer. + +### 4.4 RootRef encoding + +For: + +```text +RootRef { + node_id: NodeId + output_index: uint32 +} +``` + +Encoding: + +```text +RootRefBytes :: + node_id (u32) + output_index (u32) +``` + +* `RootRefBytes` is identical to the payload of `DagInputNode`, but without a `kind` byte. + Roots are always Node outputs, so no variant tag is needed. + +* The `roots` list in `ProgramBytes` MUST encode each `RootRef` in the logical order of `Program.roots`. + +Decoders MUST reject truncated entries (insufficient bytes for both `u32` values). + +--- + +## 5. Canonicality Requirements + +### 5.1 Node ordering in Program + +Encoders MUST: + +* encode `Program.nodes` in the **canonical topological order** defined by `PEL/PROGRAM-DAG/1 §4`: + + * Dependencies appear before dependents. + * Ties are broken by smallest `NodeId` (numeric, ascending). + +* ensure that the `node_count` written in `ProgramBytes` equals the number of encoded `NodeBytes`. + +Decoders: + +* MAY assume the encoded order corresponds to the canonical topological order. +* MAY perform additional checks (e.g., verifying acyclicity), but this is not required for basic decoding. + +### 5.2 Field ordering + +Field ordering in all structures is fixed and MUST NOT vary: + +* `ProgramBytes` — `program_version`, `node_count`, `nodes…`, `root_count`, `roots…` +* `NodeBytes` — `node_id`, `op_name`, `op_version`, `input_count`, `inputs…`, `params_len`, `params_bytes…` +* `DagInputBytes` — `kind`, then variant-specific payload. +* `RootRefBytes` — `node_id`, `output_index`. + +Any deviation MUST be treated as an encoding error. + +### 5.3 Injectivity and stability + +The mapping: + +```text +Program -> ProgramBytes +``` + +defined by this profile MUST be: + +* **Injective** — if `P1 != P2` as logical `Program` values (per `PEL/PROGRAM-DAG/1`), then `ProgramBytes(P1) != ProgramBytes(P2)`. + +* **Stable** — the same logical `Program` MUST encode to the same `ProgramBytes` across: + + * different implementations, + * platforms, + * executions, + * and times, + +given the same version of this encoding profile and the same underlying operation/param profiles. + +Encoders MUST NOT: + +* reorder Nodes other than by the canonical topological order, +* reorder inputs within a Node, +* reorder roots, +* introduce alternative encodings for integers, strings, or params. + +--- + +## 6. Program Artifact Binding + +### 6.1 TypeTag + +Program Artifacts for this scheme MUST be encoded as: + +```text +Artifact { + bytes = ProgramBytes + type_tag = TYPE_TAG_PEL_PROGRAM_DAG_1 +} +``` + +Where: + +* `TYPE_TAG_PEL_PROGRAM_DAG_1` is a `TypeTag` with a concrete `tag_id` assigned in the global TypeTag registry. + +This encoding profile: + +* uses `TYPE_TAG_PEL_PROGRAM_DAG_1` symbolically, and +* does not assign a specific numeric `tag_id`; that is done in a registry document. + +### 6.2 Identity via ASL/1-CORE and HASH/ASL1 + +Given `ENC/ASL1-CORE v1` as the canonical encoding for `Artifact` and some chosen ASL1 hash algorithm `H` (e.g. `HASH-ASL1-256` under `HashId = 0x0001`): + +1. The canonical `ArtifactBytes` for a Program Artifact is given by `ENC/ASL1-CORE v1`: + + ```text + ArtifactBytes = + encode_artifact_core_v1( + Artifact{ bytes = ProgramBytes, type_tag = TYPE_TAG_PEL_PROGRAM_DAG_1 } + ) + ``` + +2. The canonical `Reference` for that Artifact under `HashId = HID` is: + + ```text + digest = H(ArtifactBytes) + reference = Reference { hash_id = HID, digest = digest } + ``` + +All conformant implementations MUST agree on: + +* `ProgramBytes` for a given logical `Program`, +* `ArtifactBytes` for the Program Artifact, +* the resulting `Reference` for any fixed `(HashId, H)`. + +--- + +## 7. Error Handling (Encoding Level) + +This encoding profile defines only **structural encoding errors**. Handling of scheme-level validity errors (`INVALID_PROGRAM`, etc.) is done by `PEL/PROGRAM-DAG/1` and `PEL/1-CORE`. + +Decoders MUST treat as encoding errors: + +1. **Truncated fields** + + * Not enough bytes to read any declared integer, string, list, or params blob. + +2. **Unsupported `program_version`** + + * `program_version != 1`. + +3. **Invalid `DagInput.kind`** + + * `kind` is not `0x00` or `0x01`. + +4. **Invalid `Utf8String`** + + * `op_name` bytes are not valid UTF-8. + +5. **Inconsistent list lengths** + + * Fewer or more `NodeBytes` than indicated by `node_count`. + * Fewer or more `RootRefBytes` than indicated by `root_count`. + +These are **encoding-layer** issues. The exact error codes surfaced to callers (e.g., `ERR_PEL_ENC_INVALID`) are implementation-specific but MUST result in rejection of the Program bytes as malformed under this encoding profile. + +--- + +## 8. Streaming and Implementation Notes + +Implementations MUST be able to: + +* Encode any `Program` using a single forward pass over the canonical node order: + + * compute canonical topological order first (requires holding `Program` structure), + * then write fields in the order defined above. + +* Decode any `ProgramBytes` sequentially: + + * no backtracking or multi-pass parsing is required, + * all length prefixes appear before their content. + +For very large Programs: + +* Implementations MAY: + + * stream Nodes one by one into an internal representation, + * stream `params_bytes` to a buffer or directly into an operation-registry decoder. + +* They MUST ensure that any observable behavior (including error reporting) is independent of chunking or I/O strategy: two conformant decoders seeing the same `ProgramBytes` MUST reconstruct the same logical `Program` or the same encoding error. + +--- + +## 9. Conformance + +An implementation is **ENC/PEL-PROGRAM-DAG/1–conformant** if it: + +1. **Implements `ProgramBytes` encoding/decoding** + + * Encodes and decodes `ProgramBytes` exactly as defined in §4. + * Treats `program_version = 1` as the only supported version. + * Treats deviations (unknown version, malformed fields) as encoding errors. + +2. **Respects canonical ordering** + + * When encoding `Program` values, orders `nodes` in the canonical topological order defined in `PEL/PROGRAM-DAG/1`. + * Preserves the logical order of `roots`. + +3. **Uses canonical field encodings** + + * Uses `u32` lengths for lists and params as specified. + * Uses `Utf8String` for operation names. + * Uses `u8` discriminants and the specified payload layout for `DagInput`. + +4. **Preserves injectivity and stability** + + * Ensures distinct logical Programs (per `PEL/PROGRAM-DAG/1`) produce distinct `ProgramBytes`. + * Ensures the same logical Program consistently produces the same `ProgramBytes` under this profile. + +5. **Binds to Program Artifacts correctly** + + * When forming Program Artifacts for `PEL/PROGRAM-DAG/1`, sets: + + * `Artifact.bytes = ProgramBytes` + * `Artifact.type_tag = TYPE_TAG_PEL_PROGRAM_DAG_1` + + * Uses `ENC/ASL1-CORE v1` and `HASH/ASL1` for ASL/1 identity. + +Everything else — storage, transport, operation registries, traces — is outside the scope of this encoding profile, provided it does not contradict the requirements above. + +--- + +## 10. Informative Example + +> This example is non-normative and uses abbreviated hex. +> It illustrates only the field layout, not exact ASCII/UTF-8 bytes. + +Consider a tiny Program: + +* Nodes: + + 1. `N0` — `id = 1` + + * `op = (name = "add64", version = 1)` + * `inputs = [ DagInputExternal{input_index = 0}, DagInputExternal{input_index = 1} ]` + * `params =` empty + + 2. `N1` — `id = 2` + + * `op = (name = "mul64", version = 1)` + * `inputs = [ DagInputNode{node_id = 1, output_index = 0}, DagInputExternal{input_index = 2} ]` + * `params =` empty + +* Roots: + + * `RootRef{ node_id = 2, output_index = 0 }` + +Canonical topological order: + +* Node 1 has only external inputs → first. +* Node 2 depends on Node 1 → second. + +ProgramBytes (pseudo-annotated): + +```text +program_version = 0001 ; u16 = 1 + +node_count = 00000002 ; 2 nodes + +; Node 0 (id = 1) +node_id = 00000001 +op_name = 00000005 "add64" ; length=5, bytes 'a','d','d','6','4' +op_version = 00000001 +input_count = 00000002 + ; input 0: external(0) + kind = 00 + input_index = 00000000 + ; input 1: external(1) + kind = 00 + input_index = 00000001 +params_len = 00000000 ; empty params + +; Node 1 (id = 2) +node_id = 00000002 +op_name = 00000005 "mul64" +op_version = 00000001 +input_count = 00000002 + ; input 0: node(1,0) + kind = 01 + node_id = 00000001 + output_index = 00000000 + ; input 1: external(2) + kind = 00 + input_index = 00000002 +params_len = 00000000 ; empty params + +root_count = 00000001 + ; root 0: (node 2, output 0) + node_id = 00000002 + output_index = 00000000 +``` + +These bytes become `Artifact.bytes` for a Program Artifact with `type_tag = TYPE_TAG_PEL_PROGRAM_DAG_1`. All conformant encoders under `PEL_ENC_PROGRAM_DAG_V1` MUST produce the same byte sequence for this logical Program. + +--- + +**End of `ENC/PEL-PROGRAM-DAG/1 v0.2.0 — Canonical Encoding for DAG Programs`** + +--- + +## Document History + +* **0.2.0 (2025-11-16):** Registered as Tier-1 spec and aligned to the Amduat 2.0 substrate baseline. diff --git a/tier1/enc-pel-trace-dag-1.md b/tier1/enc-pel-trace-dag-1.md new file mode 100644 index 0000000..91cedab --- /dev/null +++ b/tier1/enc-pel-trace-dag-1.md @@ -0,0 +1,835 @@ +# ENC/PEL-TRACE-DAG/1 — Canonical Encoding for DAG Execution Traces + +Status: Approved +Owner: Niklas Rydberg +Version: 0.1.0 +SoT: Yes +Last Updated: 2025-11-16 +Linked Phase Pack: N/A +Tags: [binary-minimalism, traceability] + + + +**Document ID:** `ENC/PEL-TRACE-DAG/1` +**Profile ID:** `PEL_ENC_TRACE_DAG_V1 = 0x0102` +**Layer:** Scheme Encoding Profile (Trace) + +**Depends on (normative):** + +* `ASL/1-CORE v0.3.2` — value model (`Artifact`, `TypeTag`, `Reference`, integers, `OctetString`) +* `ENC/ASL1-CORE v1.0.3` — canonical encodings for `Artifact` and `Reference` +* `PEL/1-CORE v0.1.0` — primitive execution layer (ExecutionStatus, ExecutionErrorSummary) +* `PEL/PROGRAM-DAG/1 v0.2.0` — DAG Program scheme (Program, Node, NodeId, canonical node order) +* `PEL/TRACE-DAG/1 v0.1.0` — DAG execution trace profile (logical data model) + +**Integrates with (informative):** + +* `PEL/1-SURF v0.1.0` — store-backed execution surface +* `HASH/ASL1 v0.2.3` — ASL1 hash family for trace artifact identity +* TypeTag registry (for `TYPE_TAG_PEL_TRACE_DAG_1`) + +> The Profile ID `PEL_ENC_TRACE_DAG_V1` is a configuration label. +> It is **not** embedded into trace payloads. Encoders and decoders select this encoding profile by context (scheme descriptor, engine/store configuration), not per value. + +© 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. Overview + +`ENC/PEL-TRACE-DAG/1` defines the **canonical binary encoding** of the `TraceDAGValue` structure specified in `PEL/TRACE-DAG/1`, and all of its nested components: + +* `TraceDAGValue` +* `NodeTraceDAG` +* `DiagnosticEntry` + +This encoding: + +* is **injective** — distinct logical trace values → distinct byte strings; +* is **stable and deterministic** — same value → same bytes across implementations and time; +* is **streaming-friendly** — encoders/decoders can operate in a single forward pass; +* embeds ASL/1 `Reference` values using their canonical `ReferenceBytes` encoding (`ENC/ASL1-CORE v1`) inside length-prefixed frames. + +The encoded payload (`TraceDAGBytes`) is used as the `bytes` field of a trace Artifact for the `PEL/TRACE-DAG/1` profile, with: + +```text +Artifact.type_tag = TYPE_TAG_PEL_TRACE_DAG_1 +Artifact.bytes = TraceDAGBytes +```` + +Trace Artifact identity is then derived by hashing canonical `ArtifactBytes` with `"ASL1"` hash algorithms (typically `HASH-ASL1-256`). + +--- + +## 1. Scope & Layering + +### 1.1 Purpose + +This specification defines: + +* The **binary layout** of: + + * `TraceDAGBytes` + * `NodeTraceDAGBytes` + * `DiagnosticEntryBytes` + * An internal “encoded Reference” wrapper for use inside trace payloads + +* The canonical **field ordering**, integer widths, and list framing. + +It does **not** define: + +* The logical semantics of traces — those are in `PEL/TRACE-DAG/1`. +* The ASL/1 `Artifact` / `Reference` encodings — these are in `ENC/ASL1-CORE v1.0.3`. +* How traces are produced or when they are enabled — that is governed by `PEL/1-CORE`, `PEL/1-SURF`, and policy profiles. + +### 1.2 Layering constraints + +In line with `SUBSTRATE/STACK-OVERVIEW`: + +* `ENC/PEL-TRACE-DAG/1` is a **scheme-specific encoding profile**. + +* It MUST NOT redefine: + + * `Artifact`, `TypeTag`, `Reference`, `HashId` (`ASL/1-CORE`), + * the `TraceDAGValue` logical structure (`PEL/TRACE-DAG/1`). + +* It is **storage-neutral** and **policy-neutral**. + +* It defines exactly one canonical encoding for `TraceDAGValue` values in this scheme. + +--- + +## 2. Conventions + +The RFC 2119 terms **MUST**, **SHOULD**, **MAY**, etc. are normative. + +### 2.1 Integer encodings + +All multi-byte integers are encoded as **big-endian** (network byte order), as in `ENC/ASL1-CORE`: + +* `u8` — 1 byte +* `u16` — 2 bytes +* `u32` — 4 bytes +* `u64` — 8 bytes + +Only **fixed-width** integers are used in this specification. + +### 2.2 Lists + +A list of values of some type `T` is encoded as: + +```text +List = + count (u32) + element_0 + element_1 + ... + element_{count-1} +``` + +* `count` is the number of elements (MAY be zero). +* Elements are encoded in order, using the canonical encoding for `T`. + +### 2.3 UTF-8 string + +`Utf8String` is encoded as: + +```text +Utf8String = + length (u32) + bytes[0..length-1] +``` + +* `length` is the number of bytes. +* `bytes` MUST be well-formed UTF-8. +* There is no terminator or padding. + +### 2.4 Octet blob (32-bit length) + +For diagnostics and other opaque fields, we use a generic blob: + +```text +Blob32 = + length (u32) + bytes[0..length-1] +``` + +`bytes` is an arbitrary `OctetString`; interpretation is profile-specific. `length` MAY be zero. + +### 2.5 Embedded Reference + +Within this encoding, `Reference` values are embedded using a length-prefixed wrapper over canonical `ReferenceBytes` from `ENC/ASL1-CORE v1.0.3`. + +We define: + +```text +EncodedRef = + ref_len (u32) + ref_bytes (byte[0..ref_len-1]) // canonical ReferenceBytes +``` + +Where: + +* `ref_bytes` MUST be the canonical `ReferenceBytes` encoding for a `Reference` value, as defined in `ENC/ASL1-CORE v1.0.3`: + + ```text + ReferenceBytes :: + hash_id (u16) + digest (byte[...]) // remaining bytes + ``` + +* `ref_len` MUST be the exact length (in bytes) of `ref_bytes` (MUST be ≥ 2). + +Decoders MUST: + +* Read `ref_len (u32)`, then `ref_bytes[0..ref_len-1]`. +* Decode `ref_bytes` as `ReferenceBytes` per `ENC/ASL1-CORE v1.0.3`. +* Reject encodings where: + + * `ref_len < 2`, or + * `ref_bytes` is not a valid `ReferenceBytes` sequence (e.g., truncated). + +#### 2.5.1 Optional EncodedRef + +For optional `Reference` fields, we use: + +```text +OptionalEncodedRef = + has_ref (u8) + [ EncodedRef ] // only if has_ref = 0x01 +``` + +* `has_ref = 0x00` → no value present; no `EncodedRef` follows. +* `has_ref = 0x01` → exactly one `EncodedRef` follows. + +Other `has_ref` values MUST be treated as encoding errors. + +--- + +## 3. Logical Model Reference + +This section restates the logical structures from `PEL/TRACE-DAG/1` (source of truth) in condensed form. + +### 3.1 DiagnosticEntry + +```text +DiagnosticEntry { + code: uint32 // diagnostic or error code + message: OctetString // typically UTF-8 text; interpretation is profile-specific +} +``` + +### 3.2 NodeTraceStatus + +```text +NodeTraceStatus = uint8 + +NodeTraceStatus { + NODE_OK = 0 + NODE_FAILED = 1 + NODE_SKIPPED = 2 +} +``` + +### 3.3 NodeTraceDAG + +```text +NodeTraceDAG { + node_id: NodeId // uint32 + op_name: string + op_version: uint32 + + status: NodeTraceStatus + status_code: uint32 // 0 = success; non-zero = op-specific failure code + + output_refs: list + diagnostics: list +} +``` + +### 3.4 TraceDAGValue + +```text +TraceDAGValue { + pel1_version: uint16 // MUST be 1 for this version + scheme_ref: Reference // SchemeRef_DAG_1 + program_ref: Reference // Program Artifact reference + + status: ExecutionStatus + summary: ExecutionErrorSummary { + kind: ExecutionErrorKind + status_code: uint32 + } + + exec_result_ref: optional Reference + + input_refs: list + params_ref: optional Reference + + node_traces: list // one per Node in canonical node order +} +``` + +All semantics (how these fields are populated for different run outcomes) are defined in `PEL/TRACE-DAG/1`. + +--- + +## 4. Encoding + +### 4.1 DiagnosticEntry encoding + +Logical: + +```text +DiagnosticEntry { + code: uint32 + message: OctetString +} +``` + +Canonical encoding: + +```text +DiagnosticEntryBytes :: + code (u32) + message (Blob32) +``` + +Where `Blob32` is as defined in §2.4. + +* `code (u32)` encodes the diagnostic or error code. +* `message` is an opaque byte blob; profile users MAY agree to use UTF-8 here, but this encoding does not enforce it. + +Decoders MUST: + +* Read `code (u32)`. +* Read `message` as `Blob32`. +* Treat truncated blobs as encoding errors. + +--- + +### 4.2 NodeTraceDAG encoding + +Logical: + +```text +NodeTraceDAG { + node_id: NodeId + op_name: string + op_version: uint32 + + status: NodeTraceStatus + status_code: uint32 + + output_refs: list + diagnostics: list +} +``` + +Canonical encoding: + +```text +NodeTraceDAGBytes :: + node_id (u32) + op_name (Utf8String) + op_version (u32) + + status (u8) // NodeTraceStatus + status_code (u32) + + output_ref_count (u32) + output_refs (EncodedRef[0..output_ref_count-1]) + + diag_count (u32) + diagnostics (DiagnosticEntryBytes[0..diag_count-1]) +``` + +Field semantics: + +1. `node_id (u32)` + + * Encodes `NodeTraceDAG.node_id`. + +2. `op_name (Utf8String)` + + * Encodes `NodeTraceDAG.op_name` as UTF-8 (see §2.3). + +3. `op_version (u32)` + + * Encodes `NodeTraceDAG.op_version`. + +4. `status (u8)` + + * Encodes `NodeTraceStatus`: + + ```text + 0x00 -> NODE_OK + 0x01 -> NODE_FAILED + 0x02 -> NODE_SKIPPED + ``` + + * Other values MUST be treated as encoding errors. + +5. `status_code (u32)` + + * MUST be: + + * `0` if `status = NODE_OK` or `NODE_SKIPPED`. + * non-zero if `status = NODE_FAILED`. + + * Conformance to these rules is a semantic requirement, not a decoding requirement. Decoders MAY choose to validate and reject inconsistent encodings. + +6. `output_ref_count (u32)` and `output_refs` + + * Number of output references for this node. + * Each entry is an `EncodedRef` (§2.5). + +7. `diag_count (u32)` and `diagnostics` + + * Number of diagnostic entries. + * Each entry is encoded using `DiagnosticEntryBytes` (§4.1). + +#### 4.2.1 Canonical ordering of node_traces + +In `TraceDAGBytes`, the `node_traces` list (see §4.4) MUST: + +* contain each `NodeTraceDAG` exactly once for each Program `Node`, +* appear in the canonical node order defined by `PEL/PROGRAM-DAG/1` (canonical topological order with `NodeId` as tie-breaker). + +Encoders MUST enforce this; decoders MAY assume it. + +--- + +### 4.3 Summary and status encoding + +From `TraceDAGValue`: + +```text +summary: ExecutionErrorSummary { + kind: ExecutionErrorKind + status_code: uint32 +} +status: ExecutionStatus +``` + +These are encoded in `TraceDAGBytes` as: + +```text +status (u8) // ExecutionStatus +summary_kind (u8) // ExecutionErrorKind +summary_status_code (u32) +``` + +The exact value sets of `ExecutionStatus` and `ExecutionErrorKind` are defined in `PEL/1-CORE` and `PEL/TRACE-DAG/1`; this spec treats them as small enums in `u8` space. + +Decoders MUST: + +* Read `status (u8)`, `summary_kind (u8)`, `summary_status_code (u32)` as raw fields. +* Treat `status` and `summary_kind` values outside the agreed ranges as encoding errors or map them to an “unknown” variant at the semantic layer. + +--- + +### 4.4 TraceDAGValue encoding + +Logical: + +```text +TraceDAGValue { + pel1_version: uint16 + scheme_ref: Reference + program_ref: Reference + + status: ExecutionStatus + summary: ExecutionErrorSummary + + exec_result_ref: optional Reference + + input_refs: list + params_ref: optional Reference + + node_traces: list +} +``` + +Canonical encoding: + +```text +TraceDAGBytes :: + pel1_version (u16) + + scheme_ref (EncodedRef) + program_ref (EncodedRef) + + status (u8) // ExecutionStatus + summary_kind (u8) // ExecutionErrorKind + summary_status_code (u32) // ExecutionErrorSummary.status_code + + has_exec_result (u8) + [ exec_result (EncodedRef) ] // if has_exec_result == 0x01 + + input_ref_count (u32) + input_refs (EncodedRef[0..input_ref_count-1]) + + has_params_ref (u8) + [ params_ref (EncodedRef) ] // if has_params_ref == 0x01 + + node_trace_count (u32) + node_traces (NodeTraceDAGBytes[0..node_trace_count-1]) +``` + +Field semantics: + +1. `pel1_version (u16)` + + * MUST be `1` for traces produced under `PEL/TRACE-DAG/1 v0.1.0`. + * Decoders: + + * MUST accept `pel1_version = 1`. + * MUST treat other values as encoding errors for this profile revision. + +2. `scheme_ref (EncodedRef)` + + * Encodes the `Reference` to the scheme descriptor; for this profile MUST be `SchemeRef_DAG_1`. + +3. `program_ref (EncodedRef)` + + * Encodes the `Reference` of the Program Artifact executed in this run. + +4. `status`, `summary_kind`, `summary_status_code (u32)` + + * As described in §4.3. + +5. `has_exec_result (u8)` and `exec_result (EncodedRef)` + + * Encodes `exec_result_ref : optional Reference`: + + * `has_exec_result = 0x00` → absent, no `exec_result` bytes follow. + * `has_exec_result = 0x01` → exactly one `EncodedRef` follows, encoding `exec_result_ref`. + * Other values MUST be treated as encoding errors. + +6. `input_ref_count (u32)` and `input_refs (EncodedRef[..])` + + * Number and encoded values of the `input_refs` list. + * Encodes `TraceDAGValue.input_refs` in order. + +7. `has_params_ref (u8)` and `params_ref (EncodedRef)` + + * Encodes `params_ref : optional Reference`: + + * `has_params_ref = 0x00` → absent. + * `has_params_ref = 0x01` → present, encoded as `EncodedRef`. + * Other values MUST be treated as encoding errors. + +8. `node_trace_count (u32)` and `node_traces (NodeTraceDAGBytes[..])` + + * Number and encoded values of `node_traces`. + * **Canonical requirement:** encoders MUST set `node_trace_count` equal to the number of `Node`s in the Program (for runs where at least one node is attempted), and MUST encode node traces in canonical node order (§4.2.1). + +--- + +## 5. Canonicality & Injectivity + +### 5.1 Injectivity + +The mapping: + +```text +TraceDAGValue -> TraceDAGBytes +``` + +defined by this profile MUST be **injective**: + +* If `T1 != T2` as logical `TraceDAGValue` instances (per `PEL/TRACE-DAG/1`), then their encodings MUST differ: + + ```text + T1 != T2 ⇒ TraceDAGBytes(T1) != TraceDAGBytes(T2) + ``` + +This is ensured by: + +* fixed field ordering and explicit presence flags, +* deterministic list ordering, +* inclusion of all logically relevant fields. + +### 5.2 Stability + +The same logical trace value MUST always yield the same `TraceDAGBytes` across: + +* implementations, +* platforms, +* time. + +Encoders MUST NOT: + +* reorder any list elements (e.g., `input_refs`, `node_traces`, `output_refs`, `diagnostics`), +* introduce alternative encodings for integers or strings, +* omit or reorder fields. + +### 5.3 Node ordering + +For runs where at least one node is attempted and the Program is structurally valid: + +* `node_traces` MUST have exactly one entry per Program `Node` (per `PEL/PROGRAM-DAG/1`) in canonical node order. + +If the Program cannot be decoded or is structurally invalid: + +* `node_traces` MAY be empty; if non-empty, any node entries MUST still follow canonical node order for the subset present. + +--- + +## 6. Trace Artifact Binding + +### 6.1 TypeTag + +Trace Artifacts for this profile MUST be ASL/1 Artifacts with: + +```text +Artifact { + bytes = TraceDAGBytes + type_tag = TYPE_TAG_PEL_TRACE_DAG_1 +} +``` + +Where: + +* `TYPE_TAG_PEL_TRACE_DAG_1` is a `TypeTag` with a concrete `tag_id` assigned in the global TypeTag registry for DAG traces. + +This encoding profile: + +* Refers to `TYPE_TAG_PEL_TRACE_DAG_1` symbolically. +* Does not assign a numeric `tag_id`; that is handled in a separate registry. + +### 6.2 Identity via ASL/1-CORE + +With `ENC/ASL1-CORE v1` and `"ASL1"` hashes (`HASH/ASL1`): + +1. Canonical `ArtifactBytes` for a trace Artifact: + + ```text + ArtifactBytes = + encode_artifact_core_v1( + Artifact{ + bytes = TraceDAGBytes, + type_tag = TYPE_TAG_PEL_TRACE_DAG_1 + } + ) + ``` + +2. Canonical `Reference` for the trace Artifact under some `HashId = HID`: + + ```text + digest = H(ArtifactBytes) // H from HASH/ASL1, for HID + reference = Reference { hash_id = HID, + digest = digest } + ``` + +All conformant implementations MUST agree on: + +* `TraceDAGBytes` for a given logical `TraceDAGValue`, +* `ArtifactBytes` for the resulting trace Artifact, +* the resulting `Reference` for any fixed `HashId` and hash algorithm. + +--- + +## 7. Error Handling (Encoding Layer) + +Decoders for this profile MUST treat as **encoding errors**: + +1. Truncated values: + + * Any attempt to read a declared integer, length-prefixed blob (`Blob32`, `Utf8String`, `EncodedRef`), or list entry that runs out of bytes. + +2. Invalid `pel1_version`: + + * `pel1_version != 1`. + +3. Invalid `NodeTraceStatus`: + + * `status` not in `{ 0x00, 0x01, 0x02 }`. + +4. Invalid optional flags: + + * `has_exec_result` or `has_params_ref` not in `{ 0x00, 0x01 }`. + +5. Invalid `EncodedRef`: + + * `ref_len < 2`, or + * `ref_bytes` cannot be decoded as `ReferenceBytes` (per `ENC/ASL1-CORE v1.0.3`). + +6. Invalid `Utf8String` in `op_name`: + + * `op_name` bytes not valid UTF-8. + +7. Inconsistent list counts: + + * Not enough elements following a list count (e.g. `input_ref_count`, `node_trace_count`, `output_ref_count`, `diag_count`). + +Mapping from these encoding errors to external error codes (e.g. `ERR_PEL_TRACE_ENC_INVALID`) is implementation-specific. + +Semantic inconsistencies (e.g. mismatched `summary` vs `status`) are semantic-layer issues; decoders MAY validate them but are not required to at the encoding layer. + +--- + +## 8. Streaming & Implementation Notes + +Implementation requirements: + +* **Single-pass encoding**: + + * Encoders MUST be able to generate `TraceDAGBytes` in a single forward pass over the logical `TraceDAGValue`, assuming they have the structure in memory. + * They MAY need to precompute counts or sizes (e.g., `node_trace_count`), but this is standard. + +* **Single-pass decoding**: + + * Decoders MUST be able to decode `TraceDAGBytes` in a single forward pass, with no backtracking. + * All length prefixes appear before their content. + +For large traces: + +* Implementations MAY: + + * stream `NodeTraceDAGBytes` entries to consumers as they decode, + * stream diagnostic message blobs. + +* They MUST ensure that any observable behavior (including error reporting and any reconstructed `TraceDAGValue`) is independent of chunking or I/O strategy. + +--- + +## 9. Conformance + +An implementation is **ENC/PEL-TRACE-DAG/1–conformant** if it: + +1. **Implements the encoding layout** + + * Encodes and decodes `TraceDAGBytes` exactly as described in §4. + * Treats `pel1_version = 1` as the only supported version for this profile revision. + * Enforces validity of discriminants and presence flags at the encoding layer. + +2. **Preserves canonical ordering** + + * When encoding, preserves: + + * order of `input_refs`, + * canonical order of `node_traces` (per `PEL/PROGRAM-DAG/1`), + * order of `output_refs` and `diagnostics` within each `NodeTraceDAG`. + +3. **Uses canonical sub-encodings** + + * Uses `Utf8String` and `Blob32` exactly as in §2. + * Uses `EncodedRef` as defined in §2.5, with `ReferenceBytes` from `ENC/ASL1-CORE v1.0.3`. + +4. **Ensures injectivity & stability** + + * Ensures distinct logical `TraceDAGValue`s produce distinct `TraceDAGBytes`. + * Ensures the same logical value always encodes to the same bytes (no configuration affecting layout). + +5. **Binds to trace Artifacts correctly** + + * When forming trace Artifacts for `PEL/TRACE-DAG/1`, sets: + + * `Artifact.bytes = TraceDAGBytes` + * `Artifact.type_tag = TYPE_TAG_PEL_TRACE_DAG_1` + + * Uses `ENC/ASL1-CORE v1` and `HASH/ASL1` for identity. + +Everything else — storage, transport, policy, and graph interpretation — is delegated to other specifications. + +--- + +## 10. Informative Example + +> This example illustrates field layout only. +> Hex and values are illustrative, not normative test vectors. + +Assume a simple run: + +* `pel1_version = 1` +* `scheme_ref = S` (an ASL/1 Reference) +* `program_ref = P` +* `status = OK (0)` +* `summary.kind = NONE (0)`, `summary.status_code = 0` +* `exec_result_ref = R` +* Inputs: three `Reference`s `[I0, I1, I2]` +* No params (`params_ref = absent`) +* Program has two nodes in canonical order, with traces: + + * Node 1: OK, produced one output `[O0]` + * Node 2: OK, produced one output `[O1]` + +Simplified encoding sketch: + +```text +pel1_version = 0001 ; u16 + +scheme_ref = EncodedRef(S) ; 4-byte length + ReferenceBytes(S) +program_ref = EncodedRef(P) + +status = 00 ; OK +summary_kind = 00 ; NONE +summary_status_code = 00000000 ; status_code = 0 + +has_exec_result = 01 +exec_result = EncodedRef(R) + +input_ref_count = 00000003 +input_refs = EncodedRef(I0) EncodedRef(I1) EncodedRef(I2) + +has_params_ref = 00 ; none + +node_trace_count = 00000002 + +; NodeTrace #0 +node_id = 00000001 +op_name = 00000005 "add64" +op_version = 00000001 +status = 00 ; NODE_OK +status_code = 00000000 +output_ref_count = 00000001 +output_refs = EncodedRef(O0) +diag_count = 00000000 + +; NodeTrace #1 +node_id = 00000002 +op_name = 00000005 "mul64" +op_version = 00000001 +status = 00 ; NODE_OK +status_code = 00000000 +output_ref_count = 00000001 +output_refs = EncodedRef(O1) +diag_count = 00000000 +``` + +Where each `EncodedRef(X)` is: + +```text +ref_len(X) (u32) || ReferenceBytes(X) +``` + +with `ReferenceBytes(X)` = `hash_id (u16)` + `digest` bytes per `ENC/ASL1-CORE v1`. + +All conformant encoders MUST produce the same `TraceDAGBytes` for this logical trace value; all conformant decoders MUST reconstruct the same `TraceDAGValue`. + +--- + +**End of `ENC/PEL-TRACE-DAG/1 v0.1.0 — Canonical Encoding for DAG Execution Traces`** + +--- + +## Document History + +* **0.1.0 (2025-11-16):** Registered as Tier-1 spec and aligned to the Amduat 2.0 substrate baseline. diff --git a/tier1/enc-pel1-result-1.md b/tier1/enc-pel1-result-1.md new file mode 100644 index 0000000..02ae489 --- /dev/null +++ b/tier1/enc-pel1-result-1.md @@ -0,0 +1,1108 @@ +# ENC/PEL1-RESULT/1 — Canonical Encoding for PEL/1 Execution Results + +Status: Approved +Owner: Niklas Rydberg +Version: 0.3.0 +SoT: Yes +Last Updated: 2025-11-30 +Linked Phase Pack: PH06 +Tags: [binary-minimalism, deterministic, execution] + + + +**Document ID:** `ENC/PEL1-RESULT/1` +**Profile ID:** `PEL_ENC_EXECUTION_RESULT_V1 = 0x0103` +**Layer:** Scheme Encoding Profile (PEL/1 Surface Result) + +**Depends on (normative):** + +* `ASL/1-CORE v0.4.x` — value model (`Artifact`, `TypeTag`, `Reference`, `OctetString`) +* `ENC/ASL1-CORE v1.0.x` — canonical encodings for `Artifact` and `Reference` +* `PEL/1-CORE v0.3.x` — `ExecutionResultValue`, `ExecutionStatus`, `ExecutionErrorSummary`, `DiagnosticEntry` +* `PEL/1-SURF v0.2.x` — surface execution result model (`SurfaceExecutionResult` shape) + +**Integrates with (informative):** + +* `HASH/ASL1 v0.2.x` — ASL1 hash family for result Artifact identity +* `SUBSTRATE/STACK-OVERVIEW v0.4.x` — layering discipline +* `ENC/PEL-PROGRAM-DAG/1 v0.2.x` — list / Utf8 / params encoding conventions +* `ENC/PEL-TRACE-DAG/1 v0.1.x` — `DiagnosticEntry` encoding pattern +* TypeTag registry (for `TYPE_TAG_PEL1_RESULT_1` assignment) + +> The Profile ID `PEL_ENC_EXECUTION_RESULT_V1` is a configuration label. +> It is **not** embedded into payloads. Encoders and decoders select this encoding profile by context (scheme descriptor, engine/store configuration), not per value. + +© 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. Overview + +`ENC/PEL1-RESULT/1` defines the **canonical binary encoding** of the surface-level +execution result structure defined by `PEL/1-SURF` for PEL/1 executions. + +Concretely, this profile specifies: + +* the binary layout of the PEL/1 surface result value + (`SurfaceExecutionResult`), including: + + * `scheme_ref`, `program_ref`, + * `input_refs`, `output_refs`, + * `params_ref`, `store_failure`, `trace_ref`, + * an **inline** encoding of `ExecutionResultValue` from `PEL/1-CORE`; + +* how that value is embedded into ASL/1 `Artifact.bytes` as `ResultBytes`; + +* the dedicated `TypeTag` used for result Artifacts under this profile. + +Result Artifacts are ordinary ASL/1 Artifacts with: + +```text +Artifact.type_tag = TYPE_TAG_PEL1_RESULT_1 +Artifact.bytes = ResultBytes +``` + +Identity of a result Artifact is then derived from canonical `ArtifactBytes` +(`ENC/ASL1-CORE v1`) and a chosen `HashId` (typically `HASH-ASL1-256`), but hash +algorithm choice is **not** part of this encoding spec. + +This profile is: + +* **injective** — distinct logical surface result values → distinct `ResultBytes`; +* **stable and deterministic** — same logical value → same bytes across implementations and time; +* **streaming-friendly** — encoders/decoders can operate in a single forward pass. + +It does **not** redefine PEL/1 execution semantics or store behavior; it only +fixes the layout of the surface result value described by `PEL/1-SURF`. + +> **Encoding vs PEL versioning** +> `pel1_version` fields in this profile encode the PEL/1-CORE major version (`1`), +> **not** the encoding profile version. Encoding version is selected by context +> via `PEL_ENC_EXECUTION_RESULT_V1` and `TYPE_TAG_PEL1_RESULT_1`, not by any +> on-wire version field. + +--- + +## 1. Scope & Layering + +### 1.1 Purpose + +This specification defines: + +* The **binary layout** of: + + * `ResultBytes` — top-level surface result encoding (`SurfaceExecutionResult`); + * `ExecutionResultValueBytes` — inline encoding of `ExecutionResultValue` (`PEL/1-CORE`); + * `DiagnosticEntryBytes` — scheme diagnostics, reusing the pattern from `ENC/PEL-TRACE-DAG/1`; + * `StoreFailureBytes` — store-resolution diagnostics, aligned with `PEL/1-SURF`; + * an internal wrapper for embedded `Reference` values (`EncodedRef`). + +* The **field ordering**, integer widths, list framing, and presence flags. + +* The **binding** between `ResultBytes` and a dedicated result `TypeTag`. + +It does **not** define: + +* PEL/1-CORE execution semantics (`Exec_s`) or scheme semantics; +* PEL/1-SURF surface semantics (how stores are wired, which Artifacts *must* be persisted); +* hash algorithms or Reference derivation (from `ASL/1-CORE` + `HASH/ASL1`); +* trace graph semantics or edges (`TGK/1-CORE` and profiles). + +### 1.2 Layering constraints + +In line with `SUBSTRATE/STACK-OVERVIEW`: + +* `ENC/PEL1-RESULT/1` is a **scheme-specific encoding profile** for a PEL/1 + surface result structure. +* It MUST NOT redefine: + + * `Artifact`, `TypeTag`, `Reference`, `HashId` (`ASL/1-CORE`); + * `ExecutionResultValue`, `ExecutionStatus`, `ExecutionErrorSummary`, `DiagnosticEntry` (`PEL/1-CORE`); + * the logical surface result structure (from `PEL/1-SURF`). +* It is **storage-neutral** and **policy-neutral**: + + * it does not talk about particular stores, transports, or policies; + * it does not depend on TGK, CIL, FER, FCT, OI, or any domain profiles. + +This profile defines exactly one canonical encoding for PEL/1 surface result values +of the shape described in §3.1. + +--- + +## 2. Conventions + +RFC 2119 terms (**MUST**, **SHOULD**, **MAY**, etc.) are normative. + +### 2.1 Integer encodings + +All multi-byte integers in this profile are encoded as **big-endian** (network +byte order), as in `ENC/ASL1-CORE` and other PEL encodings: + +* `u8` — 1 byte +* `u16` — 2 bytes +* `u32` — 4 bytes +* `u64` — 8 bytes + +Only fixed-width integers are used in this specification. + +### 2.2 Lists + +A list of values of some type `T` is encoded as: + +```text +List = + count (u32) + element_0 + element_1 + ... + element_{count-1} +``` + +* `count` is the number of elements (MAY be zero). +* Elements are encoded in order using the canonical encoding for `T`. + +This **u32 length + elements** pattern matches `ENC/PEL-PROGRAM-DAG/1` and +`ENC/PEL-TRACE-DAG/1`. + +### 2.3 Utf8String + +If this profile needs UTF-8 strings, it uses the same `Utf8String` encoding as +other PEL encodings: + +```text +Utf8String = + length (u32) + bytes[0..length-1] +``` + +* `length` is the number of bytes. +* `bytes` MUST be well-formed UTF-8. +* There is no terminator or padding. + +The core surface result structure currently does not include free-form text +fields; diagnostics use an opaque blob type instead (§2.4). + +### 2.4 Blob32 (Octet blob with 32-bit length) + +For diagnostic messages and other opaque payloads, we use: + +```text +Blob32 = + length (u32) + bytes[0..length-1] +``` + +* `bytes` is an arbitrary `OctetString`; interpretation is profile- or scheme-specific. +* `length` MAY be zero. + +This is the same `Blob32` convention used by `ENC/PEL-TRACE-DAG/1`. It is a +profile-local convenience; it does **not** override the general `OctetString` +encoding from `ENC/ASL1-CORE` (which uses `u64` lengths at the ASL core level). + +### 2.5 Embedded Reference (`EncodedRef`) + +Within this encoding, `Reference` values are embedded using a length-prefixed +wrapper over canonical `ReferenceBytes` from `ENC/ASL1-CORE v1`: + +```text +EncodedRef = + ref_len (u32) + ref_bytes (byte[0..ref_len-1]) // canonical ReferenceBytes +``` + +Where: + +* `ref_bytes` MUST be the canonical `ReferenceBytes` encoding for a `Reference`: + + ```text + ReferenceBytes :: + hash_id (u16) + digest (byte[...]) // remaining bytes in the frame + ``` + +* `ref_len` MUST be the exact length (in bytes) of `ref_bytes`, and MUST be ≥ 2. + +Decoders MUST: + +* Read `ref_len (u32)`, then `ref_bytes[0..ref_len-1]`. +* Decode `ref_bytes` as `ReferenceBytes` per `ENC/ASL1-CORE v1`. +* Reject encodings where: + + * `ref_len < 2`, or + * `ref_bytes` is not a valid `ReferenceBytes` sequence. + +This pattern is identical to the `EncodedRef` used by `ENC/PEL-TRACE-DAG/1`. + +#### 2.5.1 Optional `EncodedRef` + +Optional `Reference` fields are encoded as: + +```text +OptionalEncodedRef = + has_ref (u8) + [ EncodedRef ] // only if has_ref = 0x01 +``` + +* `has_ref = 0x00` → no value present; no `EncodedRef` follows. +* `has_ref = 0x01` → exactly one `EncodedRef` follows. +* Any other `has_ref` value MUST be treated as an encoding error. + +### 2.6 Embedded `StoreFailure` (`EncodedStoreFailure`) + +`StoreFailure` (see §3.3) is embedded using: + +```text +StoreFailureBytes :: + phase (u8) // StoreFailurePhase + error_code (u8) // StoreErrorCode + failing_ref (EncodedRef) +``` + +Optional `StoreFailure` is encoded as: + +```text +OptionalStoreFailureBytes :: + has_store_failure (u8) + [ store_failure (StoreFailureBytes) ] // if has_store_failure = 0x01 +``` + +* `has_store_failure = 0x00` → no `store_failure` present. +* `has_store_failure = 0x01` → exactly one `StoreFailureBytes` follows. +* Any other value MUST be treated as an encoding error. + +Decoders MUST: + +* Validate `phase` and `error_code` are within the sets defined in `PEL/1-SURF` + (or treat out-of-range values as encoding errors or semantic errors per policy). +* Decode `failing_ref` via `EncodedRef`. + +--- + +## 3. Logical Surface Result Model (Reference) + +This section restates, in condensed form, the logical PEL/1 surface result +structure from `PEL/1-SURF`. The normative source for semantics is `PEL/1-SURF`; +this profile only encodes the agreed structure. + +### 3.1 SurfaceExecutionResult structure + +`PEL/1-SURF` defines the logical payload of the surface ExecutionResult artifact +as: + +```text +SurfaceExecutionResult { + pel1_version : uint16 + + core_result : ExecutionResultValue + + scheme_ref : SchemeRef // echo + program_ref : ProgramRef + input_refs : InputRefList + output_refs : OutputRefList + + params_ref : optional ParamsRef + + store_failure: optional StoreFailure + trace_ref : optional TraceRef +} +``` + +Where `ExecutionResultValue` is from `PEL/1-CORE` (§3.2), and: + +```text +SchemeRef = Reference +ProgramRef = Reference +InputRef = Reference +OutputRef = Reference +ParamsRef = Reference +TraceRef = Reference + +InputRefList = list +OutputRefList = list +``` + +Semantics (summarised from `PEL/1-SURF`): + +* `pel1_version` reaffirms the PEL major version (`1` for `PEL/1-CORE`). +* `core_result` is the PEL/1-CORE execution result for this run. +* `scheme_ref`, `program_ref`, `input_refs`, `params_ref` reflect the artifacts + used to *start* the execution. +* `output_refs` reflect the artifacts produced and successfully persisted by the + surface for this run. +* `store_failure`, when present, describes a **store-resolution error** that + prevented `Exec_s` from being called, or prevented inputs/params from being + resolved, in which case `core_result.status` is `INVALID_PROGRAM` or + `INVALID_INPUTS` for store-level reasons. +* `trace_ref`, when present, points at a trace Artifact whose logical value is + defined by some trace profile (e.g. `PEL/TRACE-DAG/1`). + +Store failures, transport errors, or environment failures that prevent +construction of this artifact are **not** part of `SurfaceExecutionResult`; +those are handled at the surface/API layer, not via this encoding. + +### 3.2 ExecutionResultValue (from PEL/1-CORE) + +From `PEL/1-CORE`: + +```text +ExecutionStatus = uint8 +ExecutionErrorKind = uint8 + +ExecutionErrorSummary { + kind : ExecutionErrorKind + status_code : uint32 +} + +DiagnosticEntry { + code : uint32 + message : OctetString // scheme-defined; SHOULD be UTF-8 text +} + +ExecutionResultValue { + pel1_version : uint16 + status : ExecutionStatus + scheme_ref : SchemeRef // Reference + summary : ExecutionErrorSummary + diagnostics : list +} +``` + +Invariants (from `PEL/1-CORE`): + +* `pel1_version` MUST be 1. + +* `status` and `summary` MUST satisfy: + + * `status = OK` → `summary.kind = NONE`, `summary.status_code = 0`. + * `status = SCHEME_UNSUPPORTED` → `summary.kind = SCHEME`. + * `status = INVALID_PROGRAM` → `summary.kind = PROGRAM`. + * `status = INVALID_INPUTS` → `summary.kind = INPUTS`. + * `status = RUNTIME_FAILED` → `summary.kind = RUNTIME` and `summary.status_code != 0`. + +* `scheme_ref` MUST match the scheme under which execution was interpreted. + +* `diagnostics` MUST be deterministic for a given `(scheme_ref, program, inputs, params)`. + +This profile defines a canonical inline encoding for `ExecutionResultValue` and +`DiagnosticEntry` inside `ResultBytes`. + +### 3.3 StoreFailure (from PEL/1-SURF) + +`PEL/1-SURF` refines store-resolution errors via: + +```text +StoreErrorCode = uint8 + +StoreErrorCode { + NOT_FOUND = 1 // Store returned ERR_NOT_FOUND + INTEGRITY = 2 // Store returned ERR_INTEGRITY + UNSUPPORTED = 3 // Store returned ERR_UNSUPPORTED +} + +StoreFailurePhase = uint8 + +StoreFailurePhase { + PROGRAM = 1 + INPUT = 2 +} + +StoreFailure { + phase : StoreFailurePhase + error_code : StoreErrorCode + failing_ref : Reference +} +``` + +Semantics: + +* `phase = PROGRAM` indicates the failure happened while resolving `program_ref`. +* `phase = INPUT` indicates it happened while resolving an input or `params_ref`. +* `failing_ref` MUST be the exact `Reference` passed to `get` that produced the error. +* `error_code` MUST match the store’s reported error. + +This encoding profile provides a canonical encoding for `StoreFailure` via +`StoreFailureBytes` and `OptionalStoreFailureBytes` (§2.6, §4.3). + +--- + +## 4. Encoding + +This section defines: + +* `DiagnosticEntryBytes` — inline diagnostic entry encoding; +* `ExecutionResultValueBytes` — inline `ExecutionResultValue` encoding; +* `StoreFailureBytes` / `OptionalStoreFailureBytes` — store-resolution diagnostics; +* `ResultBytes` — top-level PEL/1 surface result encoding. + +Field ordering, integer widths, and presence flags are fixed and MUST NOT vary. + +### 4.1 DiagnosticEntry encoding + +Logical: + +```text +DiagnosticEntry { + code : uint32 + message : OctetString +} +``` + +Canonical encoding (matching `ENC/PEL-TRACE-DAG/1`): + +```text +DiagnosticEntryBytes :: + code (u32) + message (Blob32) +``` + +Where `Blob32` is defined in §2.4. + +* `code (u32)` encodes the diagnostic or error code. +* `message` is an opaque byte blob. Schemes MAY agree to use UTF-8 text here, + but this encoding does not enforce it. + +Decoders MUST: + +* Read `code (u32)`. +* Read `message` as `Blob32`. +* Treat truncated blobs as encoding errors. + +### 4.2 ExecutionResultValue encoding + +Logical: + +```text +ExecutionResultValue { + pel1_version : uint16 + status : ExecutionStatus + scheme_ref : SchemeRef + summary : ExecutionErrorSummary + diagnostics : list +} +``` + +Canonical encoding: + +```text +ExecutionResultValueBytes :: + pel1_version (u16) + + status (u8) // ExecutionStatus + scheme_ref (EncodedRef) + + summary_kind (u8) // ExecutionErrorKind + summary_status_code (u32) // ExecutionErrorSummary.status_code + + diag_count (u32) + diagnostics (DiagnosticEntryBytes[0..diag_count-1]) +``` + +Field semantics: + +1. `pel1_version (u16)` + + * MUST be `1` for `ExecutionResultValue` values defined by `PEL/1-CORE v0.3.x`. + * Decoders: + + * MUST accept `pel1_version = 1`. + * MUST treat other values as encoding errors for this profile revision. + +2. `status (u8)` + + * Encodes `ExecutionStatus` as defined in `PEL/1-CORE`. + * Decoders SHOULD treat out-of-range `status` values as encoding errors + (rather than attempting to map them to an “unknown” variant). + +3. `scheme_ref (EncodedRef)` + + * Encodes the `SchemeRef` (`Reference`) under which the scheme was interpreted. + * MUST match the `scheme_ref` at the surface level (§4.4.2, §6.2). + +4. `summary_kind (u8)` and `summary_status_code (u32)` + + * Encode `ExecutionErrorSummary.kind` and `ExecutionErrorSummary.status_code` + respectively. + * Encoders MUST set these to satisfy the invariants from `PEL/1-CORE §2.4` + for the chosen `status`. + * Decoders MAY validate consistency; inconsistency is a semantic error, not + strictly an encoding error, but implementations MAY choose to reject such values. + +5. `diag_count (u32)` and `diagnostics (DiagnosticEntryBytes[..])` + + * Encodes the list of diagnostics as a `List`. + * `diag_count` is the number of entries; MAY be zero. + * Decoders MUST reject truncated sequences where fewer than `diag_count` + entries are present. + +This layout is also suitable for use by other profiles that choose to store +`ExecutionResultValue` as a dedicated Artifact; see §5 (informative note). + +### 4.3 StoreFailure encoding + +Logical: + +```text +StoreFailure { + phase : StoreFailurePhase + error_code : StoreErrorCode + failing_ref : Reference +} +``` + +Canonical encoding: + +```text +StoreFailureBytes :: + phase (u8) // StoreFailurePhase + error_code (u8) // StoreErrorCode + failing_ref (EncodedRef) +``` + +Optional presence: + +```text +OptionalStoreFailureBytes :: + has_store_failure (u8) + [ store_failure (StoreFailureBytes) ] // if has_store_failure = 0x01 +``` + +Field semantics: + +1. `phase (u8)` + + * Encodes `StoreFailurePhase`: + + * `1` → PROGRAM + * `2` → INPUT + + * Decoders SHOULD treat other values as encoding errors or semantic errors + per implementation policy. + +2. `error_code (u8)` + + * Encodes `StoreErrorCode`: + + * `1` → NOT_FOUND + * `2` → INTEGRITY + * `3` → UNSUPPORTED + + * Decoders SHOULD treat other values as encoding errors or semantic errors + per implementation policy. + +3. `failing_ref (EncodedRef)` + + * Encodes the `Reference` that failed to resolve. + +4. `has_store_failure (u8)` + + * `0x00` → no `store_failure` present. + * `0x01` → exactly one `StoreFailureBytes` follows. + * Any other value MUST be treated as an encoding error. + +Decoders MUST reject truncated values (e.g., missing `failing_ref` bytes). + +### 4.4 Surface Result encoding (`ResultBytes`) + +Logical (from §3.1): + +```text +SurfaceExecutionResult { + pel1_version : uint16 + + core_result : ExecutionResultValue + + scheme_ref : SchemeRef + program_ref : ProgramRef + input_refs : InputRefList + output_refs : OutputRefList + + params_ref : optional ParamsRef + + store_failure: optional StoreFailure + trace_ref : optional TraceRef +} +``` + +Canonical encoding: + +```text +ResultBytes :: + pel1_version (u16) + + scheme_ref (EncodedRef) + program_ref (EncodedRef) + + input_ref_count (u32) + input_refs (EncodedRef[0..input_ref_count-1]) + + output_ref_count (u32) + output_refs (EncodedRef[0..output_ref_count-1]) + + has_params_ref (u8) + [ params_ref (EncodedRef) ] // if has_params_ref == 0x01 + + has_store_failure (u8) + [ store_failure (StoreFailureBytes) ] // if has_store_failure == 0x01 + + has_trace_ref (u8) + [ trace_ref (EncodedRef) ] // if has_trace_ref == 0x01 + + core_result (ExecutionResultValueBytes) +``` + +Field semantics: + +1. `pel1_version (u16)` + + * MUST be `1` for surface results representing `PEL/1-CORE v0.3.x` executions. + * Decoders MUST treat any other value as an encoding error for this profile revision. + +2. `scheme_ref (EncodedRef)` + + * Encodes the scheme descriptor `SchemeRef` (`Reference`) for the run. + * MUST match `ExecutionResultValue.scheme_ref` encoded inside + `core_result.scheme_ref`; see §6.2. + +3. `program_ref (EncodedRef)` + + * Encodes the program Artifact reference used for this run. + +4. `input_ref_count (u32)` and `input_refs (EncodedRef[..])` + + * Encodes the ordered list of input references provided to the surface. + * `input_ref_count` MAY be zero. + * Encoders MUST preserve the logical order of `inputs`. + * Decoders MUST reject truncated sequences with fewer than `input_ref_count` + entries. + +5. `output_ref_count (u32)` and `output_refs (EncodedRef[..])` + + * Encodes the ordered list of output references produced and successfully + persisted by the surface. + * `output_ref_count` MAY be zero even when `core_result.status = OK` for + schemes that legitimately produce no outputs. + * Encoders MUST preserve the logical output order defined by the scheme/surface. + * Decoders MUST reject truncated sequences. + +6. `has_params_ref (u8)` and `params_ref (EncodedRef)` + + * Encodes an optional parameters Artifact reference. + * `has_params_ref = 0x00` → no `params_ref` present. + * `has_params_ref = 0x01` → exactly one `EncodedRef` follows and encodes + `params_ref`. + * Any other `has_params_ref` value MUST be treated as an encoding error. + * Surfaces that do not support parameter Artifacts MUST always emit + `has_params_ref = 0x00`. + +7. `has_store_failure (u8)` and `store_failure (StoreFailureBytes)` + + * Encodes an optional `StoreFailure` as defined in §3.3 and §4.3. + * `has_store_failure = 0x00` → no `store_failure` present. + * `has_store_failure = 0x01` → exactly one `StoreFailureBytes` follows. + * Any other `has_store_failure` value MUST be treated as an encoding error. + * When `store_failure` is present: + + * `core_result.status` MUST be `INVALID_PROGRAM` or `INVALID_INPUTS` + according to the phase (PROGRAM or INPUT) as defined in `PEL/1-SURF`. + * `core_result.summary.kind` MUST be `PROGRAM` or `INPUTS` accordingly. + +8. `has_trace_ref (u8)` and `trace_ref (EncodedRef)` + + * Encodes an optional reference to a trace Artifact (e.g. a `PEL/TRACE-DAG/1` + value encoded under `ENC/PEL-TRACE-DAG/1`). + * `has_trace_ref` uses the same conventions as `has_params_ref`. + * Surfaces that never produce traces MUST always emit `has_trace_ref = 0x00`. + +9. `core_result (ExecutionResultValueBytes)` + + * Encodes the inline `ExecutionResultValue` for this run as defined in §4.2. + * The `pel1_version`, `status`, `scheme_ref`, `summary`, and `diagnostics` + fields MUST respect `PEL/1-CORE` invariants. + * `core_result.scheme_ref` MUST equal the surface-level `scheme_ref`. + +--- + +## 5. Result Artifact Binding + +### 5.1 TypeTag + +Result Artifacts for this profile MUST be ASL/1 Artifacts with: + +```text +Artifact { + bytes = ResultBytes + type_tag = TYPE_TAG_PEL1_RESULT_1 +} +``` + +Where: + +* `TYPE_TAG_PEL1_RESULT_1` is a `TypeTag` with a concrete `tag_id` assigned in + the global TypeTag registry for PEL/1 surface results. + +This encoding profile: + +* refers to `TYPE_TAG_PEL1_RESULT_1` symbolically; and +* does not assign a numeric `tag_id` (that belongs in the registry). + +### 5.2 Identity via ASL/1-CORE + +With `ENC/ASL1-CORE v1` and the "ASL1" hash family (`HASH/ASL1`): + +1. Canonical `ArtifactBytes` for a result Artifact: + + ```text + ArtifactBytes = + encode_artifact_core_v1( + Artifact{ + bytes = ResultBytes, + type_tag = TYPE_TAG_PEL1_RESULT_1 + } + ) + ``` + +2. Canonical `Reference` for any chosen `HashId = HID`: + + ```text + digest = H(ArtifactBytes) // H from HASH/ASL1 for HID + reference = Reference { hash_id = HID, + digest = digest } + ``` + +All conformant implementations using the same `(EncodingProfileId, HashId)` pair +MUST agree on: + +* `ResultBytes` for a given logical surface result value, +* the resulting `ArtifactBytes` for the result Artifact, +* the resulting `Reference`. + +> **Informative note (ExecutionResultValue as Artifact)** +> Other profiles MAY choose to store `ExecutionResultValue` as a dedicated +> Artifact using `ExecutionResultValueBytes` as `Artifact.bytes` and a dedicated +> TypeTag (e.g. `TYPE_TAG_PEL_EXECUTION_RESULT_VALUE_1`). Such a binding is +> intentionally left out of this version; if introduced, it SHOULD reuse +> `ExecutionResultValueBytes` exactly to preserve injectivity across inline and +> stored forms. + +--- + +## 6. Canonicality & Determinism + +### 6.1 Injectivity + +The mapping: + +```text +SurfaceExecutionResult -> ResultBytes +``` + +defined by this profile MUST be **injective**: + +* If two logical `SurfaceExecutionResult` values differ (per `PEL/1-SURF` and + `PEL/1-CORE` semantics), then: + + ```text + ResultBytes(V1) != ResultBytes(V2) + ``` + +Injectivity is ensured by: + +* fixed field ordering; +* explicit presence flags for all optional references and store failures; +* deterministic list ordering (`input_refs`, `output_refs`, + `diagnostics`); +* inclusion of all logically relevant fields. + +Similarly, the mapping: + +```text +ExecutionResultValue -> ExecutionResultValueBytes +``` + +MUST be injective. + +### 6.2 Stability & consistency + +For any fixed logical value and encoding profile version: + +* All conformant encoders MUST produce the same `ExecutionResultValueBytes` and + `ResultBytes`. +* Encodings MUST be stable across implementations, platforms, and time. + +Encoders MUST NOT: + +* reorder list elements (`input_refs`, `output_refs`, `diagnostics`); +* omit or reorder fields; +* vary integer widths or encodings; +* introduce alternative layouts for version negotiation. + +In addition, encoders MUST enforce: + +* `pel1_version` fields for both the surface result and the inline + `ExecutionResultValue` are `1`. +* `scheme_ref` in the surface header and `scheme_ref` inside + `ExecutionResultValue` are identical. +* When `store_failure` is present, `core_result.status` and + `core_result.summary.kind` reflect `INVALID_PROGRAM`/`PROGRAM` or + `INVALID_INPUTS`/`INPUTS` as mandated by `PEL/1-SURF`. + +### 6.3 Extension and evolution (informative) + +`ENC/PEL1-RESULT/1` is intended to evolve **additively**: + +* New optional fields SHOULD be introduced via new encoding profiles or clearly + versioned layouts, rather than overloading this layout in incompatible ways. +* Any future revision that extends `ResultBytes` or `ExecutionResultValueBytes` + MUST preserve all existing fields, their order, and their semantics for + `pel1_version = 1`. +* Changes that would alter the meaning of existing bytes (for the same logical + value) MUST be expressed as a new profile or a new result TypeTag, not as a + silent change to this profile. + +--- + +## 7. Error Handling (Encoding Layer) + +Decoders for this profile MUST treat as **encoding errors**: + +1. **Truncated values** + + * Insufficient bytes to read any declared integer, length prefix, `EncodedRef`, + `DiagnosticEntryBytes`, `StoreFailureBytes`, or `ExecutionResultValueBytes`. + +2. **Invalid `pel1_version`** + + * `pel1_version != 1` in either: + + * the surface-level header (§4.4), or + * `ExecutionResultValueBytes` (§4.2). + +3. **Invalid presence flags** + + * `has_params_ref`, `has_store_failure`, or `has_trace_ref` not in `{0x00, 0x01}`. + +4. **Invalid `EncodedRef`** + + * `ref_len < 2`, or + * `ref_bytes` cannot be decoded as canonical `ReferenceBytes` under + `ENC/ASL1-CORE v1`. + +5. **Invalid list counts** + + * Fewer entries than indicated by `input_ref_count`, `output_ref_count`, or + `diag_count`. + +6. **Invalid `Utf8String` (if used)** + + * Any `Utf8String` field present in a future extension that is not valid UTF-8. + +Decoders SHOULD also treat as encoding errors: + +* `status` values outside the range defined by `PEL/1-CORE` for `ExecutionStatus`; +* `summary_kind` values outside the range defined for `ExecutionErrorKind`; +* `phase` or `error_code` values outside the ranges defined for `StoreFailurePhase` + and `StoreErrorCode`, if the implementation chooses to enforce these at the + encoding layer. + +Decoders MAY treat as either encoding errors or semantic errors (per +implementation policy): + +* mismatches between `status` and `summary` invariants (e.g. `status = OK` but + `summary.kind != NONE` or `summary.status_code != 0`); +* inconsistencies between `store_failure` presence and `core_result.status` / + `core_result.summary.kind`. + +Mapping from encoding errors to external error codes (e.g. +`ERR_PEL_RESULT_ENC_INVALID`) is implementation-specific. These are strictly +encoding-layer issues; store failures, scheme errors, and runtime failures are +represented inside `ExecutionResultValue` and `StoreFailure` and handled by +PEL/1-CORE and PEL/1-SURF. + +--- + +## 8. Streaming & Implementation Notes + +Encoders and decoders MUST support **single-pass** operation: + +* **Encoding**: + + * Encoders MUST be able to generate `ResultBytes` in a single forward pass over + the logical `SurfaceExecutionResult`, assuming the structure is known in + memory (or the list counts are precomputed). + * They MAY need to compute `input_ref_count`, `output_ref_count`, and + `diag_count` before emitting the corresponding lists. + +* **Decoding**: + + * Decoders MUST be able to decode `ResultBytes` in a single forward pass, with + no backtracking. + * All length prefixes appear before their content; no speculative reads are + required. + +For large results: + +* Implementations MAY stream lists (e.g. `input_refs`, `output_refs`, + `diagnostics`) to consumers as they decode them. +* Implementations MUST ensure that any observable behavior (including error + reporting and any reconstructed value) is independent of I/O chunking or + buffer boundaries. + +Implementations may also: + +* compute hashes incrementally over `ArtifactBytes` (for result Artifacts), +* pipeline decoding of `ResultBytes` directly into downstream consumers (e.g. + inspection, provenance edge generators) without materializing the entire + object in memory, as long as the logical value is representable. + +--- + +## 9. Conformance + +An implementation is **ENC/PEL1-RESULT/1–conformant** if it: + +1. **Implements the layouts exactly** + + * Encodes and decodes `ExecutionResultValueBytes`, `StoreFailureBytes`, and + `ResultBytes` exactly as defined in §4.2, §4.3, and §4.4. + * Uses the correct field ordering, integer widths, list framing, and presence + flags. + +2. **Uses canonical sub-encodings** + + * Embeds `Reference` values using `EncodedRef` over canonical `ReferenceBytes` + (`ENC/ASL1-CORE v1`). + * Encodes `DiagnosticEntry` using `DiagnosticEntryBytes` with `Blob32` as in + §4.1. + * Encodes `StoreFailure` using `StoreFailureBytes` as in §4.3. + +3. **Preserves injectivity and stability** + + * Ensures distinct logical `SurfaceExecutionResult` values produce distinct + `ResultBytes`. + * Ensures distinct logical `ExecutionResultValue` values produce distinct + `ExecutionResultValueBytes`. + * Ensures the same logical values always encode to the same bytes (no + configuration or environment affecting layout). + +4. **Binds to result Artifacts correctly** + + * When forming surface result Artifacts, sets: + + ```text + Artifact.bytes = ResultBytes + Artifact.type_tag = TYPE_TAG_PEL1_RESULT_1 + ``` + + * Uses `ENC/ASL1-CORE v1` and `HASH/ASL1` for identity derivation. + +5. **Respects layering** + + * Does not depend on TGK, CIL, FER, FCT, or OI to encode or decode result + values. + * Does not reinterpret or override `ExecutionResultValue` or `StoreFailure` + semantics beyond what is specified in `PEL/1-CORE` and `PEL/1-SURF`. + +Everything else — storage configuration, trace persistence, provenance edges, +receipts, facts, overlays — is outside the scope of this encoding profile, +provided it does not contradict the rules above. + +--- + +## 10. Informative Example + +> This example illustrates field layout only. +> Values and hex are illustrative, not normative test vectors. + +Consider a simple run with: + +* `pel1_version = 1` +* `scheme_ref = S` +* `program_ref = P` +* Inputs: `input_refs = [I0, I1]` +* Outputs: `output_refs = [O0]` +* `params_ref = absent` +* `store_failure = absent` +* `trace_ref = T` +* Inline `core_result : ExecutionResultValue`: + + ```text + pel1_version = 1 + status = OK + scheme_ref = S + summary.kind = NONE + summary.status_code = 0 + diagnostics = [] // empty + ``` + +Then `ResultBytes` are: + +```text +pel1_version = 0001 ; u16 + +scheme_ref = EncodedRef(S) +program_ref = EncodedRef(P) + +input_ref_count = 00000002 ; 2 +input_refs = EncodedRef(I0) + EncodedRef(I1) + +output_ref_count = 00000001 ; 1 +output_refs = EncodedRef(O0) + +has_params_ref = 00 ; none + +has_store_failure = 00 ; none + +has_trace_ref = 01 +trace_ref = EncodedRef(T) + +; Inline core_result (ExecutionResultValueBytes) +pel1_version = 0001 +status = 00 ; OK (ExecutionStatus) +scheme_ref = EncodedRef(S) +summary_kind = 00 ; NONE (ExecutionErrorKind) +summary_status_code = 00000000 ; 0 + +diag_count = 00000000 ; no diagnostics +; (no DiagnosticEntryBytes follow) +``` + +Where each `EncodedRef(X)` is: + +```text +ref_len(X) (u32) || ReferenceBytes(X) +``` + +with `ReferenceBytes(X)` = `hash_id (u16)` + `digest` bytes, per +`ENC/ASL1-CORE v1`. + +All conformant encoders MUST produce the same `ResultBytes` for this logical +surface result value; all conformant decoders MUST reconstruct the same logical +value from those bytes. + +--- + +## Document History + +* **0.3.0 (2025-11-17):** Promoted the ExecutionResult encoding profile to Approved status and pinned PH06 vectors/tests to this canonical layout. +* **0.2.3 (2025-11-17):** Aligned logical shape and encoding with `PEL/1-SURF v0.2.1` (added `StoreFailure` encoding, removed `exec_result_ref`, renamed inline `result_value` to `core_result`). +* **0.2.2 (2025-11-16):** Tightened consistency rules between surface header and inline `ExecutionResultValue`, clarified PEL vs encoding versioning, and strengthened decoding recommendations for out-of-range status values. +* **0.2.1 (2025-11-16):** Aligned `ExecutionResultValueBytes` field order with `PEL/1-CORE`, clarified flag semantics for unsupported optional fields, and added an evolution note for future extensions. +* **0.2.0 (2025-11-16):** Integrated earlier CODEx draft ideas; defined explicit `ExecutionResultValueBytes`, aligned diagnostics encoding with `ENC/PEL-TRACE-DAG/1`, and fixed full `ResultBytes` layout with `EncodedRef`/`u32` list framing. +* **0.1.0 (2025-11-16):** Initial skeleton for `ENC/PEL1-RESULT/1` (high-level scope, dependencies, and rough layout outline). diff --git a/tier1/opreg-pel1-kernel-params-1.md b/tier1/opreg-pel1-kernel-params-1.md new file mode 100644 index 0000000..36e37b5 --- /dev/null +++ b/tier1/opreg-pel1-kernel-params-1.md @@ -0,0 +1,674 @@ +# OPREG/PEL1-KERNEL-PARAMS/1 — Kernel Operation Parameter Encodings + +Status: Approved +Owner: Niklas Rydberg +Version: 0.1.1 +SoT: Yes +Last Updated: 2025-11-16 +Linked Phase Pack: N/A +Tags: [registry, binary-minimalism] + + + +**Document ID:** `OPREG/PEL1-KERNEL-PARAMS/1` +**Layer:** L1 Profile (Parameter Encoding for `OPREG/PEL1-KERNEL`) + +**Depends on (normative):** + +* `ASL/1-CORE v0.3.x` — `Artifact`, `TypeTag`, `Reference`, `HashId` +* `HASH/ASL1 v0.2.x` — ASL1 hash family (for `HashId` constraints) +* `OPREG/PEL1-KERNEL v0.1.1` — kernel operation semantics + +**Integrates with (informative):** + +* `PEL/PROGRAM-DAG/1` — DAG program scheme (Node parameters) +* `PEL/1-CORE` — primitive execution semantics + +© 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. Purpose and Scope + +`OPREG/PEL1-KERNEL-PARAMS/1` defines the **canonical binary encodings** for the logical Params types used by the kernel operations in `OPREG/PEL1-KERNEL` v0.1.1: + +* `pel.bytes.concat/1` +* `pel.bytes.slice/1` +* `pel.bytes.const/1` +* `pel.bytes.hash.asl1/1` + +It specifies, for each operation: + +1. The **logical Params value type** (e.g. `SliceParams`). + +2. The **canonical encoding** function: + + ```text + encode_params(OpParams) -> OctetString + ``` + +3. The **canonical decoding** function: + + ```text + decode_params(OctetString) -> OpParams | error + ``` + +4. How **decode errors** must be treated in the context of `PEL/PROGRAM-DAG/1` + (always as `INVALID_PROGRAM` for these kernel ops). + +This document is deliberately independent of any **Program encoding** format. +A Program representation (e.g. `ENC/PEL-PROGRAM-DAG/1`) simply needs to: + +* store the operation’s Params as an **opaque OctetString**, and +* feed that OctetString into the encoders/decoders defined here. + +For each Params type, the encoding defined here is the only canonical encoding; engines MUST NOT accept alternative layouts. + +--- + +## 1. Conventions + +### 1.1 Base types + +From `ASL/1-CORE`: + +```text +OctetString = finite sequence of bytes (0x00–0xFF) + +HashId = uint16 +``` + +In this document, OctetString is the logical type ‘sequence of bytes’. When we encode an OctetString as part of Params (e.g. ConstParams.bytes), we use [u64 length][bytes]. + +This document introduces the following fixed-width integer types: + +```text +u8 = unsigned 8-bit integer +u16 = unsigned 16-bit integer +u32 = unsigned 32-bit integer +u64 = unsigned 64-bit integer +``` + +### 1.2 Byte order and integer encoding + +All multi-byte integers in parameter encodings are encoded: + +* as **big-endian** (most significant byte first), +* using a **fixed width** (1, 2, 4, or 8 bytes). + +Conversions: + +* `u16` → 2 bytes +* `u32` → 4 bytes +* `u64` → 8 bytes + +### 1.3 Boolean encoding + +Booleans are encoded as: + +```text +false -> 0x00 +true -> 0x01 +``` + +Any other value is invalid and MUST be treated as a decode error. + +### 1.4 OctetString encoding (embedded) + +When an OctetString is encoded as part of Params, it uses: + +```text +[length: u64] [bytes: b[length]] +``` + +* `length` is the number of bytes. +* `length` MAY be zero. +* There is no terminator or padding. + +### 1.5 Param payload + +For each Node in a Program, the scheme-level “Params” field is represented as an **OctetString `params_bytes`**. + +This document defines, for kernel operations, what it means for `params_bytes` to represent: + +* `Unit` (no parameters), +* `SliceParams`, +* `ConstParams`, +* `HashParams`. + +Other operations (non-kernel) may use other param-encoding profiles. + +### 1.6 Error handling model + +For all kernel operations: + +> **Any param decode error MUST be treated as a static program error (`INVALID_PROGRAM`) in `PEL/PROGRAM-DAG/1`, not as a runtime error.** + +Concretely: + +* If `decode_params` fails for a Node during Program validation, + `Exec_DAG` MUST treat the Program as invalid and produce `status = INVALID_PROGRAM`, not `RUNTIME_FAILED`. + +### 1.7 Relationship to `ENC/PEL-PROGRAM-DAG/1` and size bounds + +In contexts that use `ENC/PEL-PROGRAM-DAG/1`, each `Node` carries an operation-specific parameter payload in its `params_bytes` field. For kernel operations defined in `OPREG/PEL1-KERNEL`: + +* The `params_bytes` field of a `Node` **MUST** be exactly the byte sequence returned by the corresponding `encode_params_*` function for that Node’s operation. +* Engines validating or executing a `PEL/PROGRAM-DAG/1` `Program` **MUST** feed `params_bytes` into the matching `decode_params_*` function for that operation. + +Because `ENC/PEL-PROGRAM-DAG/1` represents `params_bytes` with a `params_len : u32` length prefix, all `encode_params_*` functions defined in this document **MUST** produce parameter payloads whose length is ≤ `0xFFFF_FFFF` (2³² − 1) bytes. Any logical parameter value that would require a larger encoding **MUST** either: + +* be rejected at parameter construction time, or +* be represented via separate input `Artifact`s instead of being inlined as Node parameters. + +For kernel operations, any failure to decode `params_bytes` with the corresponding `decode_params_*` function (including violations of the size bound above) **MUST** be reported to the `PEL/PROGRAM-DAG/1` layer as a parameter-decoding error and treated as an **invalid Program**, not as a runtime failure during execution. + +--- + +## 2. Operation Param Types (Logical) + +For reference (from `OPREG/PEL1-KERNEL`): + +```text +// pel.bytes.concat/1 +ConcatParams = Unit + +// pel.bytes.slice/1 +SliceParams { + offset: u64 + length: u64 +} + +// pel.bytes.const/1 +ConstParams { + bytes: OctetString + has_tag: bool + tag_id: u32 optional // only meaningful when has_tag = true +} + +// pel.bytes.hash.asl1/1 +HashParams { + hash_id: HashId // uint16 +} +``` + +This specification does **not** change these logical types; it only provides their canonical encodings. + +--- + +## 3. Unit Params Encoding (`pel.bytes.concat`) + +Applies to: + +* `pel.bytes.concat/1` + +### 3.1 Logical type + +```text +Unit = () +``` + +There is a single value. + +### 3.2 Encoding + +```text +encode_params_unit(Unit) -> OctetString +``` + +Definition: + +```text +encode_params_unit(()) = "" // zero-length OctetString +``` + +That is, `params_bytes` MUST be an empty byte sequence. + +### 3.3 Decoding + +```text +decode_params_unit(params_bytes: OctetString) -> Unit | error +``` + +Algorithm: + +1. If `len(params_bytes) != 0`: + + * return `error`. + +2. Otherwise: + + * return `()`. + +### 3.4 Conformance rule + +For `pel.bytes.concat/1`: + +* `params_bytes` MUST be encoded using `encode_params_unit`. +* Any non-empty `params_bytes` MUST be treated as a **decode error** and thus `INVALID_PROGRAM` in `PEL/PROGRAM-DAG/1`. + +--- + +## 4. SliceParams Encoding (`pel.bytes.slice`) + +Applies to: + +* `pel.bytes.slice/1` + +### 4.1 Logical type + +```text +SliceParams { + offset: u64 // 0-based byte offset + length: u64 // number of bytes +} +``` + +### 4.2 Encoding + +```text +encode_params_slice(SliceParams) -> OctetString +``` + +Given: + +```text +SliceParams { offset, length } +``` + +The encoding is: + +```text +[offset: u64] [length: u64] +``` + +where both `offset` and `length` are encoded as big-endian `u64`. + +So: + +* Total length of `params_bytes` MUST be exactly 16 bytes. + +### 4.3 Decoding + +```text +decode_params_slice(params_bytes: OctetString) -> SliceParams | error +``` + +Algorithm: + +1. If `len(params_bytes) != 16`: + + * return `error`. + +2. Parse the first 8 bytes as `offset: u64` (big-endian). + +3. Parse the next 8 bytes as `length: u64` (big-endian). + +4. Construct: + + ```text + SliceParams { offset, length } + ``` + + and return it. + +This decoding does **not** apply any semantic checks on `offset` or `length` (such as “must be in range for a particular input”). Those checks are done at operation runtime and mapped to `RANGE_OUT_OF_BOUNDS` (`status_code = 0x0002_0001`) as specified in `OPREG/PEL1-KERNEL`. + +### 4.4 Conformance rule + +* `params_bytes` for a `pel.bytes.slice/1` Node MUST be exactly 16 bytes, encoded as described. +* Any other length or malformed encoding MUST be treated as a **decode error** ⇒ `INVALID_PROGRAM`. + +--- + +## 5. ConstParams Encoding (`pel.bytes.const`) + +Applies to: + +* `pel.bytes.const/1` + +### 5.1 Logical type + +```text +ConstParams { + bytes: OctetString + has_tag: bool + tag_id: u32 optional +} +``` + +Semantics: + +* If `has_tag == false`: + + * The output Artifact will have `type_tag = None`. + +* If `has_tag == true`: + + * The output Artifact will have `type_tag = Some(TypeTag{ tag_id })`. + +### 5.2 Encoding + +```text +encode_params_const(ConstParams) -> OctetString +``` + +Given: + +```text +ConstParams { bytes = B, has_tag, tag_id? } +``` + +The encoding is: + +```text +[has_tag: u8] [tag_id: u32?] [len: u64] [B: b[len]] +``` + +Where: + +1. `has_tag: u8` + + * `0x00` if `has_tag == false`. + * `0x01` if `has_tag == true`. + +2. `tag_id: u32` (only when `has_tag == true`) + + * Encoded as big-endian `u32`. + * Omitted entirely when `has_tag == false`. + +3. `len: u64` + + * Length in bytes of `B`. + * Encoded as big-endian `u64`. + +4. `B: b[len]` + + * The raw bytes of the payload. + +So: + +* If `has_tag == false`, layout is: + + ```text + [0x00] [len: u64] [B: b[len]] + ``` + +* If `has_tag == true`, layout is: + + ```text + [0x01] [tag_id: u32] [len: u64] [B: b[len]] + ``` + +### 5.3 Decoding + +```text +decode_params_const(params_bytes: OctetString) -> ConstParams | error +``` + +Algorithm: + +1. If `len(params_bytes) < 1`: + + * return `error`. + +2. Read the first byte as `flag: u8`. + + * If `flag == 0x00`, then `has_tag = false`. + * If `flag == 0x01`, then `has_tag = true`. + * Otherwise (any other value): return `error`. + +3. If `has_tag == false`: + + 1. Ensure at least `1 + 8` bytes are present: + + * If `len(params_bytes) < 1 + 8`: return `error`. + + 2. Parse the next 8 bytes as `len: u64` (big-endian). + + 3. Compute `expected_total = 1 + 8 + len`. + + * If `len(params_bytes) != expected_total`: return `error`. + + 4. Let `B` be the remaining `len` bytes. + + 5. Return: + + ```text + ConstParams { + bytes = B + has_tag = false + tag_id = (absent) + } + ``` + +4. If `has_tag == true`: + + 1. Ensure at least `1 + 4 + 8` bytes are present: + + * If `len(params_bytes) < 1 + 4 + 8`: return `error`. + + 2. Parse next 4 bytes as `tag_id: u32` (big-endian). + + 3. Parse next 8 bytes as `len: u64` (big-endian). + + 4. Compute `expected_total = 1 + 4 + 8 + len`. + + * If `len(params_bytes) != expected_total`: return `error`. + + 5. Let `B` be the remaining `len` bytes. + + 6. Return: + + ```text + ConstParams { + bytes = B + has_tag = true + tag_id = tag_id + } + ``` + +### 5.4 Conformance rule + +* For `pel.bytes.const/1`, `params_bytes` MUST be encoded according to `encode_params_const`. +* Any malformed encoding (invalid `has_tag`, truncated data, mismatched length, etc.) MUST be treated as a **decode error** ⇒ `INVALID_PROGRAM`. + +--- + +## 6. HashParams Encoding (`pel.bytes.hash.asl1`) + +Applies to: + +* `pel.bytes.hash.asl1/1` + +### 6.1 Logical type + +```text +HashParams { + hash_id: HashId // uint16 +} +``` + +### 6.2 Encoding + +```text +encode_params_hash(HashParams) -> OctetString +``` + +Given: + +```text +HashParams { hash_id } +``` + +The encoding is: + +```text +[hash_id: u16] +``` + +Where: + +* `hash_id` is encoded as big-endian `u16`. + +So `params_bytes` MUST be exactly 2 bytes. + +### 6.3 Decoding + +```text +decode_params_hash(params_bytes: OctetString) -> HashParams | error +``` + +Algorithm: + +1. If `len(params_bytes) != 2`: return `error`. + +2. Parse the 2 bytes as `hash_id: u16` (big-endian). + +3. For `pel.bytes.hash.asl1/1` **in this version**: + + * If `hash_id != 0x0001`: + + * return `error`. + + * Else: + + * return `HashParams { hash_id }`. + +Rationale: + +* `OPREG/PEL1-KERNEL` constrains `pel.bytes.hash.asl1/1` to `hash_id = 0x0001` (`HASH-ASL1-256`) for cross-implementation determinism. +* Supporting additional `HashId`s requires a new operation version (e.g. `pel.bytes.hash.asl1/2`) and/or an updated parameter profile. + +### 6.4 Conformance rule + +* For `pel.bytes.hash.asl1/1`, `params_bytes` MUST: + + * be exactly 2 bytes long, and + * encode `hash_id = 0x0001`. + +* Any other length or `hash_id` MUST be treated as a **decode error** ⇒ `INVALID_PROGRAM`. + +--- + +## 7. Integration with PEL/PROGRAM-DAG/1 (Informative) + +In a `PEL/PROGRAM-DAG/1` Program: + +* Each `Node` has: + + * an `OperationId { name, version }`, and + * a `params_bytes: OctetString` (representation-specific; this spec does not define the layout of the Node itself). + +* For Nodes whose `OperationId` is one of the kernel ops from `OPREG/PEL1-KERNEL`: + + * The engine MUST: + + 1. Select the appropriate `decode_params_*` function from this document based on `OperationId.name` and `version`. + + 2. Pass `params_bytes` to that function. + + 3. If decoding fails, treat the Program as `INVALID_PROGRAM` as per `PEL/PROGRAM-DAG/1`: + + ```text + ExecutionStatus = INVALID_PROGRAM + ExecutionErrorKind = PROGRAM + summary.status_code = 2 + ``` + + 4. If decoding succeeds, use the resulting Params value for the operation semantics. + +* For non-kernel operations, other param profiles apply; they MUST NOT conflict with these definitions. + +--- + +## 8. Conformance + +An implementation is **OPREG/PEL1-KERNEL-PARAMS/1–conformant** if and only if: + +1. **Encoding/decoding correctness** + + * For each kernel operation: + + * `encode_params_*` and `decode_params_*` are implemented exactly as specified. + + * For all valid Params values: + + ```text + decode_params_*(encode_params_*(value)) == value + ``` + + * For all `params_bytes` accepted by `decode_params_*`, the result is well-defined and deterministic. + +2. **Error handling** + + * Any malformed or non-canonical `params_bytes` detected by `decode_params_*` is treated as a **decode error**. + * Decode errors for kernel operations MUST be mapped to `INVALID_PROGRAM` in `PEL/PROGRAM-DAG/1`, not `RUNTIME_FAILED`. + +3. **Consistency with OPREG/PEL1-KERNEL** + + * The logical Params types used in the engine’s implementation of kernel operations match those defined in `OPREG/PEL1-KERNEL`. + * Runtime failures for kernel operations are **not** caused by param decode errors; they are caused only by data-dependent conditions specified in `OPREG/PEL1-KERNEL` (e.g. slice out-of-bounds). + +4. **Determinism** + + * For any given `params_bytes`, `decode_params_*` MUST return the same result (or error) across all conformant implementations. + +5. **Layering** + + * The implementation does not depend on any particular Program encoding. + * It only consumes `params_bytes` as an OctetString and produces/consumes logical Params as defined here. + +--- + +## 9. Change Log (Informative) + +**v0.1.1 (2025-11-15)** + +* Aligned with `OPREG/PEL1-KERNEL v0.1.1`: + + * Removed `pel.bytes.clone/1` from the normative scope of this profile; the kernel parameter profile now covers only: + + * `pel.bytes.concat/1` + * `pel.bytes.slice/1` + * `pel.bytes.const/1` + * `pel.bytes.hash.asl1/1` + + * Updated the informative reference in §4.3 so that `RANGE_OUT_OF_BOUNDS` now points to `status_code = 0x0002_0001`, reflecting the reassigned `kernel_op_code` for `pel.bytes.slice/1`. + + * Clarified the dependency on `OPREG/PEL1-KERNEL v0.1.1` in the header. + +**v0.1.0 (2025-11-15)** + +* Introduced canonical parameter encodings for: + + * `pel.bytes.clone/1` (Unit → empty `params_bytes`) + * `pel.bytes.concat/1` (Unit → empty `params_bytes`) + * `pel.bytes.slice/1` (`SliceParams { offset, length }` → 16-byte payload) + * `pel.bytes.const/1` (`ConstParams` → `[has_tag][tag_id?][len][bytes]`) + * `pel.bytes.hash.asl1/1` (`HashParams { hash_id }` → 2-byte payload; restricted to `0x0001`) + +* Locked the rule that **all kernel param decode failures imply `INVALID_PROGRAM`**, not runtime failure. + +* Kept the profile independent from any particular Program encoding (`ENC/PEL-PROGRAM-DAG/1`). + +--- + +## Document History + +* **0.1.1 (2025-11-16):** Registered as Tier-1 spec and aligned to the Amduat 2.0 substrate baseline. diff --git a/tier1/pel-1-core.md b/tier1/pel-1-core.md new file mode 100644 index 0000000..5a3d141 --- /dev/null +++ b/tier1/pel-1-core.md @@ -0,0 +1,639 @@ +# PEL/1-CORE — Primitive Execution Layer (Core Semantics) + +Status: Approved +Owner: Niklas Rydberg +Version: 0.3.0 +SoT: Yes +Last Updated: 2025-11-16 +Linked Phase Pack: N/A +Tags: [execution, deterministic] + + + +**Document ID:** `PEL/1-CORE` +**Layer:** L1 — Kernel execution semantics (pure value model) + +**Depends on (normative):** + +* `ASL/1-CORE v0.4.x` — Artifact Substrate (logical value model) +* `SUBSTRATE/STACK-OVERVIEW v0.1.x` — kernel layering and invariants + +**Integrates with (informative):** + +* `ENC/ASL1-CORE v1` — canonical encodings for Artifacts/References +* `HASH/ASL1 v0.2.x` — ASL1 hash family (identity handles) +* `ASL/1-STORE v0.4.x` — content-addressable store semantics +* `PEL/1-SURF` (planned) — store-backed execution surface +* `PEL/PROGRAM-DAG/1` (planned) — DAG-style program/profile +* `PEL/TRACE-DAG/1` (planned) — structured node-level execution trace + +© 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 + +The key words **MUST**, **MUST NOT**, **SHOULD**, **MAY**, etc. are to be interpreted as described in RFC 2119. + +`PEL/1-CORE` uses the ASL/1-CORE value model: + +```text +Artifact { + bytes: OctetString + type_tag: optional TypeTag +} + +TypeTag { + tag_id: uint32 +} + +Reference { + hash_id: HashId + digest: OctetString +} + +HashId = uint16 +``` + +`PEL/1-CORE`: + +* does **not** modify Artifact / Reference identity or equality, +* does **not** introduce new identity schemes, +* defines only **logical value semantics** — no storage layouts, protocols, or APIs. + +It is independent of: + +* any particular encoding profile, +* any hash-family choice, +* any store, graph, or certification system. + +--- + +## 1. Purpose & Scope + +### 1.1 Purpose + +`PEL/1-CORE` defines the **minimal kernel execution semantics** for Amduat 2.0: + +* Execution is expressed as **pure, deterministic functions over Artifacts**. +* A **scheme** defines how a program Artifact and input Artifacts are interpreted. +* For a given scheme, program, inputs, and optional parameters, execution produces: + + * a list of **output Artifacts**, and + * a scheme-level **ExecutionResultValue** describing the outcome. + +The goal is a **timeless, deterministic, storage- and graph-neutral execution substrate** that: + +* can be implemented as “metal-fast” engines (compiled, no dynamic dependency web), and +* can be wired into stores, provenance, and certification without entangling with them on the hot path. + +### 1.2 Non-Goals + +`PEL/1-CORE` explicitly does **not** define: + +* Store semantics (`put`/`get`, authority, replication, GC). +* Graph/provenance edges (`TGK/1` and its profiles). +* Certification, signatures, or trust semantics (`CIL/1`, `FER/1`, `FCT/1`). +* Scheduling, work queues, or distributed orchestration. +* Authentication, authorization, or access control. +* Real-world time, randomness, or I/O APIs. +* Structure of program Artifacts (DAG vs non-DAG) — those are defined by **scheme profiles**. + +All of these concerns live **above** or **around** `PEL/1-CORE`. + +--- + +## 2. Core Concepts + +### 2.1 SchemeRef + +A **scheme** defines a particular execution semantics (language, calling convention, program structure, etc.). + +At the core level, a scheme is identified by a `SchemeRef`: + +```text +SchemeRef = Reference // ASL/1-CORE Reference +``` + +* `SchemeRef` refers to a **scheme descriptor Artifact**. +* `PEL/1-CORE` does not prescribe the content or `TypeTag` of that descriptor; those are defined by scheme-specific profiles (`PEL/SCHEME/...`). +* Distinct scheme descriptors MUST have distinct `SchemeRef` values (different content ⇒ different digest under ASL/1-CORE). + +### 2.2 Logical Execution Call + +At the value level, an execution is modeled as an **ExecutionCall**: + +```text +ExecutionCall { + scheme_ref : SchemeRef + program : Artifact + inputs : list + params : optional Artifact +} +``` + +Where: + +* `scheme_ref` selects which scheme’s semantics apply. +* `program` is the program Artifact. +* `inputs` is an ordered list of input Artifacts. +* `params` is an optional parameters Artifact. + +`PEL/1-CORE` does **not** constrain the internal structure of `program`, `inputs`, or `params`; that is scheme-level. + +For a scheme `s` identified by `scheme_ref`, there exists a **total** pure function: + +```text +Exec_s : + (program: Artifact, inputs: list, params: optional Artifact) + -> (outputs: list, result: ExecutionResultValue) +``` + +> **PEL/TOTALITY/1** +> For every scheme that an environment claims to support, `Exec_s` MUST be total over all possible `(program, inputs, params)` triples: it MUST always return a well-formed `(outputs, ExecutionResultValue)` pair, never “fall off the edge” (crash, panic) in-model. Invalid combinations must be mapped to appropriate error statuses, not to undefined behavior. + +Totality at this level excludes environment failures (see §4.4). + +### 2.3 ExecutionStatus and ExecutionErrorKind + +`PEL/1-CORE` defines a shared classification of execution outcomes. + +```text +ExecutionStatus = uint8 +ExecutionErrorKind = uint8 +``` + +`ExecutionStatus` codes: + +```text +ExecutionStatus { + OK = 0 + SCHEME_UNSUPPORTED = 1 + INVALID_PROGRAM = 2 + INVALID_INPUTS = 3 + RUNTIME_FAILED = 4 +} +``` + +* `OK` — execution completed successfully under the scheme semantics. +* `SCHEME_UNSUPPORTED` — the environment does not implement the scheme identified by `scheme_ref`. +* `INVALID_PROGRAM` — the program Artifact cannot be interpreted as a valid program under the scheme. +* `INVALID_INPUTS` — inputs or parameters are invalid for the scheme. +* `RUNTIME_FAILED` — the scheme executed but signaled a deterministic failure (e.g., explicit error, assertion failure). + +`ExecutionErrorKind` refines the source: + +```text +ExecutionErrorKind { + NONE = 0 + SCHEME = 1 // scheme support/descriptor issues + PROGRAM = 2 // invalid program under scheme + INPUTS = 3 // invalid inputs/params under scheme + RUNTIME = 4 // runtime failure under scheme +} +``` + +Future values MAY be defined, but MUST maintain compatibility with existing meanings. + +### 2.4 ExecutionErrorSummary + +`ExecutionErrorSummary` gives a compact, deterministic classification: + +```text +ExecutionErrorSummary { + kind : ExecutionErrorKind + status_code : uint32 // 0 for success; non-zero for error +} +``` + +Semantics: + +* If `status = OK`: + + * `kind` MUST be `NONE`. + * `status_code` MUST be `0`. + +* If `status = SCHEME_UNSUPPORTED`: + + * `kind` MUST be `SCHEME`. + * `status_code` SHOULD be the canonical core code `1`. + +* If `status = INVALID_PROGRAM`: + + * `kind` MUST be `PROGRAM`. + * `status_code` SHOULD be the canonical core code `2`. + +* If `status = INVALID_INPUTS`: + + * `kind` MUST be `INPUTS`. + * `status_code` SHOULD be the canonical core code `3`. + +* If `status = RUNTIME_FAILED`: + + * `kind` MUST be `RUNTIME`. + * `status_code` MUST be non-zero. + * The concrete `status_code` is **scheme-defined**, but MUST be deterministic. + +Scheme profiles MAY define sub-ranges of `status_code` for category grouping but MUST respect these invariants. + +### 2.5 DiagnosticEntry + +Schemes MAY provide additional deterministic diagnostics via `DiagnosticEntry`: + +```text +DiagnosticEntry { + code : uint32 // scheme-defined code + message : OctetString // scheme-defined bytes (SHOULD be UTF-8 text) +} +``` + +Requirements: + +* For a given execution, the list of `DiagnosticEntry` values MUST be **fully deterministic**. +* Diagnostics MUST NOT include: + + * wall-clock timestamps, + * random identifiers, + * host IDs, PIDs, or environment-dependent details. + +Diagnostics are optional; their interpretation is scheme- or profile-specific. + +### 2.6 ExecutionResultValue (logical) + +`ExecutionResultValue` is the scheme-level result produced by `Exec_s`: + +```text +ExecutionResultValue { + pel1_version : uint16 + + status : ExecutionStatus + scheme_ref : SchemeRef + + summary : ExecutionErrorSummary + diagnostics : list +} +``` + +Semantics: + +* `pel1_version`: + + * MUST be set to `1` for this version of `PEL/1-CORE`. + * Future versions MAY introduce new result formats under different encodings or TypeTags but MUST NOT change the meaning of `pel1_version = 1` values. + +* `status` and `scheme_ref`: + + * `status` reflects the outcome as in §2.3. + * `scheme_ref` MUST equal the scheme identifier used for the call. + +* `summary`: + + * MUST obey §2.4 for the chosen `status`. + +* `diagnostics`: + + * MAY be empty. + * MUST be deterministic and scheme-defined. + +`ExecutionResultValue` is a logical value; its encoding as an `Artifact` is defined by a separate profile (e.g., `ENC/PEL1-RESULT/1`), not by `PEL/1-CORE`. + +--- + +## 3. Execution Schemes + +### 3.1 Scheme definition + +A **scheme** is the combination of: + +* a `SchemeRef` (descriptor Artifact), and +* a concrete semantics `Exec_s`. + +For a given `scheme_ref`, a scheme definition MUST specify: + +1. **Program representation** + + * How `program : Artifact` is interpreted. + * Which `TypeTag` values (if any) indicate valid program Artifacts. + * Structural validity rules (DAG, bytecode, WASM module, etc.). + +2. **Input and parameter representation** + + * How `inputs : list` are decoded and mapped into the scheme’s runtime model (arguments, input buffers, configs, etc.). + * How `params : optional Artifact` is interpreted, if used. + +3. **Output representation** + + * How produced values are encoded as output `Artifact`s. + * Any constraints on `TypeTag` for outputs. + +4. **Failure semantics** + + * Conditions under which `status` becomes: + + * `INVALID_PROGRAM`, + * `INVALID_INPUTS`, + * `RUNTIME_FAILED`. + + * How `status_code` is chosen for `RUNTIME_FAILED`. + + * Whether and how outputs are produced in each non-`OK` case. + + > **PEL/FAIL-OUTPUT/REC/1 (RECOMMENDED)** + > For `SCHEME_UNSUPPORTED`, `INVALID_PROGRAM`, and `INVALID_INPUTS`, schemes SHOULD produce an empty `outputs` list. For `RUNTIME_FAILED`, schemes MAY produce partial outputs, but MUST define that behavior clearly and deterministically. + +5. **Deterministic diagnostics** + + * Rules for populating `diagnostics` as deterministic functions of `(program, inputs, params)` and the scheme’s semantics. + +Formally, the scheme MUST define a total function: + +```text +Exec_s : + (program: Artifact, inputs: list, params: optional Artifact) + -> (outputs: list, result: ExecutionResultValue) +``` + +that is pure and deterministic (see §4), and always yields: + +* `pel1_version = 1`, +* `scheme_ref` equal to the scheme’s `SchemeRef`, +* a `status` and `summary` consistent with §2.3–§2.4. + +### 3.2 Scheme support in an environment + +At the core level, an environment either: + +* **Supports** a scheme identified by `scheme_ref`, in which case it implements `Exec_s`, or +* Does **not** support it, in which case any attempt to execute that scheme MUST yield an `ExecutionResultValue` with: + + * `status = SCHEME_UNSUPPORTED`, + * `scheme_ref = scheme_ref`, + * `summary.kind = SCHEME`, + * `summary.status_code = 1` (canonical). + +How scheme support is discovered (registries, configuration) is outside `PEL/1-CORE`. + +### 3.3 Scheme profiles + +Scheme definitions are typically captured in separate documents (e.g., `PEL/SCHEME/WASM-1`, `PEL/PROGRAM-DAG/1`) that: + +* Normatively reference `PEL/1-CORE`. +* Provide: + + * concrete program/data models, + * concrete error/status mappings, + * conformance vectors for `Exec_s`. + +`PEL/1-CORE` itself remains scheme-agnostic. + +--- + +## 4. Determinism and Purity + +### 4.1 Determinism contract + +For a given scheme: + +```text +Exec_s(program, inputs, params) -> (outputs, result) +``` + +> **PEL/DET/1 — Determinism Invariant** +> For any two **PEL/1-CORE–conformant** implementations `I1` and `I2` of that scheme, and for any `program : Artifact`, `inputs : list`, `params : optional Artifact`: +> +> ```text +> Exec_s_I1(program, inputs, params) +> == Exec_s_I2(program, inputs, params) +> ``` +> +> where: +> +> * `outputs` lists are the same length and pairwise equal as Artifacts (ASL/1-CORE equality), and +> * `ExecutionResultValue` fields are equal (`pel1_version`, `status`, `scheme_ref`, `summary`, and the sequence of `DiagnosticEntry`s). + +Determinism MUST hold across: + +* platforms, +* architectures, +* implementation languages, +* runtime environments, + +given the same scheme definition and the same Artifacts. + +### 4.2 Purity requirement + +> **PEL/PURITY/1 — Purity Invariant** +> `Exec_s` MUST be a pure function of `(scheme_ref, program, inputs, params)` and the scheme definition: + +* No reads from external mutable state: + + * No direct filesystem, network, environment variables, OS state, or external databases. + * Any required data MUST be supplied via Artifacts. + +* No writes to external state that influence other executions: + + * Execution MUST NOT have side effects visible outside its outputs and `ExecutionResultValue`. + * Logging, metrics, or debugging output MUST NOT affect later runs. + +* No dependence on: + + * wall-clock time, + * random sources (unless seeds are passed as inputs), + * process IDs, memory addresses, or other non-deterministic identifiers. + +If a scheme requires time or randomness, these MUST be expressed as input Artifacts. + +### 4.3 No graph, certification, or policy dependencies + +> **PEL/HOT-PATH-ISOLATION/1** +> PEL/1-CORE execution semantics MUST NOT require: + +* any graph backend (`TGK/1`) or provenance structure to compute outputs, +* any certification or trust system (`CIL/1`, `FER/1`, `FCT/1`), +* any governance or policy engine. + +Such concerns may be applied **around** `Exec_s` (e.g., to authorize execution, or to interpret the result), but: + +* `Exec_s` MUST be definable and implementable **purely** over ASL/1 Artifacts and scheme semantics, and +* no calls into TGK/1, CIL/1, or similar layers are required on the execution hot path. + +### 4.4 Environment failures (out of model) + +`PEL/1-CORE` does **not** define semantics for **environment failures** such as: + +* out-of-memory, +* OS-level crashes or kills, +* implementation bugs or panics in the host environment. + +If such failures occur: + +* No `(outputs, ExecutionResultValue)` pair is produced at the PEL/1-CORE level. +* The run is considered **out-of-model**. + +Higher layers (orchestration, SRE policy) MAY define: + +* logging, +* retries, +* escalation or incident handling, + +but such failures are not represented as valid `ExecutionResultValue` values under this spec. + +--- + +## 5. Relationship to Stores and Surfaces (Informative) + +### 5.1 Store-backed realizations + +`PEL/1-CORE` is storage-neutral, but a typical realization over `ASL/1-STORE` looks like: + +1. An engine receives: + + * `scheme_ref : SchemeRef`, + * `program_ref : Reference`, + * `input_refs : list`, + * `params_ref : optional Reference`. + +2. It uses `ASL/1-STORE` `get` to resolve: + + * `program_ref` to `program : Artifact`, + * `input_refs` to `inputs : list`, + * `params_ref` (if present) to `params : Artifact`. + +3. It calls `Exec_s(program, inputs, params)` per `PEL/1-CORE`. + +4. It uses `ASL/1-STORE` `put` to: + + * persist `outputs : list` and obtain their `Reference`s, + * encode `ExecutionResultValue` as an `Artifact` and persist it, obtaining a result `Reference`. + +A surface specification (e.g., `PEL/1-SURF`) can normatively define: + +* the store-backed API (e.g., `run(store, scheme_ref, program_ref, input_refs, params_ref)`), +* mapping of store errors into higher-level envelopes, +* encoding of `ExecutionResultValue` as an Artifact (TypeTag, bytes). + +`PEL/1-CORE` does not prescribe these surfaces; it only constrains the pure mapping implemented in step 3. + +### 5.2 Mapping errors in surfaces + +`PEL/1-CORE` is unaware of store errors like `ERR_NOT_FOUND`, `ERR_INTEGRITY`, or `ERR_UNSUPPORTED`. + +Surface profiles (e.g., `PEL/1-SURF`) MAY: + +* introduce additional wrapper structures that capture: + + * store-level failures, + * missing inputs/programs, + * multi-store scenarios. + +* map such failures into rich **surface-level** results (not `ExecutionResultValue`s), potentially with: + + * failing `Reference`s, + * store error codes, + * retry hints. + +Within `PEL/1-CORE` itself, the only “unavailability” status is `SCHEME_UNSUPPORTED`, strictly about scheme support, not store availability. + +--- + +## 6. Conformance + +An implementation is **PEL/1-CORE–conformant** if and only if it satisfies all of: + +1. **Correct use of ASL/1-CORE types** + + * Treats `Artifact`, `TypeTag`, and `Reference` exactly as in `ASL/1-CORE v0.4.x`. + * Does not redefine Artifact or Reference identity or equality. + +2. **Implements at least one scheme** + + * For each supported `scheme_ref`, implements a total function: + + ```text + Exec_s(program, inputs, params) -> (outputs, result) + ``` + + as described in §3 and §4. + + * For unsupported schemes, attempts to execute them yield an `ExecutionResultValue` with: + + * `status = SCHEME_UNSUPPORTED`, + * `scheme_ref` equal to the requested scheme, + * `summary.kind = SCHEME`, + * `summary.status_code = 1`. + +3. **Satisfies determinism** + + * For each supported scheme, satisfies **PEL/DET/1** across all PEL/1-CORE–conformant implementations. + +4. **Satisfies purity** + + * For each supported scheme, satisfies **PEL/PURITY/1**. + * Any time or randomness must be supplied as input Artifacts. + +5. **Produces valid ExecutionResultValue** + + * For every in-model execution, produces an `ExecutionResultValue` with: + + * `pel1_version = 1`, + * `scheme_ref` set correctly, + * `status` set according to scheme semantics (§2.3), + * `summary` consistent with `status` (§2.4), + * `diagnostics` deterministic (if present). + +6. **Stays independent of higher layers on the hot path** + + * Does not require TGK/1, CIL/1, FER/1, FCT/1, or OI/1 to compute `Exec_s`. + * Any provenance edges, receipts, or facts are constructed after execution using Artifacts and `ExecutionResultValue`, not during the core mapping. + +7. **Treats environment failures as out-of-model** + + * Does not attempt to represent OS/hardware/environment failures as valid `ExecutionResultValue` values. + * Clearly distinguishes: + + * valid executions (producing `(outputs, ExecutionResultValue)`), vs + * environment failures (producing neither, handled outside `PEL/1-CORE`). + +All other aspects — concrete APIs, store wiring, encoding, hash usage, scheduling, sandboxing, resource limits — belong to surfaces and higher-layer profiles, not to `PEL/1-CORE`, provided they do not contradict the requirements above. + +--- + +## 7. Evolution (Informative) + +`PEL/1-CORE` is intended to evolve **additively**: + +* New `ExecutionStatus` values MAY be added, but existing codes MUST retain their meaning. +* New `ExecutionErrorKind` variants MAY be added, but existing variants MUST retain their meaning and relationships. +* New result formats MAY be introduced under new `pel1_version` values, but `pel1_version = 1` semantics MUST remain stable. + +Any change that would: + +* alter the meaning of an existing `ExecutionStatus`, or +* alter the interpretation of an existing `pel1_version = 1` field, + +MUST be treated as a **new major kernel surface** (e.g., `PEL/2-CORE`), not an evolution of `PEL/1-CORE`. + +This aligns with the broader Amduat principle: + +> **Evolve by addition and explicit versioning; never rewrite identity or history.** + +--- + +## Document History + +* **0.3.0 (2025-11-16):** Registered as Tier-1 spec and aligned to the Amduat 2.0 substrate baseline. diff --git a/tier1/pel-1-surf.md b/tier1/pel-1-surf.md new file mode 100644 index 0000000..1165f57 --- /dev/null +++ b/tier1/pel-1-surf.md @@ -0,0 +1,853 @@ +# PEL/1-SURF — Primitive Execution Surface (Store-Backed) + +Status: Approved +Owner: Niklas Rydberg +Version: 0.2.2 +SoT: Yes +Last Updated: 2025-11-30 +Linked Phase Pack: N/A +Tags: [execution, deterministic] + + + +**Document ID:** `PEL/1-SURF` +**Layer:** L1 — Execution surface over ASL/1-STORE + +**Depends on (normative):** + +* `PEL/1-CORE v0.3.x` — Primitive Execution Layer (core semantics) +* `ASL/1-CORE v0.4.x` — Artifact Substrate (logical values) +* `ASL/1-STORE v0.4.x` — Store semantics over `Reference -> Artifact` + +**Integrates with (informative):** + +* `ENC/ASL1-CORE v1.0.4` — Canonical encodings for `Artifact` / `Reference` +* `HASH/ASL1 v0.2.4` — ASL1 hash family (`HashId` assignments) +* `ENC/PEL1-RESULT/1 v0.1.x` — Canonical encoding for PEL/1 ExecutionResult artifacts +* `PEL/PROGRAM-DAG/1` (planned) — DAG program scheme +* `PEL/TRACE-DAG/1` (planned) — Structured execution trace profile +* `TGK/1-CORE` — Trace Graph Kernel +* `CIL/1`, `FER/1`, `FCT/1` — Certification, evidence, facts + +© 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. Overview and Conventions + +`PEL/1-SURF` defines the **store-backed execution surface** for PEL: + +> Given: +> * a Store Instance, +> * a scheme (`SchemeRef`), +> * a program (`program_ref`), +> * zero or more input references, +> * and optional parameters, +> +> it: +> +> * resolves those references via `ASL/1-STORE`, +> * applies the pure scheme-level function `Exec_s` from `PEL/1-CORE`, +> * persists outputs and a surface `ExecutionResult` artifact, +> * and returns the output references and the result reference. + +This surface: + +* is what real engines expose, +* wires PEL’s pure value semantics to the content-addressable store, +* produces canonical artifacts that TGK/1-CORE, FER/1, FCT/1, and overlays can consume. + +RFC 2119 terms (**MUST**, **SHOULD**, **MAY**, etc.) apply. + +`PEL/1-SURF` uses `Artifact`, `TypeTag`, `Reference`, `HashId` exactly as defined in `ASL/1-CORE`. + +--- + +## 1. Scope and Non-Goals + +### 1.1 Purpose + +`PEL/1-SURF` defines: + +1. The **run operation**: + + ```text + run( + store: StoreInstance, + scheme_ref: SchemeRef, + program_ref: ProgramRef, + input_refs: InputRefList, + params_ref: optional ParamsRef + ) -> { + output_refs: OutputRefList, + result_ref: ResultRef + } +```` + +2. How `run(...)`: + + * interacts with `ASL/1-STORE` (`put` / `get`), + * invokes the PEL/1-CORE scheme function `Exec_s`, + * maps store-resolution failures into deterministic outcomes, + * and produces a canonical **surface ExecutionResult artifact**. + +3. The structure and semantics of the **surface ExecutionResult artifact**, which: + + * embeds the `ExecutionResultValue` from `PEL/1-CORE`, + * echoes the full set of input/output refs, + * optionally attaches store-resolution diagnostics. + +### 1.2 Explicit Non-Goals + +`PEL/1-SURF` does **not** define: + +* Scheme-specific program structures (DAG, bytecode, WASM, etc.). +* Encoding formats for programs, inputs, params, or outputs (scheme profiles do). +* Graph/provenance edges (TGK/1-CORE). +* Certificates, receipts, or fact semantics (CIL/1, FER/1, FCT/1). +* Scheduling, queuing, or distributed execution strategies. +* Access control, secrecy, or multi-tenant policies. +* Encoding details of the ExecutionResult artifact (`ENC/PEL1-RESULT/1` covers this). +* Store topology, replication, or GC behavior. + +`PEL/1-SURF` is strictly about: + +> **Given store + refs + scheme → how to run and what artifacts MUST be created.** + +--- + +## 2. Types and Dependencies + +### 2.1 SchemeRef and aliases + +From `PEL/1-CORE`: + +```text +SchemeRef = Reference +``` + +For readability, `PEL/1-SURF` defines the following aliases (all of which are simply `Reference`): + +```text +ProgramRef = Reference +InputRef = Reference +OutputRef = Reference +ParamsRef = Reference +ResultRef = Reference +TraceRef = Reference +``` + +Type aliases for lists: + +```text +InputRefList = [InputRef] // ordered, finite +OutputRefList = [OutputRef] // ordered, finite +``` + +### 2.2 StoreInstance (from ASL/1-STORE) + +A **StoreInstance** is any component conforming to `ASL/1-STORE v0.4.x`, with a `StoreConfig`: + +```text +StoreConfig { + encoding_profile: EncodingProfileId // e.g. ASL_ENC_CORE_V1 + hash_id: HashId // e.g. 0x0001 (HASH-ASL1-256) +} +``` + +`PEL/1-SURF` interacts with the store only via: + +```text +put(A: Artifact) -> Reference + +get(R: Reference) -> + Artifact + | ERR_NOT_FOUND + | ERR_INTEGRITY + | ERR_UNSUPPORTED +``` + +as defined in `ASL/1-STORE`. The store MAY expose more operations, but `PEL/1-SURF` MUST NOT depend on them. + +All `program_ref`, `input_refs`, `params_ref`, and `result_ref` values are interpreted relative to the `StoreConfig` used by the StoreInstance. + +--- + +## 3. Surface Execution Model + +### 3.1 Conceptual run signature + +At the surface level, a **run** is: + +```text +run( + store: StoreInstance, + scheme_ref: SchemeRef, + program_ref: ProgramRef, + input_refs: InputRefList, + params_ref: optional ParamsRef +) -> { + output_refs: OutputRefList, + result_ref: ResultRef +} +``` + +All references are interpreted relative to `store`’s `StoreConfig`. + +### 3.2 Relationship to PEL/1-CORE + +For a supported scheme identified by `scheme_ref`, there exists a pure function `Exec_s` per `PEL/1-CORE`: + +```text +Exec_s : + (program: Artifact, inputs: list, params: optional Artifact) + -> (outputs: list, result: ExecutionResultValue) +``` + +`PEL/1-SURF` defines `run(...)` such that, whenever: + +* `scheme_ref` is supported, +* `program_ref` and all `input_refs` (and `params_ref`, if provided) resolve successfully from `store`, + +then: + +* `run(...)` MUST behave as if it: + + 1. fetched `program`, `inputs`, `params` from `store`, + 2. invoked `Exec_s(program, inputs, params)`, and + 3. persisted `outputs` and `ExecutionResultValue` as Artifacts in `store`. + +Determinism and purity of `Exec_s` are guaranteed by `PEL/1-CORE`. `PEL/1-SURF` adds the store wiring and the definition of the surface ExecutionResult artifact. + +--- + +## 4. Surface ExecutionResult Artifact + +### 4.1 SurfaceExecutionResult (logical structure) + +`PEL/1-SURF` defines the **logical payload** of the surface ExecutionResult artifact as: + +```text +SurfaceExecutionResult { + pel1_version : uint16 + + core_result : ExecutionResultValue + + scheme_ref : SchemeRef // echo + program_ref : ProgramRef + input_refs : InputRefList + output_refs : OutputRefList + + params_ref : optional ParamsRef + + store_failure: optional StoreFailure + trace_ref : optional TraceRef +} +``` + +Where `ExecutionResultValue` is as defined in `PEL/1-CORE`: + +```text +ExecutionResultValue { + pel1_version : uint16 + status : ExecutionStatus + scheme_ref : SchemeRef + summary : ExecutionErrorSummary + diagnostics : list +} +``` + +and: + +* `ExecutionStatus ∈ { OK, SCHEME_UNSUPPORTED, INVALID_PROGRAM, INVALID_INPUTS, RUNTIME_FAILED }` +* `ExecutionErrorSummary { kind: ExecutionErrorKind, status_code: uint32 }` +* `DiagnosticEntry { code: uint32, message: OctetString }` + +(see `PEL/1-CORE` for full semantics). + +### 4.2 StoreFailure classification + +`PEL/1-SURF` refines store-resolution errors via `StoreFailure`: + +```text +StoreErrorCode = uint8 + +StoreErrorCode { + NOT_FOUND = 1 // Store returned ERR_NOT_FOUND + INTEGRITY = 2 // Store returned ERR_INTEGRITY + UNSUPPORTED = 3 // Store returned ERR_UNSUPPORTED +} + +StoreFailurePhase = uint8 + +StoreFailurePhase { + PROGRAM = 1 + INPUT = 2 +} + +StoreFailure { + phase : StoreFailurePhase + error_code : StoreErrorCode + failing_ref : Reference +} +``` + +Semantics: + +* `phase = PROGRAM` indicates the failure happened while resolving `program_ref`. +* `phase = INPUT` indicates it happened while resolving an input or `params_ref`. +* `failing_ref` MUST be the exact `Reference` passed to `get` that produced the error. +* `error_code` MUST match the store’s reported error. + +### 4.3 TypeTag and encoding + +The concrete surface ExecutionResult artifact is an `Artifact` with: + +* `type_tag = PEL1_SURFACE_EXECUTION_RESULT_TAG` +* `bytes` = canonical encoding of `SurfaceExecutionResult` under `ENC/PEL1-RESULT/1`. + +This specification: + +* Reserves the symbolic `PEL1_SURFACE_EXECUTION_RESULT_TAG` for this purpose (concretized by `ENC/PEL1-RESULT/1`). +* Defers the concrete `tag_id`, `CoreResultBytes`, and `SurfaceExecutionResultBytes` layout to `ENC/PEL1-RESULT/1`. +* Requires that, for any given logical `SurfaceExecutionResult`, there is exactly one canonical encoding, satisfying ASL/1-CORE’s canonical encoding constraints. + +--- + +## 5. Normative Behavior of `run(...)` + +### 5.1 High-level phases + +The behavior of: + +```text +run(store, scheme_ref, program_ref, input_refs, params_ref) +``` + +is defined in terms of five conceptual phases: + +1. **Scheme support check** +2. **Program resolution** +3. **Input and parameter resolution** +4. **Scheme execution (`Exec_s`)** +5. **Persistence of outputs and surface result** + +Implementations MAY pipeline or parallelize internally, but the observable behavior (artifacts written and references returned) MUST match the sequential semantics described here. + +### 5.2 Scheme support check + +The engine first determines whether it **supports** the scheme identified by `scheme_ref`: + +* If the scheme is **not supported**: + + * The engine MUST NOT attempt to resolve `program_ref` or any `input_refs` / `params_ref`. + + * It MUST construct `core_result : ExecutionResultValue` with: + + ```text + pel1_version = 1 + status = SCHEME_UNSUPPORTED + scheme_ref = scheme_ref + summary.kind = SCHEME + summary.status_code = 1 // canonical core code for SCHEME_UNSUPPORTED + diagnostics = [] // or deterministic scheme-agnostic diagnostics + ``` + + in accordance with `PEL/1-CORE`. + + * It MUST construct a `SurfaceExecutionResult` with: + + ```text + pel1_version = 1 + + core_result = (as above) + scheme_ref = scheme_ref + program_ref = program_ref // echo input + input_refs = input_refs // echo input + output_refs = [] // no outputs + + params_ref = params_ref // echo input + + store_failure = absent + trace_ref = absent + ``` + + * It MUST encode this as an `Artifact` with `type_tag = PEL1_SURFACE_EXECUTION_RESULT_TAG`, and call: + + ```text + put(surface_result_artifact) -> result_ref + ``` + + * It MUST return: + + ```text + output_refs = [] + result_ref = result_ref + ``` + +* If the scheme **is supported**, execution proceeds to program resolution. + +Whether a scheme is supported is implementation-specific (registry, plugin, etc.), but the observable effect MUST match this specification. + +### 5.3 Program resolution + +If the scheme is supported, the engine MUST resolve `program_ref`: + +```text +get(program_ref) -> + program : Artifact + | ERR_NOT_FOUND + | ERR_INTEGRITY + | ERR_UNSUPPORTED +``` + +* If `get(program_ref)` returns an `Artifact`, execution proceeds to input/params resolution. + +* If it returns an error: + + * The engine MUST NOT call `Exec_s`. + + * It MUST map the store error to `StoreErrorCode`: + + ```text + ERR_NOT_FOUND -> NOT_FOUND + ERR_INTEGRITY -> INTEGRITY + ERR_UNSUPPORTED -> UNSUPPORTED + ``` + + * It MUST construct `core_result` with: + + ```text + pel1_version = 1 + status = INVALID_PROGRAM + scheme_ref = scheme_ref + summary.kind = PROGRAM + summary.status_code = 2 // canonical core code for INVALID_PROGRAM + diagnostics = [] or deterministic diagnostics + ``` + + obeying the invariants in `PEL/1-CORE`. + + * It MUST construct `store_failure`: + + ```text + store_failure = { + phase = PROGRAM + error_code = mapped StoreErrorCode + failing_ref = program_ref + } + ``` + + * It MUST construct and persist a `SurfaceExecutionResult` with: + + ```text + pel1_version = 1 + + core_result = as above + scheme_ref = scheme_ref + program_ref = program_ref + input_refs = input_refs + output_refs = [] + + params_ref = params_ref + + store_failure = (as above) + trace_ref = absent + ``` + + * It MUST return `output_refs = []`, `result_ref = put(surface_result_artifact)`. + +### 5.4 Input and parameter resolution + +If program resolution succeeded, the engine MUST resolve `input_refs` in order, and, if `params_ref` is present, resolve it as well. + +For each `input_ref` in `input_refs`: + +```text +get(input_ref) -> + input : Artifact + | ERR_NOT_FOUND + | ERR_INTEGRITY + | ERR_UNSUPPORTED +``` + +If all `input_refs` resolve to Artifacts, and `params_ref` is absent, or present and resolves successfully via: + +```text +get(params_ref) -> params : Artifact | error +``` + +then execution proceeds to scheme execution (§5.5). + +If **any** resolution fails: + +* Let `failing_ref` be the first reference (in the order: all `input_refs` followed by `params_ref`, if present) for which `get` returned an error. +* Map the store error as in §5.3. +* The engine MUST NOT call `Exec_s`. + +It MUST then construct `core_result` with: + +```text +pel1_version = 1 +status = INVALID_INPUTS +scheme_ref = scheme_ref +summary.kind = INPUTS +summary.status_code = 3 // canonical core code for INVALID_INPUTS +diagnostics = [] or deterministic diagnostics +``` + +It MUST construct `store_failure`: + +```text +store_failure = { + phase = INPUT + error_code = mapped StoreErrorCode + failing_ref = failing_ref +} +``` + +And construct `SurfaceExecutionResult`: + +```text +pel1_version = 1 + +core_result = as above +scheme_ref = scheme_ref +program_ref = program_ref +input_refs = input_refs +output_refs = [] // no outputs + +params_ref = params_ref + +store_failure = (as above) +trace_ref = absent +``` + +It MUST persist this artifact and return `output_refs = []`, `result_ref` accordingly. + +### 5.5 Scheme execution via PEL/1-CORE + +If: + +* `scheme_ref` is supported, +* `program_ref` resolves to `program : Artifact`, +* all `input_refs` resolve to `inputs : list`, +* `params_ref`, if present, resolves to `params : Artifact`, + +then the engine MUST invoke the scheme-level PEL/1-CORE function: + +```text +Exec_s( + program : Artifact, + inputs : list, + params : optional Artifact +) -> (outputs : list, core_result : ExecutionResultValue) +``` + +with the following constraints: + +* The `scheme_ref` used for `Exec_s` MUST be the same value passed into `run`. +* The engine MUST ensure that the scheme implementation of `Exec_s` conforms to `PEL/1-CORE`: + + * `core_result.pel1_version = 1`, + * `core_result.scheme_ref = scheme_ref`, + * `core_result.status`, `core_result.summary`, and `core_result.diagnostics` obey the PEL/1-CORE invariants. + +If the scheme-level implementation of `Exec_s` violates PEL/1-CORE invariants (e.g., sets `scheme_ref` incorrectly), that is an implementation bug; `PEL/1-SURF` does not attempt to repair it, but conformance tests SHOULD reject such engines. + +### 5.6 Persisting outputs and surface result + +Given the `outputs` and `core_result` from `Exec_s`: + +1. For each output Artifact in `outputs` (in order): + + ```text + put(output_artifact) -> output_ref + ``` + + Collect the resulting `output_refs : OutputRefList` in the same order. + + Implementations MUST respect `ASL/1-STORE` semantics: repeated `put` of the same Artifact is idempotent. + +2. Optionally, schemes MAY define how a separate **trace artifact** is produced (e.g., as one of the outputs). If the engine or scheme logic identifies a dedicated `trace_ref : TraceRef`: + + * It MAY set the `trace_ref` field in `SurfaceExecutionResult` accordingly. + * It MUST ensure that any `trace_ref` it sets is resolvable in the same StoreInstance (unless policy explicitly allows otherwise). + +3. The engine MUST construct a `SurfaceExecutionResult`: + + ```text + SurfaceExecutionResult { + pel1_version = 1 + + core_result = core_result // as returned by Exec_s + + scheme_ref = scheme_ref + program_ref = program_ref + input_refs = input_refs + output_refs = output_refs + + params_ref = params_ref + + store_failure = absent + trace_ref = trace_ref or absent + } + ``` + +4. It MUST encode this as an `Artifact` with: + + * `type_tag = PEL1_SURFACE_EXECUTION_RESULT_TAG` + * `bytes` = canonical encoding per `ENC/PEL1-RESULT/1` (or equivalent profile). + + Then call: + + ```text + put(surface_result_artifact) -> result_ref + ``` + +5. It MUST return: + + ```text + output_refs = output_refs + result_ref = result_ref + ``` + +### 5.7 Fatal store write failures + +If any `put(...)` call for: + +* outputs, or +* the surface result artifact, + +fails in a way that prevents the store from persisting the Artifact (e.g., I/O failure, permission error, out-of-space): + +* `PEL/1-SURF` treats this as an **environment failure**, outside its semantic model. +* There is no defined `SurfaceExecutionResult` in this case. +* Implementations MAY signal such errors via implementation-specific APIs, logs, or metrics, but they are not represented as valid ExecutionResult artifacts under this spec. + +--- + +## 6. Error and Status Semantics + +### 6.1 Mapping of high-level cases + +`PEL/1-SURF` distinguishes: + +1. **Scheme unsupported** + + * Handled in §5.2. + * No calls to `get` or `Exec_s`. + * `core_result.status = SCHEME_UNSUPPORTED`. + * `store_failure = absent`. + +2. **Program resolution failure** + + * Handled in §5.3. + * `Exec_s` is not invoked. + * `core_result.status = INVALID_PROGRAM`. + * `store_failure.phase = PROGRAM`. + +3. **Input/params resolution failure** + + * Handled in §5.4. + * `Exec_s` is not invoked. + * `core_result.status = INVALID_INPUTS`. + * `store_failure.phase = INPUT`. + +4. **Successful scheme execution** + + * Handled in §5.5–§5.6. + + * `Exec_s` is invoked and `core_result.status` is determined by scheme semantics: + + * `OK` — scheme-level success. + * `INVALID_PROGRAM` — scheme-level program invalidity (e.g., bad DAG, invalid bytecode) after decoding. + * `INVALID_INPUTS` — scheme-level input invalidity (e.g., type mismatch or decode failure). + * `RUNTIME_FAILED` — scheme-level runtime error. + + * `store_failure = absent`. + +### 6.2 Distinguishing store vs scheme errors + +Because `INVALID_PROGRAM` and `INVALID_INPUTS` may arise from either: + +* **Store-level** failures (resolution errors before `Exec_s`), or +* **Scheme-level** failures (Exec_s returning those statuses), + +`PEL/1-SURF` uses `store_failure` to distinguish: + +* If `store_failure` is **present**, the INVALID_* status indicates a **store-resolution** issue. +* If `store_failure` is **absent**, INVALID_* indicates a **scheme-level** validation error. + +This allows higher layers (e.g., TGK/1-CORE or FER/1) to tell whether: + +* they failed to even read the program/inputs, or +* the scheme rejected them as invalid content. + +### 6.3 Consistency requirements + +For any `SurfaceExecutionResult`: + +* `surface.pel1_version` MUST equal `surface.core_result.pel1_version`. +* `surface.core_result.scheme_ref` MUST equal `surface.scheme_ref`. +* `surface.output_refs` length MUST equal the length of `outputs` from `Exec_s` (or zero if `Exec_s` was not called). +* When `store_failure` is present: + + * `surface.core_result.status` MUST be either `INVALID_PROGRAM` or `INVALID_INPUTS` as per §5.3–§5.4. + * `surface.core_result.summary.kind` MUST be `PROGRAM` or `INPUTS` accordingly. + +--- + +## 7. Determinism and Reproducibility + +### 7.1 Determinism with respect to store state + +Fix: + +* A StoreInstance with state `S`, representing a mapping `Reference -> Artifact` (ignoring environment failures). +* A tuple of arguments: + + ```text + (scheme_ref, program_ref, input_refs, params_ref) + ``` + +`PEL/1-SURF` requires: + +* For any two conformant PEL/1-SURF engines using the same store state `S` and arguments above: + + * If no environment failures occur (e.g., all `get`/`put` operations conform to `ASL/1-STORE` and succeed or fail deterministically), then: + + * The set of new Artifacts written to the store by `run(...)` MUST be equal (as `Artifact` values per ASL/1-CORE). + * The returned `output_refs` MUST be identical (`Reference` values, same order). + * The returned `result_ref` MUST be identical (`Reference` value). + * The stored surface result artifacts for the run MUST have equal `SurfaceExecutionResult` payloads. + +### 7.2 Determinism boundaries + +Sources of variation explicitly **outside** the PEL/1-SURF model: + +* Underlying store behavior that violates `ASL/1-STORE` semantics (e.g., non-repeatable `get`/`put`). +* Environment failures (e.g., abrupt program termination, hardware faults). +* Nondeterminism in scheme implementations that violate `PEL/1-CORE` purity/determinism. + +`PEL/1-SURF` assumes: + +* The store conforms to `ASL/1-STORE`. +* The scheme implementations conform to `PEL/1-CORE`. +* Host-level failures are treated as out-of-model. + +--- + +## 8. Interaction with Higher Layers (Informative) + +### 8.1 TGK/1-CORE — Trace Graph Kernel + +TGK/1-CORE can interpret: + +* `program_ref`, `input_refs`, `output_refs`, `result_ref`, and +* the surface ExecutionResult artifact, + +to derive provenance edges such as: + +* “run R (result_ref) executed program P with inputs I* to produce outputs O*”. + +The `trace_ref` (if used) or scheme-specific trace artifacts can be used to add fine-grained per-node or per-step edges (e.g., in a DAG program profile). + +### 8.2 FER/1 and FCT/1 — Evidence and Facts + +FER/1 and FCT/1 can treat: + +* `result_ref` as the entry point to the execution evidence, +* the referenced surface ExecutionResult artifact as core evidence, +* `output_refs` as the produced artifacts to be certified or turned into facts. + +The clear separation between store-level failures (`store_failure`) and scheme-level failures (core status) makes it possible to distinguish “we couldn’t even run this” from “we ran it and it failed for domain-specific reasons.” + +### 8.3 Scheme and profile binding + +Scheme-specific profiles (e.g., DAG programs, WASM schemes) can: + +* Define how `program_ref`, `input_refs`, and `params_ref` are expected to be formed. +* Define structured trace formats and how `trace_ref` is chosen. +* Provide conformance tests for `Exec_s` and for how schemes map to PEL/1-SURF. + +`PEL/1-SURF` itself remains agnostic to specific schemes; it only enforces wiring and error classification. + +--- + +## 9. Conformance + +An implementation is **PEL/1-SURF–conformant** if, for each StoreInstance it uses and for each supported scheme, it: + +1. **Implements `run(...)` as specified** + + * Accepts `(store, scheme_ref, program_ref, input_refs, params_ref)` arguments. + * Follows the phase structure of §5. + * Uses only `ASL/1-STORE` `get` / `put` to interact with the store. + * Does not mutate the store in ways that violate `ASL/1-STORE` semantics (no identity rewrites). + +2. **Respects PEL/1-CORE semantics** + + * Uses a PEL/1-CORE-conformant implementation of `Exec_s` for each supported `scheme_ref`. + * Produces `ExecutionResultValue` conforming to `PEL/1-CORE` requirements. + * Ensures `core_result.scheme_ref = scheme_ref`. + +3. **Handles scheme support correctly** + + * For unsupported schemes, never calls `get` / `Exec_s`, and produces the canonical `SCHEME_UNSUPPORTED` result as in §5.2. + +4. **Handles store-resolution failures correctly** + + * Implements program and input/params resolution as in §5.3–§5.4. + * Maps store errors to `StoreFailure` and `INVALID_PROGRAM` / `INVALID_INPUTS` statuses appropriately. + * Distinguishes store-level errors using `store_failure`. + +5. **Persists outputs and surface results correctly** + + * For successful execution, persists outputs via `put`, collects `output_refs`, constructs a `SurfaceExecutionResult` with: + + * `core_result` exactly as returned by `Exec_s`, + * `scheme_ref`, `program_ref`, `input_refs`, `output_refs`, `params_ref` echoed, + * `store_failure = absent`. + + * Persists the surface result artifact with `type_tag = PEL1_SURFACE_EXECUTION_RESULT_TAG`. + + * Returns `output_refs` and `result_ref` matching the store state. + +6. **Is deterministic with respect to store state** + + * Satisfies the determinism requirements of §7 across all conformant engines, given the same store state and run arguments. + +7. **Maintains layering** + + * Does not depend on TGK/1-CORE, CIL/1, FER/1, FCT/1, or overlay systems on the execution hot path. + * Treats graph, certificates, and policies as **consumers** of the surface result, not as dependencies of `run`. + +Everything else — concrete language bindings, protocol endpoints (HTTP, gRPC), orchestration, logging, metrics, sandboxing, resource limits — is implementation or profile behavior and is outside the scope of this specification, provided the normative behavior above is preserved. + +--- + +*End of `PEL/1-SURF v0.2.1 — Primitive Execution Surface (Store-Backed)`* + +--- + +## Document History + +* **0.2.1 (2025-11-16):** Registered as Tier-1 spec and aligned to the Amduat 2.0 substrate baseline. + +* **0.2.2 (2025-11-30):** Linked surface ExecutionResult artifacts to ENC/PEL1-RESULT/1 profile. diff --git a/tier1/pel-program-dag-1.md b/tier1/pel-program-dag-1.md new file mode 100644 index 0000000..8321564 --- /dev/null +++ b/tier1/pel-program-dag-1.md @@ -0,0 +1,836 @@ +# 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 surface +* `ENC/PEL-PROGRAM-DAG/1` (planned) — Canonical encoding for DAG programs +* `PEL/TRACE-DAG/1` (planned) — Node-level execution trace profile +* `OPREG/PEL1-KERNEL` (planned) — Operation registry for DAG ops +* `SUBSTRATE/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`: + +```text +Artifact { + bytes: OctetString + type_tag: optional TypeTag +} + +TypeTag { + tag_id: uint32 +} + +Reference { + hash_id: HashId + digest: OctetString +} + +HashId = uint16 +```` + +From `PEL/1-CORE` (summary): + +```text +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 +} +``` + +`PEL/1-CORE` defines the following shared enums: + +```text +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_DAG` uses only the following `ExecutionStatus` values: + + ```text + OK + INVALID_PROGRAM + INVALID_INPUTS + RUNTIME_FAILED + ``` + + (`SCHEME_UNSUPPORTED` is never produced by `Exec_DAG`; surfaces like `PEL/1-SURF` handle that before calling the scheme.) + +* It uses only the following `ExecutionErrorKind` values: + + ```text + NONE + PROGRAM + INPUTS + RUNTIME + ``` + +The status ↔ kind mapping for this scheme is: + +```text +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 = 0` +* `status = INVALID_PROGRAM` → `status_code = 2` (canonical core code) +* `status = INVALID_INPUTS` → `status_code = 3` (canonical core code) +* `status = RUNTIME_FAILED` → `status_code` MUST be non-zero and MUST NOT be `2` or `3` + (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 `inputs` list given to `Exec_DAG`), and + * **outputs of other Nodes** (by `NodeId` + `output_index`). + +* Programs designate **root outputs** as the scheme-level outputs. + +This profile provides: + +1. A precise **program value model**: `Program`, `Node`, `DagInput`, `RootRef`. +2. **Structural validity** rules for such programs. +3. A **canonical topological ordering** for deterministic evaluation. +4. A binding of `PEL/1-CORE`’s `Exec_s` to a specific function: + + ```text + Exec_DAG : + (program: Artifact, inputs: list, params: optional Artifact) + -> (outputs: list, 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`: + +```text +OperationId { + name: String // logical text identifier + version: uint32 // monotonic per name +} +``` + +* `String` here is a **logical** UTF-8 string; its concrete encoding into bytes is defined by + `ENC/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 new `version`. + +At this scheme level, `OperationId` is opaque. A registry profile (e.g. `OPREG/PEL1-KERNEL`) defines a function: + +```text +OpSemantics(name, version) : + (inputs: list, params: ParamsValue) + -> Ok(list) | Err(status_code: uint32, diagnostics: list) +``` + +### 2.2 Params + +Each `Node` carries **params** as an opaque parameter blob: + +```text +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 `ParamsValue` and `ParamsBytes`. + +* All conformant implementations of that operation MUST: + + * decode the same `ParamsBytes` into the same `ParamsValue`, or + * deterministically signal a decode failure. + +**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_FAILED` and surfaced via `status_code` and `diagnostics`. + +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 + +```text +NodeId = uint32 +``` + +Constraints: + +* `NodeId` MUST be unique within a single `Program`. +* When ordering Nodes, `NodeId` is compared as an unsigned integer (`0 < 1 < 2 < … < 2^32 − 1`). + +### 2.4 DAG Inputs and Roots + +Logical input references inside the Program: + +```text +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: + +```text +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: + +```text +Node { + id: NodeId + op: OperationId + inputs: list + params: ParamsBytes +} +``` + +Constraints: + +* `id` MUST be unique within the Program. +* `inputs` MAY be empty (nullary operations). +* All `DagInputNode.node_id` MUST refer to some `Node.id` in `Program.nodes`. + +Semantics: + +* When evaluated, the Node: + + * resolves each `DagInput` to an input `Artifact`, + * decodes `params` into a `ParamsValue` according to `op`, + * applies the operation’s pure function to the input Artifacts and decoded params, + * yields zero or more output Artifacts. + +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: + +```text +Program { + nodes: list + roots: list +} +``` + +Constraints (structural validity is detailed in §3): + +* All `Node.id` are unique. +* All `DagInputNode.node_id` and `RootRef.node_id` refer to some Node in `nodes`. +* The dependency graph induced by `DagInputNode` is a DAG (no cycles). + +### 2.7 Program Artifact and TypeTag + +At the ASL/1 layer: + +* A **Program Artifact** is an `Artifact` such that: + + * `type_tag = TYPE_TAG_PEL_PROGRAM_DAG_1` (symbolic constant), and + * `bytes` encode exactly one `Program` value under `ENC/PEL-PROGRAM-DAG/1`. + +`ENC/PEL-PROGRAM-DAG/1` MUST: + +* assign a concrete numeric `tag_id` for `TYPE_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: + +1. **Unique NodeIds** + + * For all `Node A`, `Node B` in `nodes`: + + ```text + A.id == B.id ⇒ A and B are the same Node + ``` + +2. **Node references are in range** + + * For every `DagInputNode{ node_id, ... }` in any `Node.inputs`: + + * There exists a `Node` in `nodes` with `id = node_id`. + +3. **Roots are in range** + + * For every `RootRef{ node_id, ... }` in `roots`: + + * There exists a `Node` in `nodes` with `id = node_id`. + +4. **Acyclic dependency graph** + + * Define a directed graph `G`: + + * vertices: all `NodeId` values in `nodes`, + * edge from `N.id` to `M.id` if `N.inputs` contains `DagInputNode{ node_id = M.id, ... }`. + + * `G` MUST be acyclic (a DAG). + + * Equivalently, there exists at least one topological ordering of Nodes where all dependencies of a Node appear before it. + +5. **OperationId syntax is well-formed** + + * Each `Node.op` is syntactically well-formed as: + + * a valid `name` string (logical UTF-8 as per `ENC/PEL-PROGRAM-DAG/1`), and + * a `uint32` `version`. + + * Whether `(name, version)` is defined in the operation registry is an **execution-time** concern (§5.3). + +6. **Parameter profile is available for each Node** + + * For every `Node` in `nodes`: + + * The active operation/parameter registry (e.g. `OPREG/PEL1-KERNEL` + parameter profiles) **MUST** provide a parameter-decoding function for that Node’s `OperationId`. + +### 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 `Program` value, +* an operation registry for this scheme, and +* a set of parameter profiles, + +validation MUST include parameter decoding for every Node: + +1. For each `Node` in `Program.nodes`: + + * Let `op` be its `OperationId`. + * Let `params_bytes` be its `ParamsBytes`. + * Let `decode_params_op` be the param-decoding function associated with `op`. + +2. The engine MUST conceptually invoke `decode_params_op(params_bytes)` during Program validation. + +3. 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 the `inputs`). + +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 `program` Artifact, either all Node params are decodable (and execution can proceed), or every call to `Exec_DAG` on that `program` yields `status = INVALID_PROGRAM`, regardless of `inputs` and `params`. + +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: + +1. Construct the dependency graph `G` as in §3.4. +2. While unplaced Nodes remain: + + * Let `C` be the set of Nodes whose dependencies are already in the order (i.e., all `DagInputNode` target `NodeId`s are in the placed set). + * From `C`, choose the Node with smallest `NodeId` (numeric ascending). + * Append that Node to the canonical order. + * Mark it as placed, and iterate. + +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: + +```text +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: + +```text +SchemeRef = SchemeRef_DAG_1 +Exec_s = Exec_DAG +``` + +### 5.2 Exec_DAG signature + +For this scheme, `Exec_s` is instantiated as: + +```text +Exec_DAG( + program: Artifact, // Program Artifact + inputs: list, // external input Artifacts + params: optional Artifact // optional global parameters +) -> (outputs: list, result: ExecutionResultValue) +``` + +Interpretation: + +* `program` MUST be an Artifact with `type_tag = TYPE_TAG_PEL_PROGRAM_DAG_1` and decodable as a `Program`. Otherwise, `status = INVALID_PROGRAM`. +* `inputs` is the list of external inputs referenced by `DagInputExternal.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`, `inputs` list, and `params.bytes`, all conformant implementations of `Exec_DAG` MUST produce identical `outputs` and `ExecutionResultValue`. + +### 5.3 High-level semantics of Exec_DAG + +`Exec_DAG` MUST proceed conceptually in the following steps: + +1. **Decode and validate Program** + + * Attempt to decode `program.bytes` as a `Program` under `ENC/PEL-PROGRAM-DAG/1`. + + * If decoding fails: + + * `outputs = []`. + + * `result`: + + ```text + 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`. + +2. **Resolve operation semantics and validate params** + + * For each `Node.op` in `Program.nodes`, resolve `(Node.op.name, Node.op.version)` against the operation registry for this scheme. + + * If any `OperationId` used in the Program is not defined: + + * Treat the Program as invalid: + + * `outputs = []`. + * `result` as `INVALID_PROGRAM` (status_code = 2) with diagnostics indicating unknown operations. + + * Return. + + * For each Node: + + * Decode its `ParamsBytes` using the parameter profile for its `OperationId`. + + * If decoding fails: + + * Treat as `INVALID_PROGRAM`: + + * `outputs = []`. + * `result` as `INVALID_PROGRAM` (status_code = 2) with appropriate diagnostics. + + * Return. + + * Otherwise, conceptually store the decoded `ParamsValue` for use in evaluation. + +3. **Evaluate Nodes in canonical order** + + * Compute the canonical topological order (§4) of Nodes. + + * Maintain: + + ```text + node_outputs : NodeId -> list + ``` + + * For each Node `N` in canonical order: + + 1. **Resolve Node inputs** + + * For each `DagInput` in `N.inputs`: + + * If `DagInputExternal{ input_index }`: + + * If `input_index >= len(inputs)`: + + * This is an **input error**: + + * `outputs = []`. + * `result`: + + ```text + 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 = []`. + * `result` as `INVALID_PROGRAM` (status_code = 2) with suitable diagnostics. + + * Return. + + * Collect the resolved input Artifacts into `node_input_artifacts`. + + 2. **Apply operation semantics** + + * Let `params_value` be the decoded `ParamsValue` for Node `N`. + + * Invoke the operation semantics: + + ```text + Op(N.op)( + node_input_artifacts: list, + params_value: ParamsValue + ) -> Ok(list) | Err(status_code: uint32, diagnostics: list) + ``` + + * 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`. + + * If `Err(status_code, diag)`: + + * This is a **runtime failure**; `Exec_DAG` MUST: + + * Set `outputs = []` (this scheme is **fail-fast**; it does not define partial outputs). + * Set `result`: + + ```text + 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. + +4. **Compute scheme outputs (success case)** + + * If all Nodes have been evaluated successfully (no early return): + + * Initialise `outputs = []`. + + * For each `RootRef` in `Program.roots` (in order): + + * Let `outs = node_outputs[root.node_id]`. + + * If `root.output_index >= len(outs)`: + + * Treat as `INVALID_PROGRAM`: + + * `outputs = []`. + * `result` as in step 1 with suitable diagnostics. + + * Return. + + * Otherwise, append `outs[root.output_index]` to `outputs`. + + * When all roots resolve successfully: + + * `outputs` is the list of root Artifacts in `Program.roots` order. + * `result`: + + ```text + pel1_version = 1 + status = OK + scheme_ref = SchemeRef_DAG_1 + summary.kind = NONE + summary.status_code = 0 + diagnostics = [] + ``` + +5. **Return** + + * Return `(outputs, result)`. + +### 5.4 Determinism guarantees + +For fixed: + +* `program.bytes`, +* `inputs` list (sequence and values), +* `params` presence and `params.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` (as `Artifact` values), and +* identical `ExecutionResultValue` payloads. + +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_ref` and a StoreInstance. +* It resolves those references to `program`, `inputs`, `params` via `ASL/1-STORE` `get`. +* It invokes `Exec_DAG(program, inputs, params)` as defined here. +* It persists: + + * each element of `outputs` via `put`, producing `output_refs`, and + * a surface ExecutionResult artifact containing `ExecutionResultValue` plus context. + +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 `TraceValue` Artifact with its own `TypeTag`. +* Specify that `Exec_DAG` (or the surface) produces a trace Artifact as one of the outputs, and that `PEL/1-SURF` exposes its `Reference` as `trace_ref` in 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: + +1. **Program handling** + + * Correctly decodes and encodes `Program` values according to this spec and `ENC/PEL-PROGRAM-DAG/1`. + * Checks structural validity as in §3, including parameter decodability. + * Treats structurally invalid or undecodable Programs as `INVALID_PROGRAM` in `Exec_DAG`. + +2. **Scheme binding** + + * Recognizes `SchemeRef_DAG_1` as the scheme identifier. + * Implements `Exec_DAG` exactly as in §5 for this scheme. + * Ensures that `result.scheme_ref = SchemeRef_DAG_1` and `result.pel1_version = 1`. + +3. **Operation registry integration** + + * Uses a registry for `(OperationId -> OpSemantics)` where: + + * each `OpSemantics` is pure and deterministic, + * param encodings are canonical and deterministic, + * runtime errors are mapped to non-zero `status_code`s and deterministic diagnostics. + + * Treats unknown `OperationId` values in a Program as `INVALID_PROGRAM`. + +4. **Status mapping** + + * Produces `ExecutionResultValue.status` only from: + + * `OK`, + * `INVALID_PROGRAM`, + * `INVALID_INPUTS`, + * `RUNTIME_FAILED`. + + * Ensures `summary.kind` and `summary.status_code` follow the mapping in §0 and §5.3. + +5. **Determinism and purity** + + * Satisfies PEL/1-CORE’s determinism contract: + + * For fixed inputs, all conformant implementations produce identical `outputs` and `ExecutionResultValue`. + + * Does not consult time, randomness, external I/O, or mutable global state as part of `Exec_DAG` semantics. + +6. **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. diff --git a/tier1/pel-program-dag-desc-1.md b/tier1/pel-program-dag-desc-1.md new file mode 100644 index 0000000..1792b53 --- /dev/null +++ b/tier1/pel-program-dag-desc-1.md @@ -0,0 +1,781 @@ +# PEL/PROGRAM-DAG-DESC/1 — Scheme Descriptor for DAG Program Scheme + +Status: Approved +Owner: Niklas Rydberg +Version: 0.1.6 +SoT: Yes +Last Updated: 2025-11-16 +Linked Phase Pack: N/A +Tags: [composition, registry] + + + +**Document ID:** `PEL/PROGRAM-DAG-DESC/1` +**Layer:** L1 Scheme Descriptor (binds PEL scheme to ASL values) + +**Depends on (normative):** + +* `ASL/1-CORE v0.4.x` — Artifact substrate (`Artifact`, `TypeTag`, `Reference`, `EncodingProfileId`) +* `ENC/ASL1-CORE v1.0.x` — canonical encoding profile (`ASL_ENC_CORE_V1`) +* `PEL/1-CORE v0.3.x` — primitive execution layer (schemes and `Exec_s`) +* `PEL/PROGRAM-DAG/1 v0.3.x` — DAG program scheme (`Program`, `Node`, `Exec_DAG`, symbolic `SchemeRef_DAG_1`) +* `ENC/PEL-PROGRAM-DAG/1 v0.2.x` — canonical encoding for DAG programs (Program Artifacts, profile ID `PEL_ENC_PROGRAM_DAG_V1`) + +**References (informative):** + +* `HASH/ASL1 v0.2.x` — ASL1 hash family (`HashId` assignments, `HASH-ASL1-256`) +* `SUBSTRATE/STACK-OVERVIEW v0.3.x` — stack layering and kernel invariants +* `ENC/PEL-TRACE-DAG/1 v0.1.x` — canonical encoding for DAG traces (Trace Artifacts) +* `OPREG/PEL1-KERNEL` and related param profiles — operation registry for DAG ops (context only) + +© 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. Overview + +`PEL/PROGRAM-DAG/1` defines the **DAG program scheme** for PEL/1-CORE and refers to its scheme identifier symbolically as `SchemeRef_DAG_1`. + +`PEL/PROGRAM-DAG-DESC/1` defines: + +* the **logical descriptor value** for the DAG scheme, +* its realization as a concrete **ASL/1 Artifact** (the *descriptor Artifact*), and +* how engines derive and recognize `SchemeRef_DAG_1` as the scheme’s `SchemeRef`. + +In particular, this document: + +* binds the DAG scheme semantics (`Exec_DAG`) to a specific `SchemeRef` value; +* specifies the descriptor’s **TypeTag** and internal structure; +* specifies how `SchemeRef_DAG_1` is derived from the descriptor Artifact under `ASL_ENC_CORE_V1` and a chosen hash algorithm; and +* fixes a **canonical** `SchemeRef_DAG_1` for the Amduat 2.0 baseline profile using `HASH-ASL1-256` (`HashId = 0x0001`), + +without introducing dependencies on stores, TGK, certificates, facts, or overlays. + +--- + +## 1. Scope & Layering + +### 1.1 Purpose + +This specification defines: + +* The **logical structure** of the DAG scheme descriptor value (`DagSchemeDescriptor`). + +* The **descriptor Artifact** that carries this value: + + * its `type_tag` (a “PEL scheme descriptor” `TypeTag`), and + * its canonical `bytes` layout. + +* The **derivation** of `SchemeRef_DAG_1` as a `Reference` to that descriptor Artifact. + +* The **binding** between `SchemeRef_DAG_1` and the DAG scheme defined in `PEL/PROGRAM-DAG/1`. + +It does **not** redefine: + +* DAG program semantics or structure (`Program`, `Node`, `Exec_DAG`), +* Program or trace encodings (`ENC/PEL-PROGRAM-DAG/1`, `ENC/PEL-TRACE-DAG/1`), +* hash algorithms or `HashId` (`HASH/ASL1`), +* store, graph, certification, or provenance behavior. + +### 1.2 Layering constraints + +In line with `SUBSTRATE/STACK-OVERVIEW`: + +* `PEL/PROGRAM-DAG-DESC/1` is a **scheme descriptor**: + + * it binds a PEL scheme (`PEL/PROGRAM-DAG/1`) to a specific ASL/1 `Reference`; and + * it describes how engines recognize and interpret that scheme. + +* It MUST NOT introduce dependencies on: + + * stores (`ASL/1-STORE`), + * graph kernels or graph stores (`TGK/1-CORE`, `TGK/STORE/1`), + * certification, evidence, facts, or overlays (`CIL/1`, `FER/1`, `FCT/1`, `OI/1`). + +* It MAY refer informatively to: + + * canonical trace encodings (`ENC/PEL-TRACE-DAG/1`), + * operation registries and parameter profiles (`OPREG/PEL1-KERNEL`, etc.), + +but MUST NOT depend on them for its core semantics. + +--- + +## 2. Dependencies, Terminology & Conventions + +RFC 2119 terms (**MUST**, **SHOULD**, **MAY**, etc.) are normative. + +### 2.1 From ASL/1-CORE + +```text +Artifact { + bytes: OctetString + type_tag: optional TypeTag +} + +TypeTag { + tag_id: uint32 +} + +Reference { + hash_id: HashId + digest: OctetString +} + +HashId = uint16 +EncodingProfileId = uint16 +``` + +This spec treats: + +* `Artifact` as the unit of identity and content, +* `TypeTag` as the typing hint for `Artifact.bytes`, +* `Reference` as the content address for Artifacts, and +* `EncodingProfileId` as the identifier of encoding profiles (e.g. `ASL_ENC_CORE_V1`, `PEL_ENC_PROGRAM_DAG_V1`). + +### 2.2 From ENC/ASL1-CORE v1 + +`ENC/ASL1-CORE v1` defines: + +* `ArtifactBytes = encode_artifact_core_v1(Artifact)` — canonical encoding for Artifacts. +* `ReferenceBytes = encode_reference_core_v1(Reference)` — canonical encoding for References. + +It also instantiates the generic ASL rule: + +```text +ArtifactBytes = encode_P(A) +digest = H(ArtifactBytes) +Reference = { hash_id = HID, digest = digest } +``` + +for `P = ASL_ENC_CORE_V1` and any hash algorithm `H` with `HashId = HID`. + +### 2.3 From PEL/1-CORE and PEL/PROGRAM-DAG/1 + +From `PEL/1-CORE`: + +```text +SchemeRef = Reference + +Exec_s : + (program: Artifact, inputs: list, params: optional Artifact) + -> (outputs: list, result: ExecutionResultValue) +``` + +From `PEL/PROGRAM-DAG/1`: + +* The DAG scheme is a concrete scheme over PEL/1-CORE: + + ```text + Exec_DAG : + (program: Artifact, inputs: list, params: optional Artifact) + -> (outputs: list, result: ExecutionResultValue) + ``` + +* This scheme is identified symbolically as `SchemeRef_DAG_1`, with: + + ```text + SchemeRef = SchemeRef_DAG_1 + Exec_s = Exec_DAG + ``` + +`PEL/PROGRAM-DAG-DESC/1` makes this symbolic `SchemeRef_DAG_1` concrete. + +### 2.4 Conventions + +Unless stated otherwise: + +* Integers: + + * `u8` — 1 byte, unsigned. + * `u16` — 2 bytes, unsigned, big-endian. + * `u32` — 4 bytes, unsigned, big-endian. + +* `Utf8String` is encoded as: + + ```text + Utf8String = + length (u32) + bytes[0..length-1] + ``` + + where: + + * `length` is the number of bytes, + * `bytes` MUST be well-formed UTF-8, + * there is no terminator or padding. + +* Embedded `Reference` values use a length-prefixed wrapper over canonical `ReferenceBytes` from `ENC/ASL1-CORE v1`: + + ```text + EncodedRef = + ref_len (u32) + ref_bytes (byte[0..ref_len-1]) // canonical ReferenceBytes + ``` + + where: + + * `ref_bytes` MUST be the canonical `ReferenceBytes` for some `Reference` value under `ENC/ASL1-CORE v1`, + * `ref_len` MUST be the exact length of `ref_bytes` (MUST be ≥ 2). + + Decoders MUST: + + * treat `ref_len < 2` as an encoding error, and + * decode `ref_bytes` using `decode_reference_core_v1`. Any failure in that decoding (including, when `HASH/ASL1` is implemented, a digest-length mismatch for a known `hash_id`) MUST be treated as an encoding error for `DagSchemeDescriptorBytes`. + +* Optional `Reference` fields use: + + ```text + OptionalEncodedRef = + has_ref (u8) + [ EncodedRef ] // only if has_ref == 0x01 + ``` + + with: + + * `has_ref = 0x00` → no value present (no `EncodedRef` follows), + * `has_ref = 0x01` → exactly one `EncodedRef` follows, + * other `has_ref` values are encoding errors. + +--- + +## 3. Descriptor Logical Model + +### 3.1 DagSchemeDescriptor (logical) + +At the logical level, the DAG scheme descriptor is modeled as: + +```text +DagSchemeDescriptor { + pel1_version: uint16 + + scheme_name: String // logical identifier, e.g. "PEL/PROGRAM-DAG/1" + + program_type_tag: TypeTag // TYPE_TAG_PEL_PROGRAM_DAG_1 + program_enc_profile: EncodingProfileId + // PEL_ENC_PROGRAM_DAG_V1 (0x0101) + + trace_profile_ref: optional Reference + // to a trace-profile descriptor (e.g. PEL/TRACE-DAG-DESC/1) + opreg_ref: optional Reference + // to a canonical operation-registry descriptor (e.g. OPREG/PEL1-KERNEL) +} +``` + +This structure is **logical**; the descriptor’s concrete bytes are defined in §4. + +The `program_type_tag` / `program_enc_profile` pair tells engines **how to interpret Program Artifacts** for this scheme: + +* `program_type_tag` ⇒ which `TypeTag` marks an Artifact as a DAG Program for this scheme; +* `program_enc_profile` ⇒ which Program encoding profile (specifically, `PEL_ENC_PROGRAM_DAG_V1` from `ENC/PEL-PROGRAM-DAG/1`) to use when decoding/encoding those Artifacts. + +This pair is independent from the **ASL-level encoding profile** used for the descriptor Artifact itself, which is always `ASL_ENC_CORE_V1` when deriving `SchemeRef_DAG_1` (§5.2). + +This document does not require engines to inspect all fields at runtime. In particular: + +* engines that already know `SchemeRef_DAG_1`, `TYPE_TAG_PEL_PROGRAM_DAG_1`, and `PEL_ENC_PROGRAM_DAG_V1` MAY treat the descriptor as metadata; and +* the canonical binding to `PEL/PROGRAM-DAG/1` is via `SchemeRef_DAG_1` and the `program_type_tag` / `program_enc_profile` pair, not via `scheme_name` or the optional references. + +### 3.2 Field semantics & invariants + +For the **Amduat 2.0 baseline DAG scheme descriptor** (the one used to define `SchemeRef_DAG_1`) the following invariants apply: + +* `pel1_version`: + + * MUST be `1`. + * Indicates the descriptor is written against `PEL/1-CORE` version 1 semantics. + * Future PEL versions MAY introduce new descriptor formats, but MUST NOT change the meaning of `pel1_version = 1`. + +* `scheme_name`: + + * MUST be the UTF-8 string `"PEL/PROGRAM-DAG/1"`, compared byte-for-byte and case-sensitively. + * Serves as a human-readable identifier and sanity check. + * Changing `scheme_name` (while keeping other fields identical) changes the descriptor value and therefore its `Reference`. + +* `program_type_tag`: + + * MUST equal the `TypeTag` used for DAG Program Artifacts, symbolically: + + ```text + program_type_tag = TYPE_TAG_PEL_PROGRAM_DAG_1 + ``` + + * The concrete numeric `tag_id` is assigned by the TypeTag registry and is not restated here. + +* `program_enc_profile`: + + * MUST be the `EncodingProfileId` of `ENC/PEL-PROGRAM-DAG/1`, symbolically: + + ```text + program_enc_profile = PEL_ENC_PROGRAM_DAG_V1 + ``` + + * In the current encoding profile, this is `0x0101`, but this spec treats it symbolically; the numeric value comes from the encoding profile registry. + +* `trace_profile_ref`: + + * Optionally points at a descriptor Artifact that defines a canonical trace profile for this scheme (e.g. a future `PEL/TRACE-DAG-DESC/1`). + + * For the **canonical Amduat 2.0 baseline descriptor used to define `SchemeRef_DAG_1`**, this field is **absent**: + + ```text + trace_profile_ref = None + ``` + + * Future companion descriptors MAY set this field, but those will not be the descriptor used for `SchemeRef_DAG_1`. + +* `opreg_ref`: + + * Optionally points at a descriptor Artifact for the canonical operation registry for this scheme (e.g. `OPREG/PEL1-KERNEL`). + + * For the **canonical Amduat 2.0 baseline descriptor used to define `SchemeRef_DAG_1`**, this field is **absent**: + + ```text + opreg_ref = None + ``` + + * As with `trace_profile_ref`, future descriptors MAY set this field, but those will define distinct `SchemeRef` values, not `SchemeRef_DAG_1`. + +### 3.3 Canonical descriptor value for the DAG scheme + +Let: + +```text +DagSchemeDescriptor_DAG_1 : DagSchemeDescriptor +``` + +denote the unique logical descriptor value that satisfies: + +```text +DagSchemeDescriptor_DAG_1 { + pel1_version = 1 + + scheme_name = "PEL/PROGRAM-DAG/1" + + program_type_tag = TYPE_TAG_PEL_PROGRAM_DAG_1 + program_enc_profile = PEL_ENC_PROGRAM_DAG_V1 + + trace_profile_ref = None + opreg_ref = None +} +``` + +All Amduat 2.0 baseline components MUST agree on this logical value as “the” DAG scheme descriptor for binding `PEL/PROGRAM-DAG/1` to a `SchemeRef`. + +Any descriptor with different logical contents (including differing optional fields) describes either: + +* a different scheme, or +* a different profile or version of the DAG scheme, + +and MUST NOT be used to derive `SchemeRef_DAG_1`. + +Because the descriptor is carried in an ASL/1 Artifact and scheme references are content-addressed, any change to this descriptor (even in optional fields) yields a different `Reference`; `SchemeRef_DAG_1` is therefore permanently tied to exactly this v1 descriptor. + +--- + +## 4. Descriptor Artifact & Encoding + +### 4.1 Descriptor TypeTag + +The DAG scheme descriptor is carried by an ASL/1 Artifact with a dedicated TypeTag: + +```text +TYPE_TAG_PEL_SCHEME_DESC_1 : TypeTag +``` + +* `TYPE_TAG_PEL_SCHEME_DESC_1` is a symbolic constant for a `TypeTag.tag_id` assigned by the global `TypeTag` registry. +* This `TypeTag` MUST be used for the DAG scheme descriptor Artifact defined in this document. +* Other PEL scheme descriptors MAY reuse this `TypeTag` and the encoding in §4.2, but such reuse MUST be specified in their own descriptor documents. + +The descriptor Artifact for the DAG scheme has the shape: + +```text +DagSchemeDescriptorArtifact_DAG_1 : Artifact { + type_tag = TYPE_TAG_PEL_SCHEME_DESC_1 + bytes = DagSchemeDescriptorBytes_DAG_1 +} +``` + +where `DagSchemeDescriptorBytes_DAG_1` is the canonical encoding of `DagSchemeDescriptor_DAG_1` defined in §4.2. + +This Artifact itself is encoded to `ArtifactBytes` using `ASL_ENC_CORE_V1` when deriving `SchemeRef_DAG_1` (§5.2). That use of `ASL_ENC_CORE_V1` is **independent** of the `program_enc_profile` field, which is about how Program Artifacts are encoded. + +### 4.2 Descriptor encoding layout + +The descriptor value `DagSchemeDescriptor` is encoded into `DagSchemeDescriptorBytes` using a small, fixed layout over primitive types: + +```text +DagSchemeDescriptorBytes :: + pel1_version (u16) + + scheme_name (Utf8String) + + program_type_tag (u32) // TypeTag.tag_id + program_enc_profile (u16) // EncodingProfileId + + has_trace_profile (u8) + [ trace_profile_ref (EncodedRef) ] // if has_trace_profile == 0x01 + + has_opreg_ref (u8) + [ opreg_ref (EncodedRef) ] // if has_opreg_ref == 0x01 +``` + +Constraints: + +* `pel1_version` MUST be `1` for descriptors governed by this document. + +* `scheme_name` MUST be encoded as a `Utf8String` (§2.4). + +* `program_type_tag` encodes `TypeTag.tag_id` for `TYPE_TAG_PEL_PROGRAM_DAG_1`. + +* `program_enc_profile` encodes the `EncodingProfileId` for `PEL_ENC_PROGRAM_DAG_V1` as defined in `ENC/PEL-PROGRAM-DAG/1`. + +* `has_trace_profile`: + + * `0x00` → `trace_profile_ref` is absent; no bytes for it follow. + * `0x01` → `trace_profile_ref` is present and encoded as `EncodedRef`. + * Any other value is an encoding error. + +* `has_opreg_ref`: + + * `0x00` → `opreg_ref` is absent. + * `0x01` → `opreg_ref` is present and encoded as `EncodedRef`. + * Any other value is an encoding error. + +For the **canonical baseline descriptor**: + +```text +pel1_version = 1 +scheme_name = "PEL/PROGRAM-DAG/1" +program_type_tag = TYPE_TAG_PEL_PROGRAM_DAG_1.tag_id +program_enc_profile = PEL_ENC_PROGRAM_DAG_V1 +has_trace_profile = 0x00 +has_opreg_ref = 0x00 +``` + +The resulting bytes are `DagSchemeDescriptorBytes_DAG_1`. + +### 4.3 Encoding and decoding (normative) + +Let: + +```text +encode_dag_scheme_descriptor_v1 : DagSchemeDescriptor -> OctetString +decode_dag_scheme_descriptor_v1 : OctetString -> DagSchemeDescriptor | error +``` + +be the encoding and decoding functions for this profile. + +**Encoding:** + +Given a `DagSchemeDescriptor` value: + +1. Emit `pel1_version (u16)`. + +2. Emit `scheme_name` as `Utf8String`. + +3. Emit `program_type_tag.tag_id` as `u32`. + +4. Emit `program_enc_profile` as `u16`. + +5. For `trace_profile_ref`: + + * If absent: + + * emit `has_trace_profile = 0x00`. + + * If present: + + * emit `has_trace_profile = 0x01`, + * emit `trace_profile_ref` as `EncodedRef` (u32 length + `ReferenceBytes`). + +6. For `opreg_ref`: + + * If absent: + + * emit `has_opreg_ref = 0x00`. + + * If present: + + * emit `has_opreg_ref = 0x01`, + * emit `opreg_ref` as `EncodedRef`. + +The concatenation of all emitted bytes is `DagSchemeDescriptorBytes`. + +**Decoding:** + +Given an `OctetString` known to contain exactly one `DagSchemeDescriptorBytes` value: + +1. Read `pel1_version (u16)`. + + * If `pel1_version != 1`, decoders that only implement this version MUST treat this as an encoding error. Future decoders MAY support additional versions but MUST NOT reinterpret `pel1_version = 1`. + +2. Read `scheme_name` as `Utf8String` and validate UTF-8. + +3. Read `program_type_tag (u32)` and construct `TypeTag { tag_id = program_type_tag }`. + +4. Read `program_enc_profile (u16)`. + +5. Read `has_trace_profile (u8)`: + + * `0x00` → `trace_profile_ref = None`. + * `0x01` → read `trace_profile_ref` as `EncodedRef` and decode to `Reference`. + * other values → encoding error. + +6. Read `has_opreg_ref (u8)`: + + * `0x00` → `opreg_ref = None`. + * `0x01` → read `opreg_ref` as `EncodedRef` and decode to `Reference`. + * other values → encoding error. + +7. If there are trailing bytes after successfully decoding all fields, decoders MAY treat this as an encoding error in contexts expecting a single `DagSchemeDescriptorBytes` value. + +The mapping between `DagSchemeDescriptor` and `DagSchemeDescriptorBytes` defined here is the only encoding/decoding pair permitted by this profile. + +### 4.4 Canonicality and injectivity + +The mapping: + +```text +DagSchemeDescriptor -> DagSchemeDescriptorBytes +``` + +defined above MUST be: + +* **Injective** — distinct logical descriptor values MUST produce distinct byte strings. +* **Stable** — the same logical descriptor MUST always encode to the same bytes across implementations and time. +* **Streaming-friendly** — encoders and decoders MUST be implementable in a single forward pass; all length prefixes appear before their content. + +In particular: + +* Field ordering MUST NOT vary. +* No alternative encodings (e.g. different integer widths, string formats, or positions) are permitted. +* `EncodedRef` MUST always use canonical `ReferenceBytes` from `ENC/ASL1-CORE v1` (and therefore, when `HASH/ASL1` is present, inherit its digest-length consistency checks). + +For the canonical DAG scheme, `DagSchemeDescriptor_DAG_1` encodes to a single, immutable `DagSchemeDescriptorBytes_DAG_1`. That byte sequence MUST NOT change once published. + +### 4.5 Error handling (encoding layer) + +Decoders for this profile MUST treat as **encoding errors** at the descriptor level: + +1. **Unsupported `pel1_version`** + + * `pel1_version != 1`. + +2. **Invalid presence flags** + + * `has_trace_profile` or `has_opreg_ref` not in `{ 0x00, 0x01 }`. + +3. **Invalid `EncodedRef`** + + * `ref_len < 2`, or + * insufficient bytes to read `ref_bytes`, or + * `ref_bytes` cannot be decoded as `ReferenceBytes` under `ENC/ASL1-CORE v1` (including digest-length mismatches for known `hash_id`s when `HASH/ASL1` is implemented). + +4. **Invalid `Utf8String` for `scheme_name`** + + * `scheme_name` bytes are not valid UTF-8. + +5. **Truncation and over-long payloads** + + * Not enough bytes to read any declared field. + * In contexts expecting a single descriptor value, additional trailing bytes after decoding all fields. + +Mapping from these encoding errors to external error codes (e.g. `ERR_PEL_SCHEME_DESC_INVALID`) is implementation-specific. + +--- + +## 5. Definition of `SchemeRef_DAG_1` + +### 5.1 General derivation from the descriptor Artifact + +Let `DagSchemeDescriptorArtifact` be any Artifact of the form: + +```text +Artifact { + type_tag = TYPE_TAG_PEL_SCHEME_DESC_1 + bytes = DagSchemeDescriptorBytes +} +``` + +such that `DagSchemeDescriptorBytes` decodes to some `DagSchemeDescriptor` value via `decode_dag_scheme_descriptor_v1`. + +Given: + +* encoding profile `P = ASL_ENC_CORE_V1`, +* hash algorithm `H` with `HashId = HID`, + +the scheme reference for this descriptor under `(P, H)` is: + +```text +ArtifactBytes = encode_artifact_core_v1(DagSchemeDescriptorArtifact) +digest = H(ArtifactBytes) +SchemeRef = Reference { hash_id = HID, digest = digest } +``` + +This is `ASL/CORE-REF-DERIVE/1` instantiated with `ASL_ENC_CORE_V1`. + +### 5.2 Canonical `SchemeRef_DAG_1` for Amduat 2.0 + +For the Amduat 2.0 baseline DAG scheme, we choose: + +* `DagSchemeDescriptorArtifact = DagSchemeDescriptorArtifact_DAG_1`, +* `P = ASL_ENC_CORE_V1` (EncodingProfileId = 0x0001), +* `H = HASH-ASL1-256` (the mandatory ASL1-256 algorithm), +* `HashId(H) = 0x0001`. + +Then: + +```text +ArtifactBytes_DAG_1 = encode_artifact_core_v1(DagSchemeDescriptorArtifact_DAG_1) +digest_DAG_1 = HASH-ASL1-256(ArtifactBytes_DAG_1) +SchemeRef_DAG_1 = Reference { hash_id = 0x0001, digest = digest_DAG_1 } +``` + +In the Amduat 2.0 baseline: + +* `PEL/PROGRAM-DAG/1` binds its scheme symbol: + + ```text + SchemeRef = SchemeRef_DAG_1 + Exec_s = Exec_DAG + ``` + + to this `Reference`. + +* Engines that implement the Amduat 2.0 baseline DAG scheme MUST treat this specific `Reference` as “the” scheme identifier for `PEL/PROGRAM-DAG/1`. + +### 5.3 Alternative deployments (non-baseline) + +Other deployments MAY derive alternative scheme references from: + +* the same descriptor Artifact but a different `(EncodingProfileId, HashId)` pair, or +* a different descriptor Artifact (e.g. one with additional optional references or different metadata). + +Such configurations: + +* are not the Amduat 2.0 baseline, and +* MUST NOT reuse the name `SchemeRef_DAG_1` for any scheme reference other than the one defined in §5.2. + +Profiles MAY define their own symbolic names (e.g. `SchemeRef_DAG_1_PQ`) for alternate `(EncodingProfileId, HashId)` combinations, but those are outside the scope of this document. + +--- + +## 6. Layering & Usage + +### 6.1 Engine behavior + +A PEL/1-CORE engine that claims support for the DAG scheme in the Amduat 2.0 baseline: + +* MUST recognize `SchemeRef_DAG_1` (as defined in §5.2) as the scheme identifier corresponding to `PEL/PROGRAM-DAG/1`. + +* MUST implement: + + ```text + Exec_s = Exec_DAG + ``` + + when invoked with `scheme_ref = SchemeRef_DAG_1`. + +* MUST treat Program Artifacts for this scheme as ASL/1 Artifacts whose `type_tag` equals the `program_type_tag` from the descriptor (i.e. `TYPE_TAG_PEL_PROGRAM_DAG_1`). The `program_enc_profile` field gives the encoding profile ID that engines use to select the appropriate Program encoding/decoding logic; in the Amduat 2.0 baseline this ID is `PEL_ENC_PROGRAM_DAG_V1` as defined in `ENC/PEL-PROGRAM-DAG/1`. + +* MUST treat the descriptor Artifact as immutable metadata. Engines MAY bake in `SchemeRef_DAG_1` and the associated `TypeTag` and profile IDs at build time; they are not required to fetch or decode the descriptor on the execution hot path. + +Nothing in this document requires engines to use `trace_profile_ref` or `opreg_ref` at runtime. Those fields exist so that tooling and profiles can discover related descriptors in a stable, content-addressed way. + +### 6.2 Configuration and discovery + +Tooling and configuration systems MAY use the descriptor to: + +* discover: + + * which `TypeTag` is used for DAG Programs, + * which encoding profile ID applies to those Programs, + * optional references to trace and operation-registry descriptors; + +* populate engine configuration tables, e.g.: + + ```text + scheme_table[SchemeRef_DAG_1] = { + program_type_tag = TYPE_TAG_PEL_PROGRAM_DAG_1, + program_enc_profile = PEL_ENC_PROGRAM_DAG_V1, + trace_profile_ref = None, + opreg_ref = None, + } + ``` + +This document: + +* MUST NOT introduce any store-level behavior, TGK edge semantics, certification rules, fact semantics, or overlay semantics. +* ONLY binds PEL-level scheme semantics (`Exec_DAG`) to ASL-level values (`Artifact`, `TypeTag`, `Reference`). + +--- + +## 7. Conformance + +An implementation is **PEL/PROGRAM-DAG-DESC/1–conformant** if it satisfies all of: + +1. **Descriptor encoding & decoding** + + * Implements `encode_dag_scheme_descriptor_v1` / `decode_dag_scheme_descriptor_v1` as in §4.2–§4.3. + * Treats `pel1_version = 1` as the only supported version for this descriptor profile. + * Enforces presence flags and `EncodedRef` structure as defined in §2.4 and §4.5. + +2. **Recognition of the descriptor Artifact** + + * Recognizes `TYPE_TAG_PEL_SCHEME_DESC_1` as the PEL scheme descriptor tag for this document. + * For `type_tag = TYPE_TAG_PEL_SCHEME_DESC_1`, is able to decode `bytes` as `DagSchemeDescriptorBytes` and obtain a `DagSchemeDescriptor`. + * Recognizes `DagSchemeDescriptor_DAG_1` (per §3.3) as the canonical descriptor value for the DAG scheme in Amduat 2.0 (e.g. by comparing decoded fields). + +3. **Derivation of `SchemeRef_DAG_1`** + + * When configured with `ASL_ENC_CORE_V1` and `HASH-ASL1-256` (`HashId = 0x0001`), derives `SchemeRef_DAG_1` exactly as in §5.2 from `DagSchemeDescriptorArtifact_DAG_1`. + * Does not derive `SchemeRef_DAG_1` from any other Artifact or under any other `(EncodingProfileId, HashId)` pair. + +4. **Scheme binding** + + * For `SchemeRef = SchemeRef_DAG_1`, implements `Exec_s` as `Exec_DAG` per `PEL/PROGRAM-DAG/1`. + * Uses `program_type_tag` and `program_enc_profile` from the descriptor when interpreting Program Artifacts for this scheme (at build time or configuration time; not necessarily on every execution). + +5. **Layering discipline** + + * Does not introduce store, TGK, certificate, fact, or overlay semantics into the descriptor logic. + * Treats this document purely as a binding between: + + * the PEL scheme (`PEL/PROGRAM-DAG/1`), + * its program representation (`TYPE_TAG_PEL_PROGRAM_DAG_1` + `PEL_ENC_PROGRAM_DAG_V1`), + * and an ASL/1 `SchemeRef` (`SchemeRef_DAG_1`). + +Everything else — store wiring, TGK edges, receipts, facts, overlays, and domain semantics — is delegated to other specifications. + +--- + +## 8. Version History + +* **0.1.6 — 2025-11-16** — Marked `ENC/PEL-PROGRAM-DAG/1` as a normative dependency and clarified that `program_enc_profile` is specifically `PEL_ENC_PROGRAM_DAG_V1`. +* **0.1.5 — 2025-11-16** — Clarified the role of `program_enc_profile` vs `ASL_ENC_CORE_V1` and tightened wording around how Program Artifacts are interpreted. +* **0.1.4 — 2025-11-16** — Tightened decoding wording, clarified the uniqueness of the encoding/decoding pair, and polished streaming/injectivity language. +* **0.1.3 — 2025-11-16** — Clarified RFC 2119 usage, layering around program encoding, and tightened conformance wording. +* **0.1.2 — 2025-11-16** — Refined encoding error rules, clarified `TYPE_TAG_PEL_SCHEME_DESC_1` reuse, and described engine usage more explicitly. +* **0.1.1 — 2025-11-16** — Added logical descriptor model, descriptor Artifact & encoding, canonical descriptor value, and explicit `SchemeRef_DAG_1` derivation. +* **0.1.0 — 2025-11-16** — Initial skeleton with structure, roles, and high-level field model for the DAG scheme descriptor. + +--- + +## Document History + +* **0.1.6 (2025-11-16):** Registered as Tier-1 spec and aligned to the Amduat 2.0 substrate baseline. diff --git a/tier1/pel-trace-dag-1.md b/tier1/pel-trace-dag-1.md new file mode 100644 index 0000000..33672de --- /dev/null +++ b/tier1/pel-trace-dag-1.md @@ -0,0 +1,748 @@ +# PEL/TRACE-DAG/1 — DAG Execution Trace Profile + +Status: Approved +Owner: Niklas Rydberg +Version: 0.2.1 +SoT: Yes +Last Updated: 2025-11-16 +Linked Phase Pack: N/A +Tags: [execution, traceability] + + + +**Document ID:** `PEL/TRACE-DAG/1` +**Layer:** L1 Scheme Trace Profile (on top of PEL/1-CORE + PEL/PROGRAM-DAG/1) + +**Depends on (normative):** + +* `ASL/1-CORE v0.4.x` — value model (`Artifact`, `Reference`, `TypeTag`, integers, `OctetString`) +* `PEL/1-CORE v0.3.x` — primitive execution layer (`ExecutionStatus`, `ExecutionErrorSummary`, diagnostics) +* `PEL/PROGRAM-DAG/1 v0.3.1` — DAG Program scheme (`Program`, `Node`, `NodeId`, canonical topological order) + +**Integrates with (informative):** + +* `PEL/1-SURF v0.2.x` — store-backed execution surface +* `ENC/PEL-TRACE-DAG/1` (planned) — canonical encoding for DAG traces +* `TGK/1-CORE` — trace graph kernel (execution edges) +* `ENC/ASL1-CORE v1.0.x` — canonical Artifact encoding +* `HASH/ASL1 v0.2.4` — ASL1 hash family + +© 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. Overview + +`PEL/TRACE-DAG/1` defines a **standard trace value** for executions of the `PEL/PROGRAM-DAG/1` scheme: + +* It records **per-node status and outputs** in **canonical node order**. +* It links the trace to: + + * the **Program Artifact** (`program_ref`), + * the **inputs** and optional **params** used by the run, + * the overall **scheme** and (optionally) the **ExecutionResult** Artifact. + +The trace is represented as a single ASL/1 Artifact (a **TraceArtifact**) whose payload is a `TraceDAGValue` (defined below) and whose `TypeTag` is dedicated to this profile. + +The trace is: + +* **Optional** — some deployments may choose not to record it. +* **Deterministic** — for fixed inputs and Program, all conformant implementations produce identical traces. +* **Graph-friendly** — TGK/1 can interpret it into node-level execution edges for provenance. + +Binary layout and `TypeTag` assignment are defined in a companion encoding profile (`ENC/PEL-TRACE-DAG/1`); this document specifies only the **logical model and semantics**. + +--- + +## 1. Purpose & Non-Goals + +### 1.1 Purpose + +The goals of `PEL/TRACE-DAG/1` are to: + +1. Provide a **canonical node-level trace** for `PEL/PROGRAM-DAG/1` runs. +2. Capture for each `Node`: + + * whether it was executed, + * whether it succeeded or failed, + * the **`Reference`s** of any Artifacts it produced, + * deterministic diagnostic entries. + +3. Link node-level information to: + + * the **Program** that defined the DAG, + * the **input** and **params** Artifacts, + * the run-level `ExecutionStatus` / `ExecutionErrorSummary`. + +This trace enables: + +* reconstruction of **per-node execution edges** in TGK/1, +* human and machine debugging of runs, +* post-hoc provenance analysis and selective re-execution. + +### 1.2 Non-Goals + +This profile does **not** define: + +* Graph/provenance edges themselves — TGK/1 defines how traces become edges. +* Concrete binary encodings — these belong to `ENC/PEL-TRACE-DAG/1`. +* How and when traces are **enabled** — that is a policy/configuration decision. +* Store interactions — those belong to `ASL/1-STORE` and `PEL/1-SURF`. +* Semantics of operations (`OperationId`), parameter schemas, or error codes — those belong to operation registries (e.g. `OPREG/PEL1-KERNEL` and extensions). + +--- + +## 2. Context and Layering + +### 2.1 Relationship to PEL/PROGRAM-DAG/1 + +`PEL/PROGRAM-DAG/1` defines: + +* The **Program** data model (`Program`, `Node`, `NodeId`, `RootRef`). +* Structural validity rules (unique `NodeId`s, DAG constraint, etc.). +* A canonical **topological order** of Nodes. + +`PEL/TRACE-DAG/1` assumes: + +* The run was performed against a particular Program Artifact (`program_ref`). +* The Program is **structurally valid** under `PEL/PROGRAM-DAG/1`. +* Node evaluation order is the scheme’s canonical topological order. + +This profile adds: + +* A `TraceDAGValue` expressed in terms of that Program’s `NodeId`s and operations. + +### 2.2 Relationship to PEL/1-CORE and PEL/1-SURF + +At the PEL/1 level, a scheme `s` is bound to a pure function: + +```text +Exec_s( + program: Artifact, + inputs: list, + params: optional Artifact +) -> (outputs: list, result: ExecutionResultValue) +```` + +For the DAG scheme (`PEL/PROGRAM-DAG/1`): + +* `Exec_DAG` plays that role. + +`PEL/TRACE-DAG/1` sits just above: + +* It defines the shape of a `TraceDAGValue` that can be produced **in addition to** the `outputs` and `ExecutionResultValue`. +* A PEL/1 surface (e.g. `PEL/1-SURF`) that implements this profile: + + * persists this trace as an Artifact with a dedicated `TypeTag`, + * obtains its `Reference` via `ASL/1-STORE.put`, + * exposes that `Reference` as `trace_ref` in its surface-level `ExecutionResult` Artifact. + +If `Exec_DAG` is **not** invoked at all (e.g. due to store-level failures handled in `PEL/1-SURF`), then `PEL/TRACE-DAG/1` is simply **not applied**: no `TraceDAGValue` and no trace Artifact are produced for that run. + +### 2.3 Relationship to TGK/1 + +TGK/1 can treat a `TraceDAGValue` as: + +* A **per-node execution log**, keyed by `program_ref` and `NodeId`. +* A list of **produced Artifacts** (`output_refs` per node). +* A basis for edges like: + + * “node N (operation O) produced artifact A by reading B, C, …”, + * “this run (ExecutionResult) used Program P and generated outputs X, Y, …”. + +This document does not prescribe exact edge types; it only ensures traces are **structured enough** for TGK/1 to derive such edges deterministically. + +--- + +## 3. Data Model + +### 3.1 Reused Types + +From `ASL/1-CORE`: + +```text +Reference { + hash_id: HashId + digest: OctetString +} + +HashId = uint16 +``` + +From `PEL/1-CORE`: + +```text +ExecutionStatus = uint8 +ExecutionErrorKind = uint8 + +ExecutionErrorSummary { + kind: ExecutionErrorKind + status_code: uint32 +} +``` + +`PEL/1-CORE` defines the shared meanings of: + +```text +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 +} +``` + +From `PEL/PROGRAM-DAG/1`: + +```text +NodeId = uint32 + +Program { nodes: list; roots: list; } + +Node { + id: NodeId + op: OperationId + inputs: list + params: ParamsBytes +} + +OperationId { + name: string // logical UTF-8 name + version: uint32 +} +``` + +The encoding of `string` and `ParamsBytes` is defined by `ENC/PEL-PROGRAM-DAG/1`. + +### 3.2 DiagnosticEntry + +For trace-level diagnostics, this profile reuses the generic diagnostic shape used in PEL/1: + +```text +DiagnosticEntry { + code: uint32 // scheme- or op-specific diagnostic code + message: OctetString // often UTF-8 text; interpretation is profile-specific +} +``` + +Requirements: + +* `code` is intended for machine use; `message` is for human-readable information. +* Both MUST be deterministic for a given run. + +### 3.3 NodeTraceStatus + +Node-level status distinguishes whether the node: + +* ran and succeeded, +* ran and failed at runtime, +* did not run due to an earlier failure. + +```text +NodeTraceStatus = uint8 + +NodeTraceStatus { + NODE_OK = 0 // Node executed successfully + NODE_FAILED = 1 // Node executed and failed (runtime failure) + NODE_SKIPPED = 2 // Node was not executed due to earlier run-level failure +} +``` + +Invariants per status: + +* `NODE_OK`: + + * The node’s operation executed successfully. + * `status_code` MUST be `0`. + * `output_refs` MAY be empty or non-empty, depending on the operation. + +* `NODE_FAILED`: + + * The node’s operation was invoked and returned a **runtime error**. + * `status_code` MUST be non-zero. + * `output_refs` MUST be empty (this profile treats failing nodes as producing no durable outputs). + +* `NODE_SKIPPED`: + + * The node was not executed because the run terminated early (`RUNTIME_FAILED`, `INVALID_INPUTS`, or `INVALID_PROGRAM`). + * `status_code` MUST be `0`. + * `output_refs` MUST be empty. + * `diagnostics` MAY contain a short deterministic explanation, but SHOULD be empty in minimal profiles. + +Relation to run-level `ExecutionStatus`: + +* If the run-level `status = OK`, then every `NodeTraceDAG.status` MUST be `NODE_OK`. +* If any `NodeTraceDAG.status = NODE_FAILED`, then the run-level `status` MUST be `RUNTIME_FAILED`. +* For `status ∈ { INVALID_PROGRAM, INVALID_INPUTS }`, node statuses MAY be `NODE_OK` (for nodes that executed before the invalid condition was detected) or `NODE_SKIPPED`, but MUST NOT be `NODE_FAILED` (runtime failures always map to `RUNTIME_FAILED`). + +### 3.4 NodeTraceDAG + +A node-level trace entry: + +```text +NodeTraceDAG { + node_id: NodeId + op_name: string // duplicate of Program.nodes[i].op.name + op_version: uint32 // duplicate of Program.nodes[i].op.version + + status: NodeTraceStatus + status_code: uint32 // 0 = success; non-zero = op-specific failure code for NODE_FAILED + + output_refs: list // artifacts produced by this node (if any) in store space + diagnostics: list +} +``` + +Requirements: + +* `node_id` MUST correspond to some `Node.id` in the Program identified by `program_ref`. + +* `op_name` / `op_version` MUST match the `OperationId` of that `Node` as decoded from the Program Artifact. They are a **denormalized copy** so traces remain understandable even if the Program is not immediately available. + +* `output_refs`: + + * MUST list, in order, the `Reference`s obtained when persisting this node’s output Artifacts (via `ASL/1-STORE.put`) in the Store used for this run. + * MAY be empty if the operation logically has no outputs, or the node failed / was skipped. + +* `diagnostics`: + + * For `NODE_OK`, MAY be empty or contain non-fatal diagnostics (e.g. warnings) if the operation registry defines them. + * For `NODE_FAILED`, SHOULD contain at least one entry describing the failure. + * For `NODE_SKIPPED`, MAY be empty or contain a short deterministic explanation. + +### 3.5 TraceDAGValue + +The top-level trace for a DAG run: + +```text +TraceDAGValue { + pel1_version: uint16 // MUST be 1 for this version + scheme_ref: Reference // MUST be SchemeRef_DAG_1 + program_ref: Reference // Program Artifact used by this run + + status: ExecutionStatus + summary: ExecutionErrorSummary + + exec_result_ref: optional Reference // Reference to surface ExecutionResult Artifact (if available) + + input_refs: list // in the same order as Exec_DAG.inputs + params_ref: optional Reference // params Artifact used for this run, if any + + node_traces: list // node-level traces in canonical node order +} +``` + +Constraints: + +* `pel1_version`: + + * MUST be `1` for traces produced under this version of the spec. + +* `scheme_ref`: + + * MUST equal `SchemeRef_DAG_1` for this scheme. + +* `program_ref`: + + * MUST be the `Reference` of the Program Artifact passed to the run. + +* `status` and `summary`: + + * MUST match the `ExecutionStatus` and `ExecutionErrorSummary` of the run-level `ExecutionResultValue` produced by `Exec_DAG`. + * `summary.kind` and `summary.status_code` MUST obey the mapping defined for `PEL/PROGRAM-DAG/1`. + +* `exec_result_ref`: + + * If the run produced a surface-level `ExecutionResult` Artifact (as in `PEL/1-SURF`), this SHOULD be its `Reference`. + * If no such Artifact exists or is not persisted, it MUST be absent. + +* `input_refs`: + + * MUST be the list of `Reference`s corresponding to the `inputs` passed to `Exec_DAG`, in the same order. + +* `params_ref`: + + * If a params Artifact was provided for the run, MUST be that `Reference`. + * If no params Artifact was provided (or the scheme ignores params), MUST be absent. + +* `node_traces`: + + * If the Program was **successfully decoded and structurally valid** and at least one Node was attempted, `node_traces` MUST contain: + + * exactly one `NodeTraceDAG` per `Node` in the Program, and + * in the **canonical node order** defined by `PEL/PROGRAM-DAG/1`. + + * For runs that fail before any Node is attempted (e.g. `INVALID_PROGRAM` due to decode failure, or `INVALID_INPUTS` detected before the first Node), `node_traces` MUST be empty, because no node evaluation took place. + +--- + +## 4. Trace Semantics for a Single Run + +This section defines how a conformant engine or surface SHOULD construct `TraceDAGValue` for a single `PEL/PROGRAM-DAG/1` run, assuming `Exec_DAG` was actually invoked. + +We consider a run characterized by: + +* `program_ref : Reference` +* `program_artifact : Artifact` (whose `bytes` decode to a `Program` under `PEL/PROGRAM-DAG/1` + its encoding profile) +* `input_refs : list` +* `inputs : list` (resolved from a Store) +* `params_ref : optional Reference` +* `params_artifact : optional Artifact` +* `status : ExecutionStatus` +* `summary : ExecutionErrorSummary` +* Internal records of per-node results: + + * `node_outputs : NodeId -> list` (for nodes that executed successfully), + * `node_runtime_errors : NodeId -> (status_code, diagnostics)` (for nodes that failed at runtime), + * and a notion of **which Nodes were evaluated**. + +Implementations MAY organize their internal state differently; the following is conceptual. + +### 4.1 Trace for successful runs (`status = OK`) + +If `status = OK`: + +1. **Program and ordering** + + * The Program MUST be structurally valid (per `PEL/PROGRAM-DAG/1`). + * The engine MUST know the canonical node order (topological order with `NodeId` tie-breakers). + +2. **Node traces** + + * For each `Node` in canonical order: + + ```text + NodeTraceDAG { + node_id = Node.id + op_name = Node.op.name + op_version = Node.op.version + status = NODE_OK + status_code = 0 + output_refs = [ R_0, ..., R_(k-1) ] // refs from store.put on each output Artifact + diagnostics = D // deterministic diagnostics, possibly empty + } + ``` + + * No nodes are `NODE_FAILED` or `NODE_SKIPPED` in a run with `status = OK`. + +3. **Top-level fields** + + * `TraceDAGValue` MUST be: + + ```text + pel1_version = 1 + scheme_ref = SchemeRef_DAG_1 + program_ref = program_ref + + status = OK + summary.kind = NONE + summary.status_code = 0 + + exec_result_ref = execution_result_ref (if available, else absent) + + input_refs = input_refs (exact order used in run) + params_ref = params_ref (if any) + + node_traces = [ NodeTraceDAG in canonical node order ] + ``` + +### 4.2 Trace for runtime failures (`status = RUNTIME_FAILED`) + +If `status = RUNTIME_FAILED` and at least one Node executed: + +1. **Nodes before the first failing node** + + * For Nodes that executed successfully before the failure: + + * Same as in §4.1: `status = NODE_OK`, `status_code = 0`, `output_refs` as persisted. + +2. **Failing node** + + * For the first Node that failed at runtime: + + ```text + NodeTraceDAG { + node_id = Node.id + op_name = Node.op.name + op_version = Node.op.version + status = NODE_FAILED + status_code = runtime_status_code // from operation semantics + output_refs = [] // no durable outputs recorded + diagnostics = runtime_diagnostics // deterministic list + } + ``` + +3. **Nodes after the failing node** + + * For all Nodes that were not executed because the run terminated: + + ```text + NodeTraceDAG { + node_id = Node.id + op_name = Node.op.name + op_version = Node.op.version + status = NODE_SKIPPED + status_code = 0 + output_refs = [] + diagnostics = [] // MAY contain a short deterministic message, but SHOULD be empty + } + ``` + +4. **Top-level fields** + + * `TraceDAGValue` MUST set: + + ```text + status = RUNTIME_FAILED + summary.kind = RUNTIME + summary.status_code = status_code // non-zero, matching ExecutionResultValue.summary.status_code + + // other fields as in §4.1 + ``` + +### 4.3 Trace for invalid inputs / invalid program + +For `status = INVALID_INPUTS` or `status = INVALID_PROGRAM` (as produced by `Exec_DAG`, not by store-level surfaces): + +* If no Node is executed at all: + + * `node_traces` MUST be empty. + * `status` and `summary` MUST match the run-level `ExecutionResultValue`. + * It is RECOMMENDED to include at least one diagnostic (in `summary` or operation-level diagnostics) indicating the error (e.g., which input index was missing, or why the Program was invalid). + +* If the engine partially evaluates the Program before detecting an invalid condition (allowed only where consistent with `PEL/PROGRAM-DAG/1`): + + * Node traces for executed Nodes MUST be recorded with `status = NODE_OK` (no runtime failure). + * Nodes that were never reached MUST be marked `NODE_SKIPPED`. + * No `NodeTraceDAG` MAY have `status = NODE_FAILED` in runs where `status ∈ { INVALID_PROGRAM, INVALID_INPUTS }`. + +In all cases, the mapping from the precise validation failure to node-level traces MUST be deterministic for a given run and MUST be consistent with the run-level `status` and `summary`. + +--- + +## 5. Determinism + +### 5.1 Determinism contract + +For fixed: + +* `program_artifact.bytes`, +* `input_refs` + the contents of their referenced Artifacts in the Store, +* `params_ref` + its Artifact contents (if present), +* the same operation registries and parameter profiles for all `OperationId`s referenced in the Program, + +then: + +> All conformant implementations that: +> +> * execute the run under the `PEL/PROGRAM-DAG/1` scheme, and +> * produce a `TraceDAGValue` under `PEL/TRACE-DAG/1` +> +> MUST produce identical `TraceDAGValue` logical values. + +This implies: + +* Identical `status` and `summary`. +* Identical `input_refs`, `params_ref`, and `program_ref`. +* Identical `node_traces` sequence, with: + + * same `node_id`, `op_name`, `op_version`, + * same `NodeTraceStatus`, `status_code`, + * identical `output_refs` (same `Reference`s, same order), + * identical `diagnostics`. + +### 5.2 No ambient environment + +Trace construction MUST NOT depend on: + +* host clocks, random number generators, environment variables, +* process IDs, thread IDs, or other non-deterministic identifiers, +* non-deterministic logging or scheduling. + +Any data that appears in diagnostics or `status_code` MUST be determined solely by: + +* the Program value, +* input/params Artifacts, +* deterministic operation semantics, +* and deterministic scheme-level logic. + +--- + +## 6. Interaction with PEL/1-SURF (Informative) + +A PEL/1 surface that implements this profile typically proceeds as follows for runs where `Exec_DAG` is invoked: + +1. **Resolve inputs** + + * Use `ASL/1-STORE.get` to resolve `program_ref`, `input_refs`, and `params_ref` (if present). + * Handle Store-level errors per `PEL/1-SURF` (mapping to `INVALID_INPUTS` / `INVALID_PROGRAM` and **not** calling `Exec_DAG`). + In these cases, **no trace Artifact is produced** under this profile. + +2. **Run Exec_DAG** + + * Call: + + ```text + (outputs, exec_result_value) = + Exec_DAG(program_artifact, input_artifacts, params_artifact?) + ``` + + * Persist `outputs` and the surface-level `ExecutionResult` Artifact, yielding `output_refs` and `exec_result_ref`. + +3. **Construct TraceDAGValue** + + * Using: + + * `program_ref`, + * `input_refs`, + * `params_ref`, + * `exec_result_ref`, + * `exec_result_value.status` and `summary`, + * node-level outputs and errors recorded during execution, + + * Construct `TraceDAGValue` as in §3–§4. + +4. **Persist TraceArtifact** + + * Encode `TraceDAGValue` as an ASL/1 Artifact: + + ```text + TraceArtifact { + bytes = TraceDAGBytes // per ENC/PEL-TRACE-DAG/1 + type_tag = TYPE_TAG_PEL_TRACE_DAG_1 + } + ``` + + * Call `ASL/1-STORE.put(TraceArtifact)` to obtain `trace_ref`. + +5. **Expose trace_ref** + + * Include `trace_ref` in the surface-level `ExecutionResult` Artifact (as an optional field). + * TGK/1 and higher layers can then traverse from `ExecutionResult` → `trace_ref` → `TraceDAGValue` → per-node `output_refs`. + +This flow is informative; implementations MAY pipeline or optimize, provided the observable `TraceDAGValue` is unchanged. + +--- + +## 7. Conformance + +An implementation is **PEL/TRACE-DAG/1–conformant** if it: + +1. **Implements the TraceDAGValue model** + + * Produces trace values that satisfy all field constraints in §3. + * Sets `pel1_version = 1` and `scheme_ref = SchemeRef_DAG_1`. + +2. **Respects status alignment** + + * Ensures `TraceDAGValue.status` and `TraceDAGValue.summary` always match the run-level `ExecutionResultValue` produced for the same run. + * Ensures that the rules relating `NodeTraceStatus` and run-level `ExecutionStatus` (§3.3, §4) are upheld. + +3. **Maintains canonical node order and coverage** + + * For structurally valid Programs where `Exec_DAG` evaluated at least one Node, emits `node_traces`: + + * in the canonical topological order defined by `PEL/PROGRAM-DAG/1`, and + * with exactly one `NodeTraceDAG` per Program `Node`. + + * For runs where no Node is attempted, emits `node_traces = []`. + +4. **Uses NodeTraceStatus correctly** + + * Uses `NODE_OK`, `NODE_FAILED`, `NODE_SKIPPED` with semantics defined in §3.3 and §4. + * Ensures `status_code` and `output_refs` obey the rules for each status. + +5. **Ensures determinism** + + * For fixed inputs and Program, always produces identical `TraceDAGValue` (across runs, machines, implementations). + * Does not inject host or environment variability into the trace. + +6. **Separates concerns** + + * Does not conflate Store-level errors (e.g., `ERR_NOT_FOUND`) with node-level runtime failures; Store errors are handled in `PEL/1-SURF` and MUST NOT produce spurious `NodeTraceDAG` entries. + * Does not encode provenance graph semantics in the trace; it only provides structured data that TGK/1 can interpret. + +--- + +## 8. Security and Privacy Considerations + +1. **Information exposure** + + * `TraceDAGValue` includes: + + * references to input Artifacts, + * references to intermediate outputs, + * per-node diagnostics. + + * Anyone with access to the trace and the Store may gain insight into: + + * data flow, + * intermediate values, + * failure modes. + + * Domains concerned with secrecy MUST treat trace Artifacts as sensitive and control access appropriately (e.g., by Store policy or overlays). + +2. **Trace volume** + + * Large Programs with many Nodes and outputs can produce large traces. + + * Implementations SHOULD consider: + + * trace sampling or truncation (but MUST keep it deterministic if used), + * separate “debug” vs “production” trace policies. + + * Any truncation or sampling profile MUST be expressed as a separate spec/profile; this baseline assumes full traces. + +3. **Integrity** + + * Trace integrity is rooted in: + + * `HASH/ASL1` (for Artifact identity), + * Store semantics (`ASL/1-STORE`), + * optional certification (`CIL/1`). + + * Tampering with trace Artifacts is detectable via mismatches between: + + * ExecutionResult, + * trace, + * Program, + * and actual Store content. + +4. **Non-repudiation** + + * By itself, `TraceDAGValue` does not prove that a run occurred in any particular environment; it is just data. + * Non-repudiation requires certification and policy layers (`FER/1`, `FCT/1`, `CIL/1`). + +--- + +**End of `PEL/TRACE-DAG/1 v0.2.1 — DAG Execution Trace Profile`** + +--- + +## Document History + +* **0.2.1 (2025-11-16):** Registered as Tier-1 spec and aligned to the Amduat 2.0 substrate baseline.