# Asset Management Mental Model Status: pedagogical Companion specs: - [`../specs/13-cartridge.md`](../specs/13-cartridge.md) - [`../specs/15-asset-management.md`](../specs/15-asset-management.md) - [`../specs/04-gfx-peripheral.md`](../specs/04-gfx-peripheral.md) Origin decisions: - [`../decisions/011-assets-pa-autocontained-runtime-contract.md`](../decisions/011-assets-pa-autocontained-runtime-contract.md) - [`../decisions/013-asset-codec-none-vs-raw.md`](../decisions/013-asset-codec-none-vs-raw.md) PROMETEU treats assets as a self-contained cart artifact, not as metadata scattered across `manifest.json`, the loader, and the runtime. ## One Runtime Artifact The right mental model is: - `manifest.json` identifies the cart and its capabilities; - `assets.pa` carries the runtime-facing asset contract; - the runtime consumes that contract without depending on auxiliary tooling artifacts. That avoids mixing: - cart editorial metadata; - internal packer details; - runtime loading policy. ## Why `assets.pa` Owns The Asset Contract `assets.pa` puts into the same binary: - a fixed prelude to locate the header; - a JSON header that is easy to inspect; - the cold binary payload of the assets. That split exists to preserve two properties at the same time: - fixed cost to open and validate the artifact; - a readable header that is simple to debug. The important point is not the format by itself. The important point is that the runtime finds everything it needs for assets behind one stable boundary. ## `preload` Is Not `asset_table` Both live in the header, but they serve different roles: - `asset_table` is the live asset-resolution index for the entire execution; - `preload` is only a boot-time instruction for initial residency. A short way to think about it: - `asset_table` stays; - `preload` passes. That prevents the runtime from treating preload as permanent metadata when, in practice, it is only an initial loading impulse. ## Payload-Relative Thinking `AssetEntry` offsets are relative to the start of the payload, not to the start of the whole file. That choice preserves the semantics of the `asset` domain: - `AssetEntry` describes where content sits inside the cold-byte region; - the outer envelope can evolve without contaminating each entry with container details. The runtime only needs to resolve: ```text payload_base + entry.offset ``` ## Slice-First Loading The correct loading mental model is not: ```text ROM -> load the whole assets.pa into RAM -> look up asset ``` The correct flow is: ```text ROM -> open_slice -> CODEX/decode -> Bank ``` or, when the codec requires it: ```text ROM -> open_slice -> temporary blob -> CODEX/decode -> Bank ``` That matters because PROMETEU was designed for low-cost hardware. Keeping the whole payload resident as a baseline wastes memory, couples IO to a monolithic blob, and weakens the runtime's real boundary. ## `OP_MODE` Belongs To The Runtime The cart describes the asset. The runtime decides how to consume the slice. In v1, that decision comes from the `codec`/CODEX: - some assets can be read directly from the limited view; - others require temporary materialization before decode. The key point is to separate: - the asset storage contract; - the generic codec layer, when one exists; - the operational strategy for reading and decode. ## Specialized Banks vs Generic Banks The current asset model works better if you do not treat every bank as equally generic. Some banks are specialized: - `TILES` - `SOUNDS` For these, the bank contract already carries most of the important format rules. That means: - serialized layout belongs primarily to the bank contract; - metadata validation belongs primarily to the bank contract; - `codec` may be absent or minimal. Other banks may be more generic in the future, such as a `BLOB`-style bank. For those, `codec` can do more real work and describe a broader generic transformation pipeline for the payload. That is the right hierarchy: - specialized bank first; - generic codec layer second. Not the other way around. ## Why `NONE` Matters The move from `RAW` to `NONE` is not just a rename. It fixes the mental model. `RAW` encouraged the wrong intuition that the bytes were somehow already "raw" in the runtime sense. But for specialized banks that is often false. `NONE` is better because it says only this: - there is no additional generic codec layer. It does not say: - there is no bank-specific decode; - there is no transformation before residency; - serialized bytes and resident memory are identical. ## Serialized Form Is Not Resident Form One of the most important asset-management lessons in the current runtime is that `codec = NONE` does not mean "bitwise identical to in-memory layout". The tile-bank path is the concrete example: - there is no additional generic codec layer for the asset; - the serialized payload stores indexed pixels as packed `4bpp`; - the palette table remains serialized as `RGB565` little-endian; - the runtime expands pixels into one `u8` index per pixel after decode. That is exactly why `codec` and bank contract must stay separate in the mental model: - `codec` may be absent; - the bank-specific decode may still be real and mandatory. That is why `AssetEntry.size` and `AssetEntry.decoded_size` must be thought of as different concepts: - `size` is transport cost inside `assets.pa`; - `decoded_size` is resident-bank cost after materialization. If those two numbers are treated as interchangeable, telemetry, budgets, and validation all become misleading. ## Tile Banks Teach The Real Boundary Tile banks are useful because they show the real separation of concerns: - `assets.pa` defines the serialized envelope and metadata needed to reconstruct the bank; - the runtime validates that metadata against the expected v1 contract; - the resident `TileBank` is a runtime object, not a direct view over the cold bytes. This is the right PROMETEU mental model: - cartridge asset bytes are transport; - bank objects are execution state. ## Why This Fits PROMETEU This model fits the project well because: - it reduces dependence on metadata scattered through `manifest.json`; - it separates `packer`, `shipper`, and runtime more cleanly; - it allows early failure when `Capability::Asset` requires `assets.pa`; - it preserves a runtime that can run on desktop and on low-cost dedicated hardware. When thinking about assets in PROMETEU, think less in terms of "a manifest with attachments" and more in terms of "a cart with an asset artifact that explains itself".