# Asset Management Domain: asset runtime surface Function: normative This chapter defines the runtime-facing asset model of PROMETEU. ## 1 Scope PROMETEU asset management is bank-centric. Assets are: - cold bytes stored in the cartridge; - described by cartridge metadata; - materialized into host-managed banks; - separate from VM heap ownership. This chapter describes the runtime contract currently visible in the codebase. It is not a full tooling pipeline specification. ## 2 Core Principles 1. asset residency is explicit; 2. asset memory belongs to the machine, not to the VM heap; 3. banks and slots are hardware/runtime concepts; 4. loading and activation are explicit operations; 5. asset memory does not participate in GC. ## 3 Cartridge Asset Artifact The runtime currently consumes one primary cartridge asset artifact: - `assets.pa`: autocontained asset artifact. `assets.pa` carries, inside the same binary: - fixed binary prelude; - JSON header; - payload bytes. The JSON header carries: - `asset_table`: metadata entries describing asset content; - `preload`: optional initial residency requests consumed during cartridge initialization. This chapter describes the runtime-facing asset contract. It does not define the Studio packer workflow or the shipper pipeline that publishes the cartridge. ### 3.1 `assets.pa` v1 envelope `assets.pa` v1 is structured as: ```text [fixed binary prelude] [json header] [binary payload region] ``` The fixed binary prelude contains, at minimum: - `magic` - `schema_version` - `header_len` - `payload_offset` It may additionally include: - `flags` - `reserved` - `header_checksum` ### 3.2 Header and payload contract The runtime loads: - `asset_table` from the JSON header and keeps it live during cartridge execution; - `preload` from the JSON header and consumes it only during boot. Payload bytes are addressed from the payload region using offsets relative to `payload_offset`, not relative to the start of the whole file. ## 4 Asset Table Current runtime-facing asset metadata includes: ```text AssetEntry { asset_id asset_name bank_type offset size decoded_size codec metadata } ``` This table describes content identity and storage layout, not live residency. `asset_id` is the stable runtime-facing asset identity and uses 32-bit signed integer semantics compatible with Java `int`. `offset` is relative to the start of the payload region inside `assets.pa`. `size` is the serialized byte count stored in the payload region. `decoded_size` is the byte count of the materialized runtime bank after decode, not necessarily the same as the serialized payload size. `codec` identifies the generic transformation pipeline applied to the serialized payload slice before the asset becomes a resident bank. `codec` does not define the bank-specific serialized layout itself. Specialized banks may still have normative decode rules even when `codec = NONE`. ### 4.1 `TILES` asset contract in v1 For `BankType::TILES`, the runtime-facing v1 contract is: - `codec = NONE` - serialized pixels use packed `u4` palette indices - serialized palettes use `RGB565` (`u16`, little-endian) - `palette_count = 64` - runtime materialization may expand pixel indices to one `u8` per pixel `NONE` for `TILES` means there is no additional generic codec layer beyond the bank contract itself. For the current transition window: - `RAW` is a deprecated legacy alias of `NONE` - new published material must use `NONE` as the canonical value Even with `codec = NONE`, `TILES` still requires deterministic bank-specific decode from its serialized payload. The serialized byte layout is therefore not required to be identical to the in-memory layout. Required `AssetEntry.metadata` fields for `TILES`: - `tile_size`: tile edge in pixels; valid values are `8`, `16`, or `32` - `width`: full bank sheet width in pixels - `height`: full bank sheet height in pixels - `palette_count`: number of palettes serialized for the bank Validation rules for `TILES` v1: - `palette_count` must be `64` - `width * height` defines the number of logical indexed pixels in the decoded sheet - additional metadata may exist, but the runtime contract must not depend on it to reconstruct the bank in memory Serialized payload layout for `TILES` v1: 1. packed indexed pixels for the full sheet, using `ceil(width * height / 2)` bytes; 2. palette table, using `palette_count * 16 * 2` bytes. The tile-bank payload therefore separates serialized storage form from runtime memory form: - serialized pixel plane: packed `4bpp` - decoded pixel plane: expanded `u8` indices, one entry per pixel - palette table: `64 * 16` colors in `RGB565` For `TILES` v1: - `size` must match `ceil(width * height / 2) + (palette_count * 16 * 2)` - `decoded_size` must match `(width * height) + (palette_count * 16 * 2)` ## 5 Banks and Slots The current runtime exposes bank types: - `TILES` - `SOUNDS` Assets are loaded into explicit slots identified by slot index at the public ABI boundary. The runtime resolves bank context from `asset_table` using `asset_id`. Internally, the runtime may still use a bank-qualified slot reference such as: ```text SlotRef { bank_type, index } ``` That internal representation is derived from the resolved `AssetEntry`, not supplied by the caller. ## 6 Load Lifecycle The runtime asset manager exposes a staged lifecycle: - `PENDING` - `LOADING` - `READY` - `COMMITTED` - `CANCELED` - `ERROR` High-level flow: 1. request load of an asset into a slot; 2. resolve the asset entry from live `asset_table`; 3. open the payload slice in `assets.pa`; 4. perform read/decode/materialization work; 5. mark the load `READY`; 6. explicitly `commit`; 7. activate the resident asset in the slot. The canonical payload paths are: - `ROM -> open_slice -> CODEX/decode -> Bank` - `ROM -> open_slice -> temporary in-memory blob -> CODEX/decode -> Bank` `open_slice` is the runtime-facing concept for opening a limited view over a payload slice. The runtime must not require the whole `assets.pa` payload to remain resident in RAM as its baseline operating mode. `OP_MODE` selects between direct slice consumption and temporary materialization in memory. For v1: - `OP_MODE` is derived from `codec`/CODEX; - explicit per-asset hinting is not part of the baseline contract. - `TILES` with `codec = NONE` may still stage in memory before bank installation because bank-specific decode expands packed pixel indices into the resident representation. - during the migration window, runtime may accept legacy `RAW` as an alias of `NONE`. The runtime does not treat asset installation as implicit side effect. ## 7 Residency and Ownership Asset banks are host/runtime-owned memory. Therefore: - VM heap does not own asset residency; - GC does not scan asset bank memory; - shutting down a cartridge can release bank residency independently of VM heap behavior. - the runtime must not keep the full `assets.pa` payload resident in RAM as a baseline requirement. ## 8 Bank Telemetry The runtime surfaces bank and slot statistics such as: - total bytes; - used bytes; - free bytes; - inflight bytes; - slot occupancy; - resident asset identity per slot. These metrics support debugging, telemetry, and certification-oriented inspection. ## 9 Preload `preload` is stored in the JSON header of `assets.pa`. The normative preload shape is: ```text PreloadEntry { asset_id slot } ``` These preload entries are consumed during cartridge initialization so the asset manager can establish initial residency before normal execution flow. Validation rules: - `preload` is resolved by `asset_id`, not by `asset_name`; - every `preload.asset_id` must exist in the same `asset_table`; - no two preload entries may resolve to the same `(bank_type, slot)` pair; - legacy preload keyed by `asset_name` is invalid for the current contract. Lifecycle rule: - `preload` is boot-time input only; - it does not need to remain live after initialization completes. Bootstrap rule: - invalid preload is a structural cartridge error and must fail cartridge bootstrap before normal execution begins. ## 10 Relationship to Other Specs - [`13-cartridge.md`](13-cartridge.md) defines the cartridge package and the requirement that `assets.pa` carries its own asset header. - [`16-host-abi-and-syscalls.md`](16-host-abi-and-syscalls.md) defines the syscall boundary used to manipulate assets. - [`03-memory-stack-heap-and-allocation.md`](03-memory-stack-heap-and-allocation.md) defines the distinction between VM heap memory and host-owned memory. ## 11 Syscall Surface and Status Policy `asset` follows status-first policy. Fault boundary: - `Trap`: structural ABI misuse (type/arity/capability/shape mismatch); - `status`: operational failure; - `Panic`: internal invariant break only. ### 11.1 MVP syscall shape - `asset.load(asset_id, slot) -> (status:int, handle:int)` - `asset.status(handle) -> status:int` - `asset.commit(handle) -> status:int` - `asset.cancel(handle) -> status:int` Rules: - `handle` is valid only when `load` status is `OK`; - failed `load` returns `handle = 0`; - `commit` and `cancel` must not be silent no-op for unknown/invalid handle state. - `asset.load` resolves the target bank type from `asset_table` using `asset_id`; - public callers must not supply `asset_name` or `bank_type` to `asset.load`; - slot validation and residency/lifecycle rejection remain in `asset` status space and are not delegated to `bank`. ### 11.2 Minimum status tables `asset.load` request statuses: - `0` = `OK` - `3` = `ASSET_NOT_FOUND` - `5` = `SLOT_INDEX_INVALID` - `6` = `BACKEND_ERROR` `asset.status` lifecycle statuses: - `0` = `PENDING` - `1` = `LOADING` - `2` = `READY` - `3` = `COMMITTED` - `4` = `CANCELED` - `5` = `ERROR` - `6` = `UNKNOWN_HANDLE` `asset.commit` and `asset.cancel` operation statuses: - `0` = `OK` - `1` = `UNKNOWN_HANDLE` - `2` = `INVALID_STATE`