--- id: DEC-0004 ticket: pbs-low-level-asset-manager-surface title: PBS Low-Level Asset Manager Surface for Runtime AssetManager status: accepted created: 2026-03-27 accepted: 2026-03-27 agenda: AGD-0008 plans: [PLN-0004] tags: [compiler, pbs, runtime, asset-manager, host-abi, stdlib, asset] --- ## Decision PBS SHALL expose the runtime asset low-level surface through a reserved host-backed owner named `LowAssets`. The canonical v1 PBS declaration owner SHALL be: ```pbs declare host LowAssets { ... } ``` This owner name is editorial PBS surface only. It MUST NOT change the canonical runtime identities published by `../runtime`. The low-level PBS asset surface SHALL bind to the current runtime syscall identities: - `("asset", "load", 1)` - `("asset", "status", 1)` - `("asset", "commit", 1)` - `("asset", "cancel", 1)` The capability spelling for this surface SHALL be `asset`, in the singular: ```pbs [Capability(name = "asset")] ``` PBS SHALL NOT publish `assets` as the capability string for this low-level surface. For v1, the low-level PBS surface SHALL use raw `int` for: - `asset_id` - `slot` - `handle` - operation and lifecycle status values The canonical illustrative shape is: ```pbs declare host LowAssets { [Host(module = "asset", name = "load", version = 1)] [Capability(name = "asset")] fn load(asset_id: int, slot: int) -> (status: int, loading_handle: int); [Host(module = "asset", name = "status", version = 1)] [Capability(name = "asset")] fn status(loading_handle: int) -> int; [Host(module = "asset", name = "commit", version = 1)] [Capability(name = "asset")] fn commit(loading_handle: int) -> int; [Host(module = "asset", name = "cancel", version = 1)] [Capability(name = "asset")] fn cancel(loading_handle: int) -> int; } ``` ## Rationale This decision locks the low-level PBS surface to the runtime contract that actually exists in the workspace on 2026-03-27. The runtime side has already converged on: - `asset.load(asset_id, slot) -> (status, handle)` - `asset.status(handle) -> status` - `asset.commit(handle) -> status` - `asset.cancel(handle) -> status` The earlier name-based low-level load path is no longer the active runtime contract. PBS SHOULD therefore stop treating that retired shape as a first-class candidate. `LowAssets` was chosen instead of `LowAsset` or `LowAssetManager` because: - it reads naturally beside higher-level surfaces such as a future `Assets` facade; - it matches the user-facing plurality of the domain without changing runtime ABI identities; - it keeps the source declaration owner concise. Raw `int` was chosen for v1 because: - the runtime ABI is slot-based and already integer-shaped; - the immediate need is to publish a stable low-level bridge, not a richer type taxonomy; - delaying the SDK interface until nominal wrappers are designed would block spec and implementation progress without changing executable semantics. Capability spelling remains singular `asset` because capability names are operational registry identities, not editorial surface names. They MUST stay aligned with the runtime capability map. ## Technical Specification ### 1. Ownership And Binding - PBS low-level asset access MUST be declared through `declare host LowAssets`. - Each member MUST carry a canonical `[Host(module = "asset", name = "...", version = 1)]` attribute. - Each member MUST carry `[Capability(name = "asset")]`. - PBS compilers and loaders MUST treat `module`, `name`, `version`, argument slots, return slots, and capability spelling as runtime-facing operational identity. ### 2. ABI Shape The required v1 members are: 1. `load(asset_id: int, slot: int) -> (status: int, loading_handle: int)` 2. `status(loading_handle: int) -> int` 3. `commit(loading_handle: int) -> int` 4. `cancel(loading_handle: int) -> int` Normative ABI correspondence: - `load` MUST lower to runtime syscall identity `("asset", "load", 1)` with `arg_slots = 2` and `ret_slots = 2`. - `status` MUST lower to `("asset", "status", 1)` with `arg_slots = 1` and `ret_slots = 1`. - `commit` MUST lower to `("asset", "commit", 1)` with `arg_slots = 1` and `ret_slots = 1`. - `cancel` MUST lower to `("asset", "cancel", 1)` with `arg_slots = 1` and `ret_slots = 1`. ### 3. Value Semantics - `asset_id` SHALL represent the runtime asset identity carried by `asset_table`. - `slot` SHALL represent the target runtime slot index. - `handle` SHALL represent the runtime load-operation handle returned by `load` when status is `OK`. - PBS source signatures SHOULD use a non-keyword parameter label such as `loading_handle` for this value. - status values SHALL remain raw integer domain values in the low-level surface for v1. PBS low-level callers MUST NOT pass: - `asset_name` - `bank_type` - any alternate symbolic asset reference directly to `LowAssets.load` The runtime resolves bank kind from `asset_table` using `asset_id`. PBS low-level surface MUST preserve that rule. ### 4. Relationship To Higher-Level Surfaces - This decision defines only the low-level host-backed asset surface. - It does NOT define the final author-facing asset API. - Any future symbolic PBS asset surface, including `Addressable` or `assets.foo.bar` style references, MUST lower to `asset_id` before the final `LowAssets.load(...)` host call. - A future `declare service Assets` MAY wrap `LowAssets`, but such facade MUST NOT alter the canonical host identities defined here. ### 5. Scope Boundary This decision covers: - the low-level owner name; - capability spelling; - canonical host bindings; - raw value shape for v1. This decision does NOT yet cover: - nominal wrapper types for `AssetId`, `Handle`, or `SlotIndex`; - enum surfaces for asset status values; - `bank.info` / `bank.slot_info` integration; - the author-facing symbolic assets surface from `AGD-0006`. ## Constraints - PBS MUST stay aligned with the runtime capability registry and syscall registry as implemented in `../runtime`. - PBS MUST NOT publish a plural capability string `assets` for this surface. - PBS MUST NOT reintroduce a name-based low-level load contract into the canonical v1 PBS host surface. - Any future editorial refinement to stronger low-level types MUST preserve the same runtime syscall identities unless a new decision explicitly revises this one. - Plans and implementation derived from this decision MUST propagate updates to PBS specs and stdlib resources before introducing user-facing wrappers. ## Revision Log - 2026-03-27: Initial accepted decision from AGD-0008. - 2026-03-27: Linked execution plan PLN-0004. - 2026-03-27: Refined illustrative PBS signatures to use named tuple returns and non-keyword handle labels without changing the locked runtime contract.