9.8 KiB
Tile Bank Packing Materialization Decision
Status: Accepted
Date: 2026-03-20
Domain Owner: docs/packer
Cross-Domain Impact: ../runtime, docs/studio
Context
The packer execution contract for packWorkspace(...) is already closed.
What remained open for tile bank was the format-specific producer contract:
- which selected files become part of the packed asset;
- how those files become the canonical
TILES/indexed_v1payload; - how
AssetEntryfields are derived for runtime consumption; - how bank palettes are declared and normalized;
- which diagnostics block the build for this format.
The agenda that closed this discussion is:
Relevant upstream references are:
../decisions/Pack Wizard Pack Execution Semantics Decision.md../specs/3. Asset Declaration and Virtual Asset Contract Specification.md../specs/4. Build Artifacts and Deterministic Packing Specification.md../pull-requests/PR-32-palette-declarations-with-explicit-index-contract.md../../../runtime/docs/runtime/specs/04-gfx-peripheral.md../../../runtime/docs/runtime/specs/15-asset-management.md../../../runtime/docs/runtime/agendas/024-asset-entry-metadata-normalization-contract.md
Decision
The first-wave packer materialization contract for tile bank adopts the following direction:
TILES/indexed_v1emits one canonical payload per asset.1 artifact = 1 tilein the first wave.- Artifacts are ordered by normalized
artifacts[*].index. - Tile placement is row-major in one fixed
256 x 256sheet. tile_idis the linear row-major slot and therefore matches the normalized artifact index.- Tile pixels are serialized as packed
u4indices. - Bank palettes are serialized as
RGB565u16values. - Palette declarations use explicit semantic identity through
{ index, palette }. AssetEntry.metadatakeeps required runtime fields readable at the root while preserving segmented codec and pipeline subtrees.- Format-structural diagnostics are raised in the walker/materialization path and therefore participate in validation before pack execution reruns the gate.
Adopted Constraints
1. Canonical Payload Shape
- the payload is produced from build-selected artifacts only;
- the payload is the canonical bank sheet raster, not a concatenation of per-artifact binary fragments;
- for
TILES/indexed_v1, the serialized payload shape is:- packed
u4pixel indices for the full emitted sheet; - one palette block of
64 * 16 * 2bytes;
- packed
- palettes are always materialized to
RGB565during pack emission; - the producer-side payload contract is aligned to what the runtime loader already requires.
2. Artifact-to-Tile Contract
1 artifact = 1 tilein v1;- artifacts are normalized by
artifacts[*].index; tile_id = artifacts[*].indexafter normalization;- the canonical emitted sheet is always
256 x 256; - placement is row-major within that fixed sheet.
Resulting tile capacities:
tile_size = 8->32 x 32 = 1024tilestile_size = 16->16 x 16 = 256tilestile_size = 32->8 x 8 = 64tiles
3. Runtime Entry Contract
Each emitted tile bank runtime entry must populate:
asset_idasset_namebank_type = TILESoffsetsizedecoded_sizecodec = NONEmetadata
For v1:
size = ceil(width * height / 2) + 2048decoded_size = (width * height) + 2048width = 256height = 256palette_count = 64
4. Palette Contract
- bank palettes are declared in
asset.json.output.pipeline.palettes; - each palette declaration must use explicit shape
{ index, palette }; - semantic ordering is ascending numeric
index, never raw array position; - palette ids are the normalized declared
indexvalues; - any tile in the bank may be rendered with any palette in the bank;
- palette choice is a runtime draw-time concern and is not embedded per tile in the packed payload.
5. Metadata Normalization Contract
AssetEntry.metadata keeps runtime-required fields readable while preserving authoring segmentation:
asset.json.output.metadata->AssetEntry.metadataasset.json.output.codec_configuration->AssetEntry.metadata.codecasset.json.output.pipeline->AssetEntry.metadata.pipeline
For tile-bank v1 this means:
tile_size,width,height, andpalette_countremain directly readable at the metadata root;- codec-specific data is nested under
metadata.codec; - pipeline-derived data is nested under
metadata.pipeline.
This packer decision intentionally aligns with the runtime-side follow-up agenda rather than flattening everything into one ambiguous map.
6. Diagnostics and Failure Semantics
The following conditions are blocking for tile-bank v1:
- duplicate
artifacts[*].index - gaps in normalized
artifacts[*].index - fixed-sheet capacity overflow
- bank without declared palettes
- palette declaration count above
64 - malformed palette declarations
- missing or invalid required format metadata
- any failure to normalize artifacts into one deterministic payload
The following condition is warning-only in the first wave:
- fragile tile indices, meaning tile indices that are not safely represented across the full declared bank palette set
7. Walker and Validation Boundary
- family walkers remain discovery-oriented;
- tile-bank structural diagnostics must be produced in the walker/materialization path;
- validation consumes those diagnostics naturally;
- pack execution reruns the validation gate on a newly created frozen execution snapshot before materialization begins.
Why This Direction Was Chosen
- It matches the runtime consumer contract instead of inventing a producer-local interpretation.
- It keeps
tile bankpayload semantics explicit and deterministic. - It avoids embedding per-artifact or per-draw palette assignment into the packed bytes.
- It preserves stable palette identity through explicit
indexdeclarations. - It keeps validation early without weakening the pack-time rerun gate.
- It gives the runtime a metadata shape that is readable and still semantically segmented.
Explicit Non-Decisions
This decision does not yet define:
- future support for
1 artifact -> many tiles; - non-
256 x 256tile-bank targets; - alternative tile-bank codecs beyond
NONE; - future palette compaction or palette-id remapping strategies;
- the runtime-side final decision for general
AssetEntry.metadatanormalization helpers; - future sprite or tilemap semantic adjustments unrelated to packer-owned payload production.
Implications
- tile-bank packing implementation must materialize a full sheet raster rather than artifact fragments;
- tile-bank payload generation must pack
u4indices explicitly in the packer; - palette declarations and palette overhauling flows must preserve explicit palette
index; - runtime-entry metadata emission must preserve both root-required fields and segmented nested maps;
- tile-bank validation logic belongs in the walker/materialization path and must be reused by pack execution gate reruns.
Propagation Targets
Specs:
../specs/3. Asset Declaration and Virtual Asset Contract Specification.md../specs/4. Build Artifacts and Deterministic Packing Specification.md
Plans:
../pull-requests/PR-32-palette-declarations-with-explicit-index-contract.md- future packer PR for tile-bank payload materialization
- future packer PR for tile-bank validation diagnostics
Cross-domain references:
../../../runtime/docs/runtime/specs/04-gfx-peripheral.md../../../runtime/docs/runtime/specs/15-asset-management.md../../../runtime/docs/runtime/agendas/023-tilemap-empty-cell-vs-tile-id-zero.md../../../runtime/docs/runtime/agendas/024-asset-entry-metadata-normalization-contract.md
Implementation surfaces:
prometeu-packer-v1tile-bank payload materializerprometeu-packer-v1tile-bank diagnostics in walker/materialization pathprometeu-packer-v1metadata convergence forAssetEntry- Studio tile-bank authoring and inspection surfaces that expose palettes or bank composition
Validation Notes
This decision is correctly implemented only when all of the following are true:
- artifact normalization produces one deterministic row-major
256 x 256bank sheet; - emitted tile ids match normalized artifact indices;
- emitted pixel bytes are packed as
u4; - emitted palette bytes are
RGB565u16; - palette declarations are read and written by explicit
index; - runtime-required metadata fields remain readable at the root;
- codec and pipeline metadata survive under
metadata.codecandmetadata.pipeline; - structural blockers are visible during validation and are rerun by pack execution before emission.