`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.
`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.
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<Artifact>`,
*`params_ref`, if present, resolves to `params : Artifact`,
then the engine MUST invoke the scheme-level PEL/1-CORE function:
* 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:
* 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.