1109 lines
34 KiB
Markdown
1109 lines
34 KiB
Markdown
# 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 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<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/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).
|