implements PLN-0091
All checks were successful
JaCoCo Coverage #### Project Overview No changes detected, that affect the code coverage. * Line Coverage: 61.08% (17147/28073) * Branch Coverage: 51.93% (6599/12707) * Lines of Code: 28073 * Cyclomatic Complexity: 11234 #### Quality Gates Summary Output truncated.
Test / Build skipped: 15, passed: 583
Intrepid/Prometeu/Studio/pipeline/head This commit looks good
Intrepid/Prometeu/Studio/pipeline/pr-master This commit looks good

This commit is contained in:
bQUARKz 2026-07-14 16:33:59 +01:00
parent ffa41db994
commit ae1ba0d2d3
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
4 changed files with 107 additions and 11 deletions

View File

@ -18,7 +18,7 @@
{"type":"discussion","id":"DSC-0002","status":"done","ticket":"palette-management-in-studio","title":"Palette Management in Studio","created_at":"2026-03-26","updated_at":"2026-04-23","tags":["studio","legacy-import","palette-management","tile-bank","packer-boundary"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0042","file":"discussion/lessons/DSC-0002-palette-management-in-studio/LSN-0042-schema-driven-palette-authoring-and-local-metadata-events.md","status":"done","created_at":"2026-04-23","updated_at":"2026-04-23"}]} {"type":"discussion","id":"DSC-0002","status":"done","ticket":"palette-management-in-studio","title":"Palette Management in Studio","created_at":"2026-03-26","updated_at":"2026-04-23","tags":["studio","legacy-import","palette-management","tile-bank","packer-boundary"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0042","file":"discussion/lessons/DSC-0002-palette-management-in-studio/LSN-0042-schema-driven-palette-authoring-and-local-metadata-events.md","status":"done","created_at":"2026-04-23","updated_at":"2026-04-23"}]}
{"type":"discussion","id":"DSC-0003","status":"done","ticket":"packer-docs-import","title":"Import docs/packer into discussion-framework artifacts","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["packer","migration","discussion-framework","docs-import"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0009","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0009-mental-model-packer-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0010","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0010-asset-identity-and-runtime-contract-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0011","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0011-foundations-workspace-runtime-and-build-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0012","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0012-runtime-ownership-and-studio-boundary-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0013","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0013-metadata-convergence-and-runtime-sink-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0014","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0014-pack-wizard-summary-validation-and-pack-execution-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0015","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0015-tile-bank-packing-contract-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0017","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0017-packer-docs-import-pattern.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"}]} {"type":"discussion","id":"DSC-0003","status":"done","ticket":"packer-docs-import","title":"Import docs/packer into discussion-framework artifacts","created_at":"2026-03-26","updated_at":"2026-03-26","tags":["packer","migration","discussion-framework","docs-import"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0009","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0009-mental-model-packer-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0010","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0010-asset-identity-and-runtime-contract-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0011","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0011-foundations-workspace-runtime-and-build-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0012","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0012-runtime-ownership-and-studio-boundary-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0013","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0013-metadata-convergence-and-runtime-sink-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0014","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0014-pack-wizard-summary-validation-and-pack-execution-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0015","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0015-tile-bank-packing-contract-legacy.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"},{"id":"LSN-0017","file":"discussion/lessons/DSC-0003-packer-docs-import/LSN-0017-packer-docs-import-pattern.md","status":"done","created_at":"2026-03-26","updated_at":"2026-03-26"}]}
{"type":"discussion","id":"DSC-0004","status":"abandoned","ticket":"tilemap-and-metatile-runtime-binary-layout","title":"Tilemap and Metatile Runtime Binary Layout","created_at":"2026-03-26","updated_at":"2026-04-24","tags":["packer","legacy-import","tilemap","metatile","runtime-layout"],"agendas":[{"id":"AGD-0004","file":"AGD-0004-tilemap-and-metatile-runtime-binary-layout.md","status":"abandoned","created_at":"2026-03-26","updated_at":"2026-04-24","_override_reason":"Explicit user request on 2026-04-24 to abandon AGD-0004 because the agenda is no longer valid."}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0004","status":"abandoned","ticket":"tilemap-and-metatile-runtime-binary-layout","title":"Tilemap and Metatile Runtime Binary Layout","created_at":"2026-03-26","updated_at":"2026-04-24","tags":["packer","legacy-import","tilemap","metatile","runtime-layout"],"agendas":[{"id":"AGD-0004","file":"AGD-0004-tilemap-and-metatile-runtime-binary-layout.md","status":"abandoned","created_at":"2026-03-26","updated_at":"2026-04-24","_override_reason":"Explicit user request on 2026-04-24 to abandon AGD-0004 because the agenda is no longer valid."}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0005","status":"open","ticket":"variable-tile-bank-palette-serialization","title":"Variable Glyph Bank Palette Serialization","created_at":"2026-03-26","updated_at":"2026-07-14","tags":["packer","legacy-import","glyph-bank","palette-serialization","rgba8888","runtime-alignment"],"agendas":[{"id":"AGD-0005","file":"AGD-0005-variable-tile-bank-palette-serialization.md","status":"accepted","created_at":"2026-03-26","updated_at":"2026-07-14"}],"decisions":[{"id":"DEC-0038","file":"DEC-0038-variable-glyph-bank-palette-serialization.md","status":"accepted","created_at":"2026-07-14","updated_at":"2026-07-14","ref_agenda":"AGD-0005"}],"plans":[{"id":"PLN-0087","file":"PLN-0087-variable-glyph-palette-spec-propagation.md","status":"done","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0038"]},{"id":"PLN-0088","file":"PLN-0088-variable-glyph-palette-metadata-and-count-model.md","status":"done","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0038"]},{"id":"PLN-0089","file":"PLN-0089-variable-glyph-palette-payload-emission-and-size-accounting.md","status":"done","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0038"]},{"id":"PLN-0090","file":"PLN-0090-studio-and-packer-projections-for-variable-glyph-palettes.md","status":"done","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0038"]},{"id":"PLN-0091","file":"PLN-0091-variable-glyph-palette-tests-fixtures-and-final-validation.md","status":"open","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0038"]}],"lessons":[]} {"type":"discussion","id":"DSC-0005","status":"open","ticket":"variable-tile-bank-palette-serialization","title":"Variable Glyph Bank Palette Serialization","created_at":"2026-03-26","updated_at":"2026-07-14","tags":["packer","legacy-import","glyph-bank","palette-serialization","rgba8888","runtime-alignment"],"agendas":[{"id":"AGD-0005","file":"AGD-0005-variable-tile-bank-palette-serialization.md","status":"accepted","created_at":"2026-03-26","updated_at":"2026-07-14"}],"decisions":[{"id":"DEC-0038","file":"DEC-0038-variable-glyph-bank-palette-serialization.md","status":"accepted","created_at":"2026-07-14","updated_at":"2026-07-14","ref_agenda":"AGD-0005"}],"plans":[{"id":"PLN-0087","file":"PLN-0087-variable-glyph-palette-spec-propagation.md","status":"done","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0038"]},{"id":"PLN-0088","file":"PLN-0088-variable-glyph-palette-metadata-and-count-model.md","status":"done","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0038"]},{"id":"PLN-0089","file":"PLN-0089-variable-glyph-palette-payload-emission-and-size-accounting.md","status":"done","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0038"]},{"id":"PLN-0090","file":"PLN-0090-studio-and-packer-projections-for-variable-glyph-palettes.md","status":"done","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0038"]},{"id":"PLN-0091","file":"PLN-0091-variable-glyph-palette-tests-fixtures-and-final-validation.md","status":"done","created_at":"2026-07-14","updated_at":"2026-07-14","ref_decisions":["DEC-0038"]}],"lessons":[]}
{"type":"discussion","id":"DSC-0006","status":"done","ticket":"pbs-game-facing-asset-refs-and-call-result-discard","title":"PBS Game-Facing Asset References and Ignored Call Result Lowering","created_at":"2026-03-27","updated_at":"2026-03-30","tags":["compiler","pbs","ergonomics","lowering","runtime","asset-identity","expression-statements"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0024","file":"discussion/lessons/DSC-0006-pbs-game-facing-asset-refs-and-call-result-discard/LSN-0024-addressable-surface-host-metadata-and-ignored-value-discipline.md","status":"done","created_at":"2026-03-30","updated_at":"2026-03-30"}]} {"type":"discussion","id":"DSC-0006","status":"done","ticket":"pbs-game-facing-asset-refs-and-call-result-discard","title":"PBS Game-Facing Asset References and Ignored Call Result Lowering","created_at":"2026-03-27","updated_at":"2026-03-30","tags":["compiler","pbs","ergonomics","lowering","runtime","asset-identity","expression-statements"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0024","file":"discussion/lessons/DSC-0006-pbs-game-facing-asset-refs-and-call-result-discard/LSN-0024-addressable-surface-host-metadata-and-ignored-value-discipline.md","status":"done","created_at":"2026-03-30","updated_at":"2026-03-30"}]}
{"type":"discussion","id":"DSC-0007","status":"done","ticket":"pbs-learn-to-discussion-lessons-migration","title":"Migrate PBS Learn Documents into Discussion Lessons","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["compiler","pbs","migration","discussion-framework","lessons","learn-prune"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0018","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0018-pbs-ast-and-parser-contract-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0019","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0019-pbs-name-resolution-and-linking-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0020","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0020-pbs-runtime-values-identity-memory-boundaries-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0021","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0021-pbs-diagnostics-and-conformance-governance-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0022","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0022-pbs-globals-lifecycle-and-published-entrypoint-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]} {"type":"discussion","id":"DSC-0007","status":"done","ticket":"pbs-learn-to-discussion-lessons-migration","title":"Migrate PBS Learn Documents into Discussion Lessons","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["compiler","pbs","migration","discussion-framework","lessons","learn-prune"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0018","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0018-pbs-ast-and-parser-contract-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0019","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0019-pbs-name-resolution-and-linking-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0020","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0020-pbs-runtime-values-identity-memory-boundaries-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0021","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0021-pbs-diagnostics-and-conformance-governance-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0022","file":"discussion/lessons/DSC-0007-pbs-learn-to-discussion-lessons-migration/LSN-0022-pbs-globals-lifecycle-and-published-entrypoint-legacy.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]}
{"type":"discussion","id":"DSC-0008","status":"done","ticket":"pbs-low-level-asset-manager-surface","title":"PBS Low-Level Asset Manager Surface for Runtime AssetManager","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["compiler","pbs","runtime","asset-manager","host-abi","stdlib","asset"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0023","file":"discussion/lessons/DSC-0008-pbs-low-level-asset-manager-surface/LSN-0023-lowassets-runtime-aligned-sdk-surface.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]} {"type":"discussion","id":"DSC-0008","status":"done","ticket":"pbs-low-level-asset-manager-surface","title":"PBS Low-Level Asset Manager Surface for Runtime AssetManager","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["compiler","pbs","runtime","asset-manager","host-abi","stdlib","asset"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0023","file":"discussion/lessons/DSC-0008-pbs-low-level-asset-manager-surface/LSN-0023-lowassets-runtime-aligned-sdk-surface.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]}

