amduat/tier1/enc-pel1-result-1.md

1109 lines
34 KiB
Markdown
Raw Normal View History

2025-12-20 12:35:10 +01:00
# 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]
<!-- Source: /amduat/docs/new/enc-pel1-result-1.md | Canonical: /amduat/tier1/enc-pel1-result-1.md -->
**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<T> =
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<InputRef>
OutputRefList = list<OutputRef>
```
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<DiagnosticEntry>
}
```
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 stores 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<DiagnosticEntry>
}
```
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<DiagnosticEntry>`.
* `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/1conformant** 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).