283 lines
7.8 KiB
Markdown
283 lines
7.8 KiB
Markdown
# 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`](../agendas/01.2.%20Asset%20Specification,%20Raw%20Assets,%20and%20Virtual%20Asset%20Contract%20Agenda.md).
|
|
|
|
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_version`
|
|
- `name`
|
|
- `type`
|
|
- `inputs`
|
|
- `output`
|
|
- `preload`
|
|
|
|
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_bank`
|
|
- `sound_bank`
|
|
|
|
The runtime-facing technical target belongs in `output.format`, not in `type`.
|
|
|
|
`name` remains the required logical asset reference label.
|
|
|
|
Meaning:
|
|
|
|
- `name` is not the stable artifact identity;
|
|
- `name` is the human-facing and code-facing reference used by asset-oriented APIs unless a future compile-time rewrite model replaces it;
|
|
- renaming `name` is an API-visible change even though it does not change `asset_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`, or `sources`;
|
|
- 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:
|
|
|
|
```json
|
|
"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.format`
|
|
- `output.codec`
|
|
|
|
`output.metadata` is optional in the common schema, but becomes required whenever the selected format spec requires additional normative parameters.
|
|
|
|
Meaning:
|
|
|
|
- `output.format` identifies the semantic/runtime format contract;
|
|
- `output.codec` identifies how payload bytes are stored for extraction and materialization;
|
|
- `output.metadata` carries 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:
|
|
|
|
- `build` may describe how the packer should transform or organize authoring inputs;
|
|
- if a parameter affects the runtime-facing output contract, it belongs in `output.metadata`;
|
|
- `build` must not become a hidden substitute for runtime-relevant output definition.
|
|
|
|
Practical interpretation:
|
|
|
|
- `output.metadata` describes the contract of the produced payload;
|
|
- `build` describes how the packer gets there.
|
|
|
|
### 7. Preload declaration
|
|
|
|
Each managed asset must declare preload intent explicitly.
|
|
|
|
The baseline shape is:
|
|
|
|
```json
|
|
"preload": {
|
|
"enabled": true
|
|
}
|
|
```
|
|
|
|
Rules:
|
|
|
|
- `preload.enabled` is 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.json` whenever 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.json` carries its own `schema_version`;
|
|
- registry schemas are versioned independently;
|
|
- runtime-facing artifact schemas are versioned independently;
|
|
- format contracts are versioned independently through `output.format` values such as `TILES/indexed_v1` or `SOUNDS/pcm16le_v1`.
|
|
|
|
This prevents unrelated schema evolution from being coupled together.
|
|
|
|
## Invariants and Constraints
|
|
|
|
The following invariants now apply:
|
|
|
|
1. The common `asset.json` contract remains compact.
|
|
2. `type` is authoring-side family identity, not runtime bank identity.
|
|
3. `inputs` is structured by semantic role.
|
|
4. Input path values are always lists.
|
|
5. `name` remains a logical asset reference label even though it is not the stable artifact identity.
|
|
6. `output.format` and `output.codec` are separate required concerns.
|
|
7. Runtime-relevant format details belong in `output.metadata`.
|
|
8. `build` is optional and must not hide runtime-facing output semantics.
|
|
9. `preload.enabled` is the only preload field closed in the current baseline.
|
|
10. Defaults affecting runtime-visible behavior must be materialized explicitly.
|
|
11. 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.pa` payload 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_v1` and `SOUNDS/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
|
|
|
|
```json
|
|
{
|
|
"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
|
|
|
|
```json
|
|
{
|
|
"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
|
|
}
|
|
}
|
|
```
|