View File

@ -2,9 +2,9 @@
id: PLN-0091 id: PLN-0091
ticket: variable-tile-bank-palette-serialization ticket: variable-tile-bank-palette-serialization
title: Variable Glyph Palette Tests, Fixtures, and Final Validation title: Variable Glyph Palette Tests, Fixtures, and Final Validation
status: open status: done
created: 2026-07-14 created: 2026-07-14
completed: completed: 2026-07-14
ref_decisions: ref_decisions:
- DEC-0038 - DEC-0038
tags: tags:
@ -134,13 +134,27 @@ max-bound, or a bug.
## Acceptance Criteria ## Acceptance Criteria
- [ ] Tests cover `palette_count = 1`, an intermediate count, and `64`. - [x] Tests cover `palette_count = 1`, an intermediate count, and `64`.
- [ ] Tests cover variable `size` and `decoded_size`. - [x] Tests cover variable `size` and `decoded_size`.
- [ ] Tests cover emitted palette byte length and RGBA order. - [x] Tests cover emitted palette byte length and RGBA order.
- [ ] Tests cover metadata/payload mismatch rejection or producer validation. - [x] Tests cover metadata/payload mismatch rejection or producer validation.
- [ ] Fixtures no longer rely on fixed 64-palette padding unless count is `64`. - [x] Fixtures no longer rely on fixed 64-palette padding unless count is `64`.
- [ ] Validation commands pass. - [x] Validation commands pass.
- [ ] Residue scan results are clean or explicitly classified. - [x] Residue scan results are clean or explicitly classified.
## Execution Notes
- Added packer workspace coverage for `palette_count = 1` and intermediate
`palette_count = 7`.
- Existing fixture and build paths continue to cover `palette_count = 64` where
a max-count payload is intended.
- Added parser coverage rejecting palette declaration index `64`, preserving
the v1 maximum effective count of `64` through direct ids `0..63`.
- `:prometeu-packer:prometeu-packer-v1:test` passed.
- `./gradlew build` passed.
- Residue scan found only intentional normative statements rejecting
`convertedRgb565`, `GLYPH/indexed_v2`, fixed-padding compatibility, and
sparse-to-dense remapping.
## Dependencies ## Dependencies

