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

186 lines
8.2 KiB
Markdown

---
id: AGD-0005
ticket: variable-tile-bank-palette-serialization
title: Variable Glyph Bank Palette Serialization
status: accepted
created: 2026-03-26
resolved: 2026-07-14
decision: DEC-0038
tags:
- 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
- [x] 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.
- [x] 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.
- [x] 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.
- [x] 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.
- [x] 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.