821 lines
22 KiB
Markdown
821 lines
22 KiB
Markdown
# OPREG/PEL1-KERNEL — Kernel Operation Registry for PEL/1
|
||
|
||
Status: Approved
|
||
Owner: Niklas Rydberg
|
||
Version: 0.1.3
|
||
SoT: Yes
|
||
Last Updated: 2025-12-22
|
||
Linked Phase Pack: N/A
|
||
Tags: [registry, execution]
|
||
|
||
<!-- Source: /amduat/docs/new/opreg-pel1-kernel.md | Canonical: /amduat/tier1/opreg-pel1-kernel.md -->
|
||
|
||
**Document ID:** `OPREG/PEL1-KERNEL`
|
||
**Layer:** L1 Profile (Operation Registry for `PEL/1-CORE` + `PEL/PROGRAM-DAG/1`)
|
||
|
||
**Depends on (normative):**
|
||
|
||
* `ASL/1-CORE v0.3.x` — `Artifact`, `TypeTag`, `Reference`, `HashId`
|
||
* `PEL/1-CORE v0.1.x` — primitive execution layer core
|
||
* `PEL/PROGRAM-DAG/1 v0.2.x` — DAG scheme for PEL
|
||
* `HASH/ASL1 v0.2.x` — ASL1 hash family (for `HASH-ASL1-256`)
|
||
|
||
**Integrates with (informative):**
|
||
|
||
* `SUBSTRATE/STACK-OVERVIEW v0.1.x`
|
||
* `ENC/PEL-PROGRAM-DAG/1` (canonical encoding of Program)
|
||
* `PEL/TRACE-DAG/1` (optional trace profile)
|
||
* Higher-level operation registries (domain-specific ops)
|
||
|
||
© 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 Non-Goals
|
||
|
||
### 0.1 Purpose
|
||
|
||
`OPREG/PEL1-KERNEL` defines a **minimal, globally stable set of PEL operations** that every “kernel-capable” PEL engine is expected to implement:
|
||
|
||
* They operate on **ASL/1 Artifacts** (bytes + optional type tag).
|
||
* They are used in **`PEL/PROGRAM-DAG/1`** programs as `OperationId { name, version }`.
|
||
* They are **pure and deterministic**: same inputs → same outputs, independent of engine or environment.
|
||
* They explicitly define:
|
||
|
||
* **Arity** (number of inputs),
|
||
* **Parameter model** (logical Params value),
|
||
* **Output shape** (number and form of outputs),
|
||
* **Runtime error conditions** and associated `status_code` values for `PEL/PROGRAM-DAG/1`.
|
||
|
||
These operations are intentionally **low-level** and **byte-centric**; richer semantics (JSON, typed records, domain-specific logic) belong in separate registries.
|
||
|
||
### 0.2 Non-goals
|
||
|
||
This registry does **not** define:
|
||
|
||
* Any storage or transport API (`ASL/1-STORE`).
|
||
* Any encoding of Programs or Params into bytes (`ENC/PEL-PROGRAM-DAG/1`, param-encoding profiles).
|
||
* Any certification or fact semantics (`CIL/1`, `FER/1`, `FCT/1`).
|
||
* Provenance graph edges (`TGK/1`).
|
||
* Human-readable diagnostics payloads (see §2.4).
|
||
|
||
---
|
||
|
||
## 1. Conventions and Context
|
||
|
||
### 1.1 Base types
|
||
|
||
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` and `PEL/PROGRAM-DAG/1` (simplified):
|
||
|
||
```text
|
||
OperationId {
|
||
name: string
|
||
version: uint32
|
||
}
|
||
|
||
ExecutionStatus = uint8 // e.g. OK, INVALID_PROGRAM, INVALID_INPUTS, RUNTIME_FAILED
|
||
ExecutionErrorKind = uint8 // e.g. NONE, PROGRAM, INPUTS, RUNTIME
|
||
|
||
ExecutionErrorSummary {
|
||
kind: ExecutionErrorKind
|
||
status_code: uint32
|
||
}
|
||
|
||
DiagnosticEntry {
|
||
code: uint32
|
||
message: OctetString
|
||
}
|
||
|
||
ExecutionResultValue {
|
||
pel1_version : uint16
|
||
status : ExecutionStatus
|
||
scheme_ref : SchemeRef
|
||
summary : ExecutionErrorSummary
|
||
diagnostics : list<DiagnosticEntry>
|
||
}
|
||
```
|
||
|
||
`PEL/PROGRAM-DAG/1` defines `Exec_DAG` as:
|
||
|
||
```text
|
||
Exec_DAG(
|
||
program: Artifact,
|
||
inputs: list<Artifact>,
|
||
params: optional Artifact
|
||
) -> (outputs: list<Artifact>, result: ExecutionResultValue)
|
||
```
|
||
|
||
and defines that each Node evaluates an `OperationId` with a logical interface:
|
||
|
||
```text
|
||
Op(name, version)(
|
||
inputs: list<Artifact>,
|
||
params: ParamsValue
|
||
) -> Ok(list<Artifact>) | Err(status_code: uint32)
|
||
```
|
||
|
||
The overall `ExecutionResultValue.summary.status_code` for `RUNTIME_FAILED` is taken from the `status_code` returned by the failing operation.
|
||
|
||
### 1.2 Status and error mapping
|
||
|
||
This registry only defines **runtime error codes** (used when `Exec_DAG` sets `status = RUNTIME_FAILED`).
|
||
|
||
Global outcome statuses:
|
||
|
||
```text
|
||
ExecutionStatus {
|
||
OK = 0
|
||
INVALID_PROGRAM = 2
|
||
INVALID_INPUTS = 3
|
||
RUNTIME_FAILED = 4
|
||
}
|
||
```
|
||
|
||
Error summary kind:
|
||
|
||
```text
|
||
ExecutionErrorKind {
|
||
NONE = 0
|
||
PROGRAM = 1
|
||
INPUTS = 2
|
||
RUNTIME = 3
|
||
}
|
||
```
|
||
|
||
Mapping (from `PEL/PROGRAM-DAG/1`):
|
||
|
||
* `status = OK` ⇒ `kind = NONE`, `status_code = 0`
|
||
* `status = INVALID_PROGRAM`⇒ `kind = PROGRAM`, `status_code = 2`
|
||
* `status = INVALID_INPUTS` ⇒ `kind = INPUTS`, `status_code = 3`
|
||
* `status = RUNTIME_FAILED` ⇒ `kind = RUNTIME`, `status_code = op-specific (> 0)`
|
||
|
||
This registry **only** defines the operation-specific `status_code` values that may appear when `status = RUNTIME_FAILED`.
|
||
|
||
### 1.3 Kernel status_code layout
|
||
|
||
For kernel ops we reserve a simple scheme for `status_code` on runtime failure:
|
||
|
||
```text
|
||
status_code = (kernel_op_code << 16) | error_index
|
||
```
|
||
|
||
Where:
|
||
|
||
* `kernel_op_code` is a 16-bit numeric code assigned per operation in this registry.
|
||
* `error_index` is a small (non-zero) 16-bit integer enumerating distinct error causes per op.
|
||
|
||
This ensures:
|
||
|
||
* No collision between error codes of different operations.
|
||
* Easy offline decoding of `status_code` into `(op, reason)`.
|
||
|
||
Concrete `kernel_op_code` assignments are given in §3.
|
||
|
||
### 1.4 Params and encodings
|
||
|
||
Each operation defines a **logical Params type** (e.g. `SliceParams { offset: u64; length: u64 }`).
|
||
|
||
This registry does **not** define byte-level encodings of Params; those are defined in a companion profile (e.g. `OPREG/PEL1-KERNEL-PARAMS/1`). This document is the **semantic** registry.
|
||
|
||
Conformance requirements:
|
||
|
||
* For each operation, there MUST exist exactly one canonical encoding and decoding for its Params type.
|
||
* All engines claiming to implement the operation MUST use that same encoding and decoding.
|
||
* If Params decoding fails, the operation MUST treat the Node as either:
|
||
|
||
* `INVALID_PROGRAM` (preferred for static malformations), or
|
||
* `RUNTIME_FAILED` with a specific `status_code` (if the registry so specifies).
|
||
|
||
For this initial kernel set, we treat **Param decoding errors as INVALID_PROGRAM**, not as runtime failures.
|
||
|
||
### 1.5 Diagnostics
|
||
|
||
To keep `ExecutionResultValue` stable and simple, kernel operations:
|
||
|
||
For kernel operations, the operation semantics MUST always return an empty diagnostics list, and the scheme’s Exec_DAG implementation MUST NOT add additional diagnostics when a failing Node is a kernel op.
|
||
|
||
Human-readable error information is expected to be carried in:
|
||
|
||
* separate trace artifacts (`PEL/TRACE-DAG/1`), or
|
||
* external logs and observability systems, not in `ExecutionResultValue.diagnostics`.
|
||
|
||
---
|
||
|
||
## 2. Common Kernel Operation Conventions
|
||
|
||
All kernel operations in this registry share these properties:
|
||
|
||
1. **Purity and determinism**
|
||
|
||
* They operate only on:
|
||
|
||
* the input `Artifact.bytes` and `Artifact.type_tag`,
|
||
* their decoded Params,
|
||
* standard pure functions (e.g. integer arithmetic, hashing as per `HASH/ASL1`).
|
||
|
||
* They MUST NOT:
|
||
|
||
* read clocks or random sources,
|
||
* perform network or filesystem I/O,
|
||
* depend on global mutable state.
|
||
|
||
2. **Type tags**
|
||
|
||
* Unless otherwise stated, operations **preserve the input type tag** when transforming a single input.
|
||
* For operations with multiple inputs, if they require consistent type tags, this is checked at runtime and may yield a runtime error.
|
||
* Operations MAY produce Artifacts with `type_tag = None` for “raw bytes” outputs.
|
||
|
||
3. **Arity and static vs dynamic errors**
|
||
|
||
* Each operation specifies `min_inputs` and `max_inputs`.
|
||
* Violations of these arity constraints are **static** (depend only on the Program) and MUST be treated as `INVALID_PROGRAM`, not `RUNTIME_FAILED`.
|
||
* Runtime errors are reserved for **data-dependent** conditions (e.g. out-of-bounds slice based on actual input length).
|
||
|
||
4. **Success vs failure**
|
||
|
||
* On success: operation returns `Ok(list<Artifact>)`, and `Exec_DAG` keeps `status = OK` (unless a different Node fails later).
|
||
* On failure: operation returns `Err(status_code)`, and `Exec_DAG` stops evaluation and sets:
|
||
|
||
```text
|
||
status = RUNTIME_FAILED
|
||
summary.kind = RUNTIME
|
||
summary.status_code = status_code
|
||
diagnostics = []
|
||
```
|
||
|
||
---
|
||
|
||
## 3. Kernel Operation Index
|
||
|
||
We define five kernel operations:
|
||
|
||
| Kernel Op Code | OperationId.name | version | Summary |
|
||
| -------------: | ----------------------- | :------ | --------------------------------------- |
|
||
| `0x0001` | `"pel.bytes.concat"` | `1` | Concatenate N artifacts |
|
||
| `0x0002` | `"pel.bytes.slice"` | `1` | Take a byte slice of one artifact |
|
||
| `0x0003` | `"pel.bytes.const"` | `1` | Produce a constant artifact from params |
|
||
| `0x0004` | `"pel.bytes.hash.asl1"` | `1` | Hash an artifact’s bytes with ASL1 |
|
||
| `0x0005` | `"pel.bytes.params"` | `1` | Return the global params artifact |
|
||
|
||
All operation names are case-sensitive UTF-8 strings.
|
||
|
||
Each operation’s `OperationId` is:
|
||
|
||
```text
|
||
OperationId {
|
||
name: <as in table>
|
||
version: 1
|
||
}
|
||
```
|
||
|
||
`kernel_op_code` in the `status_code` formula (§1.3) is the hex code in the first column.
|
||
|
||
---
|
||
|
||
## 4. Operation Specifications
|
||
|
||
### 4.1 `pel.bytes.concat` v1 (code 0x0001)
|
||
|
||
**OperationId**
|
||
|
||
```text
|
||
name = "pel.bytes.concat"
|
||
version = 1
|
||
kernel_op_code = 0x0001
|
||
```
|
||
|
||
**Intent**
|
||
|
||
Concatenate the byte payloads of N input Artifacts (N ≥ 1) into a single output Artifact. All input type tags MUST be identical (including “no type tag”).
|
||
|
||
#### 4.1.1 Arity and Params
|
||
|
||
* `min_inputs = 1`
|
||
* `max_inputs = unbounded` (any positive number)
|
||
* Params: **none** (`Unit`)
|
||
|
||
Static errors (handled as `INVALID_PROGRAM`):
|
||
|
||
* `inputs.length == 0`.
|
||
* Params not decodable as `Unit` (i.e. any non-empty Params according to the canonical encoding).
|
||
|
||
#### 4.1.2 Semantics
|
||
|
||
Given:
|
||
|
||
```text
|
||
inputs = [A0, A1, ..., A_{n-1}], n >= 1
|
||
params = ()
|
||
```
|
||
|
||
Let:
|
||
|
||
```text
|
||
Ti = Ai.type_tag
|
||
Bi = Ai.bytes
|
||
```
|
||
|
||
1. **Type tag consistency check (runtime)**
|
||
|
||
* If there exist `i, j` such that `Ti` and `Tj` are not equal in the `ASL/1-CORE` sense (i.e. one is absent and the other present, or both present but with different `tag_id`):
|
||
|
||
* Operation returns `Err(status_code = 0x0001_0001)`.
|
||
|
||
2. **Concatenation**
|
||
|
||
* Define:
|
||
|
||
```text
|
||
B_out = B0 || B1 || ... || B_{n-1} // byte-wise concatenation
|
||
T_out = T0 // they are all equal by step 1
|
||
```
|
||
|
||
* Output list is a single Artifact `C`:
|
||
|
||
```text
|
||
C.bytes = B_out
|
||
C.type_tag = T_out
|
||
```
|
||
|
||
* Operation returns `Ok([C])`.
|
||
|
||
This operation does not impose any explicit limit on the concatenated length; overflow or resource exhaustion is outside the PEL semantic layer.
|
||
|
||
#### 4.1.3 Runtime error codes
|
||
|
||
For `pel.bytes.concat` v1, runtime errors (producing `RUNTIME_FAILED`) are:
|
||
|
||
| Name | Condition | `status_code` |
|
||
| ------------------- | ---------------------------------- | ------------- |
|
||
| `TYPE_TAG_MISMATCH` | Any pair of input type tags differ | `0x0001_0001` |
|
||
|
||
On any such error:
|
||
|
||
* Operation returns `Err(status_code)` as above.
|
||
* `Exec_DAG` sets `status = RUNTIME_FAILED`, `summary.status_code = status_code`, `diagnostics = []`.
|
||
|
||
---
|
||
|
||
### 4.2 `pel.bytes.slice` v1 (code 0x0002)
|
||
|
||
**OperationId**
|
||
|
||
```text
|
||
name = "pel.bytes.slice"
|
||
version = 1
|
||
kernel_op_code = 0x0002
|
||
```
|
||
|
||
**Intent**
|
||
|
||
Take a contiguous slice from a single input Artifact’s bytes.
|
||
|
||
#### 4.2.1 Params: `SliceParams`
|
||
|
||
Logical Params:
|
||
|
||
```text
|
||
SliceParams {
|
||
offset: uint64 // byte offset, 0-based
|
||
length: uint64 // number of bytes to include
|
||
}
|
||
```
|
||
|
||
* `offset` and `length` are non-negative.
|
||
* Their canonical encoding/decoding is defined in a param-encoding profile; invalid encodings MUST result in `INVALID_PROGRAM`.
|
||
|
||
#### 4.2.2 Arity
|
||
|
||
* `min_inputs = 1`
|
||
* `max_inputs = 1`
|
||
|
||
Arity violations → `INVALID_PROGRAM`.
|
||
|
||
#### 4.2.3 Semantics
|
||
|
||
Given:
|
||
|
||
```text
|
||
inputs = [A]
|
||
params = SliceParams { offset, length }
|
||
```
|
||
|
||
Let:
|
||
|
||
```text
|
||
B = A.bytes // length = L
|
||
T = A.type_tag
|
||
L = |B|
|
||
o = offset
|
||
ℓ = length
|
||
```
|
||
|
||
1. **Range check (runtime)**
|
||
|
||
* If `o > L` or `o + ℓ > L` (with arithmetic in unbounded integers):
|
||
|
||
* Operation returns `Err(status_code = 0x0002_0001)`.
|
||
|
||
2. **Slicing**
|
||
|
||
* Define:
|
||
|
||
```text
|
||
B_out = B[o .. o+ℓ] // ℓ bytes starting at index o
|
||
```
|
||
|
||
* Output Artifact `C`:
|
||
|
||
```text
|
||
C.bytes = B_out
|
||
C.type_tag = T
|
||
```
|
||
|
||
* Operation returns `Ok([C])`.
|
||
|
||
Note: `o == L` and `ℓ == 0` is allowed and yields an empty-byte output.
|
||
|
||
#### 4.2.4 Runtime error codes
|
||
|
||
For `pel.bytes.slice` v1:
|
||
|
||
| Name | Condition | `status_code` |
|
||
| --------------------- | ------------------------------------------------------- | ------------- |
|
||
| `RANGE_OUT_OF_BOUNDS` | `offset > len(bytes)` or `offset + length > len(bytes)` | `0x0002_0001` |
|
||
|
||
On such error, `Exec_DAG` sets `status = RUNTIME_FAILED`, `summary.status_code = 0x0002_0001`, `diagnostics = []`.
|
||
|
||
---
|
||
|
||
### 4.3 `pel.bytes.const` v1 (code 0x0003)
|
||
|
||
**OperationId**
|
||
|
||
```text
|
||
name = "pel.bytes.const"
|
||
version = 1
|
||
kernel_op_code = 0x0003
|
||
```
|
||
|
||
**Intent**
|
||
|
||
Produce a constant Artifact specified entirely by Params, with no data dependencies. This is a way to embed small literal values directly in a Program.
|
||
|
||
#### 4.3.1 Params: `ConstParams`
|
||
|
||
Logical Params:
|
||
|
||
```text
|
||
ConstParams {
|
||
bytes: OctetString // payload bytes
|
||
has_tag: bool // whether a type tag is present
|
||
tag_id: uint32 optional // only meaningful if has_tag = true
|
||
}
|
||
```
|
||
|
||
Semantics:
|
||
|
||
* If `has_tag == false`:
|
||
|
||
* Output Artifact has `type_tag = None`.
|
||
|
||
* If `has_tag == true`:
|
||
|
||
* Output Artifact has `type_tag = Some(TypeTag{ tag_id })`.
|
||
|
||
Param encoding/decoding is defined in a param-encoding profile; malformed encodings ⇒ `INVALID_PROGRAM`.
|
||
|
||
#### 4.3.2 Arity
|
||
|
||
* `min_inputs = 0`
|
||
* `max_inputs = 0`
|
||
|
||
Any non-empty `inputs` list is a static error (`INVALID_PROGRAM`).
|
||
|
||
#### 4.3.3 Semantics
|
||
|
||
Given:
|
||
|
||
```text
|
||
inputs = []
|
||
params = ConstParams { bytes = B, has_tag, tag_id? }
|
||
```
|
||
|
||
Then:
|
||
|
||
* If `has_tag` is false:
|
||
|
||
```text
|
||
C.bytes = B
|
||
C.type_tag = None
|
||
```
|
||
|
||
* If `has_tag` is true:
|
||
|
||
```text
|
||
C.bytes = B
|
||
C.type_tag = Some(TypeTag{ tag_id })
|
||
```
|
||
|
||
* Output list is `[C]`.
|
||
|
||
* Operation returns `Ok([C])`.
|
||
|
||
There are no data-dependent runtime errors: this operation **always succeeds** given valid Params.
|
||
|
||
#### 4.3.4 Runtime error codes
|
||
|
||
*None.*
|
||
All failures are static (bad Params encoding, wrong arity) and must be treated as `INVALID_PROGRAM`.
|
||
|
||
---
|
||
|
||
### 4.4 `pel.bytes.hash.asl1` v1 (code 0x0004)
|
||
|
||
**OperationId**
|
||
|
||
```text
|
||
name = "pel.bytes.hash.asl1"
|
||
version = 1
|
||
kernel_op_code = 0x0004
|
||
```
|
||
|
||
**Intent**
|
||
|
||
Compute an ASL1-family hash (`HASH/ASL1`) over the raw bytes of a single input Artifact.
|
||
|
||
This operation is **not** about ASL/1 identity (which uses `ArtifactBytes` via `ENC/ASL1-CORE`), but about hashing arbitrary byte payloads for protocol or application use.
|
||
|
||
#### 4.4.1 Params: `HashParams`
|
||
|
||
Logical Params:
|
||
|
||
```text
|
||
HashParams {
|
||
hash_id: HashId // must be a valid ASL1 HashId
|
||
}
|
||
```
|
||
|
||
For this version:
|
||
|
||
* `hash_id` MUST be `0x0001` (i.e. `HASH-ASL1-256`).
|
||
* Any other `hash_id` MUST be treated as a **static error** ⇒ `INVALID_PROGRAM`.
|
||
|
||
Rationale: this ensures all conformant engines agree on the algorithm set for this op. Future versions (e.g. `pel.bytes.hash.asl1` v2) MAY support additional `HashId`s.
|
||
|
||
Param encoding/decoding is defined elsewhere; malformed encodings ⇒ `INVALID_PROGRAM`.
|
||
|
||
#### 4.4.2 Arity
|
||
|
||
* `min_inputs = 1`
|
||
* `max_inputs = 1`
|
||
|
||
Arity violations ⇒ `INVALID_PROGRAM`.
|
||
|
||
#### 4.4.3 Semantics
|
||
|
||
Given:
|
||
|
||
```text
|
||
inputs = [A]
|
||
params = HashParams { hash_id = 0x0001 }
|
||
```
|
||
|
||
Let:
|
||
|
||
```text
|
||
B = A.bytes
|
||
H = HASH-ASL1-256 // SHA-256 as defined in HASH/ASL1 for HashId 0x0001
|
||
```
|
||
|
||
Compute:
|
||
|
||
```text
|
||
digest = H(B) // 32-byte digest
|
||
```
|
||
|
||
Then:
|
||
|
||
* Output Artifact `C`:
|
||
|
||
```text
|
||
C.bytes = digest // exactly 32 bytes
|
||
C.type_tag = None // raw bytes digest, no type tag
|
||
```
|
||
|
||
* Output list is `[C]`.
|
||
|
||
* Operation returns `Ok([C])`.
|
||
|
||
There are no data-dependent runtime errors; hashing is assumed total. Any internal errors (e.g. memory failure) are outside PEL semantics.
|
||
|
||
#### 4.4.4 Runtime error codes
|
||
|
||
*None.*
|
||
All failures (unsupported `hash_id`, bad Params, wrong arity) are static and must be treated as `INVALID_PROGRAM`.
|
||
|
||
---
|
||
|
||
### 4.5 `pel.bytes.params` v1 (code 0x0005)
|
||
|
||
**OperationId**
|
||
|
||
```text
|
||
name = "pel.bytes.params"
|
||
version = 1
|
||
kernel_op_code = 0x0005
|
||
```
|
||
|
||
**Intent**
|
||
|
||
Return the scheme-level global `params` Artifact supplied to `Exec_DAG`.
|
||
|
||
#### 4.5.1 Params: `Unit`
|
||
|
||
Params: **none** (`Unit`).
|
||
|
||
Param decoding failures MUST be treated as `INVALID_PROGRAM`.
|
||
|
||
#### 4.5.2 Arity
|
||
|
||
* `min_inputs = 0`
|
||
* `max_inputs = 0`
|
||
|
||
Any non-empty `inputs` list is a static error (`INVALID_PROGRAM`).
|
||
|
||
#### 4.5.3 Semantics
|
||
|
||
Given:
|
||
|
||
```text
|
||
inputs = []
|
||
params = optional Artifact
|
||
```
|
||
|
||
If `params` is present:
|
||
|
||
* Output list is a single Artifact `C`:
|
||
|
||
```text
|
||
C.bytes = params.bytes
|
||
C.type_tag = params.type_tag
|
||
```
|
||
|
||
If `params` is absent:
|
||
|
||
* `Exec_DAG` MUST treat this as **missing inputs** and return:
|
||
|
||
```text
|
||
status = INVALID_INPUTS
|
||
summary.kind = INPUTS
|
||
summary.status_code = 3
|
||
```
|
||
|
||
* No outputs are produced.
|
||
|
||
#### 4.5.4 Runtime error codes
|
||
|
||
*None.*
|
||
All failures are static (bad Params encoding, wrong arity).
|
||
|
||
---
|
||
|
||
## 5. Conformance
|
||
|
||
An engine is **OPREG/PEL1-KERNEL–conformant** if and only if:
|
||
|
||
1. **Operation availability**
|
||
|
||
* It exposes the five operations defined in §3 with exactly the specified `OperationId { name, version }`.
|
||
|
||
2. **Arity and Params**
|
||
|
||
* For each operation, it enforces `min_inputs`/`max_inputs` as specified.
|
||
* It implements the defined logical Params types and uses the canonical param encoding/decoding for each.
|
||
* It treats:
|
||
|
||
* arity violations, and
|
||
* invalid or undecodable Params
|
||
|
||
as `INVALID_PROGRAM` per `PEL/PROGRAM-DAG/1` (i.e. `Exec_DAG` produces `status = INVALID_PROGRAM`, `summary.status_code = 2`).
|
||
* For `pel.bytes.params/1`, if the scheme-level `params` Artifact is absent,
|
||
`Exec_DAG` MUST return `INVALID_INPUTS` (`summary.status_code = 3`).
|
||
|
||
3. **Runtime semantics**
|
||
|
||
* For all supported operations:
|
||
|
||
* Given the same input Artifacts and Params, all conformant engines produce identical output Artifacts (same `bytes`, same `type_tag`) and identical `status_code` on failure.
|
||
* Runtime failure conditions (e.g. slice out-of-bounds, type tag mismatch) are detected exactly as specified and mapped to the correct `status_code` using the `kernel_op_code`/`error_index` scheme.
|
||
|
||
4. **Status and diagnostics mapping**
|
||
|
||
* When a kernel operation returns `Ok`, `Exec_DAG` MUST NOT change `status` or `summary` (beyond normal success semantics).
|
||
* When a kernel operation returns `Err(status_code)`:
|
||
|
||
* `Exec_DAG` MUST set:
|
||
|
||
```text
|
||
status = RUNTIME_FAILED
|
||
summary.kind = RUNTIME
|
||
summary.status_code = status_code
|
||
diagnostics = []
|
||
```
|
||
|
||
* `Exec_DAG` MUST NOT mutate other fields of `ExecutionResultValue` except as defined in `PEL/PROGRAM-DAG/1` (e.g. to capture which Node failed, via trace profiles).
|
||
|
||
5. **Purity**
|
||
|
||
* Kernel operations MUST not perform external I/O or observe environment state; they must behave as pure functions of their inputs and Params.
|
||
* Any caching or performance optimizations MUST NOT change observable behavior at the level of `Artifact` values and `status_code`.
|
||
|
||
6. **Layering**
|
||
|
||
* The engine does not depend on `ASL/1-STORE`, `TGK/1`, `CIL/1`, `FER/1`, `FCT/1`, or `OI/1` on the PEL core hot path. It may use those layers around PEL, but not as part of the operation semantics.
|
||
|
||
---
|
||
|
||
## 6. Change Log (informative)
|
||
|
||
**v0.1.3 (2025-12-22)**
|
||
|
||
* `pel.bytes.params/1` now requires the global `params` Artifact; missing
|
||
params MUST yield `INVALID_INPUTS` rather than an empty output.
|
||
|
||
**v0.1.2 (2025-12-22)**
|
||
|
||
* Added `pel.bytes.params/1` (code `0x0005`) to return the global `params` Artifact.
|
||
* Defined the empty-params behavior (`params` absent ⇒ empty bytes, no type tag).
|
||
|
||
**v0.1.1 (2025-11-15)**
|
||
|
||
* Removed `pel.bytes.clone/1` from the kernel operation index.
|
||
|
||
* Reassigned `kernel_op_code` values to remove the `0x01` gap:
|
||
|
||
* `pel.bytes.concat/1`: `0x0002` → `0x0001` (and `TYPE_TAG_MISMATCH` from `0x0002_0001` → `0x0001_0001`).
|
||
* `pel.bytes.slice/1`: `0x0003` → `0x0002` (and `RANGE_OUT_OF_BOUNDS` from `0x0003_0001` → `0x0002_0001`).
|
||
* `pel.bytes.const/1`: `0x0004` → `0x0003`.
|
||
* `pel.bytes.hash.asl1/1`: `0x0005` → `0x0004`.
|
||
|
||
* Updated §3, §4, and §5 to reflect the new kernel op set and codes.
|
||
|
||
**v0.1.0 (2025-11-15)**
|
||
|
||
* Initial definition of `OPREG/PEL1-KERNEL` with five kernel operations:
|
||
|
||
* `pel.bytes.clone/1`
|
||
* `pel.bytes.concat/1`
|
||
* `pel.bytes.slice/1`
|
||
* `pel.bytes.const/1`
|
||
* `pel.bytes.hash.asl1/1`
|
||
|
||
* Established `status_code = (kernel_op_code << 16) | error_index` convention.
|
||
|
||
* Restricted `pel.bytes.hash.asl1/1` to `HashId = 0x0001` (HASH-ASL1-256) for cross-implementation determinism.
|
||
|
||
* Required kernel operations to leave `ExecutionResultValue.diagnostics` empty; richer diagnostics to be handled by trace/overlay profiles.
|
||
|
||
---
|
||
|
||
## Document History
|
||
|
||
* **0.1.3 (2025-12-22):** Clarified `pel.bytes.params/1` missing-params handling.
|
||
* **0.1.2 (2025-12-22):** Added `pel.bytes.params/1` to the kernel registry.
|
||
* **0.1.1 (2025-11-16):** Registered as Tier-1 spec and aligned to the Amduat 2.0 substrate baseline.
|