# Tile Bank Packing Materialization Agenda Status: Open Domain Owner: `docs/packer` Cross-Domain Impact: `../runtime`, `docs/studio` ## Purpose Convergir a discussão sobre como um asset `tile bank` deve ser materializado durante o `packing` para produzir payload runtime-facing válido dentro de `assets.pa`. Esta agenda não trata do workflow operacional do `Pack Wizard`. Ela trata do contrato técnico de materialização do formato `TILES/indexed_v1`: - quais arquivos entram no pack; - como eles viram payload binário final; - quais campos do `asset_table` são derivados; - quais metadados convergem para `AssetEntry.metadata`; - e quais invariantes devem falhar o build. ## Problem O repositório já tem base suficiente para: 1. descobrir arquivos relevantes de `tile bank`; 2. validar metadados mínimos como `tile_size`; 3. construir snapshots runtime-backed; 4. definir `assets.pa` como artefato autoritativo do runtime. Mas ainda não existe decisão formal sobre a materialização final de `tile bank` no pack. Hoje falta fechar, pelo menos: - qual é o payload binário efetivamente emitido para um `tile bank`; - como múltiplos artifacts selecionados são agregados no payload final; - como `bank_type`, `codec`, `size`, `decoded_size` e `metadata` são derivados para a entrada runtime; - quais dados ficam em `AssetEntry.metadata` versus quais permanecem detalhe interno de pipeline; - quais condições tornam o pack inválido para esse formato. Sem isso, o `packWorkspace(...)` pode até ganhar semântica operacional correta, mas ainda ficará sem contrato suficiente para produzir `assets.pa` conformat para `tile bank`. ## Context O contrato upstream já impõe limites claros: - `assets.pa` é o artefato runtime-facing autoritativo: [`../specs/1. Domain and Artifact Boundary Specification.md`](../specs/1.%20Domain%20and%20Artifact%20Boundary%20Specification.md) - `asset_table` é determinística por `asset_id`, offsets são relativos ao payload region e metadata converge para um sink único: [`../specs/4. Build Artifacts and Deterministic Packing Specification.md`](../specs/4.%20Build%20Artifacts%20and%20Deterministic%20Packing%20Specification.md) - o runtime espera `assets.pa` autocontido com `asset_table` e `preload` válidos: [`../../../runtime/docs/runtime/specs/13-cartridge.md`](../../../runtime/docs/runtime/specs/13-cartridge.md) - o runtime consome `AssetEntry { asset_id, asset_name, bank_type, offset, size, decoded_size, codec, metadata }`: [`../../../runtime/docs/runtime/specs/15-asset-management.md`](../../../runtime/docs/runtime/specs/15-asset-management.md) Contexto atual de código: - `PackerAssetWalker` já reconhece `OutputFormatCatalog.TILES_INDEXED_V1`; - `PackerTileBankWalker` já produz probes/metadata family-relevant; - o snapshot atual já consegue expor arquivos candidatos e metadata de walk; - ainda não existe materialização final de payload de `tile bank` dentro de `assets.pa`. Consumer baseline now confirmed in `../runtime`: - `TILES` now uses `codec = NONE` as the runtime-facing v1 baseline; - serialized tile pixels are packed `u4` palette indices in payload order; - the runtime expands those packed indices into one `u8` logical index per pixel in memory after decode; - tile-bank palettes are serialized as `RGB565` `u16` values in little-endian order; - runtime-facing v1 uses `64` palettes per tile bank; - the runtime loader currently requires the following metadata fields for tile banks: - `tile_size` - `width` - `height` - `palette_count` - for v1, `palette_count` must be `64`; - for v1, the serialized tile-bank payload is: 1. packed indexed pixels for the full sheet, using `ceil(width * height / 2)` bytes; 2. one palette block of `64 * 16 * 2 = 2048` bytes; - for v1, the runtime-side size expectations are: - `size = ceil(width * height / 2) + 2048` - `decoded_size = (width * height) + 2048` - for the producer-side contract discussed here, `tile_id = 0` remains valid and must not be reserved away by the packer. Relevant confirmed runtime references: - [`../../../runtime/docs/runtime/specs/15-asset-management.md`](../../../runtime/docs/runtime/specs/15-asset-management.md) - [`../../../runtime/docs/runtime/specs/04-gfx-peripheral.md`](../../../runtime/docs/runtime/specs/04-gfx-peripheral.md) - [`../../../runtime/crates/console/prometeu-drivers/src/asset.rs`](../../../runtime/crates/console/prometeu-drivers/src/asset.rs) - [`../../../runtime/crates/console/prometeu-hal/src/tile_bank.rs`](../../../runtime/crates/console/prometeu-hal/src/tile_bank.rs) - tilemap empty-cell semantics remain under active runtime discussion and must not currently force the packer to reserve `tile_id = 0`: [`../../../runtime/docs/runtime/agendas/023-tilemap-empty-cell-vs-tile-id-zero.md`](../../../runtime/docs/runtime/agendas/023-tilemap-empty-cell-vs-tile-id-zero.md) Isso significa que o problema agora não é descoberta de arquivos. O lado consumidor já está suficientemente claro. O problema agora é fechar o contrato produtor `tile bank -> runtime asset entry + payload bytes` no packer sem contradizer esse baseline runtime. ## Options ### Option A - Concatenate selected tile artifacts as a simple raw stream Cada artifact selecionado do `tile bank` vira um segmento binário simples, e o payload final do asset é a concatenação determinística desses segmentos. `metadata` carrega apenas o mínimo necessário para o runtime interpretar o asset. ### Option B - Emit one canonical tile-bank payload plus normalized runtime metadata Os artifacts selecionados são primeiro normalizados para um modelo canônico de `tile bank`, e então o packer emite: - um único payload binário canônico para o asset; - um conjunto fechado de campos runtime-facing em `AssetEntry.metadata`. Qualquer detalhe interno adicional de pipeline fica fora do contrato runtime principal ou vai apenas para tooling metadata. ### Option C - Preserve rich per-artifact structure directly in runtime metadata O packer mantém estrutura mais rica de artifacts individuais no próprio `AssetEntry.metadata`, expondo para o runtime detalhes mais próximos da pipeline de build. ## Tradeoffs - Option A é a implementação mais simples, mas corre risco de deixar semântica demais implícita no consumidor. - Option A também pode dificultar compatibilidade futura se a concatenação simples não codificar claramente limites, forma lógica ou derivação de `decoded_size`. - Option B exige fechar um modelo canônico do `tile bank`, mas produz o contrato mais limpo entre packer e runtime. - Option B também respeita melhor a regra já vigente de convergência para `AssetEntry.metadata` sem transformar metadata runtime em espelho da pipeline. - Option C pode parecer flexível no curto prazo, mas mistura detalhe de pipeline com contrato runtime e aumenta acoplamento. - Option C tensiona diretamente o guardrail já documentado de que `asset_table[].metadata` não deve virar depósito arbitrário de estrutura interna. ## Recommendation Adotar `Option B`. O primeiro formato de packing a ser fechado deve ter payload canônico e metadata runtime-facing normalizada. ### Payload Recommendation O `tile bank` deve produzir um payload binário único por asset incluído no build. Regras recomendadas: - o payload é derivado apenas dos artifacts selecionados que realmente entram no build atual; - a ordem de agregação dos artifacts deve ser determinística by `artifacts[*].index`; - for v1, `1 artifact = 1 tile`; - for the current target, the canonical tile-bank sheet is always `256 x 256`; - tile placement inside that fixed sheet is row-major; - `tile_id` is the linear row-major slot and therefore matches the normalized `artifacts[*].index`; - resulting capacity is therefore: - `tile_size = 8` -> `32 x 32 = 1024` tiles - `tile_size = 16` -> `16 x 16 = 256` tiles - `tile_size = 32` -> `8 x 8 = 64` tiles - o payload final do asset deve ter fronteiras e interpretação definidas pelo próprio contrato do formato, não por convenção incidental de concatenação; - para `TILES/indexed_v1`, o payload v1 já deve assumir: 1. plano de pixels packed `u4`; 2. bloco de paletas `64 * 16 * u16`; - palettes must always be materialized to `RGB565` during pack emission; - `size` deve representar o tamanho emitido no payload region; - `decoded_size` deve seguir a convenção runtime já confirmada: tamanho expandido dos indices em memória mais o bloco de paletas runtime-facing. ### Runtime Entry Recommendation Cada `tile bank` emitido para o runtime deve preencher, no mínimo: - `asset_id` - `asset_name` - `bank_type = TILES` - `offset` - `size` - `decoded_size` - `codec` - `metadata` O contrato de `bank_type`, `codec` e `decoded_size` não deve ser deixado implícito no packer implementation detail. Baseline now fixed by the runtime consumer: - `bank_type = TILES` - `codec = NONE` - metadata mínima obrigatória: - `tile_size` - `width` - `height` - `palette_count = 64` - `width` and `height` are bank-sheet helpers, not per-artifact dimensions - with the current v1 target, the emitted bank sheet is fixed at `256 x 256` - producer-side metadata normalization must emit what the consumer requires while preserving segmented authoring meaning: - `asset.json.output.metadata` -> `AssetEntry.metadata` - `asset.json.output.codec_configuration` -> `AssetEntry.metadata.codec` - `asset.json.output.pipeline` -> `AssetEntry.metadata.pipeline` ### Metadata Recommendation `AssetEntry.metadata` deve receber apenas os campos runtime-consumable e format-relevant. Direção inicial recomendada: - metadados declarativos como `tile_size` entram no sink runtime; - metadados derivados necessários para leitura correta do runtime entram no sink runtime, pelo menos: - `width` - `height` - `palette_count`; - `AssetEntry.metadata` should aggregate normalized maps using this structure: - `asset.json.output.metadata` -> `metadata` - `asset.json.output.codec_configuration` -> `metadata.codec` - `asset.json.output.pipeline` -> `metadata.pipeline` - bank palettes are declared in `asset.json.output.pipeline.palettes` using explicit `{ index, palette }` entries and emitted in ascending numeric `index` order; - any tile in the bank may be rendered with any palette in the bank; - palette assignment is therefore not a per-artifact packing contract and remains a runtime draw-time concern; - the packer must nevertheless validate whether the declared bank palette set safely covers the indices used by packed tiles. - detalhes de pipeline úteis apenas para inspeção e tooling não devem dominar `AssetEntry.metadata`; - quando um detalhe interno for necessário apenas para tooling, ele deve preferir companion tooling data em vez de inflar o contrato runtime. ### Failure Recommendation O build de `tile bank` deve falhar quando qualquer uma das seguintes condições acontecer: 1. não existir conjunto suficiente de artifacts selecionados para materialização válida; 2. o metadata declarativo obrigatório do formato estiver ausente ou inválido; 3. a normalização dos artifacts para o payload canônico falhar; 4. houver colisão ou ambiguidade ao convergir metadata runtime-facing; 5. o packer não conseguir derivar de forma determinística os campos exigidos para a entry runtime. Additional first-wave diagnostic expectations: - duplicate `artifacts[*].index` is `blocking`; - gap in normalized `artifacts[*].index` ordering is `blocking`; - sheet-capacity overflow for the fixed `256 x 256` target is `blocking`; - bank without declared palettes in `asset.json.output.pipeline.palettes` is `blocking`; - declared palette list above `64` is `blocking`; - malformed palette declarations are `blocking`; - tiles that use fragile indices, meaning indices not represented safely across the full declared bank palette set, emit a `WARNING`; - that fragile-index warning is advisory in the first wave and does not block pack by itself unless later promoted by decision. ### Packing Boundary Recommendation O seletor de packing para `tile bank` deve operar sobre os probes já descobertos pelo walker family-oriented. Regras: - o walker continua generalista e orientado à family; - a seleção do que entra no payload final acontece na policy/materialization layer de packing; - somente probes do asset registrado, incluído no build, e efetivamente selecionados pelo contrato do formato entram na materialização final; - quando o `PackerRuntimeMaterializationConfig` estiver em `PACKING`, esses probes relevantes devem carregar bytes opcionais preenchidos para congelar o input do pack. - palette declarations in `asset.json.output.pipeline.palettes` carry explicit semantic identity through `index`; - palette order is ascending numeric `index`, never raw array position; - palette ids are the normalized declared `index` values from that pipeline palette list; - all tiles in the bank may use any palette declared in the bank; - palette selection is a runtime draw concern, not a tile-payload embedding concern. ## Open Questions 1. None at this stage for the tile-bank v1 producer contract. ## Expected Follow-up 1. Converter esta agenda em uma `decision` de `docs/packer` para materialização de `tile bank`. 2. Propagar o contrato resultante para: - `docs/packer/specs/4. Build Artifacts and Deterministic Packing Specification.md` - specs runtime relevantes em `../runtime` 3. Planejar PR de implementação do materializador de `tile bank` em `prometeu-packer-v1`. 4. Adicionar testes de conformance do formato: `artifacts -> payload -> asset_table entry -> runtime-read assumptions`.