7.8 KiB
Asset Specification, Raw Assets, and Virtual Asset Contract Decision
Status
Accepted
Date
2026-03-11
Context
This decision closes the architectural questions raised in 01.2. Asset Specification, Raw Assets, and Virtual Asset Contract Agenda.
The packer needs a stable baseline contract for asset.json before detailed format-specific specs are written.
This contract must:
- support one managed asset root with many internal inputs;
- distinguish raw assets from virtual assets without making the common schema monolithic;
- expose runtime-relevant output information explicitly;
- declare preload intent deterministically;
- leave room for future format families without coupling all formats together.
Decision
The packer adopts a compact common asset.json contract with explicit separation between:
- authoring asset family;
- grouped input declaration;
- runtime-facing output contract;
- preload declaration;
- optional build/process hints.
1. Common top-level shape
The common asset.json contract includes these required top-level fields:
schema_versionnametypeinputsoutputpreload
The common contract may additionally include:
build
build is optional in the shared baseline schema.
2. Meaning of type
type identifies the authoring-side asset family.
It is not the runtime bank target.
Examples of valid family-style values:
image_banksound_bank
The runtime-facing technical target belongs in output.format, not in type.
name remains the required logical asset reference label.
Meaning:
nameis not the stable artifact identity;nameis the human-facing and code-facing reference used by asset-oriented APIs unless a future compile-time rewrite model replaces it;- renaming
nameis an API-visible change even though it does not changeasset_id.
3. Input declaration model
inputs is a structured object keyed by semantic role.
Rules:
- each key identifies an input role such as
sprites,palettes, orsources; - each value is a list of paths;
- paths are relative to the asset root;
- even a single input is represented as a list, not as a scalar.
This avoids shape ambiguity and supports grouped virtual assets cleanly.
Example:
"inputs": {
"sprites": [
"sprites/confirm.png",
"sprites/cancel.png"
],
"palettes": [
"palettes/ui_main.pal"
]
}
4. Output contract
output is the runtime-relevant output declaration.
The common baseline requires:
output.formatoutput.codec
output.metadata is optional in the common schema, but becomes required whenever the selected format spec requires additional normative parameters.
Meaning:
output.formatidentifies the semantic/runtime format contract;output.codecidentifies how payload bytes are stored for extraction and materialization;output.metadatacarries format-specific runtime-relevant details.
Codec must remain explicit. It must not be hidden inside format naming when it represents a distinct storage concern.
5. Raw and virtual asset contract
The common schema must support both raw and virtual assets.
Rules:
- raw assets may declare a direct input-to-output path with minimal build metadata;
- virtual assets may declare multiple grouped inputs and optional build/process configuration;
- the common schema remains small, while detailed format behavior is defined in dedicated format specs.
6. Build/process hints
build is optional and carries process-oriented configuration.
Rules:
buildmay describe how the packer should transform or organize authoring inputs;- if a parameter affects the runtime-facing output contract, it belongs in
output.metadata; buildmust not become a hidden substitute for runtime-relevant output definition.
Practical interpretation:
output.metadatadescribes the contract of the produced payload;builddescribes how the packer gets there.
7. Preload declaration
Each managed asset must declare preload intent explicitly.
The baseline shape is:
"preload": {
"enabled": true
}
Rules:
preload.enabledis required and boolean;- preload participation is declared, not inferred;
- packer build uses this field to determine whether preload data is emitted into the runtime-facing artifact contract;
- richer preload policy fields are deferred to future decisions and must not be implied silently now.
8. Defaults and materialization
Defaults may exist in specs, but defaults that affect reproducibility, compatibility, preload, or runtime-visible output must not remain invisible.
Rules:
- runtime-relevant defaults should be materialized in
asset.jsonwhenever practical; - if materialization occurs later in the pipeline, the resulting artifact must still expose the effective value explicitly;
- packer must not rely on hidden defaults to produce runtime-visible behavior.
9. Versioning boundary
Versioning is split by concern.
Rules:
asset.jsoncarries its ownschema_version;- registry schemas are versioned independently;
- runtime-facing artifact schemas are versioned independently;
- format contracts are versioned independently through
output.formatvalues such asTILES/indexed_v1orSOUNDS/pcm16le_v1.
This prevents unrelated schema evolution from being coupled together.
Invariants and Constraints
The following invariants now apply:
- The common
asset.jsoncontract remains compact. typeis authoring-side family identity, not runtime bank identity.inputsis structured by semantic role.- Input path values are always lists.
nameremains a logical asset reference label even though it is not the stable artifact identity.output.formatandoutput.codecare separate required concerns.- Runtime-relevant format details belong in
output.metadata. buildis optional and must not hide runtime-facing output semantics.preload.enabledis the only preload field closed in the current baseline.- Defaults affecting runtime-visible behavior must be materialized explicitly.
- Format evolution is versioned independently from registry and artifact schema evolution.
Explicit Non-Decisions
This decision does not yet define:
- the full field-level schema of each format family;
- richer preload policies beyond
preload.enabled; - globbing support, input discovery shortcuts, or non-path input locators;
- CLI or Studio editor UX for authoring
asset.json; - exact
assets.papayload layout details; - exact runtime preload record encoding.
Those belong to later decisions and specs.
Propagation Targets
This decision must propagate to:
- common asset declaration spec;
- virtual asset contract spec;
- format-specific specs such as
TILES/indexed_v1andSOUNDS/pcm16le_v1; - preload emission and artifact mapping specs;
- Studio service contracts for asset authoring and validation;
- future implementation of asset parsing, validation, and deterministic build planning.
Validation Notes
Example: grouped image asset
{
"schema_version": 1,
"name": "ui_atlas",
"type": "image_bank",
"inputs": {
"sprites": [
"sprites/confirm.png",
"sprites/cancel.png"
],
"palettes": [
"palettes/ui_main.pal"
]
},
"output": {
"format": "TILES/indexed_v1",
"codec": "RAW",
"metadata": {
"tile_size": [8, 8]
}
},
"preload": {
"enabled": true
},
"build": {
"layout": "atlas"
}
}
Example: grouped sound asset
{
"schema_version": 1,
"name": "ui_sounds",
"type": "sound_bank",
"inputs": {
"sources": [
"wav/click.wav",
"wav/confirm.wav"
]
},
"output": {
"format": "SOUNDS/pcm16le_v1",
"codec": "RAW"
},
"preload": {
"enabled": false
}
}