prometeu-studio/discussion/workflow/agendas/AGD-0005-variable-tile-bank-palette-serialization.md

8.2 KiB

id ticket title status created resolved decision tags
AGD-0005 variable-tile-bank-palette-serialization Variable Glyph Bank Palette Serialization accepted 2026-03-26 2026-07-14 DEC-0038
packer
legacy-import
glyph-bank
palette-serialization
rgba8888
runtime-alignment

Pain

Domain owner: packer, with runtime-facing impact through assets.pa.

The current GLYPH/indexed_v1 glyph-bank contract serializes a fixed block of 64 palettes even when an asset authors far fewer palettes.

After DSC-0038, each palette entry is RGBA8888, so the fixed palette block is now 64 * 16 * 4 = 4096 bytes. That keeps decoding simple, but it wastes cartridge space and leaves a deliberate mismatch between authored palette count and serialized palette count.

Context

Legacy source: docs/packer/agendas/Variable Tile Bank Palette Serialization Agenda.md

Cross-domain dependency:

  • runtime

Runtime follow-up:

  • ../runtime/discussion/workflow/decisions/DEC-0041-variable-glyph-bank-palette-protocol.md is accepted and normative for the runtime side.
  • Runtime DSC-0046 keeps GLYPH/indexed_v1, adopts variable glyph-bank palette serialization, and treats this as an incompatible v1 correction.
  • Runtime plans PLN-0167 through PLN-0172 are done and publish the downstream handoff.

Current runtime-owned GLYPH/indexed_v1 contract:

  • pixels are serialized as packed u4;
  • palettes are serialized as RGBA8888 values in runtime R, G, B, A byte order;
  • palette_count is the number of serialized and resident palettes;
  • palette_count must be in 1..=64;
  • payload size includes exactly palette_count * 16 * 4 palette bytes;
  • palette index 0 is ordinary;
  • each palette has 16 usable colors;
  • transparency is represented by alpha, not by a reserved index or magenta color-key;
  • resident runtime memory materializes exactly palette_count palettes;
  • palette_id is a direct palette identity inside the loaded glyph bank and is valid only when palette_id < palette_count;
  • sparse-to-dense palette remapping is not part of v1;
  • missing palettes must fail explicitly in scene/sprite/composer use, not silently resolve to a default color.

Downstream producers must emit:

  • root effective metadata fields tile_size, width, height, and palette_count;
  • size = ceil(width * height / 2) + palette_count * 16 * 4;
  • decoded_size = width * height + palette_count * 16 * 4;
  • exactly palette_count * 16 * 4 RGBA8888 palette bytes;
  • no GLYPH/indexed_v2 payload for this change;
  • no fixed 64-palette padding unless palette_count = 64 and the payload directly satisfies the runtime v1 contract;
  • no effective runtime behavior based on palette_authored.

DSC-0038 intentionally did not solve variable palette-count serialization. It changed the color encoding from RGB565-era semantics to RGBA8888 and kept the fixed 64-palette model stable for that wave.

Runtime DEC-0041 now closes the cross-domain protocol questions that this agenda previously carried as open. The remaining Studio/packer work is downstream alignment with the runtime-owned contract.

Open Questions

  • Should variable palette serialization arrive as a new payload version or as an incompatible change to the current one?
    • Runtime DEC-0041 resolves this as an incompatible correction to GLYPH/indexed_v1, with no v2 format.
  • If palette_count becomes variable, should runtime still materialize a 64-slot resident bank, or truly shrink in-memory representation too?
    • Runtime DEC-0041 resolves this: resident runtime memory materializes exactly palette_count palettes.
  • Should sparse authored palette indices remain sparse in serialization, or be canonicalized into a dense runtime block?
    • Runtime DEC-0041 rejects sparse-to-dense remapping. palette_id remains direct identity inside the loaded glyph bank.
  • Should this repository create the owner agenda/decision for the runtime side, or should a matching runtime discussion/ artifact be opened before implementation planning?
    • Runtime DSC-0046 now owns the protocol through accepted DEC-0041. Studio/packer must follow that handoff.
  • Which domain owns compatibility for existing cartridges: packer, runtime, or shipper?
    • Runtime DEC-0041 introduces no compatibility mode. Old payloads with palette_count = 64 remain valid only when they satisfy the new contract directly.

Options

Option A - Keep fixed 64-palette serialization in v1

  • Approach: Preserve the current fixed v1 payload with zero-filled unused palette slots.
  • Pro: No runtime change and current specs/tests remain stable.
  • Con: Every glyph bank keeps paying the full 4096-byte palette cost.
  • Maintainability: Medium.

Option B - Align packer output with runtime variable palette serialization

  • Approach: Keep the GLYPH/indexed_v1 format name, serialize only palette_count palettes, and make packer metadata, payload sizes, fixtures, and Studio projections follow runtime DEC-0041.
  • Pro: Removes fixed padding while the project is still in v1, keeps the public format vocabulary stable, and makes runtime metadata match the payload.
  • Con: Requires coordinated packer/spec/test changes and fixture regeneration because the current producer still emits the old fixed-padding shape.
  • Maintainability: Strong if implemented as an explicit contract update, weak if done as an incidental size tweak. Runtime has already closed the protocol questions.

Option C - Keep fixed external metadata but trim trailing palettes

  • Approach: Keep some 64-slot assumptions in metadata or runtime memory, but serialize only a shortened trailing subset of palettes in the payload.
  • Pro: Can reduce cartridge size while limiting some runtime changes.
  • Con: Creates a split-brain contract where serialized count, logical count, and resident count can disagree.
  • Maintainability: Weak. It preserves the ambiguity this agenda is trying to remove.

Discussion

The retained concern is still valid after DSC-0038, but runtime DEC-0041 has now closed the protocol. The problem is no longer an open cross-domain design question; it is a downstream producer-alignment task for Studio/packer.

The old recommendation favored a versioned follow-up because compatibility and runtime stability were treated as constraints. Runtime DEC-0041 removes that constraint: the project is still in v1, keeps GLYPH/indexed_v1, and does not need compatibility for the old fixed 64-palette payload.

That makes Option B the required direction for this repository. Runtime already changed the contract directly; Studio/packer must now emit the runtime-owned shape and stop treating palette_authored or the old 64-padding model as effective runtime behavior.

Option C should be avoided because it preserves multiple meanings for palette count. If the payload becomes variable, palette_count should mean the number of serialized palettes, not an unrelated fixed resident capacity.

Resolution

Recommended direction: adopt Option B.

Updated recommendation:

  1. keep the format name GLYPH/indexed_v1;
  2. follow runtime DEC-0041 as the protocol authority;
  3. remove the fixed 64-palette payload block from packer output;
  4. make packer palette_count describe the serialized and resident RGBA8888 palette count;
  5. emit exactly palette_count * 16 * 4 palette bytes;
  6. update size and decoded_size formulas to use variable palette_count;
  7. do not create a v2 format solely for this change;
  8. do not preserve compatibility with the old fixed-padding payload;
  9. do not introduce sparse-to-dense palette remapping metadata;
  10. treat palette_authored as informative only unless a later runtime decision promotes it to effective metadata;
  11. add downstream fixture coverage for palette_count = 1, an intermediate count, and palette_count = 64;
  12. add a negative producer validation case for metadata/payload mismatch.

Next step suggestion: convert this agenda into a Studio/packer decision that references runtime DEC-0041 and scopes implementation to specs, packer payload emission, Studio projections if needed, tests, and fixtures.