View File

@ -338,8 +338,10 @@ final class FileSystemPackerWorkspaceServiceTest {
assertEquals(16, assetTable.get(0).path("metadata").path("tile_size").asInt()); assertEquals(16, assetTable.get(0).path("metadata").path("tile_size").asInt());
assertEquals(256, assetTable.get(0).path("metadata").path("width").asInt()); assertEquals(256, assetTable.get(0).path("metadata").path("width").asInt());
assertEquals(256, assetTable.get(0).path("metadata").path("height").asInt()); assertEquals(256, assetTable.get(0).path("metadata").path("height").asInt());
assertEquals(64, assetTable.get(0).path("metadata").path("palette_count").asInt()); assertEquals(1, assetTable.get(0).path("metadata").path("palette_count").asInt());
assertEquals(1, assetTable.get(0).path("metadata").path("palette_authored").asInt()); assertEquals(1, assetTable.get(0).path("metadata").path("palette_authored").asInt());
assertEquals(32768 + 64, assetTable.get(0).path("size").asInt());
assertEquals(65536 + 64, assetTable.get(0).path("decoded_size").asInt());
assertTrue(assetTable.get(0).path("metadata").path("pipeline").isMissingNode()); assertTrue(assetTable.get(0).path("metadata").path("pipeline").isMissingNode());
final var preload = MAPPER.readTree(projectRoot.resolve("build/preload.json").toFile()); final var preload = MAPPER.readTree(projectRoot.resolve("build/preload.json").toFile());
@ -354,6 +356,53 @@ final class FileSystemPackerWorkspaceServiceTest {
assertEquals(0, assetTableMetadata.get(0).path("metadata").path("pipeline").path("palettes").get(0).path("index").asInt()); assertEquals(0, assetTableMetadata.get(0).path("metadata").path("pipeline").path("palettes").get(0).path("index").asInt());
} }
@Test
void packWorkspaceEmitsIntermediateGlyphPaletteCountWithoutRemappingPaletteIdentity() throws Exception {
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("pack-workspace-palette-count-7"));
final Path assetRoot = projectRoot.resolve("assets/ui/atlas");
final Path manifestPath = assetRoot.resolve("asset.json");
writeTilePng(assetRoot.resolve("confirm.png"), 16);
final ObjectNode manifest = (ObjectNode) MAPPER.readTree(manifestPath.toFile());
final ObjectNode output = (ObjectNode) manifest.path("output");
output.putObject("metadata").put("tile_size", "16x16");
final ObjectNode pipeline = output.putObject("pipeline");
final var palettes = pipeline.putArray("palettes");
final ObjectNode palette = palettes.addObject();
palette.put("index", 6);
palette.putObject("palette")
.putArray("rgba8888").add(0xFF0000FF);
final var artifacts = manifest.putArray("artifacts");
artifacts.addObject().put("file", "confirm.png").put("index", 0);
MAPPER.writerWithDefaultPrettyPrinter().writeValue(manifestPath.toFile(), manifest);
final FileSystemPackerWorkspaceService service = service();
final var result = service.packWorkspace(new PackWorkspaceRequest(project(projectRoot)));
assertEquals(PackerOperationStatus.SUCCESS, result.status());
final byte[] assetsPa = Files.readAllBytes(projectRoot.resolve("build/assets.pa"));
final var assetTable = MAPPER.readTree(projectRoot.resolve("build/asset_table.json").toFile());
assertEquals(7, assetTable.get(0).path("metadata").path("palette_count").asInt());
assertEquals(1, assetTable.get(0).path("metadata").path("palette_authored").asInt());
assertEquals(32768 + (7 * 16 * 4), assetTable.get(0).path("size").asInt());
assertEquals(65536 + (7 * 16 * 4), assetTable.get(0).path("decoded_size").asInt());
final int payloadOffset = (int) readLeLong(assetsPa, 12);
final int assetOffset = assetTable.get(0).path("offset").asInt();
final int packedPixelBytes = (assetTable.get(0).path("metadata").path("width").asInt()
* assetTable.get(0).path("metadata").path("height").asInt()) / 2;
final int paletteStart = payloadOffset + assetOffset + packedPixelBytes;
final int paletteSixStart = paletteStart + (6 * 16 * 4);
assertEquals(0x00, assetsPa[paletteStart] & 0xFF);
assertEquals(0x00, assetsPa[paletteStart + 1] & 0xFF);
assertEquals(0x00, assetsPa[paletteStart + 2] & 0xFF);
assertEquals(0x00, assetsPa[paletteStart + 3] & 0xFF);
assertEquals(0xFF, assetsPa[paletteSixStart] & 0xFF);
assertEquals(0x00, assetsPa[paletteSixStart + 1] & 0xFF);
assertEquals(0x00, assetsPa[paletteSixStart + 2] & 0xFF);
assertEquals(0xFF, assetsPa[paletteSixStart + 3] & 0xFF);
}
@Test @Test
void packWorkspaceRemapsGlyphTileIndicesUsingGlyphMaterialSchema() throws Exception { void packWorkspaceRemapsGlyphTileIndicesUsingGlyphMaterialSchema() throws Exception {
final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("pack-workspace-remap-material")); final Path projectRoot = copyFixture("workspaces/managed-basic", tempDir.resolve("pack-workspace-remap-material"));

View File

@ -215,6 +215,39 @@ final class PackerAssetDeclarationParserTest {
assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("duplicate palette index 0"))); assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("duplicate palette index 0")));
} }
@Test
void rejectsPaletteDeclarationIndexAboveV1Maximum() throws Exception {
final Path manifest = tempDir.resolve("asset.json");
Files.writeString(manifest, """
{
"schema_version": 1,
"asset_uuid": "uuid-palette-too-high",
"name": "palette_asset",
"type": "glyph_bank",
"output": {
"format": "GLYPH/indexed_v1",
"codec": "NONE",
"pipeline": {
"palettes": [
{
"index": 64,
"palette": {
"rgba8888": [-16776961]
}
}
]
}
},
"preload": { "enabled": true }
}
""");
final var result = parser.parse(manifest);
assertFalse(result.valid());
assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("maximum supported index is 63")));
}
@Test @Test
void rejectsMalformedJsonWithStructuralDiagnostic() { void rejectsMalformedJsonWithStructuralDiagnostic() {
final var result = parser.parse(PackerFixtureLocator.fixtureRoot("workspaces/invalid-malformed/assets/bad/asset.json")); final var result = parser.parse(PackerFixtureLocator.fixtureRoot("workspaces/invalid-malformed/assets/bad/asset.json"));