# 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 Para `BankType::TILES`, o contrato v1 voltado para o runtime é: - `codec = NONE` - pixels serializados usam índices de paleta `u4` empacotados - paletas serializadas usam `RGB565` (`u16`, little-endian) - `palette_count = 64` - a materialização em runtime pode expandir índices de pixel para um `u8` por pixel `NONE` para `TILES` significa que não há camada de codec genérica adicional além do próprio contrato do banco. Para a janela de transição atual: - `RAW` é um alias legado e depreciado de `NONE` - novos materiais publicados devem usar `NONE` como valor canônico Mesmo com `codec = NONE`, `TILES` ainda requer decode específico de banco a partir de seu payload serializado. O layout de bytes serializados não precisa, portanto, ser idêntico ao layout em memória. #### 4.1.1 Metadata Normalization Seguindo a `DEC-0004`, o campo `AssetEntry.metadata` deve ser estruturado de forma segmentada para evitar ambiguidades. Campos de metadados obrigatórios (efetivos) para `TILES` no nível raiz: - `tile_size`: aresta do tile em pixels; valores válidos são `8`, `16`, ou `32` - `width`: largura total da folha do banco em pixels - `height`: altura total da folha do banco em pixels - `palette_count`: número de paletas serializadas para o banco Subárvores opcionais e informativas: - `metadata.codec`: Configuração específica do codec/compressor (ex: dicionários, flags de compressão). - `metadata.pipeline`: Metadados informativos do processo de build do Studio (ex: source hashes, timestamps, tool versions). Regras de validação para `TILES` v1: - `palette_count` deve ser `64` - `width * height` define o número de pixels indexados lógicos na folha decodificada - metadados adicionais podem existir, mas o contrato do runtime não deve depender deles para reconstruir o banco em memória (exceto se definidos na raiz como campos efetivos). #### 4.1.2 Payload Layout 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: - `GLYPH` - `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. ### 5.1 Canonical Bank Telemetry The canonical visible bank telemetry contract is exposed by `AssetManager`. The per-bank summary is slot-first and uses this shape: ```text BankTelemetry { bank_type used_slots total_slots } ``` Rules: - the visible contract MUST NOT expose byte-oriented bank occupancy as the canonical summary; - canonical bank names are `GLYPH` and `SOUNDS`; - detailed occupancy inspection remains slot-based through slot references, not bank byte totals; - any residual byte accounting MAY exist internally, but it is not part of the visible bank telemetry contract. ## 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`