--- id: LSN-0054 ticket: variable-tile-bank-palette-serialization title: Runtime-Owned Variable Glyph Palette Counts created: 2026-07-14 tags: - packer - studio - glyph-bank - palette-serialization - rgba8888 - runtime-alignment --- # Runtime-Owned Variable Glyph Palette Counts ### Original Problem The first RGBA8888 alignment wave made Studio and packer emit palette colors in the runtime channel order, but it deliberately kept the old fixed glyph-bank palette payload shape. Every `GLYPH/indexed_v1` payload still reserved room for 64 palettes, so the emitted palette block was always `64 * 16 * 4 = 4096` bytes even when the bank used one or a few palettes. That fixed shape left two problems: - packer output was larger than the runtime needed; - `palette_count` could not be trusted as the effective runtime resident count, because the payload still looked like a 64-palette bank. Runtime later accepted the variable-palette protocol for `GLYPH/indexed_v1`. Studio and packer had to follow that runtime-owned wire contract instead of keeping a stale producer-side convention. ### Consolidated Decision Runtime owns the `GLYPH/indexed_v1` protocol. Studio and packer are downstream producers of runtime-conforming `assets.pa` payloads. The accepted rule is: 1. `palette_count` is the effective serialized and resident palette count. 2. `palette_count` must be in the inclusive range `1..=64`. 3. Palette bytes are exactly `palette_count * 16 * 4`. 4. `size = ceil(width * height / 2) + palette_count * 16 * 4`. 5. `decoded_size = width * height + palette_count * 16 * 4`. 6. Palette bytes remain RGBA8888 in `R`, `G`, `B`, `A` order. 7. `GLYPH/indexed_v1` remains the format name. This change does not introduce `GLYPH/indexed_v2`. 8. There is no compatibility mode for the old fixed 64-palette padding. 9. Palette IDs remain direct. There is no sparse-to-dense remapping metadata in v1. 10. `palette_authored`, when present, is informative tooling metadata only. ### Implementation Result The packer specs now define `palette_count` as runtime-effective metadata for glyph banks and describe the variable palette payload in the build-artifact contract. Studio-facing asset workspace docs now keep `palette_count` distinct from any authored or tooling-only palette count. The packer metadata model derives and validates effective palette count instead of hard-coding `64`. Sparse direct palette IDs are preserved by materializing intermediate palette slots up to the highest referenced ID; those slots are not a remapping table. Payload emission now writes exactly the effective number of palette slots. Size and decoded-size accounting use the same formula as the runtime contract, so metadata, asset-table entries, and payload lengths agree. Tests and fixtures cover `palette_count = 1`, intermediate counts, `64`, sparse direct identity, invalid counts above `64`, RGBA8888 byte order, and payload size accounting. Final validation passed with: - `discussion validate`; - `./gradlew :prometeu-packer:prometeu-packer-v1:test`; - `./gradlew build`. ### Practical Examples A 256 by 256 glyph bank with one palette has: ```text pixel bytes = ceil(256 * 256 / 2) = 32768 palette bytes = 1 * 16 * 4 = 64 size = 32768 + 64 = 32832 decoded_size = 256 * 256 + 64 = 65600 ``` A bank that references palette ID `6` has an effective `palette_count` of at least `7`. Palette slots `0` through `6` exist in the serialized palette block. If some lower slots were not authored directly, they are deterministic placeholders; palette `6` is not remapped to palette `0`. ### Common Pitfalls and Anti-patterns - Do not infer runtime payload length from the maximum allowed palette count. `64` is the upper bound, not the default emitted count. - Do not use `palette_authored` as a runtime-effective field. It may describe a tool view, but runtime loads `palette_count`. - Do not preserve fixed 4096-byte palette padding for banks with fewer than 64 effective palettes. - Do not create `GLYPH/indexed_v2` to carry this change. The accepted runtime protocol keeps the v1 format name. - Do not introduce sparse-to-dense palette remapping metadata. Existing scene, sprite, packer, and runtime references use direct palette identity. - Do not combine RGBA8888 channel-order work with palette-count semantics. RGBA8888 defines each color value; `palette_count` defines how many palettes are serialized and resident. ### References - Runtime decision: `../runtime/discussion/workflow/decisions/DEC-0041-variable-glyph-bank-palette-protocol.md`. - Studio/packer decision: `discussion/workflow/decisions/DEC-0038-variable-glyph-bank-palette-serialization.md`. - `PLN-0087`: Variable glyph palette spec propagation. - `PLN-0088`: Variable glyph palette metadata and count model. - `PLN-0089`: Variable glyph palette payload emission and size accounting. - `PLN-0090`: Studio and packer projections for variable glyph palettes. - `PLN-0091`: Variable glyph palette tests, fixtures, and final validation. - Prior lesson: `discussion/lessons/DSC-0038-studio-packer-rgba8888-asset-pipeline/LSN-0053-rgba8888-is-the-canonical-studio-packer-palette-contract.md`. - `docs/specs/packer/3. Asset Declaration and Virtual Asset Contract Specification.md`. - `docs/specs/packer/4. Build Artifacts and Deterministic Packing Specification.md`. - `docs/specs/studio/4. Assets Workspace Specification.md`.