From 26686c288c96db2c3998cead57415b7fa78cd2a1 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Fri, 24 Apr 2026 10:57:48 +0100 Subject: [PATCH] implements PLN-0040 scene glyph spec and wire layout --- crates/console/prometeu-hal/src/asset.rs | 6 +- discussion/index.ndjson | 2 +- ...e-glyph-dependency-spec-and-wire-layout.md | 5 +- docs/specs/runtime/04-gfx-peripheral.md | 14 +++- docs/specs/runtime/15-asset-management.md | 71 +++++++++++++++++-- .../specs/runtime/16-host-abi-and-syscalls.md | 6 ++ 6 files changed, 91 insertions(+), 13 deletions(-) diff --git a/crates/console/prometeu-hal/src/asset.rs b/crates/console/prometeu-hal/src/asset.rs index 36aaf3f9..aa451b0f 100644 --- a/crates/console/prometeu-hal/src/asset.rs +++ b/crates/console/prometeu-hal/src/asset.rs @@ -53,7 +53,11 @@ pub const SCENE_PAYLOAD_MAGIC_V1: [u8; 4] = *b"SCNE"; pub const SCENE_PAYLOAD_VERSION_V1: u16 = 1; pub const SCENE_LAYER_COUNT_V1: usize = 4; pub const SCENE_HEADER_BYTES_V1: usize = 12; -pub const SCENE_LAYER_HEADER_BYTES_V1: usize = 28; +// Per-layer header: +// flags:u8 + glyph_asset_id:i32 + tile_size:u8 + reserved:u16 +// + parallax_x:f32 + parallax_y:f32 + width:u32 + height:u32 +// + tile_count:u32 + reserved:u32 +pub const SCENE_LAYER_HEADER_BYTES_V1: usize = 32; pub const SCENE_TILE_RECORD_BYTES_V1: usize = 4; pub const SCENE_DECODED_LAYER_OVERHEAD_BYTES_V1: usize = 16; diff --git a/discussion/index.ndjson b/discussion/index.ndjson index 17137cf0..91a2e89b 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -21,7 +21,7 @@ {"type":"discussion","id":"DSC-0026","status":"done","ticket":"render-all-scene-cache-and-camera-integration","title":"Integrate render_all with Scene Cache and Camera","created_at":"2026-04-14","updated_at":"2026-04-18","tags":["gfx","runtime","render","camera","scene"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0031","file":"lessons/DSC-0026-render-all-scene-cache-and-camera-integration/LSN-0031-frame-composition-belongs-above-the-render-backend.md","status":"done","created_at":"2026-04-18","updated_at":"2026-04-18"}]} {"type":"discussion","id":"DSC-0027","status":"done","ticket":"frame-composer-public-syscall-surface","title":"Agenda - FrameComposer Public Syscall Surface","created_at":"2026-04-17","updated_at":"2026-04-18","tags":["gfx","runtime","syscall","abi","frame-composer","scene","camera","sprites"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0032","file":"lessons/DSC-0027-frame-composer-public-syscall-surface/LSN-0032-public-abi-must-follow-the-canonical-service-boundary.md","status":"done","created_at":"2026-04-18","updated_at":"2026-04-18"}]} {"type":"discussion","id":"DSC-0028","status":"done","ticket":"deferred-overlay-and-primitive-composition","title":"Deferred Overlay and Primitive Composition over FrameComposer","created_at":"2026-04-18","updated_at":"2026-04-18","tags":["gfx","runtime","render","frame-composer","overlay","primitives","hud"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0033","file":"lessons/DSC-0028-deferred-overlay-and-primitive-composition/LSN-0033-debug-primitives-should-be-a-final-overlay-not-part-of-game-composition.md","status":"done","created_at":"2026-04-18","updated_at":"2026-04-18"}]} -{"type":"discussion","id":"DSC-0029","status":"open","ticket":"scene-bank-glyph-runtime-binding-leak","title":"Scene Bank Glyph Runtime Binding Leak","created_at":"2026-04-24","updated_at":"2026-04-24","tags":["gfx","runtime","asset","scene","glyph","format","architecture"],"agendas":[{"id":"AGD-0029","file":"AGD-0029-scene-bank-glyph-runtime-binding-leak.md","status":"accepted","created_at":"2026-04-24","updated_at":"2026-04-24"}],"decisions":[{"id":"DEC-0021","file":"DEC-0021-scene-bank-glyph-dependencies-must-bind-by-asset-id.md","status":"in_progress","created_at":"2026-04-24","updated_at":"2026-04-24","ref_agenda":"AGD-0029"}],"plans":[{"id":"PLN-0040","file":"PLN-0040-scene-glyph-dependency-spec-and-wire-layout.md","status":"review","created_at":"2026-04-24","updated_at":"2026-04-24","ref_decisions":["DEC-0021"]},{"id":"PLN-0041","file":"PLN-0041-scene-glyph-runtime-resolution-and-fatal-paths.md","status":"review","created_at":"2026-04-24","updated_at":"2026-04-24","ref_decisions":["DEC-0021"]},{"id":"PLN-0042","file":"PLN-0042-scene-glyph-tooling-and-fixture-migration.md","status":"review","created_at":"2026-04-24","updated_at":"2026-04-24","ref_decisions":["DEC-0021"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0029","status":"open","ticket":"scene-bank-glyph-runtime-binding-leak","title":"Scene Bank Glyph Runtime Binding Leak","created_at":"2026-04-24","updated_at":"2026-04-24","tags":["gfx","runtime","asset","scene","glyph","format","architecture"],"agendas":[{"id":"AGD-0029","file":"AGD-0029-scene-bank-glyph-runtime-binding-leak.md","status":"accepted","created_at":"2026-04-24","updated_at":"2026-04-24"}],"decisions":[{"id":"DEC-0021","file":"DEC-0021-scene-bank-glyph-dependencies-must-bind-by-asset-id.md","status":"in_progress","created_at":"2026-04-24","updated_at":"2026-04-24","ref_agenda":"AGD-0029"}],"plans":[{"id":"PLN-0040","file":"PLN-0040-scene-glyph-dependency-spec-and-wire-layout.md","status":"done","created_at":"2026-04-24","updated_at":"2026-04-24","ref_decisions":["DEC-0021"]},{"id":"PLN-0041","file":"PLN-0041-scene-glyph-runtime-resolution-and-fatal-paths.md","status":"review","created_at":"2026-04-24","updated_at":"2026-04-24","ref_decisions":["DEC-0021"]},{"id":"PLN-0042","file":"PLN-0042-scene-glyph-tooling-and-fixture-migration.md","status":"review","created_at":"2026-04-24","updated_at":"2026-04-24","ref_decisions":["DEC-0021"]}],"lessons":[]} {"type":"discussion","id":"DSC-0014","status":"done","ticket":"perf-vm-allocation-and-copy-pressure","title":"Agenda - [PERF] VM Allocation and Copy Pressure","created_at":"2026-03-27","updated_at":"2026-04-20","tags":[],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0035","file":"lessons/DSC-0014-perf-vm-allocation-and-copy-pressure/LSN-0035-first-materialization-is-not-the-same-as-hot-path-copy-pressure.md","status":"done","created_at":"2026-04-20","updated_at":"2026-04-20"}]} {"type":"discussion","id":"DSC-0015","status":"abandoned","ticket":"perf-cartridge-boot-and-program-ownership","title":"Agenda - [PERF] Cartridge Boot and Program Ownership","created_at":"2026-03-27","updated_at":"2026-04-20","tags":[],"agendas":[{"id":"AGD-0014","file":"workflow/agendas/AGD-0014-perf-cartridge-boot-and-program-ownership.md","status":"abandoned","created_at":"2026-03-27","updated_at":"2026-04-20","_override_reason":"User explicitly chose to close the discussion without decision because FS->memory copy for the program is already acceptable."}],"decisions":[],"plans":[],"lessons":[],"_override_reason":"User explicitly chose to abandon the discussion without creating a decision because FS->memory copy for the program is already acceptable."} {"type":"discussion","id":"DSC-0016","status":"done","ticket":"tilemap-empty-cell-vs-tile-id-zero","title":"Tilemap Empty Cell vs Tile ID Zero","created_at":"2026-03-27","updated_at":"2026-04-09","tags":[],"agendas":[{"id":"AGD-0015","file":"workflow/agendas/AGD-0015-tilemap-empty-cell-vs-tile-id-zero.md","status":"done","created_at":"2026-03-27","updated_at":"2026-04-09"}],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0022","file":"lessons/DSC-0016-tilemap-empty-cell-semantics/LSN-0022-tilemap-empty-cell-convergence.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]} diff --git a/discussion/workflow/plans/PLN-0040-scene-glyph-dependency-spec-and-wire-layout.md b/discussion/workflow/plans/PLN-0040-scene-glyph-dependency-spec-and-wire-layout.md index 0835ac0f..34e13a0e 100644 --- a/discussion/workflow/plans/PLN-0040-scene-glyph-dependency-spec-and-wire-layout.md +++ b/discussion/workflow/plans/PLN-0040-scene-glyph-dependency-spec-and-wire-layout.md @@ -2,9 +2,9 @@ id: PLN-0040 ticket: scene-bank-glyph-runtime-binding-leak title: Scene glyph dependency spec and wire layout migration -status: review +status: done created: 2026-04-24 -completed: +completed: 2026-04-24 tags: [gfx, runtime, asset, scene, glyph, format, spec] --- @@ -102,4 +102,3 @@ Remove proposed/legacy wording that frames missing dependencies as recoverable s - Spec-only edits can drift from the runtime implementation if later plans are not executed promptly. - Scene payload layout changes may require touching multiple docs; missing one will leave contradictory contract text. - If HAL constants are updated here before runtime code, temporary compile or test fallout may appear in later plans and must be handled intentionally. - diff --git a/docs/specs/runtime/04-gfx-peripheral.md b/docs/specs/runtime/04-gfx-peripheral.md index 8b63b63a..9948ec54 100644 --- a/docs/specs/runtime/04-gfx-peripheral.md +++ b/docs/specs/runtime/04-gfx-peripheral.md @@ -579,7 +579,7 @@ Fault boundary: | `gfx.draw_square` | `void` | no real operational failure path in v1 | | `gfx.draw_text` | `void` | no real operational failure path in v1 | | `gfx.clear_565` | `void` | no real operational failure path in v1 | -| `composer.bind_scene` | `status:int` | explicit orchestration-domain operational result | +| `composer.bind_scene` | `status:int` | status-returning API, but missing scene glyph dependencies are fatal runtime errors | | `composer.unbind_scene` | `status:int` | explicit orchestration-domain operational result | | `composer.set_camera` | `void` | no real operational failure path in v1 | | `composer.emit_sprite` | `status:int` | explicit orchestration-domain operational rejection | @@ -596,6 +596,18 @@ The public `gfx.*` primitive family remains valid in v1, but its stable operatio This means callers MUST NOT rely on stable immediate writes to the working back buffer as the public contract for `gfx.draw_text(...)` or sibling primitives. +### 19.1.b Scene dependency fatal boundary + +`composer.bind_scene` remains a status-returning syscall in the public ABI, but scene glyph dependency absence is outside the accepted passive operational-error model. + +Rules: + +- the target scene slot may still be rejected through ordinary status-returning behavior when the scene itself is unavailable; +- missing glyph dependencies referenced by a resident scene are not a passive `status:int` case; +- if scene activation discovers that a layer dependency cannot be resolved to a committed glyph asset, the machine MUST fail fatally and emit a clear log; +- if scene composition later discovers that a layer dependency can no longer be resolved, the machine MUST fail fatally and emit a clear log; +- runtime MUST NOT continue canonical scene composition after such a dependency failure. + ### 19.2 `composer.emit_sprite` `composer.emit_sprite` returns `status:int`. diff --git a/docs/specs/runtime/15-asset-management.md b/docs/specs/runtime/15-asset-management.md index 9de63331..cf76e3c3 100644 --- a/docs/specs/runtime/15-asset-management.md +++ b/docs/specs/runtime/15-asset-management.md @@ -108,9 +108,9 @@ This table describes content identity and storage layout, not live residency. `codec` does not define the bank-specific serialized layout itself. Specialized banks may still have normative decode rules even when `codec = NONE`. -### 4.1 `TILES` asset contract in v1 +### 4.1 `GLYPH` asset contract in v1 -For `BankType::TILES`, the v1 runtime-facing contract is: +For `BankType::GLYPH`, the v1 runtime-facing contract is: - `codec = NONE` - serialized pixels use packed `u4` palette indices @@ -118,20 +118,20 @@ For `BankType::TILES`, the v1 runtime-facing contract is: - `palette_count = 64` - runtime materialization may expand pixel indices to one `u8` per pixel -For `TILES`, `NONE` means there is no additional generic codec layer beyond the bank contract itself. +For `GLYPH`, `NONE` means there is no additional generic codec layer beyond the bank contract itself. For the current transition window: - `RAW` is a legacy deprecated alias of `NONE` - newly published material must use `NONE` as the canonical value -Even with `codec = NONE`, `TILES` still requires bank-specific decode from its serialized payload. The serialized byte layout therefore does not need to match the in-memory layout. +Even with `codec = NONE`, `GLYPH` still requires bank-specific decode from its serialized payload. The serialized byte layout therefore does not need to match the in-memory layout. #### 4.1.1 Metadata Normalization Following `DEC-0004`, the `AssetEntry.metadata` field must be structured in segmented form to avoid ambiguity. -Required effective metadata fields for `TILES` at the root level: +Required effective metadata fields for `GLYPH` at the root level: - `tile_size`: tile edge in pixels; valid values are `8`, `16`, or `32` - `width`: total sheet width in pixels @@ -143,7 +143,7 @@ Optional informative subtrees: - `metadata.codec`: codec/compressor-specific configuration (for example dictionaries or compression flags). - `metadata.pipeline`: informative metadata from the Studio build process (for example source hashes, timestamps, or tool versions). -Validation rules for `TILES` v1: +Validation rules for `GLYPH` v1: - `palette_count` must be `64` - `width * height` defines the number of logical indexed pixels in the decoded sheet @@ -160,17 +160,74 @@ The tile-bank payload therefore separates serialized storage form from runtime m - decoded pixel plane: expanded `u8` indices, one entry per pixel - palette table: `64 * 16` colors in `RGB565` -For `TILES` v1: +For `GLYPH` v1: - `size` must match `ceil(width * height / 2) + (palette_count * 16 * 2)` - `decoded_size` must match `(width * height) + (palette_count * 16 * 2)` +### 4.2 `SCENE` asset contract in v1 + +For `BankType::SCENE`, the v1 runtime-facing contract is: + +- `codec = NONE` +- payload magic is `SCNE` +- payload version is `1` +- layer count is fixed at `4` +- each layer carries its own glyph dependency +- the glyph dependency is serialized as canonical `AssetId` using signed 32-bit little-endian width +- the payload MUST describe cold asset dependencies only and MUST NOT encode runtime glyph slot topology + +Even with `codec = NONE`, `SCENE` still has a bank-specific binary contract. The serialized layout is normative and distinct from any transient runtime residency state. + +#### 4.2.1 Layer Header Layout + +Each scene layer header is: + +1. `flags: u8` +2. `glyph_asset_id: i32` (little-endian) +3. `tile_size: u8` +4. `reserved: u16` +5. `parallax_x: f32` +6. `parallax_y: f32` +7. `width: u32` +8. `height: u32` +9. `tile_count: u32` +10. `reserved: u32` + +This makes the canonical per-layer header size `32` bytes. + +Normative rules: + +- `glyph_asset_id` MUST be interpreted as the cold identity of the glyph dependency for that layer; +- `glyph_asset_id` MUST NOT be interpreted as a runtime slot id; +- each layer MUST own exactly one glyph dependency reference; +- no shared dependency table exists in this contract; +- `tile_count` MUST equal `width * height`. + +#### 4.2.2 Runtime Binding Semantics + +`SCENE` assets do not own runtime residency. + +Therefore: + +- the runtime MUST resolve scene glyph dependencies through runtime-owned residency state; +- the runtime MUST maintain a runtime-owned mapping from committed glyph `AssetId` to glyph slot; +- scene bind and scene draw MUST consult runtime residency, not serialized slot topology; +- a missing scene glyph dependency is not a passive asset-status condition. + +If a scene glyph dependency cannot be resolved: + +- `bind_scene` MUST fail as a fatal machine error with clear logging; +- draw/composition MUST fail as a fatal machine error with clear logging; +- runtime MUST NOT continue scene composition after the missing dependency is detected. + ## 5 Banks and Slots The current runtime exposes bank types: - `GLYPH` - `SOUNDS` +- `SCENE` Assets are loaded into explicit slots identified by slot index at the public ABI boundary. diff --git a/docs/specs/runtime/16-host-abi-and-syscalls.md b/docs/specs/runtime/16-host-abi-and-syscalls.md index 77d66115..89595065 100644 --- a/docs/specs/runtime/16-host-abi-and-syscalls.md +++ b/docs/specs/runtime/16-host-abi-and-syscalls.md @@ -229,6 +229,12 @@ For mutating composer operations: - `set_camera` remains `void` in v1; - no caller-provided sprite index or `active` flag is part of the canonical contract. +Fatal boundary clarification for `bind_scene`: + +- `bind_scene` remains status-returning for ordinary orchestration-domain rejection such as unresolved target scene slot; +- missing glyph dependencies inside a resident scene are not accepted as passive `ComposerOpStatus` outcomes; +- if scene activation or later composition detects an unresolved scene glyph dependency, runtime MUST escalate that condition as a fatal machine error with clear logging. + ## 7 Syscalls as Callable Entities (Not First-Class) Syscalls behave like call sites, not like first-class guest values.