prometeu-studio/docs/packer/decisions/Tile Bank Packing Materialization Decision.md
2026-03-24 13:42:56 +00:00

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_v1 payload;
  • how AssetEntry fields 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:

Decision

The first-wave packer materialization contract for tile bank adopts the following direction:

  1. TILES/indexed_v1 emits one canonical payload per asset.
  2. 1 artifact = 1 tile in the first wave.
  3. Artifacts are ordered by normalized artifacts[*].index.
  4. Tile placement is row-major in one fixed 256 x 256 sheet.
  5. tile_id is the linear row-major slot and therefore matches the normalized artifact index.
  6. Tile pixels are serialized as packed u4 indices.
  7. Bank palettes are serialized as RGB565 u16 values.
  8. Palette declarations use explicit semantic identity through { index, palette }.
  9. AssetEntry.metadata keeps required runtime fields readable at the root while preserving segmented codec and pipeline subtrees.
  10. 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:
    1. packed u4 pixel indices for the full emitted sheet;
    2. one palette block of 64 * 16 * 2 bytes;
  • palettes are always materialized to RGB565 during pack emission;
  • the producer-side payload contract is aligned to what the runtime loader already requires.

2. Artifact-to-Tile Contract

  • 1 artifact = 1 tile in v1;
  • artifacts are normalized by artifacts[*].index;
  • tile_id = artifacts[*].index after 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 = 1024 tiles
  • tile_size = 16 -> 16 x 16 = 256 tiles
  • tile_size = 32 -> 8 x 8 = 64 tiles

3. Runtime Entry Contract

Each emitted tile bank runtime entry must populate:

  • asset_id
  • asset_name
  • bank_type = TILES
  • offset
  • size
  • decoded_size
  • codec = NONE
  • metadata

For v1:

  • size = ceil(width * height / 2) + 2048
  • decoded_size = (width * height) + 2048
  • width = 256
  • height = 256
  • palette_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 index values;
  • 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.metadata
  • asset.json.output.codec_configuration -> AssetEntry.metadata.codec
  • asset.json.output.pipeline -> AssetEntry.metadata.pipeline

For tile-bank v1 this means:

  • tile_size, width, height, and palette_count remain 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 bank payload 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 index declarations.
  • 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 256 tile-bank targets;
  • alternative tile-bank codecs beyond NONE;
  • future palette compaction or palette-id remapping strategies;
  • the runtime-side final decision for general AssetEntry.metadata normalization 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 u4 indices 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:

Plans:

Cross-domain references:

Implementation surfaces:

  • prometeu-packer-v1 tile-bank payload materializer
  • prometeu-packer-v1 tile-bank diagnostics in walker/materialization path
  • prometeu-packer-v1 metadata convergence for AssetEntry
  • 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 256 bank sheet;
  • emitted tile ids match normalized artifact indices;
  • emitted pixel bytes are packed as u4;
  • emitted palette bytes are RGB565 u16;
  • 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.codec and metadata.pipeline;
  • structural blockers are visible during validation and are rerun by pack execution before emission.