From c83b0402ffde1a547e123e238f83897ca2c3d9ec Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Thu, 9 Apr 2026 07:59:55 +0100 Subject: [PATCH 01/10] implements Glyph and update Tile to use active (dsc16: done) --- crates/console/prometeu-drivers/src/gfx.rs | 19 +++++++++++++------ crates/console/prometeu-hal/src/glyph.rs | 5 +++++ crates/console/prometeu-hal/src/lib.rs | 1 + crates/console/prometeu-hal/src/sprite.rs | 4 ++-- crates/console/prometeu-hal/src/tile.rs | 6 ++++-- .../src/virtual_machine_runtime/dispatch.rs | 9 ++++++--- discussion/index.ndjson | 2 +- 7 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 crates/console/prometeu-hal/src/glyph.rs diff --git a/crates/console/prometeu-drivers/src/gfx.rs b/crates/console/prometeu-drivers/src/gfx.rs index 3c98bdea..b9b9d4dc 100644 --- a/crates/console/prometeu-drivers/src/gfx.rs +++ b/crates/console/prometeu-drivers/src/gfx.rs @@ -6,6 +6,7 @@ use prometeu_hal::tile::Tile; use prometeu_hal::tile_bank::{TileBank, TileSize}; use prometeu_hal::tile_layer::{HudTileLayer, ScrollableTileLayer, TileMap}; use std::sync::Arc; +use prometeu_hal::glyph::Glyph; /// Blending modes inspired by classic 16-bit hardware. /// Defines how source pixels are combined with existing pixels in the framebuffer. @@ -274,8 +275,14 @@ impl GfxBridge for Gfx { impl Gfx { /// Initializes the graphics system with a specific resolution and shared memory banks. pub fn new(w: usize, h: usize, tile_banks: Arc) -> Self { + + const EMPTY_GLYPH: Glyph = Glyph { + glyph_id: 0, + palette_id: 0, + }; + const EMPTY_SPRITE: Sprite = Sprite { - tile: Tile { id: 0, flip_x: false, flip_y: false, palette_id: 0 }, + glyph: EMPTY_GLYPH, x: 0, y: 0, bank_id: 0, @@ -706,7 +713,7 @@ impl Gfx { let tile = map.tiles[map_y * map.width + map_x]; // Optimized skip for empty (ID 0) tiles. - if tile.id == 0 { + if !tile.active { continue; } @@ -757,7 +764,7 @@ impl Gfx { let fetch_y = if tile.flip_y { size - 1 - local_y } else { local_y }; // 1. Get the pixel color index (0-15) from the bank. - let px_index = bank.get_pixel_index(tile.id, fetch_x, fetch_y); + let px_index = bank.get_pixel_index(tile.glyph.glyph_id, fetch_x, fetch_y); // 2. Hardware rule: Color index 0 is always fully transparent. if px_index == 0 { @@ -765,7 +772,7 @@ impl Gfx { } // 3. Resolve the virtual index to a real RGB565 color using the tile's assigned palette. - let color = bank.resolve_color(tile.palette_id, px_index); + let color = bank.resolve_color(tile.glyph.palette_id, px_index); back[world_y as usize * screen_w + world_x as usize] = color.raw(); } @@ -812,7 +819,7 @@ impl Gfx { let fetch_y = if sprite.flip_y { size - 1 - local_y } else { local_y }; // 1. Get index - let px_index = bank.get_pixel_index(sprite.tile.id, fetch_x, fetch_y); + let px_index = bank.get_pixel_index(sprite.glyph.glyph_id, fetch_x, fetch_y); // 2. Transparency if px_index == 0 { @@ -820,7 +827,7 @@ impl Gfx { } // 3. Resolve color via palette (from the tile inside the sprite) - let color = bank.resolve_color(sprite.tile.palette_id, px_index); + let color = bank.resolve_color(sprite.glyph.palette_id, px_index); back[world_y as usize * screen_w + world_x as usize] = color.raw(); } diff --git a/crates/console/prometeu-hal/src/glyph.rs b/crates/console/prometeu-hal/src/glyph.rs new file mode 100644 index 00000000..e6d41e3a --- /dev/null +++ b/crates/console/prometeu-hal/src/glyph.rs @@ -0,0 +1,5 @@ +#[derive(Clone, Copy, Debug, Default)] +pub struct Glyph { + pub glyph_id: u16, + pub palette_id: u8, +} \ No newline at end of file diff --git a/crates/console/prometeu-hal/src/lib.rs b/crates/console/prometeu-hal/src/lib.rs index b9c9681d..0495586e 100644 --- a/crates/console/prometeu-hal/src/lib.rs +++ b/crates/console/prometeu-hal/src/lib.rs @@ -26,6 +26,7 @@ pub mod tile_layer; pub mod touch_bridge; pub mod vm_fault; pub mod window; +pub mod glyph; pub use asset_bridge::AssetBridge; pub use audio_bridge::{AudioBridge, AudioOpStatus, LoopMode}; diff --git a/crates/console/prometeu-hal/src/sprite.rs b/crates/console/prometeu-hal/src/sprite.rs index ba56e23b..e0da7397 100644 --- a/crates/console/prometeu-hal/src/sprite.rs +++ b/crates/console/prometeu-hal/src/sprite.rs @@ -1,8 +1,8 @@ -use crate::tile::Tile; +use crate::glyph::Glyph; #[derive(Clone, Copy, Debug, Default)] pub struct Sprite { - pub tile: Tile, + pub glyph: Glyph, pub x: i32, pub y: i32, pub bank_id: u8, diff --git a/crates/console/prometeu-hal/src/tile.rs b/crates/console/prometeu-hal/src/tile.rs index 07216fc2..50012691 100644 --- a/crates/console/prometeu-hal/src/tile.rs +++ b/crates/console/prometeu-hal/src/tile.rs @@ -1,7 +1,9 @@ +use crate::glyph::Glyph; + #[derive(Clone, Copy, Debug, Default)] pub struct Tile { - pub id: u16, + pub glyph: Glyph, + pub active: bool, pub flip_x: bool, pub flip_y: bool, - pub palette_id: u8, } diff --git a/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs b/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs index 5b597bd8..f4ec871a 100644 --- a/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs +++ b/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs @@ -7,12 +7,12 @@ use prometeu_hal::color::Color; use prometeu_hal::log::{LogLevel, LogSource}; use prometeu_hal::sprite::Sprite; use prometeu_hal::syscalls::Syscall; -use prometeu_hal::tile::Tile; use prometeu_hal::vm_fault::VmFault; use prometeu_hal::{ AudioOpStatus, GfxOpStatus, HostContext, HostReturn, NativeInterface, SyscallId, expect_bool, expect_int, }; +use prometeu_hal::glyph::Glyph; impl VirtualMachineRuntime { fn syscall_log_write(&mut self, level_val: i64, tag: u16, msg: String) -> Result<(), VmFault> { @@ -139,7 +139,7 @@ impl NativeInterface for VirtualMachineRuntime { let index = expect_int(args, 1)? as usize; let x = expect_int(args, 2)? as i32; let y = expect_int(args, 3)? as i32; - let tile_id = expect_int(args, 4)? as u16; + let glyph_id = expect_int(args, 4)? as u16; let palette_id = expect_int(args, 5)? as u8; let active = expect_bool(args, 6)?; let flip_x = expect_bool(args, 7)?; @@ -162,7 +162,10 @@ impl NativeInterface for VirtualMachineRuntime { } *hw.gfx_mut().sprite_mut(index) = Sprite { - tile: Tile { id: tile_id, flip_x: false, flip_y: false, palette_id }, + glyph: Glyph { + glyph_id, + palette_id, + }, x, y, bank_id, diff --git a/discussion/index.ndjson b/discussion/index.ndjson index 1904b150..fea171ee 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -16,7 +16,7 @@ {"type":"discussion","id":"DSC-0013","status":"open","ticket":"perf-host-debug-overlay-isolation","title":"Agenda - [PERF] Host Debug Overlay Isolation","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0012","file":"AGD-0012-perf-host-debug-overlay-isolation.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0014","status":"open","ticket":"perf-vm-allocation-and-copy-pressure","title":"Agenda - [PERF] VM Allocation and Copy Pressure","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0013","file":"AGD-0013-perf-vm-allocation-and-copy-pressure.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0015","status":"open","ticket":"perf-cartridge-boot-and-program-ownership","title":"Agenda - [PERF] Cartridge Boot and Program Ownership","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0014","file":"AGD-0014-perf-cartridge-boot-and-program-ownership.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} -{"type":"discussion","id":"DSC-0016","status":"open","ticket":"tilemap-empty-cell-vs-tile-id-zero","title":"Tilemap Empty Cell vs Tile ID Zero","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0015","file":"AGD-0015-tilemap-empty-cell-vs-tile-id-zero.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"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-03-27","tags":[],"agendas":[{"id":"AGD-0015","file":"AGD-0015-tilemap-empty-cell-vs-tile-id-zero.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0017","status":"open","ticket":"asset-entry-metadata-normalization-contract","title":"Asset Entry Metadata Normalization Contract","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0016","file":"AGD-0016-asset-entry-metadata-normalization-contract.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0018","status":"done","ticket":"asset-load-asset-id-int-contract","title":"Asset Load Asset ID Int Contract","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["asset","runtime","abi"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0019","file":"lessons/DSC-0018-asset-load-asset-id-int-contract/LSN-0019-asset-load-id-abi-convergence.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]} {"type":"discussion","id":"DSC-0019","status":"done","ticket":"jenkinsfile-correction","title":"Jenkinsfile Correction and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins"],"agendas":[{"id":"AGD-0017","file":"AGD-0017-jenkinsfile-correction.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0002","file":"DEC-0002-jenkinsfile-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0002","file":"PLN-0002-jenkinsfile-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0020","file":"lessons/DSC-0019-jenkins-ci-standardization/LSN-0020-jenkins-standard-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]} -- 2.47.2 From 24c4c3f9aa426dac7e1772883dea4e3ebac971a7 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Thu, 9 Apr 2026 08:02:24 +0100 Subject: [PATCH 02/10] clean up --- discussion/index.ndjson | 36 +++++++++---------- ...LSN-0022-tilemap-empty-cell-convergence.md | 20 +++++++++++ ...0015-tilemap-empty-cell-vs-tile-id-zero.md | 6 ++-- 3 files changed, 41 insertions(+), 21 deletions(-) create mode 100644 discussion/lessons/DSC-0016-tilemap-empty-cell-semantics/LSN-0022-tilemap-empty-cell-convergence.md diff --git a/discussion/index.ndjson b/discussion/index.ndjson index fea171ee..e4ed9054 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -1,22 +1,22 @@ -{"type":"meta","next_id":{"DSC":21,"AGD":19,"DEC":3,"PLN":3,"LSN":21,"CLSN":1}} +{"type":"meta","next_id":{"DSC":21,"AGD":19,"DEC":3,"PLN":3,"LSN":23,"CLSN":1}} ... (mantendo as linhas anteriores) ... {"type":"discussion","id":"DSC-0020","status":"done","ticket":"jenkins-gitea-integration","title":"Jenkins Gitea Integration and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins","gitea"],"agendas":[{"id":"AGD-0018","file":"workflow/agendas/AGD-0018-jenkins-gitea-integration-and-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0003","file":"workflow/decisions/DEC-0003-jenkins-gitea-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0003","file":"workflow/plans/PLN-0003-jenkins-gitea-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0021","file":"lessons/DSC-0020-jenkins-gitea-integration/LSN-0021-jenkins-gitea-integration.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]} {"type":"discussion","id":"DSC-0001","status":"done","ticket":"legacy-runtime-learn-import","title":"Import legacy runtime learn into discussion lessons","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["migration","tech-debt"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0001","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0001-prometeu-learn-index.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0002","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0002-historical-asset-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0003","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0003-historical-audio-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0004","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0004-historical-cartridge-boot-protocol-and-manifest-authority.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0005","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0005-historical-game-memcard-slots-surface-and-semantics.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0006","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0006-historical-gfx-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0007","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0007-historical-retired-fault-and-input-decisions.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0008","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0008-historical-vm-core-and-assets.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0009","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0009-mental-model-asset-management.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0010","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0010-mental-model-audio.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0011","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0011-mental-model-gfx.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0012","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0012-mental-model-input.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0013","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0013-mental-model-observability-and-debugging.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0014","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0014-mental-model-portability-and-cross-platform.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0015","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0015-mental-model-save-memory-and-memcard.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0016","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0016-mental-model-status-first-and-fault-thinking.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0017","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0017-mental-model-time-and-cycles.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0018","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0018-mental-model-touch.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]} -{"type":"discussion","id":"DSC-0002","status":"open","ticket":"runtime-edge-test-plan","title":"Agenda - Runtime Edge Test Plan","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0001","file":"AGD-0001-runtime-edge-test-plan.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} -{"type":"discussion","id":"DSC-0003","status":"open","ticket":"packed-cartridge-loader-pmc","title":"Agenda - Packed Cartridge Loader PMC","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0002","file":"AGD-0002-packed-cartridge-loader-pmc.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} -{"type":"discussion","id":"DSC-0004","status":"open","ticket":"system-run-cart","title":"Agenda - System Run Cart","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0003","file":"AGD-0003-system-run-cart.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} -{"type":"discussion","id":"DSC-0005","status":"open","ticket":"system-fault-semantics-and-control-surface","title":"Agenda - System Fault Semantics and Control Surface","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0004","file":"AGD-0004-system-fault-semantics-and-control-surface.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} -{"type":"discussion","id":"DSC-0006","status":"open","ticket":"vm-owned-random-service","title":"Agenda - VM-Owned Random Service","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0005","file":"AGD-0005-vm-owned-random-service.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} -{"type":"discussion","id":"DSC-0007","status":"open","ticket":"app-home-filesystem-surface-and-semantics","title":"Agenda - App Home Filesystem Surface and Semantics","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0006","file":"AGD-0006-app-home-filesystem-surface-and-semantics.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} -{"type":"discussion","id":"DSC-0008","status":"open","ticket":"perf-runtime-telemetry-hot-path","title":"Agenda - [PERF] Runtime Telemetry Hot Path","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0007","file":"AGD-0007-perf-runtime-telemetry-hot-path.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} -{"type":"discussion","id":"DSC-0009","status":"open","ticket":"perf-async-background-work-lanes-for-assets-and-fs","title":"Agenda - [PERF] Async Background Work Lanes for Assets and FS","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0008","file":"AGD-0008-perf-async-background-work-lanes-for-assets-and-fs.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} -{"type":"discussion","id":"DSC-0010","status":"open","ticket":"perf-host-desktop-frame-pacing-and-presentation","title":"Agenda - [PERF] Host Desktop Frame Pacing and Presentation","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0009","file":"AGD-0009-perf-host-desktop-frame-pacing-and-presentation.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} -{"type":"discussion","id":"DSC-0011","status":"open","ticket":"perf-gfx-render-pipeline-and-dirty-regions","title":"Agenda - [PERF] GFX Render Pipeline and Dirty Regions","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0010","file":"AGD-0010-perf-gfx-render-pipeline-and-dirty-regions.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} -{"type":"discussion","id":"DSC-0012","status":"open","ticket":"perf-runtime-introspection-syscalls","title":"Agenda - [PERF] Runtime Introspection Syscalls","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0011","file":"AGD-0011-perf-runtime-introspection-syscalls.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} -{"type":"discussion","id":"DSC-0013","status":"open","ticket":"perf-host-debug-overlay-isolation","title":"Agenda - [PERF] Host Debug Overlay Isolation","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0012","file":"AGD-0012-perf-host-debug-overlay-isolation.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} -{"type":"discussion","id":"DSC-0014","status":"open","ticket":"perf-vm-allocation-and-copy-pressure","title":"Agenda - [PERF] VM Allocation and Copy Pressure","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0013","file":"AGD-0013-perf-vm-allocation-and-copy-pressure.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} -{"type":"discussion","id":"DSC-0015","status":"open","ticket":"perf-cartridge-boot-and-program-ownership","title":"Agenda - [PERF] Cartridge Boot and Program Ownership","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0014","file":"AGD-0014-perf-cartridge-boot-and-program-ownership.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} -{"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-03-27","tags":[],"agendas":[{"id":"AGD-0015","file":"AGD-0015-tilemap-empty-cell-vs-tile-id-zero.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} -{"type":"discussion","id":"DSC-0017","status":"open","ticket":"asset-entry-metadata-normalization-contract","title":"Asset Entry Metadata Normalization Contract","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0016","file":"AGD-0016-asset-entry-metadata-normalization-contract.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0002","status":"open","ticket":"runtime-edge-test-plan","title":"Agenda - Runtime Edge Test Plan","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0001","file":"workflow/agendas/AGD-0001-runtime-edge-test-plan.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0003","status":"open","ticket":"packed-cartridge-loader-pmc","title":"Agenda - Packed Cartridge Loader PMC","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0002","file":"workflow/agendas/AGD-0002-packed-cartridge-loader-pmc.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0004","status":"open","ticket":"system-run-cart","title":"Agenda - System Run Cart","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0003","file":"workflow/agendas/AGD-0003-system-run-cart.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0005","status":"open","ticket":"system-fault-semantics-and-control-surface","title":"Agenda - System Fault Semantics and Control Surface","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0004","file":"workflow/agendas/AGD-0004-system-fault-semantics-and-control-surface.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0006","status":"open","ticket":"vm-owned-random-service","title":"Agenda - VM-Owned Random Service","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0005","file":"workflow/agendas/AGD-0005-vm-owned-random-service.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0007","status":"open","ticket":"app-home-filesystem-surface-and-semantics","title":"Agenda - App Home Filesystem Surface and Semantics","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0006","file":"workflow/agendas/AGD-0006-app-home-filesystem-surface-and-semantics.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0008","status":"open","ticket":"perf-runtime-telemetry-hot-path","title":"Agenda - [PERF] Runtime Telemetry Hot Path","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0007","file":"workflow/agendas/AGD-0007-perf-runtime-telemetry-hot-path.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0009","status":"open","ticket":"perf-async-background-work-lanes-for-assets-and-fs","title":"Agenda - [PERF] Async Background Work Lanes for Assets and FS","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0008","file":"workflow/agendas/AGD-0008-perf-async-background-work-lanes-for-assets-and-fs.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0010","status":"open","ticket":"perf-host-desktop-frame-pacing-and-presentation","title":"Agenda - [PERF] Host Desktop Frame Pacing and Presentation","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0009","file":"workflow/agendas/AGD-0009-perf-host-desktop-frame-pacing-and-presentation.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0011","status":"open","ticket":"perf-gfx-render-pipeline-and-dirty-regions","title":"Agenda - [PERF] GFX Render Pipeline and Dirty Regions","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0010","file":"workflow/agendas/AGD-0010-perf-gfx-render-pipeline-and-dirty-regions.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0012","status":"open","ticket":"perf-runtime-introspection-syscalls","title":"Agenda - [PERF] Runtime Introspection Syscalls","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0011","file":"workflow/agendas/AGD-0011-perf-runtime-introspection-syscalls.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0013","status":"open","ticket":"perf-host-debug-overlay-isolation","title":"Agenda - [PERF] Host Debug Overlay Isolation","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0012","file":"workflow/agendas/AGD-0012-perf-host-debug-overlay-isolation.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0014","status":"open","ticket":"perf-vm-allocation-and-copy-pressure","title":"Agenda - [PERF] VM Allocation and Copy Pressure","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0013","file":"workflow/agendas/AGD-0013-perf-vm-allocation-and-copy-pressure.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0015","status":"open","ticket":"perf-cartridge-boot-and-program-ownership","title":"Agenda - [PERF] Cartridge Boot and Program Ownership","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0014","file":"workflow/agendas/AGD-0014-perf-cartridge-boot-and-program-ownership.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"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"}]} +{"type":"discussion","id":"DSC-0017","status":"open","ticket":"asset-entry-metadata-normalization-contract","title":"Asset Entry Metadata Normalization Contract","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0016","file":"workflow/agendas/AGD-0016-asset-entry-metadata-normalization-contract.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0018","status":"done","ticket":"asset-load-asset-id-int-contract","title":"Asset Load Asset ID Int Contract","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["asset","runtime","abi"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0019","file":"lessons/DSC-0018-asset-load-asset-id-int-contract/LSN-0019-asset-load-id-abi-convergence.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]} -{"type":"discussion","id":"DSC-0019","status":"done","ticket":"jenkinsfile-correction","title":"Jenkinsfile Correction and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins"],"agendas":[{"id":"AGD-0017","file":"AGD-0017-jenkinsfile-correction.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0002","file":"DEC-0002-jenkinsfile-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0002","file":"PLN-0002-jenkinsfile-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0020","file":"lessons/DSC-0019-jenkins-ci-standardization/LSN-0020-jenkins-standard-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]} +{"type":"discussion","id":"DSC-0019","status":"done","ticket":"jenkinsfile-correction","title":"Jenkinsfile Correction and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins"],"agendas":[{"id":"AGD-0017","file":"workflow/agendas/AGD-0017-jenkinsfile-correction.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0002","file":"workflow/decisions/DEC-0002-jenkinsfile-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0002","file":"workflow/plans/PLN-0002-jenkinsfile-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0020","file":"lessons/DSC-0019-jenkins-ci-standardization/LSN-0020-jenkins-standard-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]} diff --git a/discussion/lessons/DSC-0016-tilemap-empty-cell-semantics/LSN-0022-tilemap-empty-cell-convergence.md b/discussion/lessons/DSC-0016-tilemap-empty-cell-semantics/LSN-0022-tilemap-empty-cell-convergence.md new file mode 100644 index 00000000..98cfe356 --- /dev/null +++ b/discussion/lessons/DSC-0016-tilemap-empty-cell-semantics/LSN-0022-tilemap-empty-cell-convergence.md @@ -0,0 +1,20 @@ +# LSN-0022: Tilemap Empty Cell Semantics and Glyph Convergence + +## Context +During the initial runtime design, `tile_id = 0` was used as a sentinel value for "empty" or "missing" tiles in tilemaps. However, as the asset banking and packer systems evolved, it became clear that `0` should be a valid index for any asset bank, including tile banks. This conflict was formally tracked in `AGD-0015`. + +## Lessons Learned + +### 1. Explicit Presence Over Magic Values +Modeling absence with a magic value (like `tile_id = 0`) creates friction when that value is also a valid member of the domain (index 0). The project moved towards explicit presence checks. + +### 2. Convergence During Parallel Implementation +The resolution of the tilemap empty cell semantics (moving to explicit presence) was crystallized during the implementation of the `glyph` system. Instead of maintaining two different ways of handling "empty" slots, the tilemap logic was harmonized with the newer, cleaner approach used for glyphs and other render paths. + +### 3. Impact on Render Pipeline +By allowing `tile_id = 0` to be a valid tile, the render pipeline now relies on a separate bit or `Option`-like structure to determine if a cell should be drawn. This makes the code more robust and the asset indices more intuitive. + +## References +- Discussion: `DSC-0016` +- Agenda: `AGD-0015` (Tilemap Empty Cell vs Tile ID Zero) +- Implementation: Glyph system integration diff --git a/discussion/workflow/agendas/AGD-0015-tilemap-empty-cell-vs-tile-id-zero.md b/discussion/workflow/agendas/AGD-0015-tilemap-empty-cell-vs-tile-id-zero.md index 5b4873ab..4ee35336 100644 --- a/discussion/workflow/agendas/AGD-0015-tilemap-empty-cell-vs-tile-id-zero.md +++ b/discussion/workflow/agendas/AGD-0015-tilemap-empty-cell-vs-tile-id-zero.md @@ -2,10 +2,10 @@ id: AGD-0015 ticket: tilemap-empty-cell-vs-tile-id-zero title: Tilemap Empty Cell vs Tile ID Zero -status: open +status: done created: 2026-03-27 -resolved: -decision: +resolved: 2026-04-09 +decision: Resolved during glyph implementation. tags: [] --- -- 2.47.2 From 9228fb1e29a3392c97e6cca48d4f28b8347f5608 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Thu, 9 Apr 2026 08:04:19 +0100 Subject: [PATCH 03/10] clean up --- ...0015-tilemap-empty-cell-vs-tile-id-zero.md | 110 ------------------ .../AGD-0017-jenkinsfile-correction.md | 49 -------- ...enkins-gitea-integration-and-relocation.md | 48 -------- .../DEC-0002-jenkinsfile-strategy.md | 52 --------- .../DEC-0003-jenkins-gitea-strategy.md | 48 -------- .../plans/PLN-0002-jenkinsfile-execution.md | 56 --------- .../plans/PLN-0003-jenkins-gitea-execution.md | 55 --------- 7 files changed, 418 deletions(-) delete mode 100644 discussion/workflow/agendas/AGD-0015-tilemap-empty-cell-vs-tile-id-zero.md delete mode 100644 discussion/workflow/agendas/AGD-0017-jenkinsfile-correction.md delete mode 100644 discussion/workflow/agendas/AGD-0018-jenkins-gitea-integration-and-relocation.md delete mode 100644 discussion/workflow/decisions/DEC-0002-jenkinsfile-strategy.md delete mode 100644 discussion/workflow/decisions/DEC-0003-jenkins-gitea-strategy.md delete mode 100644 discussion/workflow/plans/PLN-0002-jenkinsfile-execution.md delete mode 100644 discussion/workflow/plans/PLN-0003-jenkins-gitea-execution.md diff --git a/discussion/workflow/agendas/AGD-0015-tilemap-empty-cell-vs-tile-id-zero.md b/discussion/workflow/agendas/AGD-0015-tilemap-empty-cell-vs-tile-id-zero.md deleted file mode 100644 index 4ee35336..00000000 --- a/discussion/workflow/agendas/AGD-0015-tilemap-empty-cell-vs-tile-id-zero.md +++ /dev/null @@ -1,110 +0,0 @@ ---- -id: AGD-0015 -ticket: tilemap-empty-cell-vs-tile-id-zero -title: Tilemap Empty Cell vs Tile ID Zero -status: done -created: 2026-03-27 -resolved: 2026-04-09 -decision: Resolved during glyph implementation. -tags: [] ---- - -# Tilemap Empty Cell vs Tile ID Zero - -## Contexto - -O runtime hoje usa `tile.id == 0` como atalho para "tile ausente" no caminho de render de tilemap. - -Ao mesmo tempo, o contrato de `tile bank` em evolucao no packer quer permitir que `tile_id = 0` seja um tile real e valido dentro do bank. - -Isso cria conflito direto entre: - -- semantica atual do tilemap no runtime; -- e o contrato de packing/runtime asset bank que trata `tile_id` como indice valido do bank desde zero. - -## Problema - -Precisamos decidir qual e a semantica correta para representar ausencia de tile em tilemaps. - -Hoje o comportamento implicito e: - -- `tile.id == 0` significa "nao desenhar"; -- portanto o tile de indice `0` no bank fica inutilizavel no caminho de tilemap; -- sprites, por outro lado, nao carregam a mesma semantica implicita de vazio. - -Isso deixa o modelo inconsistente: - -1. o mesmo `tile_id` nao significa a mesma coisa em todos os consumidores; -2. o bank `TileBank` nao explicita reserva de `tile_id = 0`; -3. o packer teria que compensar uma regra de consumidor que nao esta modelada no contrato do asset bank. - -## Pontos Criticos - -- `tile_id` deve representar um indice valido no bank, nao um sentinel escondido, sempre que possivel; -- ausencia de tile e um conceito de tilemap/layer, nao necessariamente de bank asset; -- usar indices fora da faixa como sentinel tende a piorar validacao e debug; -- mudar o shape do tilemap pode ter custo de ABI/runtime, mas pode deixar o contrato mais honesto; -- manter o comportamento atual preserva compatibilidade local, mas cristaliza uma semantica implicita fraca. - -## Opcoes - -### Opcao A - Manter `tile.id == 0` como empty sentinel - -O runtime segue tratando `tile_id = 0` como ausencia de tile no tilemap. - -Consequencia: - -- o primeiro tile real do bank precisaria comecar em `1` para o caminho de tilemap; -- o packer precisaria compensar isso; -- sprites e tilemaps continuariam com semanticas diferentes para o mesmo id. - -### Opcao B - Tornar vazio explicito no tilemap model - -O tilemap passa a representar vazio explicitamente, por exemplo com: - -- `Option`; -- ou `Tile { active/present, ... }`. - -Consequencia: - -- `tile_id = 0` volta a ser um tile valido no bank; -- ausencia de tile deixa de depender de valor especial do id; -- o runtime fica semanticamente mais consistente. - -### Opcao C - Usar sentinel fora da faixa valida - -O tilemap passa a tratar um `tile_id >= tile_capacity` como "empty". - -Consequencia: - -- evita usar `0` como sentinel; -- mas cria acoplamento ruim entre tilemap e capacidade do bank; -- e torna a validacao mais fragil e menos didatica. - -## Sugestao / Recomendacao - -Adotar `Opcao B`. - -Ausencia de tile deve ser modelada explicitamente no tilemap/layer, e nao por um `tile_id` especial. - -Direcao recomendada: - -1. `tile_id` deve significar apenas "indice do tile no bank"; -2. `tile_id = 0` deve ser permitido como tile real; -3. tilemaps devem ter vazio explicito no proprio modelo; -4. o render do tilemap deve testar esse estado explicito, nao `tile.id == 0`. - -## Perguntas em Aberto - -1. Qual shape e melhor para o primeiro wave: - `Option` ou `Tile` com flag `active/present`? -2. Essa mudanca afeta apenas o tilemap path ou tambem deve harmonizar outros consumidores do modelo `Tile`? -3. Precisamos de estrategia de compatibilidade/migracao para codigo e testes que hoje assumem `tile.id == 0` como vazio? - -## Criterio para Encerrar - -Esta agenda pode ser encerrada quando houver decisao clara sobre: - -- como vazio e representado no tilemap; -- se `tile_id = 0` volta a ser indice valido universal do bank; -- e quais pontos de spec/codigo do runtime precisam de propagacao. diff --git a/discussion/workflow/agendas/AGD-0017-jenkinsfile-correction.md b/discussion/workflow/agendas/AGD-0017-jenkinsfile-correction.md deleted file mode 100644 index 3167d49c..00000000 --- a/discussion/workflow/agendas/AGD-0017-jenkinsfile-correction.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -id: AGD-0017 -ticket: jenkinsfile-correction -title: Agenda - Jenkinsfile Correction and Relocation -status: open -created: 2026-04-07 -resolved: -decision: -tags: ["ci", "jenkins", "infrastructure"] ---- - -# Agenda - Jenkinsfile Correction and Relocation - -## Contexto - -O projeto possui um `Jenkinsfile` localizado em `files/config/Jenkinsfile`. O conteúdo deste arquivo está básico e utiliza uma versão do Rust (`1.77`) que pode não ser a ideal em comparação com o resto do projeto que usa a versão estável definida em `rust-toolchain.toml`. Além disso, o pipeline de CI principal do projeto está definido no GitHub Actions e o comportamento esperado de CI (formatação, clippy, testes) também está documentado no `Makefile`. - -## Problema - -1. **Localização Não Convencional**: O `Jenkinsfile` na pasta `files/config/` dificulta a descoberta e manutenção, além de fugir do padrão do Jenkins de buscar o arquivo na raiz do repositório. -2. **Desalinhamento de Comandos**: O `Jenkinsfile` atual executa comandos de forma ligeiramente diferente do `Makefile` e do GitHub Actions (ex: `cargo fmt --all` vs `cargo fmt -- --check`). -3. **Falta de Padronização**: Não há garantias de que o pipeline do Jenkins execute as mesmas verificações de qualidade que o pipeline do GitHub. - -## Pontos Críticos - -- **Sincronia com o Makefile**: O `Jenkinsfile` deve idealmente delegar para o `Makefile` para evitar duplicidade de lógica de comandos. -- **Ambiente Docker**: A imagem Docker utilizada deve ser compatível com as ferramentas necessárias (ex: `make`, `cargo`). -- **Workspace Completo**: Deve garantir que todas as crates do workspace sejam testadas. - -## Opções - -1. **Manter como está**: Apenas corrigir o conteúdo no local atual. -2. **Mover para a raiz e atualizar**: Seguir o padrão de mercado movendo para a raiz e alinhando o conteúdo com o `Makefile`. -3. **Remover o Jenkinsfile**: Se o projeto foca apenas no GitHub Actions (como sugere o `dist-workspace.toml`), o Jenkinsfile pode ser redundante. Contudo, a solicitação explícita foi para corrigi-lo. - -## Sugestão / Recomendação - -**Opção 2**: Mover o `Jenkinsfile` para a raiz do projeto e atualizar seu conteúdo para utilizar o comando `make ci` definido no `Makefile`. Isso garante consistência entre o ambiente local, o Jenkins e o GitHub Actions. - -## Perguntas em Aberto - -- Existe alguma restrição técnica no ambiente Jenkins deste projeto que exija o arquivo em `files/config/`? (Assumindo que não, dada a solicitação de "corrigir"). -- A imagem Docker `rust:stable` (ou similar) possui as dependências necessárias para rodar o `Makefile`? - -## Criterio para Encerrar - -- O `Jenkinsfile` estar na raiz do projeto. -- O conteúdo refletir as mesmas etapas de validação do resto do projeto (fmt, clippy, test). -- O arquivo antigo em `files/config/` ter sido removido. diff --git a/discussion/workflow/agendas/AGD-0018-jenkins-gitea-integration-and-relocation.md b/discussion/workflow/agendas/AGD-0018-jenkins-gitea-integration-and-relocation.md deleted file mode 100644 index 3c0336a7..00000000 --- a/discussion/workflow/agendas/AGD-0018-jenkins-gitea-integration-and-relocation.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -id: AGD-0018 -ticket: jenkins-gitea-integration-and-relocation -title: Agenda - Jenkins Gitea Integration and Relocation -status: open -created: 2026-04-07 -resolved: -decision: -tags: ["ci", "jenkins", "gitea"] ---- - -# Agenda - Jenkins Gitea Integration and Relocation - -## Contexto - -Na sessão anterior, o `Jenkinsfile` foi movido para a raiz do repositório para seguir padrões comuns de mercado. No entanto, o usuário solicitou explicitamente que ele permaneça em `files/config/Jenkinsfile`. Além disso, a estratégia de CI mudou de GitHub Actions para Jenkins integrado ao Gitea. - -## Problema - -1. O local atual do `Jenkinsfile` (raiz no histórico, mas residindo em `files/config` no FS atual) precisa ser consolidado como `files/config/Jenkinsfile` para cumprir o requisito do usuário. -2. A integração do CI deve ser com o Gitea, exigindo a propagação de status dos commits. -3. Não deve haver dependência ou uso do GitHub CI para este projeto. - -## Pontos Críticos - -- **Sincronização de Status**: Garantir que o Jenkins envie o feedback de `make ci` (testes/lint) corretamente para o Gitea. -- **Localização não-padrão**: Jenkins precisa ser configurado no lado do servidor para buscar o script de pipeline em `files/config/Jenkinsfile` (o que é trivial, mas foge do padrão `Jenkinsfile` na raiz). -- **Abandono do GitHub CI**: Remover qualquer resquício de configuração voltada ao GitHub. - -## Opções - -1. **Opção A**: Manter na raiz (rejeitada pelo usuário). -2. **Opção B**: Manter em `files/config/Jenkinsfile` e usar o plugin de Gitea no Jenkins para notificação automática ou via `giteaStatus` no pipeline. - -## Sugestão / Recomendação - -Adotar a **Opção B**. Atualizar o `Jenkinsfile` para incluir blocos de `post` que notifiquem o Gitea sobre o sucesso ou falha do pipeline. - -## Perguntas em Aberto - -- O Jenkins em questão já tem o plugin do Gitea configurado? (Assumiremos que sim ou que o pipeline deve usar o comando padrão `giteaStatus`). -- Existem arquivos `.github/workflows` que devem ser removidos? (Verificar e remover). - -## Criterio para Encerrar - -- `Jenkinsfile` atualizado e testado localmente (validado sintaticamente). -- Documentação da decisão no framework. -- Localização confirmada em `files/config/Jenkinsfile`. diff --git a/discussion/workflow/decisions/DEC-0002-jenkinsfile-strategy.md b/discussion/workflow/decisions/DEC-0002-jenkinsfile-strategy.md deleted file mode 100644 index b2bbf1d6..00000000 --- a/discussion/workflow/decisions/DEC-0002-jenkinsfile-strategy.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -id: DEC-0002 -discussion: DSC-0019 -title: Decision - Jenkinsfile Location and Strategy -status: accepted -created: 2026-04-07 -resolved: -tags: ["ci", "jenkins"] ---- - -# Decision - Jenkinsfile Location and Strategy - -## Status - -Accepted. - -## Contexto - -O arquivo `Jenkinsfile` estava localizado em `files/config/Jenkinsfile`, o que dificultava a manutenção e automação via Jenkins (que por padrão busca na raiz). Além disso, o conteúdo estava divergente das definições de CI do `Makefile` e do GitHub Actions. - -## Decisao - -1. **Mover** o `Jenkinsfile` para a raiz do repositório. -2. **Atualizar** o conteúdo do `Jenkinsfile` para utilizar uma imagem Docker `rust:stable` (conforme `rust-toolchain.toml`). -3. **Delegar** a execução do pipeline para o comando `make ci` definido no `Makefile`. -4. **Remover** o arquivo residual em `files/config/Jenkinsfile`. - -## Rationale - -- **Padronização**: Seguir o padrão de mercado de manter o arquivo de configuração de pipeline na raiz. -- **DRY (Don't Repeat Yourself)**: Ao usar o `Makefile`, evitamos duplicar os comandos de `fmt`, `clippy` e `test` em múltiplos lugares (Makefile, GHA e Jenkins). -- **Consistência**: Garante que o desenvolvedor rodando `make ci` localmente tenha o mesmo resultado que o servidor de CI. - -## Invariantes / Contrato - -- O comando `make ci` deve sempre englobar as verificações mínimas de qualidade (format, clippy, tests). -- O `Jenkinsfile` deve sempre usar um ambiente que possua `make` e `rust`. - -## Impactos - -- **Jenkins**: A configuração do job no Jenkins pode precisar ser atualizada se o "Script Path" estiver explicitamente apontando para `files/config/Jenkinsfile`. (Geralmente aponta para `Jenkinsfile` na raiz). -- **Manutenção**: Facilita a manutenção, pois mudanças no processo de build só precisam ser feitas no `Makefile`. - -## Referencias - -- `.github/workflows/ci.yml` -- `Makefile` -- `rust-toolchain.toml` - -## Propagacao Necessaria - -- N/A. diff --git a/discussion/workflow/decisions/DEC-0003-jenkins-gitea-strategy.md b/discussion/workflow/decisions/DEC-0003-jenkins-gitea-strategy.md deleted file mode 100644 index d382c2c1..00000000 --- a/discussion/workflow/decisions/DEC-0003-jenkins-gitea-strategy.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -id: DEC-0003 -agenda: AGD-0018 -title: Decisão - Jenkins Gitea Integration and Relocation -status: accepted -created: 2026-04-07 -tags: ["ci", "jenkins", "gitea"] ---- - -# Decisão - Jenkins Gitea Integration and Relocation - -## Status - -Aceito. - -## Contexto - -O projeto deve utilizar Jenkins integrado ao Gitea para o pipeline de CI, ignorando o GitHub Actions. O arquivo `Jenkinsfile` deve residir em um local específico solicitado pelo usuário: `files/config/Jenkinsfile`. - -## Decisao - -1. **Localização**: O `Jenkinsfile` será mantido em `files/config/Jenkinsfile`. -2. **Integração Gitea**: O pipeline deve utilizar comandos compatíveis com o plugin do Gitea no Jenkins para propagar o status da execução (Success/Failure/Pending). -3. **Remoção de GitHub CI**: Qualquer configuração de `.github/workflows` relacionada ao CI será removida para evitar confusão. - -## Rationale - -- Cumpre o requisito direto do usuário sobre a localização do arquivo. -- Alinha a infraestrutura de CI com o servidor Git interno (Gitea). -- Centraliza a execução no `Makefile` (`make ci`) para manter o `Jenkinsfile` simples e portável. - -## Invariantes / Contrato - -- O `Jenkinsfile` deve sempre chamar `make ci` para garantir que o mesmo padrão de qualidade seja aplicado localmente e no CI. -- Notificações de status devem ser enviadas ao Gitea no início e no fim da execução. - -## Impactos - -- **Jenkins**: O job no Jenkins deve ser configurado para apontar o "Script Path" para `files/config/Jenkinsfile`. -- **Desenvolvedores**: Devem focar no Gitea para verificar o status dos builds. - -## Referencias - -- AGD-0018 - -## Propagacao Necessaria - -- Comunicar ao time de infraestrutura sobre o novo local do `Jenkinsfile` para ajuste no Job do Jenkins. diff --git a/discussion/workflow/plans/PLN-0002-jenkinsfile-execution.md b/discussion/workflow/plans/PLN-0002-jenkinsfile-execution.md deleted file mode 100644 index f415d321..00000000 --- a/discussion/workflow/plans/PLN-0002-jenkinsfile-execution.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -id: PLN-0002 -discussion: DSC-0019 -title: Plan - Jenkinsfile Relocation and Content Alignment -status: open -created: 2026-04-07 -resolved: -tags: ["ci", "jenkins"] ---- - -# Plan - Jenkinsfile Relocation and Content Alignment - -## Briefing - -Este plano descreve as etapas técnicas para mover o `Jenkinsfile` de sua localização atual para a raiz do repositório e atualizar seu conteúdo para delegar as tarefas de CI ao `Makefile`. - -## Decisions de Origem - -- DEC-0002: Jenkinsfile Location and Strategy - -## Alvo - -- `files/config/Jenkinsfile` (Remoção) -- `/Jenkinsfile` (Criação/Movimentação) - -## Escopo - -- Movimentação do arquivo no sistema de arquivos. -- Edição do conteúdo Groovy do Jenkinsfile. -- Validação básica da sintaxe. - -## Fora de Escopo - -- Configuração do servidor Jenkins externo. -- Criação de novos comandos no `Makefile` (usaremos o `make ci` existente). - -## Plano de Execucao - -1. Criar o novo `Jenkinsfile` na raiz com o conteúdo atualizado. -2. Remover o arquivo original em `files/config/Jenkinsfile`. -3. Validar se o `Makefile` está acessível no ambiente Docker especificado. - -## Criterios de Aceite - -- O arquivo `Jenkinsfile` deve existir na raiz. -- O arquivo `files/config/Jenkinsfile` não deve mais existir. -- O novo `Jenkinsfile` deve conter uma chamada para `make ci`. - -## Tests / Validacao - -- Verificar a existência dos arquivos via terminal. -- Simular a execução do comando `make ci` (opcional, já validado pelo GHA). - -## Riscos - -- **Quebra de Pipeline Existente**: Se o Jenkins estiver configurado para ler especificamente de `files/config/Jenkinsfile`, o pipeline quebrará até que a configuração do Job seja atualizada. (Risco baixo, pois o padrão é a raiz). diff --git a/discussion/workflow/plans/PLN-0003-jenkins-gitea-execution.md b/discussion/workflow/plans/PLN-0003-jenkins-gitea-execution.md deleted file mode 100644 index 5e74565c..00000000 --- a/discussion/workflow/plans/PLN-0003-jenkins-gitea-execution.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -id: PLN-0003 -decisions: ["DEC-0003"] -title: Plano de Execução - Jenkins Gitea Integration -status: open -created: 2026-04-07 ---- - -# Plano de Execução - Jenkins Gitea Integration - -## Briefing - -Atualizar o Jenkinsfile para integração com Gitea e garantir sua localização em `files/config/Jenkinsfile`. Remover resquícios de GitHub CI. - -## Decisions de Origem - -- DEC-0003 - -## Alvo - -- `files/config/Jenkinsfile` -- `.github/workflows/` (limpeza) - -## Escopo - -- Atualização do script Groovy do `Jenkinsfile` com suporte a `giteaStatus`. -- Garantir que o diretório `files/config` existe. -- Remover diretório `.github/workflows` se existir. - -## Fora de Escopo - -- Configuração real do servidor Jenkins (fora do repositório). - -## Plano de Execucao - -1. Verificar existência do diretório `files/config` e criar se necessário. -2. Atualizar/Mover o `Jenkinsfile` para `files/config/Jenkinsfile`. -3. Adicionar blocos `post` no `Jenkinsfile` para notificação ao Gitea. -4. Excluir `.github/workflows` se presente. -5. Validar sintaxe básica do Jenkinsfile. - -## Criterios de Aceite - -- O arquivo `Jenkinsfile` reside em `files/config/Jenkinsfile`. -- O conteúdo do `Jenkinsfile` inclui `make ci` e chamadas ao Gitea. -- Não existem workflows de GitHub CI. - -## Tests / Validacao - -- Execução manual de `make ci` para garantir que o comando base funciona. -- Verificação visual do `Jenkinsfile`. - -## Riscos - -- **Incompatibilidade de Plugin**: Se o Jenkins do usuário não tiver o plugin do Gitea, as chamadas `giteaStatus` podem falhar. No entanto, estamos seguindo o requisito de "propagar resultados para gitea". -- 2.47.2 From 52d4a91c71cac18b4da31395445fd3fdfbdf9631 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Thu, 9 Apr 2026 08:25:37 +0100 Subject: [PATCH 04/10] Asset Entry Metadata Normalization Contract --- crates/console/prometeu-drivers/src/asset.rs | 27 ++--- crates/console/prometeu-hal/src/asset.rs | 37 +++++++ discussion/index.ndjson | 4 +- .../LSN-0023-typed-asset-metadata-helpers.md | 33 ++++++ ...t-entry-metadata-normalization-contract.md | 100 ------------------ docs/specs/runtime/15-asset-management.md | 47 ++++---- 6 files changed, 110 insertions(+), 138 deletions(-) create mode 100644 discussion/lessons/DSC-0017-asset-metadata-normalization/LSN-0023-typed-asset-metadata-helpers.md delete mode 100644 discussion/workflow/agendas/AGD-0016-asset-entry-metadata-normalization-contract.md diff --git a/crates/console/prometeu-drivers/src/asset.rs b/crates/console/prometeu-drivers/src/asset.rs index 26b82eab..b5df47e4 100644 --- a/crates/console/prometeu-drivers/src/asset.rs +++ b/crates/console/prometeu-drivers/src/asset.rs @@ -186,29 +186,22 @@ impl AssetManager { fn decode_tile_bank_layout( entry: &AssetEntry, ) -> Result<(TileSize, usize, usize, usize), String> { - let tile_size_val = - entry.metadata.get("tile_size").and_then(|v| v.as_u64()).ok_or("Missing tile_size")?; - let width = - entry.metadata.get("width").and_then(|v| v.as_u64()).ok_or("Missing width")? as usize; - let height = - entry.metadata.get("height").and_then(|v| v.as_u64()).ok_or("Missing height")? as usize; - let palette_count = entry - .metadata - .get("palette_count") - .and_then(|v| v.as_u64()) - .ok_or("Missing palette_count")? as usize; + let meta = entry.metadata_as_tiles()?; - let tile_size = match tile_size_val { + let tile_size = match meta.tile_size { 8 => TileSize::Size8, 16 => TileSize::Size16, 32 => TileSize::Size32, - _ => return Err(format!("Invalid tile_size: {}", tile_size_val)), + _ => return Err(format!("Invalid tile_size: {}", meta.tile_size)), }; - if palette_count != TILE_BANK_PALETTE_COUNT_V1 { - return Err(format!("Invalid palette_count: {}", palette_count)); + if meta.palette_count as usize != TILE_BANK_PALETTE_COUNT_V1 { + return Err(format!("Invalid palette_count: {}", meta.palette_count)); } + let width = meta.width as usize; + let height = meta.height as usize; + let logical_pixels = width.checked_mul(height).ok_or("TileBank dimensions overflow")?; let serialized_pixel_bytes = logical_pixels.div_ceil(2); let serialized_size = serialized_pixel_bytes @@ -637,8 +630,8 @@ impl AssetManager { entry: &AssetEntry, buffer: &[u8], ) -> Result { - let sample_rate = - entry.metadata.get("sample_rate").and_then(|v| v.as_u64()).unwrap_or(44100) as u32; + let meta = entry.metadata_as_sounds()?; + let sample_rate = meta.sample_rate; let mut data = Vec::with_capacity(buffer.len() / 2); for i in (0..buffer.len()).step_by(2) { diff --git a/crates/console/prometeu-hal/src/asset.rs b/crates/console/prometeu-hal/src/asset.rs index 6ca8ee49..ccab4b2d 100644 --- a/crates/console/prometeu-hal/src/asset.rs +++ b/crates/console/prometeu-hal/src/asset.rs @@ -24,6 +24,43 @@ pub struct AssetEntry { pub metadata: serde_json::Value, } +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct TilesMetadata { + pub tile_size: u32, + pub width: u32, + pub height: u32, + pub palette_count: u32, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct SoundsMetadata { + pub sample_rate: u32, +} + +impl AssetEntry { + pub fn metadata_as_tiles(&self) -> Result { + if self.bank_type != BankType::TILES { + return Err(format!( + "Asset {} is not a TILES bank (type: {:?})", + self.asset_id, self.bank_type + )); + } + serde_json::from_value(self.metadata.clone()) + .map_err(|e| format!("Invalid TILES metadata for asset {}: {}", self.asset_id, e)) + } + + pub fn metadata_as_sounds(&self) -> Result { + if self.bank_type != BankType::SOUNDS { + return Err(format!( + "Asset {} is not a SOUNDS bank (type: {:?})", + self.asset_id, self.bank_type + )); + } + serde_json::from_value(self.metadata.clone()) + .map_err(|e| format!("Invalid SOUNDS metadata for asset {}: {}", self.asset_id, e)) + } +} + #[derive(Debug, Clone, Deserialize, Serialize)] pub struct PreloadEntry { pub asset_id: AssetId, diff --git a/discussion/index.ndjson b/discussion/index.ndjson index e4ed9054..2de2de00 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -1,4 +1,4 @@ -{"type":"meta","next_id":{"DSC":21,"AGD":19,"DEC":3,"PLN":3,"LSN":23,"CLSN":1}} +{"type":"meta","next_id":{"DSC":21,"AGD":19,"DEC":5,"PLN":3,"LSN":24,"CLSN":1}} ... (mantendo as linhas anteriores) ... {"type":"discussion","id":"DSC-0020","status":"done","ticket":"jenkins-gitea-integration","title":"Jenkins Gitea Integration and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins","gitea"],"agendas":[{"id":"AGD-0018","file":"workflow/agendas/AGD-0018-jenkins-gitea-integration-and-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0003","file":"workflow/decisions/DEC-0003-jenkins-gitea-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0003","file":"workflow/plans/PLN-0003-jenkins-gitea-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0021","file":"lessons/DSC-0020-jenkins-gitea-integration/LSN-0021-jenkins-gitea-integration.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]} {"type":"discussion","id":"DSC-0001","status":"done","ticket":"legacy-runtime-learn-import","title":"Import legacy runtime learn into discussion lessons","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["migration","tech-debt"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0001","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0001-prometeu-learn-index.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0002","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0002-historical-asset-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0003","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0003-historical-audio-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0004","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0004-historical-cartridge-boot-protocol-and-manifest-authority.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0005","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0005-historical-game-memcard-slots-surface-and-semantics.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0006","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0006-historical-gfx-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0007","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0007-historical-retired-fault-and-input-decisions.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0008","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0008-historical-vm-core-and-assets.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0009","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0009-mental-model-asset-management.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0010","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0010-mental-model-audio.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0011","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0011-mental-model-gfx.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0012","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0012-mental-model-input.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0013","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0013-mental-model-observability-and-debugging.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0014","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0014-mental-model-portability-and-cross-platform.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0015","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0015-mental-model-save-memory-and-memcard.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0016","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0016-mental-model-status-first-and-fault-thinking.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0017","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0017-mental-model-time-and-cycles.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0018","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0018-mental-model-touch.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]} @@ -17,6 +17,6 @@ {"type":"discussion","id":"DSC-0014","status":"open","ticket":"perf-vm-allocation-and-copy-pressure","title":"Agenda - [PERF] VM Allocation and Copy Pressure","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0013","file":"workflow/agendas/AGD-0013-perf-vm-allocation-and-copy-pressure.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0015","status":"open","ticket":"perf-cartridge-boot-and-program-ownership","title":"Agenda - [PERF] Cartridge Boot and Program Ownership","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0014","file":"workflow/agendas/AGD-0014-perf-cartridge-boot-and-program-ownership.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} {"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"}]} -{"type":"discussion","id":"DSC-0017","status":"open","ticket":"asset-entry-metadata-normalization-contract","title":"Asset Entry Metadata Normalization Contract","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0016","file":"workflow/agendas/AGD-0016-asset-entry-metadata-normalization-contract.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} +{"type":"discussion","id":"DSC-0017","status":"done","ticket":"asset-entry-metadata-normalization-contract","title":"Asset Entry Metadata Normalization Contract","created_at":"2026-03-27","updated_at":"2026-04-09","tags":[],"agendas":[{"id":"AGD-0016","file":"workflow/agendas/AGD-0016-asset-entry-metadata-normalization-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-04-09"}],"decisions":[{"id":"DEC-0004","file":"workflow/decisions/DEC-0004-asset-entry-metadata-normalization-contract.md","status":"accepted","created_at":"2026-04-09","updated_at":"2026-04-09"}],"plans":[],"lessons":[{"id":"LSN-0023","file":"lessons/DSC-0017-asset-metadata-normalization/LSN-0023-typed-asset-metadata-helpers.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]} {"type":"discussion","id":"DSC-0018","status":"done","ticket":"asset-load-asset-id-int-contract","title":"Asset Load Asset ID Int Contract","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["asset","runtime","abi"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0019","file":"lessons/DSC-0018-asset-load-asset-id-int-contract/LSN-0019-asset-load-id-abi-convergence.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]} {"type":"discussion","id":"DSC-0019","status":"done","ticket":"jenkinsfile-correction","title":"Jenkinsfile Correction and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins"],"agendas":[{"id":"AGD-0017","file":"workflow/agendas/AGD-0017-jenkinsfile-correction.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0002","file":"workflow/decisions/DEC-0002-jenkinsfile-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0002","file":"workflow/plans/PLN-0002-jenkinsfile-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0020","file":"lessons/DSC-0019-jenkins-ci-standardization/LSN-0020-jenkins-standard-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]} diff --git a/discussion/lessons/DSC-0017-asset-metadata-normalization/LSN-0023-typed-asset-metadata-helpers.md b/discussion/lessons/DSC-0017-asset-metadata-normalization/LSN-0023-typed-asset-metadata-helpers.md new file mode 100644 index 00000000..51b462c5 --- /dev/null +++ b/discussion/lessons/DSC-0017-asset-metadata-normalization/LSN-0023-typed-asset-metadata-helpers.md @@ -0,0 +1,33 @@ +# LSN-0023: Typed Helpers for Asset Metadata + +Status: Published +Decisions: [[DEC-0004]] +Tags: [asset, runtime, rust, pattern] + +## Contexto + +A decisão [[DEC-0004]] estabeleceu um contrato de metadados segmentados para assets, mantendo campos críticos na raiz do JSON e detalhes técnicos em subárvores (`codec`, `pipeline`). No entanto, o `AssetEntry.metadata` no runtime é um `serde_json::Value` dinâmico. + +## Lição + +O uso de `serde_json::Value` diretamente nos loaders do runtime introduz riscos de runtime (erros de tipo, campos ausentes) e polui o código com chamadas repetitivas de `.get()`, `.as_u64()`, etc. + +### Abordagem Adotada + +Para mitigar isso, implementamos o padrão de **Typed Metadata Helpers**: + +1. **Structs Dedicadas**: Criamos structs Rust (ex: `TilesMetadata`, `SoundsMetadata`) que representam o contrato exato de cada banco. +2. **Conversion Methods**: Adicionamos métodos ao `AssetEntry` (ex: `metadata_as_tiles()`) que utilizam `serde_json::from_value` para realizar o "cast" do JSON dinâmico para a struct tipada. +3. **Fail-Fast**: A falha no parsing dos metadados deve ser tratada como erro de carregamento do asset, garantindo que o motor não opere com metadados corrompidos ou incompletos. + +### Benefícios + +- **Segurança de Tipo**: Erros de estrutura de metadados são detectados no momento do carregamento. +- **Ergonomia**: O código dos drivers passa a usar `meta.tile_size` em vez de parsing manual. +- **Desacoplamento**: A complexidade do JSON fica encapsulada nos helpers de conversão. + +## Referências + +- DEC-0004: Asset Entry Metadata Normalization Contract +- `prometeu-hal/src/asset.rs` +- `prometeu-drivers/src/asset.rs` diff --git a/discussion/workflow/agendas/AGD-0016-asset-entry-metadata-normalization-contract.md b/discussion/workflow/agendas/AGD-0016-asset-entry-metadata-normalization-contract.md deleted file mode 100644 index e9df8640..00000000 --- a/discussion/workflow/agendas/AGD-0016-asset-entry-metadata-normalization-contract.md +++ /dev/null @@ -1,100 +0,0 @@ ---- -id: AGD-0016 -ticket: asset-entry-metadata-normalization-contract -title: Asset Entry Metadata Normalization Contract -status: open -created: 2026-03-27 -resolved: -decision: -tags: [] ---- - -# Asset Entry Metadata Normalization Contract - -Status: Open -Domain Owner: `docs/runtime` -Cross-Domain Impact: `../studio/docs/packer`, `shipper`, `asset` loader - -## Purpose - -Normatizar como `AssetEntry.metadata` deve preservar a convergencia entre metadata autoral, metadata de codec e metadata de pipeline sem colapsar tudo num mapa plano ambiguo. - -## Problem - -O lado produtor (`packer`) ja convergiu para um contrato em que o runtime precisa ler campos obrigatorios diretamente de `AssetEntry.metadata`, mas tambem precisa manter segmentacao suficiente para nao perder significado entre: - -- `asset.json.output.metadata` -- `asset.json.output.codec_configuration` -- `asset.json.output.pipeline` - -Sem um contrato explicito no runtime: - -- o packer pode materializar estruturas diferentes entre formatos; -- o loader/runtime pode passar a depender de flattening incidental; -- tooling e debug surfaces perdem previsibilidade; -- futuros formatos tendem a misturar metadata efetiva com detalhe interno de pipeline. - -## Context - -No ciclo atual de `tile bank`, o produtor ja fechou esta direcao: - -- `asset.json.output.metadata` -> `AssetEntry.metadata` -- `asset.json.output.codec_configuration` -> `AssetEntry.metadata.codec` -- `asset.json.output.pipeline` -> `AssetEntry.metadata.pipeline` - -Ao mesmo tempo, o runtime ainda consome alguns campos obrigatorios do tile bank diretamente no nivel raiz de `AssetEntry.metadata`, em especial: - -- `tile_size` -- `width` -- `height` -- `palette_count` - -A agenda precisa fechar se esse shape vira contrato geral de runtime para metadata normalizada de assets, e como o consumidor deve tratar campos obrigatorios format-specific versus subtrees segmentadas. - -## Options - -### Option A - Flat effective metadata map only - -Tudo converge para um unico mapa plano em `AssetEntry.metadata`. - -### Option B - Root effective metadata plus stable segmented subtrees - -Campos runtime-obrigatorios ficam legiveis no nivel raiz, enquanto dados de codec e pipeline ficam em subtrees estaveis: - -- `metadata.` -- `metadata.codec.` -- `metadata.pipeline.` - -### Option C - Fully segmented metadata only - -Nada fica no nivel raiz; todo consumo passa por subtrees por origem. - -## Tradeoffs - -- Option A simplifica leitura curta, mas perde origem semantica e aumenta risco de colisao. -- Option B preserva leitura direta para campos obrigatorios do runtime e mantem segmentacao estavel para evolucao futura. -- Option C e semanticamente limpa, mas quebra o consumo direto atual de formatos como `tile bank` e introduz custo de migracao desnecessario agora. - -## Recommendation - -Adotar `Option B`. - -Direcao recomendada: - -- campos format-specific obrigatorios para decode/runtime continuam legiveis no nivel raiz de `AssetEntry.metadata`; -- `output.codec_configuration` materializa em `AssetEntry.metadata.codec`; -- `output.pipeline` materializa em `AssetEntry.metadata.pipeline`; -- o runtime nao deve exigir flattening total para consumir metadata segmentada; -- specs format-specific devem declarar explicitamente quais campos sao obrigatorios no nivel raiz. - -## Open Questions - -1. O contrato deve tratar o subtree raiz como semanticamente equivalente a `output.metadata` ou como effective metadata map mais amplo? -2. Quais readers/helpers do runtime devem ser criados para evitar parsing manual disperso de `metadata.codec` e `metadata.pipeline`? - -## Expected Follow-up - -1. Converter esta agenda em decision no `runtime`. -2. Propagar a decisao para `15-asset-management.md`. -3. Ajustar loaders format-specific para usar helpers consistentes de metadata quando necessario. -4. Alinhar o `packer` e testes de conformance com o shape final. diff --git a/docs/specs/runtime/15-asset-management.md b/docs/specs/runtime/15-asset-management.md index c14093fa..3feb76eb 100644 --- a/docs/specs/runtime/15-asset-management.md +++ b/docs/specs/runtime/15-asset-management.md @@ -110,37 +110,46 @@ This table describes content identity and storage layout, not live residency. ### 4.1 `TILES` asset contract in v1 -For `BankType::TILES`, the runtime-facing v1 contract is: +Para `BankType::TILES`, o contrato v1 voltado para o runtime é: - `codec = NONE` -- serialized pixels use packed `u4` palette indices -- serialized palettes use `RGB565` (`u16`, little-endian) +- pixels serializados usam índices de paleta `u4` empacotados +- paletas serializadas usam `RGB565` (`u16`, little-endian) - `palette_count = 64` -- runtime materialization may expand pixel indices to one `u8` per pixel +- a materialização em runtime pode expandir índices de pixel para um `u8` por pixel -`NONE` for `TILES` means there is no additional generic codec layer beyond the bank contract itself. +`NONE` para `TILES` significa que não há camada de codec genérica adicional além do próprio contrato do banco. -For the current transition window: +Para a janela de transição atual: -- `RAW` is a deprecated legacy alias of `NONE` -- new published material must use `NONE` as the canonical value +- `RAW` é um alias legado e depreciado de `NONE` +- novos materiais publicados devem usar `NONE` como valor canônico -Even with `codec = NONE`, `TILES` still requires deterministic bank-specific decode from its serialized payload. The serialized byte layout is therefore not required to be identical to the in-memory layout. +Mesmo com `codec = NONE`, `TILES` ainda requer decode específico de banco a partir de seu payload serializado. O layout de bytes serializados não precisa, portanto, ser idêntico ao layout em memória. -Required `AssetEntry.metadata` fields for `TILES`: +#### 4.1.1 Metadata Normalization -- `tile_size`: tile edge in pixels; valid values are `8`, `16`, or `32` -- `width`: full bank sheet width in pixels -- `height`: full bank sheet height in pixels -- `palette_count`: number of palettes serialized for the bank +Seguindo a `DEC-0004`, o campo `AssetEntry.metadata` deve ser estruturado de forma segmentada para evitar ambiguidades. -Validation rules for `TILES` v1: +Campos de metadados obrigatórios (efetivos) para `TILES` no nível raiz: -- `palette_count` must be `64` -- `width * height` defines the number of logical indexed pixels in the decoded sheet -- additional metadata may exist, but the runtime contract must not depend on it to reconstruct the bank in memory +- `tile_size`: aresta do tile em pixels; valores válidos são `8`, `16`, ou `32` +- `width`: largura total da folha do banco em pixels +- `height`: altura total da folha do banco em pixels +- `palette_count`: número de paletas serializadas para o banco -Serialized payload layout for `TILES` v1: +Subárvores opcionais e informativas: + +- `metadata.codec`: Configuração específica do codec/compressor (ex: dicionários, flags de compressão). +- `metadata.pipeline`: Metadados informativos do processo de build do Studio (ex: source hashes, timestamps, tool versions). + +Regras de validação para `TILES` v1: + +- `palette_count` deve ser `64` +- `width * height` define o número de pixels indexados lógicos na folha decodificada +- metadados adicionais podem existir, mas o contrato do runtime não deve depender deles para reconstruir o banco em memória (exceto se definidos na raiz como campos efetivos). + +#### 4.1.2 Payload Layout 1. packed indexed pixels for the full sheet, using `ceil(width * height / 2)` bytes; 2. palette table, using `palette_count * 16 * 2` bytes. -- 2.47.2 From 7215edfe038121e85619b8e17e9e5ea6ed48ecc2 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Thu, 9 Apr 2026 09:43:47 +0100 Subject: [PATCH 05/10] Asset Entry Metadata Normalization Contract --- crates/console/prometeu-hal/src/asset.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/console/prometeu-hal/src/asset.rs b/crates/console/prometeu-hal/src/asset.rs index ccab4b2d..40f49462 100644 --- a/crates/console/prometeu-hal/src/asset.rs +++ b/crates/console/prometeu-hal/src/asset.rs @@ -30,11 +30,13 @@ pub struct TilesMetadata { pub width: u32, pub height: u32, pub palette_count: u32, + pub palette_authored: u32, } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct SoundsMetadata { pub sample_rate: u32, + pub channels: u32, } impl AssetEntry { -- 2.47.2 From 5faf9ac6db9306894fb2a6cef7874ee37e836886 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Thu, 9 Apr 2026 18:44:41 +0100 Subject: [PATCH 06/10] Asset Entry Codec as Enum with Metadata --- crates/console/prometeu-drivers/src/asset.rs | 46 ++--- crates/console/prometeu-drivers/src/gfx.rs | 8 +- crates/console/prometeu-hal/src/asset.rs | 8 +- .../prometeu-hal/src/cartridge_loader.rs | 96 ++++++++- crates/console/prometeu-hal/src/glyph.rs | 2 +- crates/console/prometeu-hal/src/lib.rs | 2 +- .../src/virtual_machine_runtime/dispatch.rs | 7 +- .../src/virtual_machine_runtime/tests.rs | 9 +- discussion/index.ndjson | 4 +- ...19-asset-entry-codec-enum-with-metadata.md | 183 ++++++++++++++++++ ...EC-0005-asset-entry-codec-enum-contract.md | 91 +++++++++ ...N-0004-asset-entry-codec-enum-execution.md | 141 ++++++++++++++ 12 files changed, 547 insertions(+), 50 deletions(-) create mode 100644 discussion/workflow/agendas/AGD-0019-asset-entry-codec-enum-with-metadata.md create mode 100644 discussion/workflow/decisions/DEC-0005-asset-entry-codec-enum-contract.md create mode 100644 discussion/workflow/plans/PLN-0004-asset-entry-codec-enum-execution.md diff --git a/crates/console/prometeu-drivers/src/asset.rs b/crates/console/prometeu-drivers/src/asset.rs index b5df47e4..73397f18 100644 --- a/crates/console/prometeu-drivers/src/asset.rs +++ b/crates/console/prometeu-drivers/src/asset.rs @@ -2,8 +2,8 @@ use crate::memory_banks::{SoundBankPoolInstaller, TileBankPoolInstaller}; use prometeu_hal::AssetBridge; use prometeu_hal::asset::{ - AssetEntry, AssetId, AssetLoadError, AssetOpStatus, BankStats, BankType, HandleId, LoadStatus, - PreloadEntry, SlotRef, SlotStats, + AssetCodec, AssetEntry, AssetId, AssetLoadError, AssetOpStatus, BankStats, BankType, HandleId, + LoadStatus, PreloadEntry, SlotRef, SlotStats, }; use prometeu_hal::cartridge::AssetsPayloadSource; use prometeu_hal::color::Color; @@ -179,10 +179,6 @@ impl AssetBridge for AssetManager { } impl AssetManager { - fn codec_is_none_or_legacy_raw(codec: &str) -> bool { - matches!(codec, "NONE" | "RAW") - } - fn decode_tile_bank_layout( entry: &AssetEntry, ) -> Result<(TileSize, usize, usize, usize), String> { @@ -242,14 +238,9 @@ impl AssetManager { } fn op_mode_for(entry: &AssetEntry) -> Result { - match entry.bank_type { - BankType::TILES if Self::codec_is_none_or_legacy_raw(entry.codec.as_str()) => { - Ok(AssetOpMode::StageInMemory) - } - BankType::SOUNDS if Self::codec_is_none_or_legacy_raw(entry.codec.as_str()) => { - Ok(AssetOpMode::DirectFromSlice) - } - _ => Err(format!("Unsupported codec: {}", entry.codec)), + match (entry.bank_type, entry.codec) { + (BankType::TILES, AssetCodec::None) => Ok(AssetOpMode::StageInMemory), + (BankType::SOUNDS, AssetCodec::None) => Ok(AssetOpMode::DirectFromSlice), } } @@ -882,6 +873,7 @@ impl AssetManager { mod tests { use super::*; use crate::memory_banks::{MemoryBanks, SoundBankPoolAccess, TileBankPoolAccess}; + use prometeu_hal::asset::AssetCodec; fn expected_tile_payload_size(width: usize, height: usize) -> usize { (width * height).div_ceil(2) + TILE_BANK_PALETTE_BYTES_V1 @@ -905,12 +897,13 @@ mod tests { offset: 0, size: expected_tile_payload_size(width, height) as u64, decoded_size: expected_tile_decoded_size(width, height) as u64, - codec: "NONE".to_string(), + codec: AssetCodec::None, metadata: serde_json::json!({ "tile_size": 16, "width": width, "height": height, - "palette_count": TILE_BANK_PALETTE_COUNT_V1 + "palette_count": TILE_BANK_PALETTE_COUNT_V1, + "palette_authored": TILE_BANK_PALETTE_COUNT_V1 }), } } @@ -964,10 +957,8 @@ mod tests { } #[test] - fn test_op_mode_for_tiles_raw_is_legacy_alias() { - let mut entry = test_tile_asset_entry("tiles", 16, 16); - entry.codec = "RAW".to_string(); - + fn test_op_mode_for_tiles_none_uses_typed_codec() { + let entry = test_tile_asset_entry("tiles", 16, 16); assert_eq!(AssetManager::op_mode_for(&entry), Ok(AssetOpMode::StageInMemory)); } @@ -980,9 +971,10 @@ mod tests { offset: 0, size: 8, decoded_size: 8, - codec: "NONE".to_string(), + codec: AssetCodec::None, metadata: serde_json::json!({ - "sample_rate": 44100 + "sample_rate": 44100, + "channels": 1 }), }; @@ -1074,9 +1066,10 @@ mod tests { offset: 0, size: data.len() as u64, decoded_size: data.len() as u64, - codec: "NONE".to_string(), + codec: AssetCodec::None, metadata: serde_json::json!({ - "sample_rate": 44100 + "sample_rate": 44100, + "channels": 1 }), }; @@ -1117,9 +1110,10 @@ mod tests { offset: 0, size: data.len() as u64, decoded_size: data.len() as u64, - codec: "NONE".to_string(), + codec: AssetCodec::None, metadata: serde_json::json!({ - "sample_rate": 44100 + "sample_rate": 44100, + "channels": 1 }), }; diff --git a/crates/console/prometeu-drivers/src/gfx.rs b/crates/console/prometeu-drivers/src/gfx.rs index b9b9d4dc..13acba61 100644 --- a/crates/console/prometeu-drivers/src/gfx.rs +++ b/crates/console/prometeu-drivers/src/gfx.rs @@ -1,12 +1,12 @@ use crate::memory_banks::TileBankPoolAccess; use prometeu_hal::GfxBridge; use prometeu_hal::color::Color; +use prometeu_hal::glyph::Glyph; use prometeu_hal::sprite::Sprite; use prometeu_hal::tile::Tile; use prometeu_hal::tile_bank::{TileBank, TileSize}; use prometeu_hal::tile_layer::{HudTileLayer, ScrollableTileLayer, TileMap}; use std::sync::Arc; -use prometeu_hal::glyph::Glyph; /// Blending modes inspired by classic 16-bit hardware. /// Defines how source pixels are combined with existing pixels in the framebuffer. @@ -275,11 +275,7 @@ impl GfxBridge for Gfx { impl Gfx { /// Initializes the graphics system with a specific resolution and shared memory banks. pub fn new(w: usize, h: usize, tile_banks: Arc) -> Self { - - const EMPTY_GLYPH: Glyph = Glyph { - glyph_id: 0, - palette_id: 0, - }; + const EMPTY_GLYPH: Glyph = Glyph { glyph_id: 0, palette_id: 0 }; const EMPTY_SPRITE: Sprite = Sprite { glyph: EMPTY_GLYPH, diff --git a/crates/console/prometeu-hal/src/asset.rs b/crates/console/prometeu-hal/src/asset.rs index 40f49462..8e9346eb 100644 --- a/crates/console/prometeu-hal/src/asset.rs +++ b/crates/console/prometeu-hal/src/asset.rs @@ -12,6 +12,12 @@ pub enum BankType { // BLOBS, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)] +pub enum AssetCodec { + #[serde(rename = "NONE")] + None, +} + #[derive(Debug, Clone, Deserialize, Serialize)] pub struct AssetEntry { pub asset_id: AssetId, @@ -20,7 +26,7 @@ pub struct AssetEntry { pub offset: u64, pub size: u64, pub decoded_size: u64, - pub codec: String, // e.g., "NONE" (legacy alias: "RAW") + pub codec: AssetCodec, pub metadata: serde_json::Value, } diff --git a/crates/console/prometeu-hal/src/cartridge_loader.rs b/crates/console/prometeu-hal/src/cartridge_loader.rs index 9ed03612..4b410015 100644 --- a/crates/console/prometeu-hal/src/cartridge_loader.rs +++ b/crates/console/prometeu-hal/src/cartridge_loader.rs @@ -198,7 +198,7 @@ fn validate_preload( #[cfg(test)] mod tests { use super::*; - use crate::asset::{AssetEntry, BankType, PreloadEntry}; + use crate::asset::{AssetCodec, AssetEntry, BankType, PreloadEntry}; use crate::cartridge::{ASSETS_PA_MAGIC, ASSETS_PA_SCHEMA_VERSION, AssetsPackPrelude}; use crate::tile_bank::TILE_BANK_PALETTE_COUNT_V1; use serde_json::json; @@ -367,12 +367,13 @@ mod tests { offset, size, decoded_size: 16 * 16 + (TILE_BANK_PALETTE_COUNT_V1 as u64 * 16 * 2), - codec: "NONE".to_string(), + codec: AssetCodec::None, metadata: json!({ "tile_size": 16, "width": 16, "height": 16, - "palette_count": TILE_BANK_PALETTE_COUNT_V1 + "palette_count": TILE_BANK_PALETTE_COUNT_V1, + "palette_authored": TILE_BANK_PALETTE_COUNT_V1 }), } } @@ -449,12 +450,13 @@ mod tests { offset: 4, size: 4, decoded_size: 16 * 16 + (TILE_BANK_PALETTE_COUNT_V1 as u64 * 16 * 2), - codec: "NONE".to_string(), + codec: AssetCodec::None, metadata: json!({ "tile_size": 16, "width": 16, "height": 16, - "palette_count": TILE_BANK_PALETTE_COUNT_V1 + "palette_count": TILE_BANK_PALETTE_COUNT_V1, + "palette_authored": TILE_BANK_PALETTE_COUNT_V1 }), }, ]; @@ -497,4 +499,88 @@ mod tests { assert!(matches!(error, CartridgeError::InvalidFormat)); } + + #[test] + fn load_rejects_unknown_codec_string_in_assets_pa_header() { + let dir = TestCartridgeDir::new(manifest_with_capabilities(Some(vec!["asset"]))); + let header = serde_json::json!({ + "asset_table": [{ + "asset_id": 7, + "asset_name": "tiles", + "bank_type": "TILES", + "offset": 0, + "size": 4, + "decoded_size": 768, + "codec": "LZ4", + "metadata": { + "tile_size": 16, + "width": 16, + "height": 16, + "palette_count": TILE_BANK_PALETTE_COUNT_V1, + "palette_authored": TILE_BANK_PALETTE_COUNT_V1 + } + }], + "preload": [] + }); + let header_bytes = serde_json::to_vec(&header).expect("header must serialize"); + let prelude = AssetsPackPrelude { + magic: ASSETS_PA_MAGIC, + schema_version: ASSETS_PA_SCHEMA_VERSION, + header_len: header_bytes.len() as u32, + payload_offset: (ASSETS_PA_PRELUDE_SIZE + header_bytes.len()) as u64, + flags: 0, + reserved: 0, + header_checksum: 0, + }; + let mut bytes = prelude.to_bytes().to_vec(); + bytes.extend_from_slice(&header_bytes); + bytes.extend_from_slice(&[1_u8, 2, 3, 4]); + fs::write(dir.path().join("assets.pa"), bytes).expect("must write assets.pa"); + + let error = DirectoryCartridgeLoader::load(dir.path()).unwrap_err(); + + assert!(matches!(error, CartridgeError::InvalidFormat)); + } + + #[test] + fn load_rejects_legacy_raw_codec_string_in_assets_pa_header() { + let dir = TestCartridgeDir::new(manifest_with_capabilities(Some(vec!["asset"]))); + let header = serde_json::json!({ + "asset_table": [{ + "asset_id": 7, + "asset_name": "tiles", + "bank_type": "TILES", + "offset": 0, + "size": 4, + "decoded_size": 768, + "codec": "RAW", + "metadata": { + "tile_size": 16, + "width": 16, + "height": 16, + "palette_count": TILE_BANK_PALETTE_COUNT_V1, + "palette_authored": TILE_BANK_PALETTE_COUNT_V1 + } + }], + "preload": [] + }); + let header_bytes = serde_json::to_vec(&header).expect("header must serialize"); + let prelude = AssetsPackPrelude { + magic: ASSETS_PA_MAGIC, + schema_version: ASSETS_PA_SCHEMA_VERSION, + header_len: header_bytes.len() as u32, + payload_offset: (ASSETS_PA_PRELUDE_SIZE + header_bytes.len()) as u64, + flags: 0, + reserved: 0, + header_checksum: 0, + }; + let mut bytes = prelude.to_bytes().to_vec(); + bytes.extend_from_slice(&header_bytes); + bytes.extend_from_slice(&[1_u8, 2, 3, 4]); + fs::write(dir.path().join("assets.pa"), bytes).expect("must write assets.pa"); + + let error = DirectoryCartridgeLoader::load(dir.path()).unwrap_err(); + + assert!(matches!(error, CartridgeError::InvalidFormat)); + } } diff --git a/crates/console/prometeu-hal/src/glyph.rs b/crates/console/prometeu-hal/src/glyph.rs index e6d41e3a..793e8ece 100644 --- a/crates/console/prometeu-hal/src/glyph.rs +++ b/crates/console/prometeu-hal/src/glyph.rs @@ -2,4 +2,4 @@ pub struct Glyph { pub glyph_id: u16, pub palette_id: u8, -} \ No newline at end of file +} diff --git a/crates/console/prometeu-hal/src/lib.rs b/crates/console/prometeu-hal/src/lib.rs index 0495586e..1f7bd7af 100644 --- a/crates/console/prometeu-hal/src/lib.rs +++ b/crates/console/prometeu-hal/src/lib.rs @@ -7,6 +7,7 @@ pub mod cartridge_loader; pub mod color; pub mod debugger_protocol; pub mod gfx_bridge; +pub mod glyph; pub mod hardware_bridge; pub mod host_context; pub mod host_return; @@ -26,7 +27,6 @@ pub mod tile_layer; pub mod touch_bridge; pub mod vm_fault; pub mod window; -pub mod glyph; pub use asset_bridge::AssetBridge; pub use audio_bridge::{AudioBridge, AudioOpStatus, LoopMode}; diff --git a/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs b/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs index f4ec871a..d8fd483e 100644 --- a/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs +++ b/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs @@ -4,6 +4,7 @@ use prometeu_bytecode::{TRAP_INVALID_SYSCALL, TRAP_OOB, TRAP_TYPE, Value}; use prometeu_hal::asset::{AssetId, AssetOpStatus, BankType, SlotRef}; use prometeu_hal::cartridge::AppMode; use prometeu_hal::color::Color; +use prometeu_hal::glyph::Glyph; use prometeu_hal::log::{LogLevel, LogSource}; use prometeu_hal::sprite::Sprite; use prometeu_hal::syscalls::Syscall; @@ -12,7 +13,6 @@ use prometeu_hal::{ AudioOpStatus, GfxOpStatus, HostContext, HostReturn, NativeInterface, SyscallId, expect_bool, expect_int, }; -use prometeu_hal::glyph::Glyph; impl VirtualMachineRuntime { fn syscall_log_write(&mut self, level_val: i64, tag: u16, msg: String) -> Result<(), VmFault> { @@ -162,10 +162,7 @@ impl NativeInterface for VirtualMachineRuntime { } *hw.gfx_mut().sprite_mut(index) = Sprite { - glyph: Glyph { - glyph_id, - palette_id, - }, + glyph: Glyph { glyph_id, palette_id }, x, y, bank_id, diff --git a/crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs b/crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs index 810c73c1..2dd34c41 100644 --- a/crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs +++ b/crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs @@ -8,7 +8,9 @@ use prometeu_drivers::hardware::Hardware; use prometeu_hal::AudioOpStatus; use prometeu_hal::GfxOpStatus; use prometeu_hal::InputSignals; -use prometeu_hal::asset::{AssetEntry, AssetLoadError, AssetOpStatus, BankType, LoadStatus}; +use prometeu_hal::asset::{ + AssetCodec, AssetEntry, AssetLoadError, AssetOpStatus, BankType, LoadStatus, +}; use prometeu_hal::cartridge::{AssetsPayloadSource, Cartridge}; use prometeu_hal::syscalls::caps; use prometeu_hal::tile_bank::TILE_BANK_PALETTE_COUNT_V1; @@ -108,12 +110,13 @@ fn test_tile_asset_entry(asset_name: &str, data_len: usize) -> AssetEntry { offset: 0, size: data_len as u64, decoded_size: test_tile_decoded_size(16, 16) as u64, - codec: "NONE".to_string(), + codec: AssetCodec::None, metadata: serde_json::json!({ "tile_size": 16, "width": 16, "height": 16, - "palette_count": TILE_BANK_PALETTE_COUNT_V1 + "palette_count": TILE_BANK_PALETTE_COUNT_V1, + "palette_authored": TILE_BANK_PALETTE_COUNT_V1 }), } } diff --git a/discussion/index.ndjson b/discussion/index.ndjson index 2de2de00..391a384b 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -1,6 +1,6 @@ -{"type":"meta","next_id":{"DSC":21,"AGD":19,"DEC":5,"PLN":3,"LSN":24,"CLSN":1}} -... (mantendo as linhas anteriores) ... +{"type":"meta","next_id":{"DSC":22,"AGD":20,"DEC":6,"PLN":5,"LSN":24,"CLSN":1}} {"type":"discussion","id":"DSC-0020","status":"done","ticket":"jenkins-gitea-integration","title":"Jenkins Gitea Integration and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins","gitea"],"agendas":[{"id":"AGD-0018","file":"workflow/agendas/AGD-0018-jenkins-gitea-integration-and-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0003","file":"workflow/decisions/DEC-0003-jenkins-gitea-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0003","file":"workflow/plans/PLN-0003-jenkins-gitea-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0021","file":"lessons/DSC-0020-jenkins-gitea-integration/LSN-0021-jenkins-gitea-integration.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]} +{"type":"discussion","id":"DSC-0021","status":"open","ticket":"asset-entry-codec-enum-with-metadata","title":"Asset Entry Codec Enum Contract","created_at":"2026-04-09","updated_at":"2026-04-09","tags":["asset","runtime","codec","metadata"],"agendas":[{"id":"AGD-0019","file":"AGD-0019-asset-entry-codec-enum-with-metadata.md","status":"accepted","created_at":"2026-04-09","updated_at":"2026-04-09"}],"decisions":[{"id":"DEC-0005","file":"DEC-0005-asset-entry-codec-enum-contract.md","status":"accepted","created_at":"2026-04-09","updated_at":"2026-04-09","ref_agenda":"AGD-0019"}],"plans":[{"id":"PLN-0004","file":"PLN-0004-asset-entry-codec-enum-execution.md","status":"accepted","created_at":"2026-04-09","updated_at":"2026-04-09","ref_decisions":["DEC-0005"]}],"lessons":[]} {"type":"discussion","id":"DSC-0001","status":"done","ticket":"legacy-runtime-learn-import","title":"Import legacy runtime learn into discussion lessons","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["migration","tech-debt"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0001","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0001-prometeu-learn-index.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0002","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0002-historical-asset-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0003","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0003-historical-audio-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0004","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0004-historical-cartridge-boot-protocol-and-manifest-authority.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0005","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0005-historical-game-memcard-slots-surface-and-semantics.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0006","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0006-historical-gfx-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0007","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0007-historical-retired-fault-and-input-decisions.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0008","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0008-historical-vm-core-and-assets.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0009","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0009-mental-model-asset-management.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0010","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0010-mental-model-audio.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0011","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0011-mental-model-gfx.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0012","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0012-mental-model-input.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0013","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0013-mental-model-observability-and-debugging.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0014","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0014-mental-model-portability-and-cross-platform.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0015","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0015-mental-model-save-memory-and-memcard.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0016","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0016-mental-model-status-first-and-fault-thinking.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0017","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0017-mental-model-time-and-cycles.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0018","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0018-mental-model-touch.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]} {"type":"discussion","id":"DSC-0002","status":"open","ticket":"runtime-edge-test-plan","title":"Agenda - Runtime Edge Test Plan","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0001","file":"workflow/agendas/AGD-0001-runtime-edge-test-plan.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0003","status":"open","ticket":"packed-cartridge-loader-pmc","title":"Agenda - Packed Cartridge Loader PMC","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0002","file":"workflow/agendas/AGD-0002-packed-cartridge-loader-pmc.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} diff --git a/discussion/workflow/agendas/AGD-0019-asset-entry-codec-enum-with-metadata.md b/discussion/workflow/agendas/AGD-0019-asset-entry-codec-enum-with-metadata.md new file mode 100644 index 00000000..00f57657 --- /dev/null +++ b/discussion/workflow/agendas/AGD-0019-asset-entry-codec-enum-with-metadata.md @@ -0,0 +1,183 @@ +--- +id: AGD-0019 +ticket: asset-entry-codec-enum-with-metadata +title: Agenda - Asset Entry Codec as Enum with Metadata +status: accepted +created: 2026-04-09 +resolved: 2026-04-09 +decision: DEC-0005 +tags: [asset, runtime, codec, metadata] +--- + +# Agenda - Asset Entry Codec as Enum with Metadata + +## Contexto + +A `DSC-0017` consolidou a normalizacao de `AssetEntry.metadata`, separando campos criticos na raiz e empurrando detalhes tecnicos para subarvores como `codec` e `pipeline`. + +Isso resolveu a organizacao do contrato, mas nao fechou a representacao tipada de `codec` dentro do runtime. Hoje o dado ainda e percebido mais como JSON dinamico do que como parte explicita do modelo de dominio. + +O tema desta agenda e decidir se `codec` em `AssetEntry` deve deixar de ser apenas um blob/subarvore e passar a ser um enum tipado, capaz de carregar os metadados pertinentes a cada codec. + +## Problema + +Quando `codec` e tratado como JSON generico ou como informacao frouxa em metadados, o runtime perde: + +- clareza sobre quais codecs existem de fato; +- validacao estrutural forte por variante; +- ergonomia para loaders e drivers; +- separacao nitida entre metadata do banco e metadata do codec. + +Ao mesmo tempo, tipar `codec` cedo demais ou de forma larga demais pode misturar responsabilidades e transformar o enum em um deposito generico de qualquer detalhe tecnico do asset. + +## Pontos Criticos + +1. Limite de responsabilidade. + `codec` deve carregar apenas dados necessarios para decodificacao, nao toda a metadata do asset. + +2. Compatibilidade com a normalizacao anterior. + A agenda nao deve reabrir a decisao de segmentar metadata; ela deve apenas fechar como o segmento `codec` vira modelo tipado no runtime. + +3. Evolucao de formato. + O modelo precisa permitir codecs sem payload adicional e codecs com configuracao especifica, sem cair em campos opcionais demais. + +4. Unknown/forward compatibility. + Precisamos decidir se codecs desconhecidos falham no parse, se ficam preservados como raw JSON, ou se existe modo hibrido. + +5. Impacto nos callers. + A mudanca afeta serializacao/deserializacao, helpers, validacao de cart e ergonomia dos loaders. + +## Opcoes + +### Opcao A - Manter `codec` como JSON dinamico + +- **Abordagem:** manter `codec` como subarvore em `metadata` ou como `serde_json::Value`, usando helpers tipados apenas nos pontos de consumo. +- **Pro:** menor migracao imediata e maior flexibilidade para formatos experimentais. +- **Con:** o contrato real continua implicito, com checagens espalhadas e mais risco de drift entre parser e consumidores. +- **Tradeoff:** adia a decisao de dominio e preserva a ambiguidade exatamente no ponto onde o runtime precisa de semantica forte. + +### Opcao B - Tornar `codec` um enum tipado com payload por variante + +- **Abordagem:** introduzir algo como `AssetCodec`, com variantes explicitas (`None`, `Png { ... }`, `Jpeg { ... }`, etc.) carregando apenas os metadados do proprio codec. +- **Pro:** o runtime ganha exaustividade, parse centralizado e um modelo de dominio mais honesto. +- **Con:** exige decidir fronteiras claras entre metadata do codec e metadata do banco/asset; tambem aumenta custo de evolucao quando novos codecs surgem. +- **Tradeoff:** troca flexibilidade irrestrita por contrato forte e melhor ergonomia operacional. + +### Opcao C - Separar `codec_kind` de um payload opcional tipado + +- **Abordagem:** usar um enum leve para o tipo de codec e um campo separado para configuracao adicional. +- **Pro:** reduz acoplamento do discriminante com o payload e pode simplificar alguns formatos. +- **Con:** reintroduz estados invalidos (`kind` e payload divergentes), exigindo validacao cruzada manual. +- **Tradeoff:** parece mais flexivel, mas perde a principal vantagem de um tagged enum: coerencia estrutural por construcao. + +## Sugestao / Recomendacao + +Seguir com a **Opcao B**. + +`codec` em `AssetEntry` deveria ser um enum tipado e capaz de carregar os metadados estritamente relativos a ele. Isso fecha melhor o modelo de dominio: o runtime deixa de tratar codec como detalhe textual solto e passa a reconhece-lo como parte semantica do contrato do asset. + +A recomendacao, no entanto, vem com uma fronteira explicita: + +- metadata especifica de decodificacao fica dentro do enum do codec; +- metadata do banco/formato de consumo continua fora dele; +- metadata editorial ou operacional do asset tambem continua fora dele. + +Em outras palavras, a discussao nao e "colocar toda metadata dentro de `codec`", e sim "dar ao segmento `codec` uma representacao tipada, exaustiva e com payload por variante quando necessario". + +Com as respostas atuais, a recomendacao fica mais concreta: + +- o JSON serializado pelo Studio dentro de `asset.pa` pode manter `codec` como string opaca; +- o runtime Rust deve desserializar essa string diretamente para um enum em `AssetEntry`; +- o nome canonico de cada codec no JSON deve seguir o contrato definido pelo runtime, e o Studio/paker deve se conformar exatamente a ele; +- a variante inicial canonica e apenas `None`; +- `Raw` deve ser considerada removida do modelo; +- codec desconhecido no runtime e erro fatal de carregamento e deve impedir a execucao do cartucho; +- `pipeline` nao deve sobreviver no runtime e pode ser descartado completamente desse lado. + +## Perguntas em Aberto + +- Como o modelo evolui quando surgir o primeiro codec real com payload adicional sem perder a separacao entre o discriminante string no JSON e a interpretacao tipada no runtime? + +## Discussao + +### Direcao fechada ate aqui + +1. **Serializacao no Studio** + O JSON serializado pelo Studio dentro de `asset.pa` deve expor `codec` como string opaca. + +2. **Desserializacao no runtime** + O runtime Rust deve carregar essa string diretamente em um enum no proprio `AssetEntry`. O wire format continua string, mas o modelo carregado no runtime passa a ser tipado. + +3. **Canon do nome** + O nome textual do codec no JSON nao e arbitrario por produtor. Ele deve seguir exatamente o canon definido pelo contrato. O canon fechado para este tema e `SCREAMING_SNAKE_CASE`, alinhado a `BankType`, e o packer deve seguir esse formato sem aliases livres. + +4. **Variante inicial** + O unico codec canonico agora e `None`. + +5. **Fim de `Raw`** + `Raw` nao deve mais existir como conceito no modelo. + +6. **Codecs desconhecidos** + Nao devem ser empacotados. Se mesmo assim chegarem ao runtime, isso e erro de validacao do `AssetEntry` e o cartucho deve ser fechado antes de seguir. + +7. **Escopo atual de codecs** + Como ainda nao existe codec real implementado, a primeira versao do enum pode ser minima e conter apenas `None`. + +8. **Destino de `pipeline`** + `pipeline` nao tem valor operacional no runtime atual e pode ser descartado completamente desse lado. + +### Leitura arquitetural dessas respostas + +As respostas empurram a agenda para um contrato bem mais estrito do que o texto inicial sugeria: + +- `codec` existe como conceito de dominio mesmo antes de haver codecs concretos alem de `None`; +- o JSON de `asset.pa` pode manter um discriminante simples e opaco, enquanto o runtime desserializa isso diretamente para enum e usa esse valor para escolher a interpretacao tipada de `metadata.codec`; +- a autoridade sobre o spelling do codec pertence ao contrato compartilhado, nao ao Studio isoladamente; +- o runtime nao deve carregar extensibilidade aberta para codecs desconhecidos; +- a compatibilidade futura deve ser dirigida pelo Studio e pelo empacotamento, nao por tolerancia dinamica no runtime; +- `pipeline` deixa de disputar espaco conceitual com `codec` no runtime. + +Isso favorece um modelo de enum fechado, validado cedo e com semantica fail-fast. + +## Criterio para Encerrar + +Esta agenda pode ser encerrada quando houver consenso escrito sobre: + +- o papel exato de `codec` no modelo de dominio do `AssetEntry`; +- a fronteira entre payload do codec e restante da metadata; +- a estrategia para codecs sem metadata adicional e para codecs desconhecidos; +- o shape string de `codec` no JSON e sua relacao com o enum carregado no runtime e com `metadata.codec`; +- o proximo passo normativo para transformar isso em decisao sem reabrir `DSC-0017`. + +## Resolucao Provisoria + +Ha consenso provisoriamente estabelecido sobre os seguintes pontos: + +- `codec` deve existir como enum tipado no modelo do runtime; +- a variante inicial unica e `None`; +- `Raw` sai do contrato; +- codecs desconhecidos sao invalidos e devem falhar de forma fatal no carregamento do cartucho; +- `pipeline` pode ser descartado do runtime; +- o JSON de `asset.pa` deve serializar `codec` como string opaca; +- o runtime Rust deve desserializar essa string diretamente para enum no `AssetEntry`; +- o nome textual do codec no JSON deve seguir `SCREAMING_SNAKE_CASE`, e o Studio deve se conformar exatamente a ele; +- codec desconhecido deve falhar na validacao do `AssetEntry`, antes de qualquer carga efetiva. + +O unico ponto em aberto passa a ser a politica de evolucao para o primeiro codec real com payload, preservando a distincao entre: + +- discriminante string no JSON do asset; e +- shape tipado de `metadata.codec` no runtime. + +## Resolucao + +A agenda fica encerrada com a seguinte orientacao: + +- `codec` permanece `string` no JSON de `asset.pa`; +- o runtime Rust deve desserializar essa string diretamente para enum no `AssetEntry`; +- o nome textual do codec segue `SCREAMING_SNAKE_CASE`, alinhado a `BankType`; +- `None` e a unica variante inicial; +- `Raw` sai do contrato; +- codec desconhecido falha na validacao do `AssetEntry` e fecha o cartucho; +- `pipeline` e descartado do runtime. + +A evolucao para codecs com payload adicional fica explicitamente adiada para uma decisao futura motivada por um codec real. diff --git a/discussion/workflow/decisions/DEC-0005-asset-entry-codec-enum-contract.md b/discussion/workflow/decisions/DEC-0005-asset-entry-codec-enum-contract.md new file mode 100644 index 00000000..bb2a0911 --- /dev/null +++ b/discussion/workflow/decisions/DEC-0005-asset-entry-codec-enum-contract.md @@ -0,0 +1,91 @@ +--- +id: DEC-0005 +ticket: asset-entry-codec-enum-with-metadata +title: Asset Entry Codec Enum Contract +status: accepted +created: 2026-04-09 +accepted: 2026-04-09 +agenda: AGD-0019 +plans: [PLN-0004] +tags: [asset, runtime, codec, metadata] +--- + +## Status + +Accepted on 2026-04-09. + +## Contexto + +A discussao `AGD-0019` fechou que o campo `codec` de `AssetEntry` faz parte do contrato de dominio do runtime e nao deve permanecer como texto frouxo consumido ad hoc por loaders. + +Hoje o empacotamento e produzido no Studio em Java/Jackson e o runtime consome o header JSON de `asset.pa` em Rust. Portanto, o contrato precisa distinguir claramente: + +- o wire format serializado pelo Studio; +- o modelo tipado carregado pelo runtime; +- a relacao entre o discriminante do codec e qualquer metadata especifica de codec. + +A decisao tambem precisa consolidar o fim do alias legado `RAW` e a politica de falha para codecs desconhecidos. + +## Decisao + +1. `AssetEntry.codec` no runtime MUST ser representado por um enum tipado, e MUST NOT permanecer como `String` no modelo carregado. +2. O JSON serializado em `asset.pa` MUST continuar representando `codec` como uma `string`. +3. O runtime Rust MUST desserializar essa `string` diretamente para o enum de codec no proprio `AssetEntry`. +4. O spelling textual do codec no JSON MUST seguir exatamente o canon definido por este contrato. O canon inicial MUST ser `SCREAMING_SNAKE_CASE`, alinhado ao padrao ja usado por `BankType`. O Studio/paker MUST emitir exatamente esse spelling e MUST NOT introduzir aliases livres. +5. A variante inicial unica do enum MUST ser `None`. +6. O alias/conceito `Raw` MUST be removed from the contract and MUST NOT be emitted pelo Studio nem aceito como shape valido futuro. +7. Se o runtime encontrar um codec desconhecido para aquele binario, a validacao de `AssetEntry` MUST falhar antes de qualquer carga efetiva do asset, e o cartucho MUST ser rejeitado. +8. `pipeline` MUST NOT fazer parte do modelo operacional do runtime para esse contrato e MAY ser descartado completamente no lado do runtime. +9. Metadados especificos de codec, quando existirem, MUST ser interpretados a partir do codec resolvido e MUST permanecer separados da metadata editorial ou da metadata propria do banco consumidor. +10. A estrategia para o primeiro codec com payload adicional is deferred. Esta decisao fecha apenas o contrato atual e o ponto de extensao, sem antecipar um shape normativo para codecs futuros com payload. + +## Rationale + +- O wire format em `string` preserva simplicidade e compatibilidade com o Studio em Java/Jackson. +- O enum no runtime torna o contrato exaustivo, evita parsing textual espalhado e reduz drift entre loader, validacao e consumidores. +- Rejeitar codec desconhecido cedo mantem o sistema fail-fast e evita estados parcialmente carregados. +- Remover `Raw` evita manter semantica historica ambigua em paralelo ao contrato novo. +- Expulsar `pipeline` do runtime reduz ruido conceitual e impede sobreposicao de responsabilidade com `codec`. +- Adiar o desenho de codecs com payload evita overdesign antes de existir um caso concreto. + +## Invariantes / Contrato + +- O valor de `codec` no JSON de `asset.pa` e um discriminante textual canonico. +- O discriminante textual canonico de `codec` no JSON usa `SCREAMING_SNAKE_CASE`. +- O valor de `codec` carregado em Rust e um enum fechado para o conjunto de codecs suportados por aquele runtime. +- O Studio e o runtime compartilham o mesmo spelling canonico para cada variante. +- O runtime nao oferece modo tolerante para codecs desconhecidos. +- O contrato inicial possui uma unica variante valida: `None`. +- `Raw` nao pertence mais ao contrato canonico. +- `pipeline` nao participa do contrato operacional do runtime. +- Quando `metadata.codec` existir para algum codec futuro, sua interpretacao dependera do discriminante ja validado. + +## Impactos + +- `prometeu-hal` deve trocar `AssetEntry.codec: String` por um enum serializavel/desserializavel com canon textual explicito. +- `prometeu-drivers` deve remover a aceitacao de `RAW` como alias legado e passar a operar sobre enum, nao sobre comparacao textual solta. +- O loader/validacao do cart deve falhar cedo se a desserializacao ou validacao do codec falhar. +- O Studio/paker deve emitir exatamente o nome canonico definido para o codec. +- O nome canonico inicial de `None` no JSON deve ser `NONE`. +- Nao ha necessidade de definir agora um DTO separado apenas para codec; o proprio `AssetEntry` continua sendo o contrato serializado e desserializado. + +## Referencias + +- AGD-0019: Asset Entry Codec as Enum with Metadata +- DSC-0017: Asset Entry Metadata Normalization Contract +- LSN-0023: Typed Helpers for Asset Metadata +- `crates/console/prometeu-hal/src/asset.rs` +- `crates/console/prometeu-hal/src/cartridge_loader.rs` +- `crates/console/prometeu-drivers/src/asset.rs` + +## Propagacao Necessaria + +- Atualizar o modelo Rust de `AssetEntry` para usar enum de codec. +- Atualizar a validacao/desserializacao do header de `asset.pa`. +- Atualizar drivers para abandonar `RAW` e comparacoes por `String`. +- Ajustar o Studio/paker para emitir o spelling canonico escolhido pelo contrato. +- Escrever um plano de execucao antes de alterar spec/codigo. + +## Revision Log + +- 2026-04-09: Initial accepted decision from AGD-0019. diff --git a/discussion/workflow/plans/PLN-0004-asset-entry-codec-enum-execution.md b/discussion/workflow/plans/PLN-0004-asset-entry-codec-enum-execution.md new file mode 100644 index 00000000..71222e63 --- /dev/null +++ b/discussion/workflow/plans/PLN-0004-asset-entry-codec-enum-execution.md @@ -0,0 +1,141 @@ +--- +id: PLN-0004 +ticket: asset-entry-codec-enum-with-metadata +title: Asset Entry Codec Enum Execution +status: accepted +created: 2026-04-09 +completed: +tags: [asset, runtime, codec, metadata] +--- + +## Briefing + +Implement DEC-0005 by replacing `AssetEntry.codec: String` with a typed Rust enum while keeping the `assets.pa` JSON wire format as a canonical `SCREAMING_SNAKE_CASE` string. The initial supported codec set contains only `None`, serialized as `NONE`. Unknown codecs must fail during `AssetEntry` validation, `RAW` must be removed from the accepted contract, and runtime code must stop relying on free-form string comparisons. + +## Decisions de Origem + +- DEC-0005: Asset Entry Codec Enum Contract + +## Alvo + +Land a runtime-side implementation that: + +- introduces a serializable/deserializable `AssetCodec` enum in `prometeu-hal`; +- migrates `AssetEntry` to use the enum directly; +- rejects unknown codec strings during `assets.pa` parsing/validation; +- removes legacy `RAW` handling from runtime consumers and tests; +- preserves the current JSON transport contract for the Studio packer: `codec` remains a `SCREAMING_SNAKE_CASE` string in `assets.pa`. + +## Escopo + +- Runtime contract changes in `prometeu-hal` for `AssetEntry` and codec serialization. +- Cartridge loading and `assets.pa` validation behavior. +- Asset driver logic and tests that currently assume `codec` is a `String`. +- Runtime test fixtures that construct `AssetEntry` instances directly. + +## Fora de Escopo + +- Studio/Java packer implementation work. +- Introduction of any real codec beyond `None`. +- Design or implementation of `metadata.codec` payload shapes for future codecs. +- Discussion or implementation of a separate DTO just for `AssetEntry`. + +## Plano de Execucao + +### Step 1 - Introduce `AssetCodec` in `prometeu-hal` + +**What:** +Define the runtime enum for asset codecs and migrate `AssetEntry.codec` from `String` to that enum. + +**How:** +Add `AssetCodec` to `crates/console/prometeu-hal/src/asset.rs` with idiomatic Rust variant naming and explicit serde mapping to `SCREAMING_SNAKE_CASE`. The initial enum must contain only `None`, serialized as `NONE`. Update `AssetEntry` to use `AssetCodec` and remove the legacy comment about `RAW`. + +**File(s):** +- `crates/console/prometeu-hal/src/asset.rs` + +### Step 2 - Make `assets.pa` parsing fail on unknown codecs + +**What:** +Ensure unknown codec strings are rejected before any asset load path proceeds. + +**How:** +Rely on enum deserialization failure or an explicit validation hook during `assets.pa` header parsing so that invalid codec values produce `CartridgeError::InvalidFormat`. Keep the failure at `AssetEntry` validation time inside cartridge loading, not delayed to driver execution. + +**File(s):** +- `crates/console/prometeu-hal/src/cartridge_loader.rs` +- Any supporting type definitions in `crates/console/prometeu-hal/src/cartridge.rs` if required by parsing flow + +### Step 3 - Remove runtime string-based codec branching + +**What:** +Update asset runtime behavior to consume the enum directly and remove legacy `RAW` acceptance. + +**How:** +Replace `codec_is_none_or_legacy_raw` and string matches in `crates/console/prometeu-drivers/src/asset.rs` with enum-based matching on `AssetCodec`. Ensure unsupported codecs remain an error path, but `RAW` is no longer recognized anywhere in runtime logic. + +**File(s):** +- `crates/console/prometeu-drivers/src/asset.rs` + +### Step 4 - Update test fixtures and regression coverage + +**What:** +Bring existing tests and fixtures in line with the new typed contract and add regression tests for rejection behavior. + +**How:** +Replace direct string fixture values such as `"NONE"` and `"RAW"` with enum construction where tests instantiate `AssetEntry` directly. Add or update loader tests to cover: + +- successful parse of `codec: "NONE"`; +- failure on unknown codec strings; +- failure on legacy `RAW` if it still appears in serialized input. + +Update driver tests to assert enum-based behavior for the supported codec set. + +**File(s):** +- `crates/console/prometeu-hal/src/cartridge_loader.rs` +- `crates/console/prometeu-drivers/src/asset.rs` +- `crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs` + +### Step 5 - Align downstream packer work item + +**What:** +Capture the non-runtime propagation requirement for the Studio packer. + +**How:** +Record in implementation notes, issue tracking, or follow-up execution context that the Studio/Java packer must emit canonical `SCREAMING_SNAKE_CASE` codec strings and must stop producing `RAW`. No runtime code should add compatibility shims for packer drift. + +**File(s):** +- No runtime file changes required in this repository for this step + +## Criterios de Aceite + +- `AssetEntry.codec` is an enum in Rust, not a `String`. +- `assets.pa` still serializes/deserializes codec as a JSON string. +- The canonical wire spelling for the initial codec is `NONE`. +- Unknown codec strings cause cartridge loading to fail before asset loading proceeds. +- `RAW` is no longer accepted by runtime code or runtime tests. +- Asset driver code branches on `AssetCodec`, not string literals. +- Existing runtime tests pass after fixture migration, and regression tests cover the new failure behavior. + +## Tests / Validacao + +### Unit Tests + +- Serialization/deserialization tests for `AssetCodec` proving `AssetCodec::None <-> "NONE"`. +- Asset driver tests that match on enum variants instead of strings. + +### Integration Tests + +- Cartridge loader test that accepts `assets.pa` headers containing `codec: "NONE"`. +- Cartridge loader test that rejects an unknown codec string. +- Cartridge loader test that rejects legacy `RAW`. + +### Manual Verification + +- Inspect generated `assets.pa` header JSON from a known-good sample and verify `codec` remains a string field with `SCREAMING_SNAKE_CASE`. +- Run the relevant Rust test suites for `prometeu-hal`, `prometeu-drivers`, and any affected runtime tests. + +## Riscos + +- Existing tests and fixtures may be numerous because `AssetEntry` is widely constructed directly. +- If deserialization failure maps too generically, debugging bad packer output may become opaque unless tests assert the intended failure path clearly. +- Studio work is out of scope for this repository, so rollout coordination is required to avoid runtime/packer contract skew. -- 2.47.2 From 59101baf00564bf5f0c0db971985ca9b4b5c1bb5 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Thu, 9 Apr 2026 18:47:35 +0100 Subject: [PATCH 07/10] clean up --- discussion/index.ndjson | 4 +- ...0024-string-on-the-wire-enum-in-runtime.md | 45 +++++ ...19-asset-entry-codec-enum-with-metadata.md | 183 ------------------ ...EC-0005-asset-entry-codec-enum-contract.md | 91 --------- ...N-0004-asset-entry-codec-enum-execution.md | 141 -------------- 5 files changed, 47 insertions(+), 417 deletions(-) create mode 100644 discussion/lessons/DSC-0021-asset-entry-codec-enum-contract/LSN-0024-string-on-the-wire-enum-in-runtime.md delete mode 100644 discussion/workflow/agendas/AGD-0019-asset-entry-codec-enum-with-metadata.md delete mode 100644 discussion/workflow/decisions/DEC-0005-asset-entry-codec-enum-contract.md delete mode 100644 discussion/workflow/plans/PLN-0004-asset-entry-codec-enum-execution.md diff --git a/discussion/index.ndjson b/discussion/index.ndjson index 391a384b..4f6a488d 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -1,6 +1,6 @@ -{"type":"meta","next_id":{"DSC":22,"AGD":20,"DEC":6,"PLN":5,"LSN":24,"CLSN":1}} +{"type":"meta","next_id":{"DSC":22,"AGD":20,"DEC":6,"PLN":5,"LSN":25,"CLSN":1}} {"type":"discussion","id":"DSC-0020","status":"done","ticket":"jenkins-gitea-integration","title":"Jenkins Gitea Integration and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins","gitea"],"agendas":[{"id":"AGD-0018","file":"workflow/agendas/AGD-0018-jenkins-gitea-integration-and-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0003","file":"workflow/decisions/DEC-0003-jenkins-gitea-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0003","file":"workflow/plans/PLN-0003-jenkins-gitea-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0021","file":"lessons/DSC-0020-jenkins-gitea-integration/LSN-0021-jenkins-gitea-integration.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]} -{"type":"discussion","id":"DSC-0021","status":"open","ticket":"asset-entry-codec-enum-with-metadata","title":"Asset Entry Codec Enum Contract","created_at":"2026-04-09","updated_at":"2026-04-09","tags":["asset","runtime","codec","metadata"],"agendas":[{"id":"AGD-0019","file":"AGD-0019-asset-entry-codec-enum-with-metadata.md","status":"accepted","created_at":"2026-04-09","updated_at":"2026-04-09"}],"decisions":[{"id":"DEC-0005","file":"DEC-0005-asset-entry-codec-enum-contract.md","status":"accepted","created_at":"2026-04-09","updated_at":"2026-04-09","ref_agenda":"AGD-0019"}],"plans":[{"id":"PLN-0004","file":"PLN-0004-asset-entry-codec-enum-execution.md","status":"accepted","created_at":"2026-04-09","updated_at":"2026-04-09","ref_decisions":["DEC-0005"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0021","status":"done","ticket":"asset-entry-codec-enum-with-metadata","title":"Asset Entry Codec Enum Contract","created_at":"2026-04-09","updated_at":"2026-04-09","tags":["asset","runtime","codec","metadata"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0024","file":"lessons/DSC-0021-asset-entry-codec-enum-contract/LSN-0024-string-on-the-wire-enum-in-runtime.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]} {"type":"discussion","id":"DSC-0001","status":"done","ticket":"legacy-runtime-learn-import","title":"Import legacy runtime learn into discussion lessons","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["migration","tech-debt"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0001","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0001-prometeu-learn-index.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0002","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0002-historical-asset-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0003","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0003-historical-audio-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0004","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0004-historical-cartridge-boot-protocol-and-manifest-authority.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0005","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0005-historical-game-memcard-slots-surface-and-semantics.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0006","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0006-historical-gfx-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0007","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0007-historical-retired-fault-and-input-decisions.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0008","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0008-historical-vm-core-and-assets.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0009","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0009-mental-model-asset-management.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0010","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0010-mental-model-audio.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0011","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0011-mental-model-gfx.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0012","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0012-mental-model-input.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0013","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0013-mental-model-observability-and-debugging.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0014","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0014-mental-model-portability-and-cross-platform.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0015","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0015-mental-model-save-memory-and-memcard.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0016","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0016-mental-model-status-first-and-fault-thinking.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0017","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0017-mental-model-time-and-cycles.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0018","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0018-mental-model-touch.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]} {"type":"discussion","id":"DSC-0002","status":"open","ticket":"runtime-edge-test-plan","title":"Agenda - Runtime Edge Test Plan","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0001","file":"workflow/agendas/AGD-0001-runtime-edge-test-plan.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0003","status":"open","ticket":"packed-cartridge-loader-pmc","title":"Agenda - Packed Cartridge Loader PMC","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0002","file":"workflow/agendas/AGD-0002-packed-cartridge-loader-pmc.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} diff --git a/discussion/lessons/DSC-0021-asset-entry-codec-enum-contract/LSN-0024-string-on-the-wire-enum-in-runtime.md b/discussion/lessons/DSC-0021-asset-entry-codec-enum-contract/LSN-0024-string-on-the-wire-enum-in-runtime.md new file mode 100644 index 00000000..8cb65825 --- /dev/null +++ b/discussion/lessons/DSC-0021-asset-entry-codec-enum-contract/LSN-0024-string-on-the-wire-enum-in-runtime.md @@ -0,0 +1,45 @@ +--- +id: LSN-0024 +ticket: asset-entry-codec-enum-with-metadata +title: String on the Wire, Enum in the Runtime +created: 2026-04-09 +tags: [asset, runtime, codec, rust, contract] +--- + +## Context + +This discussion closed the contract for `AssetEntry.codec` in the runtime asset model. The system already serialized `assets.pa` as JSON produced by the Studio in Java, while the runtime consumed that JSON in Rust. The original runtime model kept `codec` as a free-form `String`, which allowed legacy aliases such as `RAW` and pushed validation and branching into scattered string comparisons. + +The implemented change moved the runtime to a typed enum while preserving the existing transport simplicity of the pack format. + +## Key Decisions + +### Asset Entry Codec Enum Contract + +**What:** +Keep `codec` as a JSON string in `assets.pa`, but deserialize it directly into a Rust enum in `AssetEntry`. The initial codec set contains only `None`, serialized canonically as `NONE` in `SCREAMING_SNAKE_CASE`. + +**Why:** +This preserves a simple cross-language wire contract for the Studio packer while making the runtime honest about the domain model it operates on. The loader now rejects unknown codecs early, and runtime consumers branch on enum variants instead of raw strings. + +**Trade-offs:** +The runtime loses open-ended tolerance for arbitrary codec names, which is intentional. Future codecs with payload-specific metadata still require a dedicated follow-up decision once a real codec exists. + +## Patterns and Algorithms + +- Use a stable textual discriminant on the wire and a typed enum in the runtime model. +- Let deserialization enforce the supported variant set instead of adding compatibility shims deeper in the execution path. +- Keep the discriminant contract canonical across producers and consumers; do not allow per-producer aliases. +- Preserve editorial or bank-specific metadata outside the codec discriminant so future codec payload metadata can be added without collapsing the whole asset model into an untyped blob. + +## Pitfalls + +- A runtime that accepts legacy aliases such as `RAW` keeps historical ambiguity alive and makes it harder to reason about the true contract. +- If fixtures are constructed directly in tests, typed metadata helpers can expose missing required fields that were previously masked by weaker validation. +- Cross-language contracts become fragile if the wire spelling is not explicitly canonized. The producer must conform exactly to the runtime contract. + +## Takeaways + +- Use a string on the wire when cross-language transport simplicity matters, but deserialize into an enum as soon as the runtime needs semantic guarantees. +- Canonical spelling belongs to the shared contract, not to whichever producer happens to serialize first. +- Rejecting unknown codec values at asset-entry validation time keeps failures local, early, and easier to debug. diff --git a/discussion/workflow/agendas/AGD-0019-asset-entry-codec-enum-with-metadata.md b/discussion/workflow/agendas/AGD-0019-asset-entry-codec-enum-with-metadata.md deleted file mode 100644 index 00f57657..00000000 --- a/discussion/workflow/agendas/AGD-0019-asset-entry-codec-enum-with-metadata.md +++ /dev/null @@ -1,183 +0,0 @@ ---- -id: AGD-0019 -ticket: asset-entry-codec-enum-with-metadata -title: Agenda - Asset Entry Codec as Enum with Metadata -status: accepted -created: 2026-04-09 -resolved: 2026-04-09 -decision: DEC-0005 -tags: [asset, runtime, codec, metadata] ---- - -# Agenda - Asset Entry Codec as Enum with Metadata - -## Contexto - -A `DSC-0017` consolidou a normalizacao de `AssetEntry.metadata`, separando campos criticos na raiz e empurrando detalhes tecnicos para subarvores como `codec` e `pipeline`. - -Isso resolveu a organizacao do contrato, mas nao fechou a representacao tipada de `codec` dentro do runtime. Hoje o dado ainda e percebido mais como JSON dinamico do que como parte explicita do modelo de dominio. - -O tema desta agenda e decidir se `codec` em `AssetEntry` deve deixar de ser apenas um blob/subarvore e passar a ser um enum tipado, capaz de carregar os metadados pertinentes a cada codec. - -## Problema - -Quando `codec` e tratado como JSON generico ou como informacao frouxa em metadados, o runtime perde: - -- clareza sobre quais codecs existem de fato; -- validacao estrutural forte por variante; -- ergonomia para loaders e drivers; -- separacao nitida entre metadata do banco e metadata do codec. - -Ao mesmo tempo, tipar `codec` cedo demais ou de forma larga demais pode misturar responsabilidades e transformar o enum em um deposito generico de qualquer detalhe tecnico do asset. - -## Pontos Criticos - -1. Limite de responsabilidade. - `codec` deve carregar apenas dados necessarios para decodificacao, nao toda a metadata do asset. - -2. Compatibilidade com a normalizacao anterior. - A agenda nao deve reabrir a decisao de segmentar metadata; ela deve apenas fechar como o segmento `codec` vira modelo tipado no runtime. - -3. Evolucao de formato. - O modelo precisa permitir codecs sem payload adicional e codecs com configuracao especifica, sem cair em campos opcionais demais. - -4. Unknown/forward compatibility. - Precisamos decidir se codecs desconhecidos falham no parse, se ficam preservados como raw JSON, ou se existe modo hibrido. - -5. Impacto nos callers. - A mudanca afeta serializacao/deserializacao, helpers, validacao de cart e ergonomia dos loaders. - -## Opcoes - -### Opcao A - Manter `codec` como JSON dinamico - -- **Abordagem:** manter `codec` como subarvore em `metadata` ou como `serde_json::Value`, usando helpers tipados apenas nos pontos de consumo. -- **Pro:** menor migracao imediata e maior flexibilidade para formatos experimentais. -- **Con:** o contrato real continua implicito, com checagens espalhadas e mais risco de drift entre parser e consumidores. -- **Tradeoff:** adia a decisao de dominio e preserva a ambiguidade exatamente no ponto onde o runtime precisa de semantica forte. - -### Opcao B - Tornar `codec` um enum tipado com payload por variante - -- **Abordagem:** introduzir algo como `AssetCodec`, com variantes explicitas (`None`, `Png { ... }`, `Jpeg { ... }`, etc.) carregando apenas os metadados do proprio codec. -- **Pro:** o runtime ganha exaustividade, parse centralizado e um modelo de dominio mais honesto. -- **Con:** exige decidir fronteiras claras entre metadata do codec e metadata do banco/asset; tambem aumenta custo de evolucao quando novos codecs surgem. -- **Tradeoff:** troca flexibilidade irrestrita por contrato forte e melhor ergonomia operacional. - -### Opcao C - Separar `codec_kind` de um payload opcional tipado - -- **Abordagem:** usar um enum leve para o tipo de codec e um campo separado para configuracao adicional. -- **Pro:** reduz acoplamento do discriminante com o payload e pode simplificar alguns formatos. -- **Con:** reintroduz estados invalidos (`kind` e payload divergentes), exigindo validacao cruzada manual. -- **Tradeoff:** parece mais flexivel, mas perde a principal vantagem de um tagged enum: coerencia estrutural por construcao. - -## Sugestao / Recomendacao - -Seguir com a **Opcao B**. - -`codec` em `AssetEntry` deveria ser um enum tipado e capaz de carregar os metadados estritamente relativos a ele. Isso fecha melhor o modelo de dominio: o runtime deixa de tratar codec como detalhe textual solto e passa a reconhece-lo como parte semantica do contrato do asset. - -A recomendacao, no entanto, vem com uma fronteira explicita: - -- metadata especifica de decodificacao fica dentro do enum do codec; -- metadata do banco/formato de consumo continua fora dele; -- metadata editorial ou operacional do asset tambem continua fora dele. - -Em outras palavras, a discussao nao e "colocar toda metadata dentro de `codec`", e sim "dar ao segmento `codec` uma representacao tipada, exaustiva e com payload por variante quando necessario". - -Com as respostas atuais, a recomendacao fica mais concreta: - -- o JSON serializado pelo Studio dentro de `asset.pa` pode manter `codec` como string opaca; -- o runtime Rust deve desserializar essa string diretamente para um enum em `AssetEntry`; -- o nome canonico de cada codec no JSON deve seguir o contrato definido pelo runtime, e o Studio/paker deve se conformar exatamente a ele; -- a variante inicial canonica e apenas `None`; -- `Raw` deve ser considerada removida do modelo; -- codec desconhecido no runtime e erro fatal de carregamento e deve impedir a execucao do cartucho; -- `pipeline` nao deve sobreviver no runtime e pode ser descartado completamente desse lado. - -## Perguntas em Aberto - -- Como o modelo evolui quando surgir o primeiro codec real com payload adicional sem perder a separacao entre o discriminante string no JSON e a interpretacao tipada no runtime? - -## Discussao - -### Direcao fechada ate aqui - -1. **Serializacao no Studio** - O JSON serializado pelo Studio dentro de `asset.pa` deve expor `codec` como string opaca. - -2. **Desserializacao no runtime** - O runtime Rust deve carregar essa string diretamente em um enum no proprio `AssetEntry`. O wire format continua string, mas o modelo carregado no runtime passa a ser tipado. - -3. **Canon do nome** - O nome textual do codec no JSON nao e arbitrario por produtor. Ele deve seguir exatamente o canon definido pelo contrato. O canon fechado para este tema e `SCREAMING_SNAKE_CASE`, alinhado a `BankType`, e o packer deve seguir esse formato sem aliases livres. - -4. **Variante inicial** - O unico codec canonico agora e `None`. - -5. **Fim de `Raw`** - `Raw` nao deve mais existir como conceito no modelo. - -6. **Codecs desconhecidos** - Nao devem ser empacotados. Se mesmo assim chegarem ao runtime, isso e erro de validacao do `AssetEntry` e o cartucho deve ser fechado antes de seguir. - -7. **Escopo atual de codecs** - Como ainda nao existe codec real implementado, a primeira versao do enum pode ser minima e conter apenas `None`. - -8. **Destino de `pipeline`** - `pipeline` nao tem valor operacional no runtime atual e pode ser descartado completamente desse lado. - -### Leitura arquitetural dessas respostas - -As respostas empurram a agenda para um contrato bem mais estrito do que o texto inicial sugeria: - -- `codec` existe como conceito de dominio mesmo antes de haver codecs concretos alem de `None`; -- o JSON de `asset.pa` pode manter um discriminante simples e opaco, enquanto o runtime desserializa isso diretamente para enum e usa esse valor para escolher a interpretacao tipada de `metadata.codec`; -- a autoridade sobre o spelling do codec pertence ao contrato compartilhado, nao ao Studio isoladamente; -- o runtime nao deve carregar extensibilidade aberta para codecs desconhecidos; -- a compatibilidade futura deve ser dirigida pelo Studio e pelo empacotamento, nao por tolerancia dinamica no runtime; -- `pipeline` deixa de disputar espaco conceitual com `codec` no runtime. - -Isso favorece um modelo de enum fechado, validado cedo e com semantica fail-fast. - -## Criterio para Encerrar - -Esta agenda pode ser encerrada quando houver consenso escrito sobre: - -- o papel exato de `codec` no modelo de dominio do `AssetEntry`; -- a fronteira entre payload do codec e restante da metadata; -- a estrategia para codecs sem metadata adicional e para codecs desconhecidos; -- o shape string de `codec` no JSON e sua relacao com o enum carregado no runtime e com `metadata.codec`; -- o proximo passo normativo para transformar isso em decisao sem reabrir `DSC-0017`. - -## Resolucao Provisoria - -Ha consenso provisoriamente estabelecido sobre os seguintes pontos: - -- `codec` deve existir como enum tipado no modelo do runtime; -- a variante inicial unica e `None`; -- `Raw` sai do contrato; -- codecs desconhecidos sao invalidos e devem falhar de forma fatal no carregamento do cartucho; -- `pipeline` pode ser descartado do runtime; -- o JSON de `asset.pa` deve serializar `codec` como string opaca; -- o runtime Rust deve desserializar essa string diretamente para enum no `AssetEntry`; -- o nome textual do codec no JSON deve seguir `SCREAMING_SNAKE_CASE`, e o Studio deve se conformar exatamente a ele; -- codec desconhecido deve falhar na validacao do `AssetEntry`, antes de qualquer carga efetiva. - -O unico ponto em aberto passa a ser a politica de evolucao para o primeiro codec real com payload, preservando a distincao entre: - -- discriminante string no JSON do asset; e -- shape tipado de `metadata.codec` no runtime. - -## Resolucao - -A agenda fica encerrada com a seguinte orientacao: - -- `codec` permanece `string` no JSON de `asset.pa`; -- o runtime Rust deve desserializar essa string diretamente para enum no `AssetEntry`; -- o nome textual do codec segue `SCREAMING_SNAKE_CASE`, alinhado a `BankType`; -- `None` e a unica variante inicial; -- `Raw` sai do contrato; -- codec desconhecido falha na validacao do `AssetEntry` e fecha o cartucho; -- `pipeline` e descartado do runtime. - -A evolucao para codecs com payload adicional fica explicitamente adiada para uma decisao futura motivada por um codec real. diff --git a/discussion/workflow/decisions/DEC-0005-asset-entry-codec-enum-contract.md b/discussion/workflow/decisions/DEC-0005-asset-entry-codec-enum-contract.md deleted file mode 100644 index bb2a0911..00000000 --- a/discussion/workflow/decisions/DEC-0005-asset-entry-codec-enum-contract.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -id: DEC-0005 -ticket: asset-entry-codec-enum-with-metadata -title: Asset Entry Codec Enum Contract -status: accepted -created: 2026-04-09 -accepted: 2026-04-09 -agenda: AGD-0019 -plans: [PLN-0004] -tags: [asset, runtime, codec, metadata] ---- - -## Status - -Accepted on 2026-04-09. - -## Contexto - -A discussao `AGD-0019` fechou que o campo `codec` de `AssetEntry` faz parte do contrato de dominio do runtime e nao deve permanecer como texto frouxo consumido ad hoc por loaders. - -Hoje o empacotamento e produzido no Studio em Java/Jackson e o runtime consome o header JSON de `asset.pa` em Rust. Portanto, o contrato precisa distinguir claramente: - -- o wire format serializado pelo Studio; -- o modelo tipado carregado pelo runtime; -- a relacao entre o discriminante do codec e qualquer metadata especifica de codec. - -A decisao tambem precisa consolidar o fim do alias legado `RAW` e a politica de falha para codecs desconhecidos. - -## Decisao - -1. `AssetEntry.codec` no runtime MUST ser representado por um enum tipado, e MUST NOT permanecer como `String` no modelo carregado. -2. O JSON serializado em `asset.pa` MUST continuar representando `codec` como uma `string`. -3. O runtime Rust MUST desserializar essa `string` diretamente para o enum de codec no proprio `AssetEntry`. -4. O spelling textual do codec no JSON MUST seguir exatamente o canon definido por este contrato. O canon inicial MUST ser `SCREAMING_SNAKE_CASE`, alinhado ao padrao ja usado por `BankType`. O Studio/paker MUST emitir exatamente esse spelling e MUST NOT introduzir aliases livres. -5. A variante inicial unica do enum MUST ser `None`. -6. O alias/conceito `Raw` MUST be removed from the contract and MUST NOT be emitted pelo Studio nem aceito como shape valido futuro. -7. Se o runtime encontrar um codec desconhecido para aquele binario, a validacao de `AssetEntry` MUST falhar antes de qualquer carga efetiva do asset, e o cartucho MUST ser rejeitado. -8. `pipeline` MUST NOT fazer parte do modelo operacional do runtime para esse contrato e MAY ser descartado completamente no lado do runtime. -9. Metadados especificos de codec, quando existirem, MUST ser interpretados a partir do codec resolvido e MUST permanecer separados da metadata editorial ou da metadata propria do banco consumidor. -10. A estrategia para o primeiro codec com payload adicional is deferred. Esta decisao fecha apenas o contrato atual e o ponto de extensao, sem antecipar um shape normativo para codecs futuros com payload. - -## Rationale - -- O wire format em `string` preserva simplicidade e compatibilidade com o Studio em Java/Jackson. -- O enum no runtime torna o contrato exaustivo, evita parsing textual espalhado e reduz drift entre loader, validacao e consumidores. -- Rejeitar codec desconhecido cedo mantem o sistema fail-fast e evita estados parcialmente carregados. -- Remover `Raw` evita manter semantica historica ambigua em paralelo ao contrato novo. -- Expulsar `pipeline` do runtime reduz ruido conceitual e impede sobreposicao de responsabilidade com `codec`. -- Adiar o desenho de codecs com payload evita overdesign antes de existir um caso concreto. - -## Invariantes / Contrato - -- O valor de `codec` no JSON de `asset.pa` e um discriminante textual canonico. -- O discriminante textual canonico de `codec` no JSON usa `SCREAMING_SNAKE_CASE`. -- O valor de `codec` carregado em Rust e um enum fechado para o conjunto de codecs suportados por aquele runtime. -- O Studio e o runtime compartilham o mesmo spelling canonico para cada variante. -- O runtime nao oferece modo tolerante para codecs desconhecidos. -- O contrato inicial possui uma unica variante valida: `None`. -- `Raw` nao pertence mais ao contrato canonico. -- `pipeline` nao participa do contrato operacional do runtime. -- Quando `metadata.codec` existir para algum codec futuro, sua interpretacao dependera do discriminante ja validado. - -## Impactos - -- `prometeu-hal` deve trocar `AssetEntry.codec: String` por um enum serializavel/desserializavel com canon textual explicito. -- `prometeu-drivers` deve remover a aceitacao de `RAW` como alias legado e passar a operar sobre enum, nao sobre comparacao textual solta. -- O loader/validacao do cart deve falhar cedo se a desserializacao ou validacao do codec falhar. -- O Studio/paker deve emitir exatamente o nome canonico definido para o codec. -- O nome canonico inicial de `None` no JSON deve ser `NONE`. -- Nao ha necessidade de definir agora um DTO separado apenas para codec; o proprio `AssetEntry` continua sendo o contrato serializado e desserializado. - -## Referencias - -- AGD-0019: Asset Entry Codec as Enum with Metadata -- DSC-0017: Asset Entry Metadata Normalization Contract -- LSN-0023: Typed Helpers for Asset Metadata -- `crates/console/prometeu-hal/src/asset.rs` -- `crates/console/prometeu-hal/src/cartridge_loader.rs` -- `crates/console/prometeu-drivers/src/asset.rs` - -## Propagacao Necessaria - -- Atualizar o modelo Rust de `AssetEntry` para usar enum de codec. -- Atualizar a validacao/desserializacao do header de `asset.pa`. -- Atualizar drivers para abandonar `RAW` e comparacoes por `String`. -- Ajustar o Studio/paker para emitir o spelling canonico escolhido pelo contrato. -- Escrever um plano de execucao antes de alterar spec/codigo. - -## Revision Log - -- 2026-04-09: Initial accepted decision from AGD-0019. diff --git a/discussion/workflow/plans/PLN-0004-asset-entry-codec-enum-execution.md b/discussion/workflow/plans/PLN-0004-asset-entry-codec-enum-execution.md deleted file mode 100644 index 71222e63..00000000 --- a/discussion/workflow/plans/PLN-0004-asset-entry-codec-enum-execution.md +++ /dev/null @@ -1,141 +0,0 @@ ---- -id: PLN-0004 -ticket: asset-entry-codec-enum-with-metadata -title: Asset Entry Codec Enum Execution -status: accepted -created: 2026-04-09 -completed: -tags: [asset, runtime, codec, metadata] ---- - -## Briefing - -Implement DEC-0005 by replacing `AssetEntry.codec: String` with a typed Rust enum while keeping the `assets.pa` JSON wire format as a canonical `SCREAMING_SNAKE_CASE` string. The initial supported codec set contains only `None`, serialized as `NONE`. Unknown codecs must fail during `AssetEntry` validation, `RAW` must be removed from the accepted contract, and runtime code must stop relying on free-form string comparisons. - -## Decisions de Origem - -- DEC-0005: Asset Entry Codec Enum Contract - -## Alvo - -Land a runtime-side implementation that: - -- introduces a serializable/deserializable `AssetCodec` enum in `prometeu-hal`; -- migrates `AssetEntry` to use the enum directly; -- rejects unknown codec strings during `assets.pa` parsing/validation; -- removes legacy `RAW` handling from runtime consumers and tests; -- preserves the current JSON transport contract for the Studio packer: `codec` remains a `SCREAMING_SNAKE_CASE` string in `assets.pa`. - -## Escopo - -- Runtime contract changes in `prometeu-hal` for `AssetEntry` and codec serialization. -- Cartridge loading and `assets.pa` validation behavior. -- Asset driver logic and tests that currently assume `codec` is a `String`. -- Runtime test fixtures that construct `AssetEntry` instances directly. - -## Fora de Escopo - -- Studio/Java packer implementation work. -- Introduction of any real codec beyond `None`. -- Design or implementation of `metadata.codec` payload shapes for future codecs. -- Discussion or implementation of a separate DTO just for `AssetEntry`. - -## Plano de Execucao - -### Step 1 - Introduce `AssetCodec` in `prometeu-hal` - -**What:** -Define the runtime enum for asset codecs and migrate `AssetEntry.codec` from `String` to that enum. - -**How:** -Add `AssetCodec` to `crates/console/prometeu-hal/src/asset.rs` with idiomatic Rust variant naming and explicit serde mapping to `SCREAMING_SNAKE_CASE`. The initial enum must contain only `None`, serialized as `NONE`. Update `AssetEntry` to use `AssetCodec` and remove the legacy comment about `RAW`. - -**File(s):** -- `crates/console/prometeu-hal/src/asset.rs` - -### Step 2 - Make `assets.pa` parsing fail on unknown codecs - -**What:** -Ensure unknown codec strings are rejected before any asset load path proceeds. - -**How:** -Rely on enum deserialization failure or an explicit validation hook during `assets.pa` header parsing so that invalid codec values produce `CartridgeError::InvalidFormat`. Keep the failure at `AssetEntry` validation time inside cartridge loading, not delayed to driver execution. - -**File(s):** -- `crates/console/prometeu-hal/src/cartridge_loader.rs` -- Any supporting type definitions in `crates/console/prometeu-hal/src/cartridge.rs` if required by parsing flow - -### Step 3 - Remove runtime string-based codec branching - -**What:** -Update asset runtime behavior to consume the enum directly and remove legacy `RAW` acceptance. - -**How:** -Replace `codec_is_none_or_legacy_raw` and string matches in `crates/console/prometeu-drivers/src/asset.rs` with enum-based matching on `AssetCodec`. Ensure unsupported codecs remain an error path, but `RAW` is no longer recognized anywhere in runtime logic. - -**File(s):** -- `crates/console/prometeu-drivers/src/asset.rs` - -### Step 4 - Update test fixtures and regression coverage - -**What:** -Bring existing tests and fixtures in line with the new typed contract and add regression tests for rejection behavior. - -**How:** -Replace direct string fixture values such as `"NONE"` and `"RAW"` with enum construction where tests instantiate `AssetEntry` directly. Add or update loader tests to cover: - -- successful parse of `codec: "NONE"`; -- failure on unknown codec strings; -- failure on legacy `RAW` if it still appears in serialized input. - -Update driver tests to assert enum-based behavior for the supported codec set. - -**File(s):** -- `crates/console/prometeu-hal/src/cartridge_loader.rs` -- `crates/console/prometeu-drivers/src/asset.rs` -- `crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs` - -### Step 5 - Align downstream packer work item - -**What:** -Capture the non-runtime propagation requirement for the Studio packer. - -**How:** -Record in implementation notes, issue tracking, or follow-up execution context that the Studio/Java packer must emit canonical `SCREAMING_SNAKE_CASE` codec strings and must stop producing `RAW`. No runtime code should add compatibility shims for packer drift. - -**File(s):** -- No runtime file changes required in this repository for this step - -## Criterios de Aceite - -- `AssetEntry.codec` is an enum in Rust, not a `String`. -- `assets.pa` still serializes/deserializes codec as a JSON string. -- The canonical wire spelling for the initial codec is `NONE`. -- Unknown codec strings cause cartridge loading to fail before asset loading proceeds. -- `RAW` is no longer accepted by runtime code or runtime tests. -- Asset driver code branches on `AssetCodec`, not string literals. -- Existing runtime tests pass after fixture migration, and regression tests cover the new failure behavior. - -## Tests / Validacao - -### Unit Tests - -- Serialization/deserialization tests for `AssetCodec` proving `AssetCodec::None <-> "NONE"`. -- Asset driver tests that match on enum variants instead of strings. - -### Integration Tests - -- Cartridge loader test that accepts `assets.pa` headers containing `codec: "NONE"`. -- Cartridge loader test that rejects an unknown codec string. -- Cartridge loader test that rejects legacy `RAW`. - -### Manual Verification - -- Inspect generated `assets.pa` header JSON from a known-good sample and verify `codec` remains a string field with `SCREAMING_SNAKE_CASE`. -- Run the relevant Rust test suites for `prometeu-hal`, `prometeu-drivers`, and any affected runtime tests. - -## Riscos - -- Existing tests and fixtures may be numerous because `AssetEntry` is widely constructed directly. -- If deserialization failure maps too generically, debugging bad packer output may become opaque unless tests assert the intended failure path clearly. -- Studio work is out of scope for this repository, so rollout coordination is required to avoid runtime/packer contract skew. -- 2.47.2 From 54f65f58e679e51f2e60ab1532bd078a1621ca09 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Fri, 10 Apr 2026 06:00:47 +0100 Subject: [PATCH 08/10] Glyph Bank Domain Naming Contract --- crates/console/prometeu-drivers/src/asset.rs | 214 ++++++++--------- crates/console/prometeu-drivers/src/gfx.rs | 38 +-- .../console/prometeu-drivers/src/hardware.rs | 10 +- .../prometeu-drivers/src/memory_banks.rs | 38 +-- crates/console/prometeu-hal/src/asset.rs | 14 +- .../prometeu-hal/src/cartridge_loader.rs | 30 +-- .../src/{tile_bank.rs => glyph_bank.rs} | 16 +- crates/console/prometeu-hal/src/lib.rs | 2 +- crates/console/prometeu-hal/src/tile_layer.rs | 4 +- .../src/virtual_machine_runtime/dispatch.rs | 4 +- .../src/virtual_machine_runtime/tests.rs | 42 ++-- .../src/virtual_machine_runtime/tick.rs | 2 +- crates/tools/pbxgen-stress/src/lib.rs | 2 +- discussion/index.ndjson | 3 +- .../LSN-0009-mental-model-asset-management.md | 10 +- .../LSN-0011-mental-model-gfx.md | 10 +- ...LSN-0022-tilemap-empty-cell-convergence.md | 2 +- .../LSN-0023-typed-asset-metadata-helpers.md | 4 +- ...0-tile-bank-vs-glyph-bank-domain-naming.md | 221 ++++++++++++++++++ ...-0006-glyph-bank-domain-naming-contract.md | 93 ++++++++ ...0005-glyph-bank-domain-naming-execution.md | 161 +++++++++++++ 21 files changed, 700 insertions(+), 220 deletions(-) rename crates/console/prometeu-hal/src/{tile_bank.rs => glyph_bank.rs} (84%) create mode 100644 discussion/workflow/agendas/AGD-0020-tile-bank-vs-glyph-bank-domain-naming.md create mode 100644 discussion/workflow/decisions/DEC-0006-glyph-bank-domain-naming-contract.md create mode 100644 discussion/workflow/plans/PLN-0005-glyph-bank-domain-naming-execution.md diff --git a/crates/console/prometeu-drivers/src/asset.rs b/crates/console/prometeu-drivers/src/asset.rs index 73397f18..88558910 100644 --- a/crates/console/prometeu-drivers/src/asset.rs +++ b/crates/console/prometeu-drivers/src/asset.rs @@ -1,5 +1,5 @@ #![allow(clippy::collapsible_if)] -use crate::memory_banks::{SoundBankPoolInstaller, TileBankPoolInstaller}; +use crate::memory_banks::{GlyphBankPoolInstaller, SoundBankPoolInstaller}; use prometeu_hal::AssetBridge; use prometeu_hal::asset::{ AssetCodec, AssetEntry, AssetId, AssetLoadError, AssetOpStatus, BankStats, BankType, HandleId, @@ -7,19 +7,19 @@ use prometeu_hal::asset::{ }; use prometeu_hal::cartridge::AssetsPayloadSource; use prometeu_hal::color::Color; +use prometeu_hal::glyph_bank::{GlyphBank, TileSize}; use prometeu_hal::sample::Sample; use prometeu_hal::sound_bank::SoundBank; -use prometeu_hal::tile_bank::{TileBank, TileSize}; use std::collections::HashMap; use std::io::Read; use std::sync::{Arc, Mutex, RwLock}; use std::thread; use std::time::Instant; -const TILE_BANK_PALETTE_COUNT_V1: usize = 64; -const TILE_BANK_COLORS_PER_PALETTE: usize = 16; -const TILE_BANK_PALETTE_BYTES_V1: usize = - TILE_BANK_PALETTE_COUNT_V1 * TILE_BANK_COLORS_PER_PALETTE * std::mem::size_of::(); +const GLYPH_BANK_PALETTE_COUNT_V1: usize = 64; +const GLYPH_BANK_COLORS_PER_PALETTE: usize = 16; +const GLYPH_BANK_PALETTE_BYTES_V1: usize = + GLYPH_BANK_PALETTE_COUNT_V1 * GLYPH_BANK_COLORS_PER_PALETTE * std::mem::size_of::(); /// Resident metadata for a decoded/materialized asset inside a BankPolicy. #[derive(Debug)] @@ -115,15 +115,15 @@ pub struct AssetManager { assets_data: Arc>, /// Narrow hardware interfaces - gfx_installer: Arc, + gfx_installer: Arc, sound_installer: Arc, /// Track what is installed in each hardware slot (for stats/info). gfx_slots: Arc; 16]>>, sound_slots: Arc; 16]>>, - /// Residency policy for GFX tile banks. - gfx_policy: BankPolicy, + /// Residency policy for GFX glyph banks. + gfx_policy: BankPolicy, /// Residency policy for sound banks. sound_policy: BankPolicy, @@ -179,10 +179,10 @@ impl AssetBridge for AssetManager { } impl AssetManager { - fn decode_tile_bank_layout( + fn decode_glyph_bank_layout( entry: &AssetEntry, ) -> Result<(TileSize, usize, usize, usize), String> { - let meta = entry.metadata_as_tiles()?; + let meta = entry.metadata_as_glyphs()?; let tile_size = match meta.tile_size { 8 => TileSize::Size8, @@ -191,32 +191,32 @@ impl AssetManager { _ => return Err(format!("Invalid tile_size: {}", meta.tile_size)), }; - if meta.palette_count as usize != TILE_BANK_PALETTE_COUNT_V1 { + if meta.palette_count as usize != GLYPH_BANK_PALETTE_COUNT_V1 { return Err(format!("Invalid palette_count: {}", meta.palette_count)); } let width = meta.width as usize; let height = meta.height as usize; - let logical_pixels = width.checked_mul(height).ok_or("TileBank dimensions overflow")?; + let logical_pixels = width.checked_mul(height).ok_or("GlyphBank dimensions overflow")?; let serialized_pixel_bytes = logical_pixels.div_ceil(2); let serialized_size = serialized_pixel_bytes - .checked_add(TILE_BANK_PALETTE_BYTES_V1) - .ok_or("TileBank serialized size overflow")?; + .checked_add(GLYPH_BANK_PALETTE_BYTES_V1) + .ok_or("GlyphBank serialized size overflow")?; let decoded_size = logical_pixels - .checked_add(TILE_BANK_PALETTE_BYTES_V1) - .ok_or("TileBank decoded size overflow")?; + .checked_add(GLYPH_BANK_PALETTE_BYTES_V1) + .ok_or("GlyphBank decoded size overflow")?; if entry.size != serialized_size as u64 { return Err(format!( - "Invalid TILEBANK serialized size: expected {}, got {}", + "Invalid GLYPHBANK serialized size: expected {}, got {}", serialized_size, entry.size )); } if entry.decoded_size != decoded_size as u64 { return Err(format!( - "Invalid TILEBANK decoded_size: expected {}, got {}", + "Invalid GLYPHBANK decoded_size: expected {}, got {}", decoded_size, entry.decoded_size )); } @@ -224,7 +224,7 @@ impl AssetManager { Ok((tile_size, width, height, serialized_pixel_bytes)) } - fn unpack_tile_bank_pixels(packed_pixels: &[u8], logical_pixels: usize) -> Vec { + fn unpack_glyph_bank_pixels(packed_pixels: &[u8], logical_pixels: usize) -> Vec { let mut pixel_indices = Vec::with_capacity(logical_pixels); for &packed in packed_pixels { if pixel_indices.len() < logical_pixels { @@ -239,7 +239,7 @@ impl AssetManager { fn op_mode_for(entry: &AssetEntry) -> Result { match (entry.bank_type, entry.codec) { - (BankType::TILES, AssetCodec::None) => Ok(AssetOpMode::StageInMemory), + (BankType::GLYPH, AssetCodec::None) => Ok(AssetOpMode::StageInMemory), (BankType::SOUNDS, AssetCodec::None) => Ok(AssetOpMode::DirectFromSlice), } } @@ -247,7 +247,7 @@ impl AssetManager { pub fn new( assets: Vec, assets_data: AssetsPayloadSource, - gfx_installer: Arc, + gfx_installer: Arc, sound_installer: Arc, ) -> Self { let mut asset_map = HashMap::new(); @@ -296,9 +296,9 @@ impl AssetManager { if let Some(entry) = entry_opt { let slot_index = item.slot; match entry.bank_type { - BankType::TILES => { + BankType::GLYPH => { if let Ok(bank) = - Self::perform_load_tile_bank(&entry, self.assets_data.clone()) + Self::perform_load_glyph_bank(&entry, self.assets_data.clone()) { let bank_arc = Arc::new(bank); self.gfx_policy.put_resident( @@ -306,7 +306,7 @@ impl AssetManager { Arc::clone(&bank_arc), entry.decoded_size as usize, ); - self.gfx_installer.install_tile_bank(slot_index, bank_arc); + self.gfx_installer.install_glyph_bank(slot_index, bank_arc); let mut slots = self.gfx_slots.write().unwrap(); if slot_index < slots.len() { slots[slot_index] = Some(entry.asset_id); @@ -367,7 +367,7 @@ impl AssetManager { assets.get(&asset_id).ok_or(AssetLoadError::AssetNotFound)?.clone() }; let slot = match entry.bank_type { - BankType::TILES => SlotRef::gfx(slot_index), + BankType::GLYPH => SlotRef::gfx(slot_index), BankType::SOUNDS => SlotRef::audio(slot_index), }; @@ -377,7 +377,7 @@ impl AssetManager { // Check if already resident (Dedup) let already_resident = match entry.bank_type { - BankType::TILES => { + BankType::GLYPH => { if let Some(bank) = self.gfx_policy.get_resident(asset_id) { self.gfx_policy.stage(handle_id, bank); true @@ -435,8 +435,8 @@ impl AssetManager { } match entry_clone.bank_type { - BankType::TILES => { - let result = Self::perform_load_tile_bank(&entry_clone, assets_data); + BankType::GLYPH => { + let result = Self::perform_load_glyph_bank(&entry_clone, assets_data); if let Ok(tilebank) = result { let bank_arc = Arc::new(tilebank); let resident_arc = { @@ -507,10 +507,10 @@ impl AssetManager { Ok(handle_id) } - fn perform_load_tile_bank( + fn perform_load_glyph_bank( entry: &AssetEntry, assets_data: Arc>, - ) -> Result { + ) -> Result { let op_mode = Self::op_mode_for(entry)?; let slice = { let assets_data = assets_data.read().unwrap(); @@ -523,30 +523,33 @@ impl AssetManager { AssetOpMode::StageInMemory => { let buffer = slice.read_all().map_err(|_| "Asset payload read failed".to_string())?; - Self::decode_tile_bank_from_buffer(entry, &buffer) + Self::decode_glyph_bank_from_buffer(entry, &buffer) } AssetOpMode::DirectFromSlice => { let mut reader = slice.open_reader().map_err(|_| "Asset payload read failed".to_string())?; - Self::decode_tile_bank_from_reader(entry, &mut reader) + Self::decode_glyph_bank_from_reader(entry, &mut reader) } } } - fn decode_tile_bank_from_buffer(entry: &AssetEntry, buffer: &[u8]) -> Result { - let (tile_size, width, height, packed_pixel_bytes) = Self::decode_tile_bank_layout(entry)?; - if buffer.len() < packed_pixel_bytes + TILE_BANK_PALETTE_BYTES_V1 { - return Err("Buffer too small for TILEBANK".to_string()); + fn decode_glyph_bank_from_buffer( + entry: &AssetEntry, + buffer: &[u8], + ) -> Result { + let (tile_size, width, height, packed_pixel_bytes) = Self::decode_glyph_bank_layout(entry)?; + if buffer.len() < packed_pixel_bytes + GLYPH_BANK_PALETTE_BYTES_V1 { + return Err("Buffer too small for GLYPHBANK".to_string()); } let logical_pixels = width * height; let packed_pixels = &buffer[0..packed_pixel_bytes]; - let pixel_indices = Self::unpack_tile_bank_pixels(packed_pixels, logical_pixels); + let pixel_indices = Self::unpack_glyph_bank_pixels(packed_pixels, logical_pixels); let palette_data = - &buffer[packed_pixel_bytes..packed_pixel_bytes + TILE_BANK_PALETTE_BYTES_V1]; + &buffer[packed_pixel_bytes..packed_pixel_bytes + GLYPH_BANK_PALETTE_BYTES_V1]; let mut palettes = - [[Color::BLACK; TILE_BANK_COLORS_PER_PALETTE]; TILE_BANK_PALETTE_COUNT_V1]; + [[Color::BLACK; GLYPH_BANK_COLORS_PER_PALETTE]; GLYPH_BANK_PALETTE_COUNT_V1]; for (p, pal) in palettes.iter_mut().enumerate() { for (c, slot) in pal.iter_mut().enumerate() { let offset = (p * 16 + c) * 2; @@ -556,29 +559,29 @@ impl AssetManager { } } - Ok(TileBank { tile_size, width, height, pixel_indices, palettes }) + Ok(GlyphBank { tile_size, width, height, pixel_indices, palettes }) } - fn decode_tile_bank_from_reader( + fn decode_glyph_bank_from_reader( entry: &AssetEntry, reader: &mut impl Read, - ) -> Result { - let (tile_size, width, height, packed_pixel_bytes) = Self::decode_tile_bank_layout(entry)?; + ) -> Result { + let (tile_size, width, height, packed_pixel_bytes) = Self::decode_glyph_bank_layout(entry)?; let logical_pixels = width * height; let mut packed_pixels = vec![0_u8; packed_pixel_bytes]; reader .read_exact(&mut packed_pixels) - .map_err(|_| "Buffer too small for TILEBANK".to_string())?; + .map_err(|_| "Buffer too small for GLYPHBANK".to_string())?; - let pixel_indices = Self::unpack_tile_bank_pixels(&packed_pixels, logical_pixels); + let pixel_indices = Self::unpack_glyph_bank_pixels(&packed_pixels, logical_pixels); - let mut palette_data = [0_u8; TILE_BANK_PALETTE_BYTES_V1]; + let mut palette_data = [0_u8; GLYPH_BANK_PALETTE_BYTES_V1]; reader .read_exact(&mut palette_data) - .map_err(|_| "Buffer too small for TILEBANK".to_string())?; + .map_err(|_| "Buffer too small for GLYPHBANK".to_string())?; let mut palettes = - [[Color::BLACK; TILE_BANK_COLORS_PER_PALETTE]; TILE_BANK_PALETTE_COUNT_V1]; + [[Color::BLACK; GLYPH_BANK_COLORS_PER_PALETTE]; GLYPH_BANK_PALETTE_COUNT_V1]; for (p, pal) in palettes.iter_mut().enumerate() { for (c, slot) in pal.iter_mut().enumerate() { let offset = (p * 16 + c) * 2; @@ -588,7 +591,7 @@ impl AssetManager { } } - Ok(TileBank { tile_size, width, height, pixel_indices, palettes }) + Ok(GlyphBank { tile_size, width, height, pixel_indices, palettes }) } fn perform_load_sound_bank( @@ -695,9 +698,9 @@ impl AssetManager { if let Some(h) = handles.get_mut(&handle_id) { if h.status == LoadStatus::READY { match h.slot.asset_type { - BankType::TILES => { + BankType::GLYPH => { if let Some(bank) = self.gfx_policy.take_staging(handle_id) { - self.gfx_installer.install_tile_bank(h.slot.index, bank); + self.gfx_installer.install_glyph_bank(h.slot.index, bank); let mut slots = self.gfx_slots.write().unwrap(); if h.slot.index < slots.len() { slots[h.slot.index] = Some(h._asset_id); @@ -723,7 +726,7 @@ impl AssetManager { pub fn bank_info(&self, kind: BankType) -> BankStats { match kind { - BankType::TILES => { + BankType::GLYPH => { let mut used_bytes = 0; { let resident = self.gfx_policy.resident.read().unwrap(); @@ -814,7 +817,7 @@ impl AssetManager { pub fn slot_info(&self, slot: SlotRef) -> SlotStats { match slot.asset_type { - BankType::TILES => { + BankType::GLYPH => { let slots = self.gfx_slots.read().unwrap(); let asset_id = slots.get(slot.index).and_then(|s| *s); @@ -872,93 +875,94 @@ impl AssetManager { #[cfg(test)] mod tests { use super::*; - use crate::memory_banks::{MemoryBanks, SoundBankPoolAccess, TileBankPoolAccess}; + use crate::memory_banks::{GlyphBankPoolAccess, MemoryBanks, SoundBankPoolAccess}; use prometeu_hal::asset::AssetCodec; - fn expected_tile_payload_size(width: usize, height: usize) -> usize { - (width * height).div_ceil(2) + TILE_BANK_PALETTE_BYTES_V1 + fn expected_glyph_payload_size(width: usize, height: usize) -> usize { + (width * height).div_ceil(2) + GLYPH_BANK_PALETTE_BYTES_V1 } - fn expected_tile_decoded_size(width: usize, height: usize) -> usize { - width * height + TILE_BANK_PALETTE_BYTES_V1 + fn expected_glyph_decoded_size(width: usize, height: usize) -> usize { + width * height + GLYPH_BANK_PALETTE_BYTES_V1 } - fn test_tile_asset_data() -> Vec { + fn test_glyph_asset_data() -> Vec { let mut data = vec![0x11u8; 128]; - data.extend_from_slice(&[0u8; TILE_BANK_PALETTE_BYTES_V1]); + data.extend_from_slice(&[0u8; GLYPH_BANK_PALETTE_BYTES_V1]); data } - fn test_tile_asset_entry(asset_name: &str, width: usize, height: usize) -> AssetEntry { + fn test_glyph_asset_entry(asset_name: &str, width: usize, height: usize) -> AssetEntry { AssetEntry { asset_id: 0, asset_name: asset_name.to_string(), - bank_type: BankType::TILES, + bank_type: BankType::GLYPH, offset: 0, - size: expected_tile_payload_size(width, height) as u64, - decoded_size: expected_tile_decoded_size(width, height) as u64, + size: expected_glyph_payload_size(width, height) as u64, + decoded_size: expected_glyph_decoded_size(width, height) as u64, codec: AssetCodec::None, metadata: serde_json::json!({ "tile_size": 16, "width": width, "height": height, - "palette_count": TILE_BANK_PALETTE_COUNT_V1, - "palette_authored": TILE_BANK_PALETTE_COUNT_V1 + "palette_count": GLYPH_BANK_PALETTE_COUNT_V1, + "palette_authored": GLYPH_BANK_PALETTE_COUNT_V1 }), } } #[test] - fn test_decode_tile_bank_unpacks_packed_pixels_and_reads_palette_colors() { - let entry = test_tile_asset_entry("tiles", 2, 2); + fn test_decode_glyph_bank_unpacks_packed_pixels_and_reads_palette_colors() { + let entry = test_glyph_asset_entry("tiles", 2, 2); let mut data = vec![0x10, 0x23]; - data.extend_from_slice(&[0u8; TILE_BANK_PALETTE_BYTES_V1]); + data.extend_from_slice(&[0u8; GLYPH_BANK_PALETTE_BYTES_V1]); data[2] = 0x34; data[3] = 0x12; - let bank = AssetManager::decode_tile_bank_from_buffer(&entry, &data).expect("tile decode"); + let bank = + AssetManager::decode_glyph_bank_from_buffer(&entry, &data).expect("glyph decode"); assert_eq!(bank.pixel_indices, vec![1, 0, 2, 3]); assert_eq!(bank.palettes[0][0], Color(0x1234)); } #[test] - fn test_decode_tile_bank_rejects_short_packed_buffer() { - let entry = test_tile_asset_entry("tiles", 16, 16); - let data = vec![0u8; expected_tile_payload_size(16, 16) - 1]; + fn test_decode_glyph_bank_rejects_short_packed_buffer() { + let entry = test_glyph_asset_entry("tiles", 16, 16); + let data = vec![0u8; expected_glyph_payload_size(16, 16) - 1]; - let err = match AssetManager::decode_tile_bank_from_buffer(&entry, &data) { - Ok(_) => panic!("tile decode should reject short buffer"), + let err = match AssetManager::decode_glyph_bank_from_buffer(&entry, &data) { + Ok(_) => panic!("glyph decode should reject short buffer"), Err(err) => err, }; - assert_eq!(err, "Buffer too small for TILEBANK"); + assert_eq!(err, "Buffer too small for GLYPHBANK"); } #[test] - fn test_decode_tile_bank_requires_palette_count_64() { - let mut entry = test_tile_asset_entry("tiles", 16, 16); + fn test_decode_glyph_bank_requires_palette_count_64() { + let mut entry = test_glyph_asset_entry("tiles", 16, 16); entry.metadata["palette_count"] = serde_json::json!(32); - let err = match AssetManager::decode_tile_bank_from_buffer(&entry, &test_tile_asset_data()) - { - Ok(_) => panic!("tile decode should reject invalid palette_count"), - Err(err) => err, - }; + let err = + match AssetManager::decode_glyph_bank_from_buffer(&entry, &test_glyph_asset_data()) { + Ok(_) => panic!("glyph decode should reject invalid palette_count"), + Err(err) => err, + }; assert_eq!(err, "Invalid palette_count: 32"); } #[test] - fn test_op_mode_for_tiles_none_stages_in_memory() { - let entry = test_tile_asset_entry("tiles", 16, 16); + fn test_op_mode_for_glyphs_none_stages_in_memory() { + let entry = test_glyph_asset_entry("tiles", 16, 16); assert_eq!(AssetManager::op_mode_for(&entry), Ok(AssetOpMode::StageInMemory)); } #[test] - fn test_op_mode_for_tiles_none_uses_typed_codec() { - let entry = test_tile_asset_entry("tiles", 16, 16); + fn test_op_mode_for_glyphs_none_uses_typed_codec() { + let entry = test_glyph_asset_entry("tiles", 16, 16); assert_eq!(AssetManager::op_mode_for(&entry), Ok(AssetOpMode::StageInMemory)); } @@ -984,11 +988,11 @@ mod tests { #[test] fn test_asset_loading_flow() { let banks = Arc::new(MemoryBanks::new()); - let gfx_installer = Arc::clone(&banks) as Arc; + let gfx_installer = Arc::clone(&banks) as Arc; let sound_installer = Arc::clone(&banks) as Arc; - let data = test_tile_asset_data(); - let asset_entry = test_tile_asset_entry("test_tiles", 16, 16); + let data = test_glyph_asset_data(); + let asset_entry = test_glyph_asset_entry("test_tiles", 16, 16); let am = AssetManager::new( vec![asset_entry], @@ -1016,17 +1020,17 @@ mod tests { am.apply_commits(); assert_eq!(am.status(handle), LoadStatus::COMMITTED); - assert!(banks.tile_bank_slot(0).is_some()); + assert!(banks.glyph_bank_slot(0).is_some()); } #[test] fn test_asset_dedup() { let banks = Arc::new(MemoryBanks::new()); - let gfx_installer = Arc::clone(&banks) as Arc; + let gfx_installer = Arc::clone(&banks) as Arc; let sound_installer = Arc::clone(&banks) as Arc; - let data = test_tile_asset_data(); - let asset_entry = test_tile_asset_entry("test_tiles", 16, 16); + let data = test_glyph_asset_data(); + let asset_entry = test_glyph_asset_entry("test_tiles", 16, 16); let am = AssetManager::new( vec![asset_entry], @@ -1053,7 +1057,7 @@ mod tests { #[test] fn test_sound_asset_loading() { let banks = Arc::new(MemoryBanks::new()); - let gfx_installer = Arc::clone(&banks) as Arc; + let gfx_installer = Arc::clone(&banks) as Arc; let sound_installer = Arc::clone(&banks) as Arc; // 100 samples of 16-bit PCM (zeros) @@ -1098,7 +1102,7 @@ mod tests { #[test] fn test_preload_on_init() { let banks = Arc::new(MemoryBanks::new()); - let gfx_installer = Arc::clone(&banks) as Arc; + let gfx_installer = Arc::clone(&banks) as Arc; let sound_installer = Arc::clone(&banks) as Arc; let data = vec![0u8; 200]; @@ -1139,7 +1143,7 @@ mod tests { #[test] fn test_load_returns_asset_not_found() { let banks = Arc::new(MemoryBanks::new()); - let gfx_installer = Arc::clone(&banks) as Arc; + let gfx_installer = Arc::clone(&banks) as Arc; let sound_installer = Arc::clone(&banks) as Arc; let am = AssetManager::new(vec![], AssetsPayloadSource::empty(), gfx_installer, sound_installer); @@ -1152,11 +1156,11 @@ mod tests { #[test] fn test_load_returns_slot_index_invalid() { let banks = Arc::new(MemoryBanks::new()); - let gfx_installer = Arc::clone(&banks) as Arc; + let gfx_installer = Arc::clone(&banks) as Arc; let sound_installer = Arc::clone(&banks) as Arc; - let data = test_tile_asset_data(); + let data = test_glyph_asset_data(); let am = AssetManager::new( - vec![test_tile_asset_entry("test_tiles", 16, 16)], + vec![test_glyph_asset_entry("test_tiles", 16, 16)], AssetsPayloadSource::from_bytes(data), gfx_installer, sound_installer, @@ -1170,7 +1174,7 @@ mod tests { #[test] fn test_status_returns_unknown_handle() { let banks = Arc::new(MemoryBanks::new()); - let gfx_installer = Arc::clone(&banks) as Arc; + let gfx_installer = Arc::clone(&banks) as Arc; let sound_installer = Arc::clone(&banks) as Arc; let am = AssetManager::new(vec![], AssetsPayloadSource::empty(), gfx_installer, sound_installer); @@ -1181,11 +1185,11 @@ mod tests { #[test] fn test_commit_and_cancel_return_explicit_statuses() { let banks = Arc::new(MemoryBanks::new()); - let gfx_installer = Arc::clone(&banks) as Arc; + let gfx_installer = Arc::clone(&banks) as Arc; let sound_installer = Arc::clone(&banks) as Arc; - let data = test_tile_asset_data(); + let data = test_glyph_asset_data(); let am = AssetManager::new( - vec![test_tile_asset_entry("test_tiles", 16, 16)], + vec![test_glyph_asset_entry("test_tiles", 16, 16)], AssetsPayloadSource::from_bytes(data), gfx_installer, sound_installer, diff --git a/crates/console/prometeu-drivers/src/gfx.rs b/crates/console/prometeu-drivers/src/gfx.rs index 13acba61..ae525130 100644 --- a/crates/console/prometeu-drivers/src/gfx.rs +++ b/crates/console/prometeu-drivers/src/gfx.rs @@ -1,10 +1,10 @@ -use crate::memory_banks::TileBankPoolAccess; +use crate::memory_banks::GlyphBankPoolAccess; use prometeu_hal::GfxBridge; use prometeu_hal::color::Color; use prometeu_hal::glyph::Glyph; +use prometeu_hal::glyph_bank::{GlyphBank, TileSize}; use prometeu_hal::sprite::Sprite; use prometeu_hal::tile::Tile; -use prometeu_hal::tile_bank::{TileBank, TileSize}; use prometeu_hal::tile_layer::{HudTileLayer, ScrollableTileLayer, TileMap}; use std::sync::Arc; @@ -57,12 +57,12 @@ pub struct Gfx { /// Back buffer: the "Work RAM" where new frames are composed. back: Vec, - /// 4 scrollable background layers. Each can have its own scroll (X, Y) and TileBank. + /// 4 scrollable background layers. Each can have its own scroll (X, Y) and GlyphBank. pub layers: [ScrollableTileLayer; 4], /// 1 fixed layer for User Interface (HUD). It doesn't scroll. pub hud: HudTileLayer, /// Shared access to graphical memory banks (tiles and palettes). - pub tile_banks: Arc, + pub glyph_banks: Arc, /// Hardware sprite list (512 slots). Equivalent to OAM (Object Attribute Memory). pub sprites: [Sprite; 512], @@ -274,7 +274,7 @@ impl GfxBridge for Gfx { impl Gfx { /// Initializes the graphics system with a specific resolution and shared memory banks. - pub fn new(w: usize, h: usize, tile_banks: Arc) -> Self { + pub fn new(w: usize, h: usize, glyph_banks: Arc) -> Self { const EMPTY_GLYPH: Glyph = Glyph { glyph_id: 0, palette_id: 0 }; const EMPTY_SPRITE: Sprite = Sprite { @@ -303,7 +303,7 @@ impl Gfx { back: vec![0; len], layers, hud: HudTileLayer::new(64, 32), - tile_banks, + glyph_banks, sprites: [EMPTY_SPRITE; 512], scene_fade_level: 31, scene_fade_color: Color::BLACK, @@ -583,14 +583,14 @@ impl Gfx { self.h, &self.priority_buckets[0], &self.sprites, - &*self.tile_banks, + &*self.glyph_banks, ); // 2. Main layers and prioritized sprites. // Order: Layer 0 -> Sprites 1 -> Layer 1 -> Sprites 2 ... for i in 0..self.layers.len() { let bank_id = self.layers[i].bank_id as usize; - if let Some(bank) = self.tile_banks.tile_bank_slot(bank_id) { + if let Some(bank) = self.glyph_banks.glyph_bank_slot(bank_id) { Self::draw_tile_map( &mut self.back, self.w, @@ -609,7 +609,7 @@ impl Gfx { self.h, &self.priority_buckets[i + 1], &self.sprites, - &*self.tile_banks, + &*self.glyph_banks, ); } @@ -617,7 +617,7 @@ impl Gfx { Self::apply_fade_to_buffer(&mut self.back, self.scene_fade_level, self.scene_fade_color); // 5. HUD: The fixed interface layer, always drawn on top of the world. - Self::render_hud_with_pool(&mut self.back, self.w, self.h, &self.hud, &*self.tile_banks); + Self::render_hud_with_pool(&mut self.back, self.w, self.h, &self.hud, &*self.glyph_banks); // 6. HUD Fade: Independent fade effect for the UI. Self::apply_fade_to_buffer(&mut self.back, self.hud_fade_level, self.hud_fade_color); @@ -633,7 +633,7 @@ impl Gfx { let scroll_x = self.layers[layer_idx].scroll_x; let scroll_y = self.layers[layer_idx].scroll_y; - let bank = match self.tile_banks.tile_bank_slot(bank_id) { + let bank = match self.glyph_banks.glyph_bank_slot(bank_id) { Some(b) => b, _ => return, }; @@ -651,7 +651,7 @@ impl Gfx { /// Renders the HUD (fixed position, no scroll). pub fn render_hud(&mut self) { - Self::render_hud_with_pool(&mut self.back, self.w, self.h, &self.hud, &*self.tile_banks); + Self::render_hud_with_pool(&mut self.back, self.w, self.h, &self.hud, &*self.glyph_banks); } fn render_hud_with_pool( @@ -659,10 +659,10 @@ impl Gfx { w: usize, h: usize, hud: &HudTileLayer, - tile_banks: &dyn TileBankPoolAccess, + glyph_banks: &dyn GlyphBankPoolAccess, ) { let bank_id = hud.bank_id as usize; - let bank = match tile_banks.tile_bank_slot(bank_id) { + let bank = match glyph_banks.glyph_bank_slot(bank_id) { Some(b) => b, _ => return, }; @@ -676,7 +676,7 @@ impl Gfx { screen_w: usize, screen_h: usize, map: &TileMap, - bank: &TileBank, + bank: &GlyphBank, scroll_x: i32, scroll_y: i32, ) { @@ -739,7 +739,7 @@ impl Gfx { x: i32, y: i32, tile: Tile, - bank: &TileBank, + bank: &GlyphBank, ) { let size = bank.tile_size as usize; @@ -781,12 +781,12 @@ impl Gfx { screen_h: usize, bucket: &[usize], sprites: &[Sprite], - tile_banks: &dyn TileBankPoolAccess, + glyph_banks: &dyn GlyphBankPoolAccess, ) { for &idx in bucket { let s = &sprites[idx]; let bank_id = s.bank_id as usize; - if let Some(bank) = tile_banks.tile_bank_slot(bank_id) { + if let Some(bank) = glyph_banks.glyph_bank_slot(bank_id) { Self::draw_sprite_pixel_by_pixel(back, screen_w, screen_h, s, &bank); } } @@ -797,7 +797,7 @@ impl Gfx { screen_w: usize, screen_h: usize, sprite: &Sprite, - bank: &TileBank, + bank: &GlyphBank, ) { // ... (same bounds/clipping calculation we already had) ... let size = bank.tile_size as usize; diff --git a/crates/console/prometeu-drivers/src/hardware.rs b/crates/console/prometeu-drivers/src/hardware.rs index 21834223..1eb6598e 100644 --- a/crates/console/prometeu-drivers/src/hardware.rs +++ b/crates/console/prometeu-drivers/src/hardware.rs @@ -2,8 +2,8 @@ use crate::asset::AssetManager; use crate::audio::Audio; use crate::gfx::Gfx; use crate::memory_banks::{ - MemoryBanks, SoundBankPoolAccess, SoundBankPoolInstaller, TileBankPoolAccess, - TileBankPoolInstaller, + GlyphBankPoolAccess, GlyphBankPoolInstaller, MemoryBanks, SoundBankPoolAccess, + SoundBankPoolInstaller, }; use crate::pad::Pad; use crate::touch::Touch; @@ -32,7 +32,7 @@ pub struct Hardware { pub pad: Pad, /// The absolute pointer input device (Mouse/Touchscreen). pub touch: Touch, - /// The Asset Management system (DMA). Handles loading data into VRAM (TileBanks) and ARAM (SoundBanks). + /// The Asset Management system (DMA). Handles loading data into VRAM (GlyphBanks) and ARAM (SoundBanks). pub assets: AssetManager, } @@ -92,7 +92,7 @@ impl Hardware { gfx: Gfx::new( Self::W, Self::H, - Arc::clone(&memory_banks) as Arc, + Arc::clone(&memory_banks) as Arc, ), audio: Audio::new(Arc::clone(&memory_banks) as Arc), pad: Pad::default(), @@ -100,7 +100,7 @@ impl Hardware { assets: AssetManager::new( vec![], AssetsPayloadSource::empty(), - Arc::clone(&memory_banks) as Arc, + Arc::clone(&memory_banks) as Arc, Arc::clone(&memory_banks) as Arc, ), } diff --git a/crates/console/prometeu-drivers/src/memory_banks.rs b/crates/console/prometeu-drivers/src/memory_banks.rs index d4d59fd6..b2b48307 100644 --- a/crates/console/prometeu-drivers/src/memory_banks.rs +++ b/crates/console/prometeu-drivers/src/memory_banks.rs @@ -1,19 +1,19 @@ +use prometeu_hal::glyph_bank::GlyphBank; use prometeu_hal::sound_bank::SoundBank; -use prometeu_hal::tile_bank::TileBank; use std::sync::{Arc, RwLock}; -/// Non-generic interface for peripherals to access graphical tile banks. -pub trait TileBankPoolAccess: Send + Sync { - /// Returns a reference to the resident TileBank in the specified slot, if any. - fn tile_bank_slot(&self, slot: usize) -> Option>; +/// Non-generic interface for peripherals to access graphical glyph banks. +pub trait GlyphBankPoolAccess: Send + Sync { + /// Returns a reference to the resident GlyphBank in the specified slot, if any. + fn glyph_bank_slot(&self, slot: usize) -> Option>; /// Returns the total number of slots available in this bank. - fn tile_bank_slot_count(&self) -> usize; + fn glyph_bank_slot_count(&self) -> usize; } -/// Non-generic interface for the AssetManager to install graphical tile banks. -pub trait TileBankPoolInstaller: Send + Sync { - /// Atomically swaps the resident TileBank in the specified slot. - fn install_tile_bank(&self, slot: usize, bank: Arc); +/// Non-generic interface for the AssetManager to install graphical glyph banks. +pub trait GlyphBankPoolInstaller: Send + Sync { + /// Atomically swaps the resident GlyphBank in the specified slot. + fn install_glyph_bank(&self, slot: usize, bank: Arc); } /// Non-generic interface for peripherals to access sound banks. @@ -36,7 +36,7 @@ pub trait SoundBankPoolInstaller: Send + Sync { /// Peripherals consume this state via narrow, non-generic traits. /// AssetManager coordinates residency and installs assets into these slots. pub struct MemoryBanks { - tile_bank_pool: Arc>; 16]>>, + glyph_bank_pool: Arc>; 16]>>, sound_bank_pool: Arc>; 16]>>, } @@ -50,26 +50,26 @@ impl MemoryBanks { /// Creates a new set of memory banks with empty slots. pub fn new() -> Self { Self { - tile_bank_pool: Arc::new(RwLock::new(std::array::from_fn(|_| None))), + glyph_bank_pool: Arc::new(RwLock::new(std::array::from_fn(|_| None))), sound_bank_pool: Arc::new(RwLock::new(std::array::from_fn(|_| None))), } } } -impl TileBankPoolAccess for MemoryBanks { - fn tile_bank_slot(&self, slot: usize) -> Option> { - let pool = self.tile_bank_pool.read().unwrap(); +impl GlyphBankPoolAccess for MemoryBanks { + fn glyph_bank_slot(&self, slot: usize) -> Option> { + let pool = self.glyph_bank_pool.read().unwrap(); pool.get(slot).and_then(|s| s.as_ref().map(Arc::clone)) } - fn tile_bank_slot_count(&self) -> usize { + fn glyph_bank_slot_count(&self) -> usize { 16 } } -impl TileBankPoolInstaller for MemoryBanks { - fn install_tile_bank(&self, slot: usize, bank: Arc) { - let mut pool = self.tile_bank_pool.write().unwrap(); +impl GlyphBankPoolInstaller for MemoryBanks { + fn install_glyph_bank(&self, slot: usize, bank: Arc) { + let mut pool = self.glyph_bank_pool.write().unwrap(); if slot < 16 { pool[slot] = Some(bank); } diff --git a/crates/console/prometeu-hal/src/asset.rs b/crates/console/prometeu-hal/src/asset.rs index 8e9346eb..443595f3 100644 --- a/crates/console/prometeu-hal/src/asset.rs +++ b/crates/console/prometeu-hal/src/asset.rs @@ -6,7 +6,7 @@ pub type AssetId = i32; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)] #[allow(non_camel_case_types)] pub enum BankType { - TILES, + GLYPH, SOUNDS, // TILEMAPS, // BLOBS, @@ -31,7 +31,7 @@ pub struct AssetEntry { } #[derive(Debug, Clone, Deserialize, Serialize)] -pub struct TilesMetadata { +pub struct GlyphsMetadata { pub tile_size: u32, pub width: u32, pub height: u32, @@ -46,15 +46,15 @@ pub struct SoundsMetadata { } impl AssetEntry { - pub fn metadata_as_tiles(&self) -> Result { - if self.bank_type != BankType::TILES { + pub fn metadata_as_glyphs(&self) -> Result { + if self.bank_type != BankType::GLYPH { return Err(format!( - "Asset {} is not a TILES bank (type: {:?})", + "Asset {} is not a GLYPH bank (type: {:?})", self.asset_id, self.bank_type )); } serde_json::from_value(self.metadata.clone()) - .map_err(|e| format!("Invalid TILES metadata for asset {}: {}", self.asset_id, e)) + .map_err(|e| format!("Invalid GLYPH metadata for asset {}: {}", self.asset_id, e)) } pub fn metadata_as_sounds(&self) -> Result { @@ -130,7 +130,7 @@ pub struct SlotRef { impl SlotRef { pub fn gfx(index: usize) -> Self { - Self { asset_type: BankType::TILES, index } + Self { asset_type: BankType::GLYPH, index } } pub fn audio(index: usize) -> Self { diff --git a/crates/console/prometeu-hal/src/cartridge_loader.rs b/crates/console/prometeu-hal/src/cartridge_loader.rs index 4b410015..ed46a15f 100644 --- a/crates/console/prometeu-hal/src/cartridge_loader.rs +++ b/crates/console/prometeu-hal/src/cartridge_loader.rs @@ -200,7 +200,7 @@ mod tests { use super::*; use crate::asset::{AssetCodec, AssetEntry, BankType, PreloadEntry}; use crate::cartridge::{ASSETS_PA_MAGIC, ASSETS_PA_SCHEMA_VERSION, AssetsPackPrelude}; - use crate::tile_bank::TILE_BANK_PALETTE_COUNT_V1; + use crate::glyph_bank::GLYPH_BANK_PALETTE_COUNT_V1; use serde_json::json; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicU64, Ordering}; @@ -363,17 +363,17 @@ mod tests { AssetEntry { asset_id: 7, asset_name: "tiles".to_string(), - bank_type: BankType::TILES, + bank_type: BankType::GLYPH, offset, size, - decoded_size: 16 * 16 + (TILE_BANK_PALETTE_COUNT_V1 as u64 * 16 * 2), + decoded_size: 16 * 16 + (GLYPH_BANK_PALETTE_COUNT_V1 as u64 * 16 * 2), codec: AssetCodec::None, metadata: json!({ "tile_size": 16, "width": 16, "height": 16, - "palette_count": TILE_BANK_PALETTE_COUNT_V1, - "palette_authored": TILE_BANK_PALETTE_COUNT_V1 + "palette_count": GLYPH_BANK_PALETTE_COUNT_V1, + "palette_authored": GLYPH_BANK_PALETTE_COUNT_V1 }), } } @@ -446,17 +446,17 @@ mod tests { AssetEntry { asset_id: 8, asset_name: "other_tiles".to_string(), - bank_type: BankType::TILES, + bank_type: BankType::GLYPH, offset: 4, size: 4, - decoded_size: 16 * 16 + (TILE_BANK_PALETTE_COUNT_V1 as u64 * 16 * 2), + decoded_size: 16 * 16 + (GLYPH_BANK_PALETTE_COUNT_V1 as u64 * 16 * 2), codec: AssetCodec::None, metadata: json!({ "tile_size": 16, "width": 16, "height": 16, - "palette_count": TILE_BANK_PALETTE_COUNT_V1, - "palette_authored": TILE_BANK_PALETTE_COUNT_V1 + "palette_count": GLYPH_BANK_PALETTE_COUNT_V1, + "palette_authored": GLYPH_BANK_PALETTE_COUNT_V1 }), }, ]; @@ -507,7 +507,7 @@ mod tests { "asset_table": [{ "asset_id": 7, "asset_name": "tiles", - "bank_type": "TILES", + "bank_type": "GLYPH", "offset": 0, "size": 4, "decoded_size": 768, @@ -516,8 +516,8 @@ mod tests { "tile_size": 16, "width": 16, "height": 16, - "palette_count": TILE_BANK_PALETTE_COUNT_V1, - "palette_authored": TILE_BANK_PALETTE_COUNT_V1 + "palette_count": GLYPH_BANK_PALETTE_COUNT_V1, + "palette_authored": GLYPH_BANK_PALETTE_COUNT_V1 } }], "preload": [] @@ -549,7 +549,7 @@ mod tests { "asset_table": [{ "asset_id": 7, "asset_name": "tiles", - "bank_type": "TILES", + "bank_type": "GLYPH", "offset": 0, "size": 4, "decoded_size": 768, @@ -558,8 +558,8 @@ mod tests { "tile_size": 16, "width": 16, "height": 16, - "palette_count": TILE_BANK_PALETTE_COUNT_V1, - "palette_authored": TILE_BANK_PALETTE_COUNT_V1 + "palette_count": GLYPH_BANK_PALETTE_COUNT_V1, + "palette_authored": GLYPH_BANK_PALETTE_COUNT_V1 } }], "preload": [] diff --git a/crates/console/prometeu-hal/src/tile_bank.rs b/crates/console/prometeu-hal/src/glyph_bank.rs similarity index 84% rename from crates/console/prometeu-hal/src/tile_bank.rs rename to crates/console/prometeu-hal/src/glyph_bank.rs index 422ba496..5c8bbce1 100644 --- a/crates/console/prometeu-hal/src/tile_bank.rs +++ b/crates/console/prometeu-hal/src/glyph_bank.rs @@ -1,7 +1,7 @@ use crate::color::Color; -pub const TILE_BANK_PALETTE_COUNT_V1: usize = 64; -pub const TILE_BANK_COLORS_PER_PALETTE: usize = 16; +pub const GLYPH_BANK_PALETTE_COUNT_V1: usize = 64; +pub const GLYPH_BANK_COLORS_PER_PALETTE: usize = 16; /// Standard sizes for square tiles. #[derive(Clone, Copy, Debug, PartialEq)] @@ -16,12 +16,12 @@ pub enum TileSize { /// A container for graphical assets. /// -/// A TileBank stores the decoded runtime representation of a tile-bank asset. +/// A GlyphBank stores the decoded runtime representation of a glyph-bank asset. /// /// Serialized `assets.pa` payloads keep pixel indices packed as `4bpp` nibbles. /// After decode, the runtime expands them to one `u8` palette index per pixel /// while preserving the same `0..15` logical range. -pub struct TileBank { +pub struct GlyphBank { /// Dimension of each individual tile in the bank. pub tile_size: TileSize, /// Width of the full bank sheet in pixels. @@ -34,18 +34,18 @@ pub struct TileBank { /// Index 0 is always reserved for transparency. pub pixel_indices: Vec, /// Runtime-facing v1 palette table: 64 palettes of 16 RGB565 colors each. - pub palettes: [[Color; TILE_BANK_COLORS_PER_PALETTE]; TILE_BANK_PALETTE_COUNT_V1], + pub palettes: [[Color; GLYPH_BANK_COLORS_PER_PALETTE]; GLYPH_BANK_PALETTE_COUNT_V1], } -impl TileBank { - /// Creates an empty tile bank with the specified dimensions. +impl GlyphBank { + /// Creates an empty glyph bank with the specified dimensions. pub fn new(tile_size: TileSize, width: usize, height: usize) -> Self { Self { tile_size, width, height, pixel_indices: vec![0; width * height], // Index 0 = Transparent - palettes: [[Color::BLACK; TILE_BANK_COLORS_PER_PALETTE]; TILE_BANK_PALETTE_COUNT_V1], + palettes: [[Color::BLACK; GLYPH_BANK_COLORS_PER_PALETTE]; GLYPH_BANK_PALETTE_COUNT_V1], } } diff --git a/crates/console/prometeu-hal/src/lib.rs b/crates/console/prometeu-hal/src/lib.rs index 1f7bd7af..cd789e09 100644 --- a/crates/console/prometeu-hal/src/lib.rs +++ b/crates/console/prometeu-hal/src/lib.rs @@ -8,6 +8,7 @@ pub mod color; pub mod debugger_protocol; pub mod gfx_bridge; pub mod glyph; +pub mod glyph_bank; pub mod hardware_bridge; pub mod host_context; pub mod host_return; @@ -22,7 +23,6 @@ pub mod sprite; pub mod syscalls; pub mod telemetry; pub mod tile; -pub mod tile_bank; pub mod tile_layer; pub mod touch_bridge; pub mod vm_fault; diff --git a/crates/console/prometeu-hal/src/tile_layer.rs b/crates/console/prometeu-hal/src/tile_layer.rs index 07078ebd..7ac6afea 100644 --- a/crates/console/prometeu-hal/src/tile_layer.rs +++ b/crates/console/prometeu-hal/src/tile_layer.rs @@ -1,6 +1,6 @@ +use crate::glyph_bank::TileSize; +use crate::glyph_bank::TileSize::Size8; use crate::tile::Tile; -use crate::tile_bank::TileSize; -use crate::tile_bank::TileSize::Size8; pub struct TileMap { pub width: usize, diff --git a/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs b/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs index d8fd483e..2504f0b1 100644 --- a/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs +++ b/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs @@ -481,7 +481,7 @@ impl NativeInterface for VirtualMachineRuntime { } Syscall::BankInfo => { let asset_type = match expect_int(args, 0)? as u32 { - 0 => BankType::TILES, + 0 => BankType::GLYPH, 1 => BankType::SOUNDS, _ => return Err(VmFault::Trap(TRAP_TYPE, "Invalid asset type".to_string())), }; @@ -492,7 +492,7 @@ impl NativeInterface for VirtualMachineRuntime { } Syscall::BankSlotInfo => { let asset_type = match expect_int(args, 0)? as u32 { - 0 => BankType::TILES, + 0 => BankType::GLYPH, 1 => BankType::SOUNDS, _ => return Err(VmFault::Trap(TRAP_TYPE, "Invalid asset type".to_string())), }; diff --git a/crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs b/crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs index 2dd34c41..293a35a9 100644 --- a/crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs +++ b/crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs @@ -12,8 +12,8 @@ use prometeu_hal::asset::{ AssetCodec, AssetEntry, AssetLoadError, AssetOpStatus, BankType, LoadStatus, }; use prometeu_hal::cartridge::{AssetsPayloadSource, Cartridge}; +use prometeu_hal::glyph_bank::GLYPH_BANK_PALETTE_COUNT_V1; use prometeu_hal::syscalls::caps; -use prometeu_hal::tile_bank::TILE_BANK_PALETTE_COUNT_V1; use prometeu_vm::VmInitError; use std::collections::HashMap; @@ -94,37 +94,37 @@ fn serialized_single_function_module_with_consts( .serialize() } -fn test_tile_payload_size(width: usize, height: usize) -> usize { - (width * height).div_ceil(2) + (TILE_BANK_PALETTE_COUNT_V1 * 16 * std::mem::size_of::()) +fn test_glyph_payload_size(width: usize, height: usize) -> usize { + (width * height).div_ceil(2) + (GLYPH_BANK_PALETTE_COUNT_V1 * 16 * std::mem::size_of::()) } -fn test_tile_decoded_size(width: usize, height: usize) -> usize { - width * height + (TILE_BANK_PALETTE_COUNT_V1 * 16 * std::mem::size_of::()) +fn test_glyph_decoded_size(width: usize, height: usize) -> usize { + width * height + (GLYPH_BANK_PALETTE_COUNT_V1 * 16 * std::mem::size_of::()) } -fn test_tile_asset_entry(asset_name: &str, data_len: usize) -> AssetEntry { +fn test_glyph_asset_entry(asset_name: &str, data_len: usize) -> AssetEntry { AssetEntry { asset_id: 7, asset_name: asset_name.to_string(), - bank_type: BankType::TILES, + bank_type: BankType::GLYPH, offset: 0, size: data_len as u64, - decoded_size: test_tile_decoded_size(16, 16) as u64, + decoded_size: test_glyph_decoded_size(16, 16) as u64, codec: AssetCodec::None, metadata: serde_json::json!({ "tile_size": 16, "width": 16, "height": 16, - "palette_count": TILE_BANK_PALETTE_COUNT_V1, - "palette_authored": TILE_BANK_PALETTE_COUNT_V1 + "palette_count": GLYPH_BANK_PALETTE_COUNT_V1, + "palette_authored": GLYPH_BANK_PALETTE_COUNT_V1 }), } } -fn test_tile_asset_data() -> Vec { +fn test_glyph_asset_data() -> Vec { let mut data = - vec![0x11u8; test_tile_payload_size(16, 16) - (TILE_BANK_PALETTE_COUNT_V1 * 16 * 2)]; - data.extend_from_slice(&[0u8; TILE_BANK_PALETTE_COUNT_V1 * 16 * 2]); + vec![0x11u8; test_glyph_payload_size(16, 16) - (GLYPH_BANK_PALETTE_COUNT_V1 * 16 * 2)]; + data.extend_from_slice(&[0u8; GLYPH_BANK_PALETTE_COUNT_V1 * 16 * 2]); data } @@ -439,10 +439,10 @@ fn tick_gfx_set_sprite_invalid_range_returns_status_not_crash() { }], ); let cartridge = cartridge_with_program(program, caps::GFX); - let asset_data = test_tile_asset_data(); + let asset_data = test_glyph_asset_data(); hardware.assets.initialize_for_cartridge( - vec![test_tile_asset_entry("tile_asset", asset_data.len())], + vec![test_glyph_asset_entry("tile_asset", asset_data.len())], vec![prometeu_hal::asset::PreloadEntry { asset_id: 7, slot: 0 }], AssetsPayloadSource::from_bytes(asset_data), ); @@ -637,9 +637,9 @@ fn tick_asset_load_invalid_slot_returns_status_and_zero_handle() { let mut vm = VirtualMachine::default(); let mut hardware = Hardware::new(); let signals = InputSignals::default(); - let asset_data = test_tile_asset_data(); + let asset_data = test_glyph_asset_data(); hardware.assets.initialize_for_cartridge( - vec![test_tile_asset_entry("tile_asset", asset_data.len())], + vec![test_glyph_asset_entry("tile_asset", asset_data.len())], vec![], AssetsPayloadSource::from_bytes(asset_data), ); @@ -721,9 +721,9 @@ fn tick_asset_commit_invalid_transition_returns_status_not_crash() { ); let cartridge = cartridge_with_program(program, caps::ASSET); - let asset_data = test_tile_asset_data(); + let asset_data = test_glyph_asset_data(); hardware.assets.initialize_for_cartridge( - vec![test_tile_asset_entry("tile_asset", asset_data.len())], + vec![test_glyph_asset_entry("tile_asset", asset_data.len())], vec![], AssetsPayloadSource::from_bytes(asset_data), ); @@ -782,9 +782,9 @@ fn tick_asset_cancel_invalid_transition_returns_status_not_crash() { ); let cartridge = cartridge_with_program(program, caps::ASSET); - let asset_data = test_tile_asset_data(); + let asset_data = test_glyph_asset_data(); hardware.assets.initialize_for_cartridge( - vec![test_tile_asset_entry("tile_asset", asset_data.len())], + vec![test_glyph_asset_entry("tile_asset", asset_data.len())], vec![], AssetsPayloadSource::from_bytes(asset_data), ); diff --git a/crates/console/prometeu-system/src/virtual_machine_runtime/tick.rs b/crates/console/prometeu-system/src/virtual_machine_runtime/tick.rs index 61d19946..40c4f951 100644 --- a/crates/console/prometeu-system/src/virtual_machine_runtime/tick.rs +++ b/crates/console/prometeu-system/src/virtual_machine_runtime/tick.rs @@ -166,7 +166,7 @@ impl VirtualMachineRuntime { self.last_frame_cpu_time_us = start.elapsed().as_micros() as u64; - let gfx_stats = hw.assets().bank_info(BankType::TILES); + let gfx_stats = hw.assets().bank_info(BankType::GLYPH); self.telemetry_current.gfx_used_bytes = gfx_stats.used_bytes; self.telemetry_current.gfx_inflight_bytes = gfx_stats.inflight_bytes; self.telemetry_current.gfx_slots_occupied = gfx_stats.slots_occupied as u32; diff --git a/crates/tools/pbxgen-stress/src/lib.rs b/crates/tools/pbxgen-stress/src/lib.rs index 14bb6bea..79a42dfc 100644 --- a/crates/tools/pbxgen-stress/src/lib.rs +++ b/crates/tools/pbxgen-stress/src/lib.rs @@ -67,7 +67,7 @@ pub fn generate() -> Result<()> { const_pool: vec![ ConstantPoolEntry::String("stress".into()), ConstantPoolEntry::String("frame".into()), - ConstantPoolEntry::String("missing_tile_bank".into()), + ConstantPoolEntry::String("missing_glyph_bank".into()), ], functions, code: rom, diff --git a/discussion/index.ndjson b/discussion/index.ndjson index 4f6a488d..eb065250 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -1,6 +1,7 @@ -{"type":"meta","next_id":{"DSC":22,"AGD":20,"DEC":6,"PLN":5,"LSN":25,"CLSN":1}} +{"type":"meta","next_id":{"DSC":23,"AGD":21,"DEC":7,"PLN":6,"LSN":25,"CLSN":1}} {"type":"discussion","id":"DSC-0020","status":"done","ticket":"jenkins-gitea-integration","title":"Jenkins Gitea Integration and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins","gitea"],"agendas":[{"id":"AGD-0018","file":"workflow/agendas/AGD-0018-jenkins-gitea-integration-and-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0003","file":"workflow/decisions/DEC-0003-jenkins-gitea-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0003","file":"workflow/plans/PLN-0003-jenkins-gitea-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0021","file":"lessons/DSC-0020-jenkins-gitea-integration/LSN-0021-jenkins-gitea-integration.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]} {"type":"discussion","id":"DSC-0021","status":"done","ticket":"asset-entry-codec-enum-with-metadata","title":"Asset Entry Codec Enum Contract","created_at":"2026-04-09","updated_at":"2026-04-09","tags":["asset","runtime","codec","metadata"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0024","file":"lessons/DSC-0021-asset-entry-codec-enum-contract/LSN-0024-string-on-the-wire-enum-in-runtime.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]} +{"type":"discussion","id":"DSC-0022","status":"open","ticket":"tile-bank-vs-glyph-bank-domain-naming","title":"Glyph Bank Domain Naming Contract","created_at":"2026-04-09","updated_at":"2026-04-10","tags":["gfx","runtime","naming","domain-model"],"agendas":[{"id":"AGD-0020","file":"AGD-0020-tile-bank-vs-glyph-bank-domain-naming.md","status":"accepted","created_at":"2026-04-09","updated_at":"2026-04-10"}],"decisions":[{"id":"DEC-0006","file":"DEC-0006-glyph-bank-domain-naming-contract.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10","ref_agenda":"AGD-0020"}],"plans":[{"id":"PLN-0005","file":"PLN-0005-glyph-bank-domain-naming-execution.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10","ref_decisions":["DEC-0006"]}],"lessons":[]} {"type":"discussion","id":"DSC-0001","status":"done","ticket":"legacy-runtime-learn-import","title":"Import legacy runtime learn into discussion lessons","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["migration","tech-debt"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0001","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0001-prometeu-learn-index.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0002","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0002-historical-asset-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0003","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0003-historical-audio-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0004","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0004-historical-cartridge-boot-protocol-and-manifest-authority.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0005","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0005-historical-game-memcard-slots-surface-and-semantics.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0006","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0006-historical-gfx-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0007","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0007-historical-retired-fault-and-input-decisions.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0008","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0008-historical-vm-core-and-assets.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0009","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0009-mental-model-asset-management.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0010","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0010-mental-model-audio.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0011","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0011-mental-model-gfx.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0012","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0012-mental-model-input.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0013","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0013-mental-model-observability-and-debugging.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0014","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0014-mental-model-portability-and-cross-platform.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0015","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0015-mental-model-save-memory-and-memcard.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0016","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0016-mental-model-status-first-and-fault-thinking.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0017","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0017-mental-model-time-and-cycles.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0018","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0018-mental-model-touch.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]} {"type":"discussion","id":"DSC-0002","status":"open","ticket":"runtime-edge-test-plan","title":"Agenda - Runtime Edge Test Plan","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0001","file":"workflow/agendas/AGD-0001-runtime-edge-test-plan.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0003","status":"open","ticket":"packed-cartridge-loader-pmc","title":"Agenda - Packed Cartridge Loader PMC","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0002","file":"workflow/agendas/AGD-0002-packed-cartridge-loader-pmc.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} diff --git a/discussion/lessons/DSC-0001-runtime-learn-legacy-import/LSN-0009-mental-model-asset-management.md b/discussion/lessons/DSC-0001-runtime-learn-legacy-import/LSN-0009-mental-model-asset-management.md index 59e6a78b..fddf9d11 100644 --- a/discussion/lessons/DSC-0001-runtime-learn-legacy-import/LSN-0009-mental-model-asset-management.md +++ b/discussion/lessons/DSC-0001-runtime-learn-legacy-import/LSN-0009-mental-model-asset-management.md @@ -123,7 +123,7 @@ The current asset model works better if you do not treat every bank as equally g Some banks are specialized: -- `TILES` +- `GLYPH` - `SOUNDS` For these, the bank contract already carries most of the important format rules. That means: @@ -163,7 +163,7 @@ It does not say: One of the most important asset-management lessons in the current runtime is that `codec = NONE` does not mean "bitwise identical to in-memory layout". -The tile-bank path is the concrete example: +The glyph-bank path is the concrete example: - there is no additional generic codec layer for the asset; - the serialized payload stores indexed pixels as packed `4bpp`; @@ -182,13 +182,13 @@ That is why `AssetEntry.size` and `AssetEntry.decoded_size` must be thought of a If those two numbers are treated as interchangeable, telemetry, budgets, and validation all become misleading. -## Tile Banks Teach The Real Boundary +## Glyph Banks Teach The Real Boundary -Tile banks are useful because they show the real separation of concerns: +Glyph banks are useful because they show the real separation of concerns: - `assets.pa` defines the serialized envelope and metadata needed to reconstruct the bank; - the runtime validates that metadata against the expected v1 contract; -- the resident `TileBank` is a runtime object, not a direct view over the cold bytes. +- the resident `GlyphBank` is a runtime object, not a direct view over the cold bytes. This is the right PROMETEU mental model: diff --git a/discussion/lessons/DSC-0001-runtime-learn-legacy-import/LSN-0011-mental-model-gfx.md b/discussion/lessons/DSC-0001-runtime-learn-legacy-import/LSN-0011-mental-model-gfx.md index 36c0a623..584ee5d0 100644 --- a/discussion/lessons/DSC-0001-runtime-learn-legacy-import/LSN-0011-mental-model-gfx.md +++ b/discussion/lessons/DSC-0001-runtime-learn-legacy-import/LSN-0011-mental-model-gfx.md @@ -18,7 +18,7 @@ PROMETEU treats graphics as an explicit peripheral, not as a modern GPU. The right mental model is a retro 2D machine with: - framebuffer; -- tile banks; +- glyph banks; - tile layers; - sprites ordered by draw order; - deterministic composition per frame. @@ -67,7 +67,7 @@ That enables: - HUD themes; - day and night cycles. -In the current tile-bank v1 baseline, this palette model is intentionally bounded: +In the current glyph-bank v1 baseline, this palette model is intentionally bounded: - each bank carries `64` palettes; - each palette carries `16` colors; @@ -75,7 +75,7 @@ In the current tile-bank v1 baseline, this palette model is intentionally bounde That limit is not incidental bookkeeping. It is part of how art packaging, runtime validation, and rendering stay aligned. -## Tile Banks Are Decoded Runtime Objects +## Glyph Banks Are Decoded Runtime Objects The most useful intuition is to separate three layers: @@ -83,13 +83,13 @@ The most useful intuition is to separate three layers: - serialized cart payload; - resident runtime bank. -For tile banks in v1: +For glyph banks in v1: - authored pixels are logical indices `0..15`; - serialized payload stores those indices as packed `4bpp`; - runtime memory expands them to one `u8` index per pixel after decode. -So when reading the graphics model, do not imagine the renderer reading packed nibbles directly from cartridge storage. The renderer consumes a decoded `TileBank` object whose memory shape is optimized for runtime lookup, not for transport density. +So when reading the graphics model, do not imagine the renderer reading packed nibbles directly from cartridge storage. The renderer consumes a decoded `GlyphBank` object whose memory shape is optimized for runtime lookup, not for transport density. ## Use Cases diff --git a/discussion/lessons/DSC-0016-tilemap-empty-cell-semantics/LSN-0022-tilemap-empty-cell-convergence.md b/discussion/lessons/DSC-0016-tilemap-empty-cell-semantics/LSN-0022-tilemap-empty-cell-convergence.md index 98cfe356..938f6672 100644 --- a/discussion/lessons/DSC-0016-tilemap-empty-cell-semantics/LSN-0022-tilemap-empty-cell-convergence.md +++ b/discussion/lessons/DSC-0016-tilemap-empty-cell-semantics/LSN-0022-tilemap-empty-cell-convergence.md @@ -1,7 +1,7 @@ # LSN-0022: Tilemap Empty Cell Semantics and Glyph Convergence ## Context -During the initial runtime design, `tile_id = 0` was used as a sentinel value for "empty" or "missing" tiles in tilemaps. However, as the asset banking and packer systems evolved, it became clear that `0` should be a valid index for any asset bank, including tile banks. This conflict was formally tracked in `AGD-0015`. +During the initial runtime design, `tile_id = 0` was used as a sentinel value for "empty" or "missing" tiles in tilemaps. However, as the asset banking and packer systems evolved, it became clear that `0` should be a valid index for any asset bank, including glyph banks. This conflict was formally tracked in `AGD-0015`. ## Lessons Learned diff --git a/discussion/lessons/DSC-0017-asset-metadata-normalization/LSN-0023-typed-asset-metadata-helpers.md b/discussion/lessons/DSC-0017-asset-metadata-normalization/LSN-0023-typed-asset-metadata-helpers.md index 51b462c5..5c7d779d 100644 --- a/discussion/lessons/DSC-0017-asset-metadata-normalization/LSN-0023-typed-asset-metadata-helpers.md +++ b/discussion/lessons/DSC-0017-asset-metadata-normalization/LSN-0023-typed-asset-metadata-helpers.md @@ -16,8 +16,8 @@ O uso de `serde_json::Value` diretamente nos loaders do runtime introduz riscos Para mitigar isso, implementamos o padrão de **Typed Metadata Helpers**: -1. **Structs Dedicadas**: Criamos structs Rust (ex: `TilesMetadata`, `SoundsMetadata`) que representam o contrato exato de cada banco. -2. **Conversion Methods**: Adicionamos métodos ao `AssetEntry` (ex: `metadata_as_tiles()`) que utilizam `serde_json::from_value` para realizar o "cast" do JSON dinâmico para a struct tipada. +1. **Structs Dedicadas**: Criamos structs Rust (ex: `GlyphsMetadata`, `SoundsMetadata`) que representam o contrato exato de cada banco. +2. **Conversion Methods**: Adicionamos métodos ao `AssetEntry` (ex: `metadata_as_glyphs()`) que utilizam `serde_json::from_value` para realizar o "cast" do JSON dinâmico para a struct tipada. 3. **Fail-Fast**: A falha no parsing dos metadados deve ser tratada como erro de carregamento do asset, garantindo que o motor não opere com metadados corrompidos ou incompletos. ### Benefícios diff --git a/discussion/workflow/agendas/AGD-0020-tile-bank-vs-glyph-bank-domain-naming.md b/discussion/workflow/agendas/AGD-0020-tile-bank-vs-glyph-bank-domain-naming.md new file mode 100644 index 00000000..7eda50a4 --- /dev/null +++ b/discussion/workflow/agendas/AGD-0020-tile-bank-vs-glyph-bank-domain-naming.md @@ -0,0 +1,221 @@ +--- +id: AGD-0020 +ticket: tile-bank-vs-glyph-bank-domain-naming +title: Agenda - Tile Bank vs Glyph Bank Domain Naming +status: accepted +created: 2026-04-09 +resolved: 2026-04-10 +decision: DEC-0006 +tags: [gfx, runtime, naming, domain-model] +--- + +# Agenda - Tile Bank vs Glyph Bank Domain Naming + +## Contexto + +Hoje o runtime usa `TileBank`, `tile_bank.rs`, `TileBankPool*` e termos derivados para nomear o banco grafico consumido pelo renderer e pelo pipeline de assets. + +Ao mesmo tempo, existe a vontade de elevar `Tile` para um conceito mais amplo do dominio, nao restrito ao sheet grafico concreto. Nessa leitura: + +- `Tile` passa a ser uma ideia mais geral da grade/elemento logico; +- o que hoje e o artefato grafico concreto chamado `TileBank` deveria passar a se chamar `GlyphBank`; +- a intencao inicial nao e mudar formato, memoria, payload ou algoritmo, e sim alinhar a linguagem do projeto. + +Esse tema ja encosta em documentacao e lessons existentes, porque o vocabulario atual mistura: + +- tile como unidade de composicao visual; +- tile bank como sheet grafico concreto; +- referencias esparsas a glyph em lições e agendas. + +## Problema + +Se o projeto mudar o centro semantico de `tile` e `glyph` sem uma decisao explicita, o repositorio tende a ficar com vocabulário hibrido: + +- tipos antigos com nome legado; +- docs novas com nome novo; +- renderer e assets falando uma lingua; +- lessons e discussoes falando outra. + +O problema principal nao e tecnico-algoritmico. E semantico e operacional: qual vocabulario o projeto quer tornar canonico para o banco grafico concreto, e qual parte dessa mudanca e apenas rename mecanico versus mudanca real de modelo. + +## Pontos Criticos + +1. Escopo da mudanca. + Precisamos separar rename de dominio de qualquer mudanca de formato ou comportamento. + +2. Contrato externo. + Precisamos fechar quais contratos externos tambem entram no rename. Ja existe direcao para migrar `BankType::TILES` para `BankType::GLYPH`. + +3. Consistencia de linguagem. + A mudanca so vale a pena se atingir codigo, tests, docs e lessons de forma coordenada. + +4. Custo de churn. + Mesmo sem mudar comportamento, o rename atravessa muitos modulos (`hal`, `drivers`, renderer, pools, mensagens de erro, tests e docs). + +5. Fronteira conceitual. + Precisamos definir o que `Tile` passa a significar depois do rename, para evitar trocar um overload semantico por outro. + +## Opcoes + +### Opcao A - Manter `TileBank` como esta + +- **Abordagem:** preservar `TileBank` como nome canonico do banco grafico concreto e aceitar que `tile` continue carregando tanto o lado logico quanto o lado visual. +- **Pro:** zero churn nominal imediato e nenhuma migracao de docs/codigo. +- **Con:** o overload conceitual de `tile` permanece e pode continuar poluindo a linguagem de dominio. +- **Tradeoff:** economiza trabalho agora ao custo de clareza futura. + +### Opcao B - Renomear `TileBank` para `GlyphBank` como refactor semantico + +- **Abordagem:** tratar a mudanca como rename consistente de tipos, modulos, docs, testes e mensagens, sem alterar formato de payload, layout em memoria ou renderer. +- **Pro:** melhora a linguagem do projeto sem reabrir a arquitetura grafica. +- **Con:** exige disciplina para manter a promessa de “rename only” e nao misturar isso com redesign. +- **Tradeoff:** churn mecanico relativamente alto para ganhar clareza conceitual. + +### Opcao C - Fazer rename parcial + +- **Abordagem:** adotar `glyph` apenas em docs novas ou em algumas camadas, preservando nomes antigos em APIs e modulos centrais. +- **Pro:** menor custo inicial. +- **Con:** cria o pior estado intermediario: dois vocabulários concorrentes para o mesmo conceito. +- **Tradeoff:** parece barato, mas deixa a linguagem do projeto menos confiavel. + +## Sugestao / Recomendacao + +Seguir inicialmente com a **Opcao B**, desde que a discussao confirme que a mudanca e de nomenclatura e nao de semantica operacional. + +A recomendacao provisoria e: + +- `GlyphBank` se torna o nome do artefato grafico concreto que hoje chamamos de `TileBank`; +- `Tile` fica livre para representar um conceito mais geral do dominio; +- `BankType::TILES` passa a `BankType::GLYPH`; +- a migracao deve ser consistente em codigo, tests, docs e lessons; +- `TileLayer` e derivados nao entram automaticamente no rename, porque ja pertencem a outra fronteira de dominio e precisam de triagem separada; +- a discussao deve explicitar quais superfícies mudam juntas para impedir vocabulario hibrido sem reabrir a arquitetura grafica. + +## Perguntas em Aberto + +- Confirmar a superficie exata do rename para evitar misturar banco grafico concreto com conceitos de layer/mapa. + +## Discussao + +### Direcao fechada ate aqui + +1. **Natureza da mudanca** + A mudanca e `rename-only`. Nao ha intencao de alterar formato, algoritmo, layout em memoria ou comportamento. + +2. **Contrato externo** + `BankType::TILES` nao deve permanecer. O contrato deve migrar para `BankType::GLYPH`. + +3. **Escopo documental** + A migracao deve ser completa, inclusive em documentacao e lessons historicas. + +4. **Colisao semantica** + `glyph` nao colide com outro artefato canonico relevante nesta etapa. + +### Fronteira importante + +Nem todo `Tile*` deve migrar automaticamente para `Glyph*`. + +Existe uma fronteira entre: + +- nomes que descrevem o banco grafico concreto e seu circuito de carga/uso; e +- nomes que ja descrevem dominio de layer, mapa, grade ou composicao. + +Por isso, a proxima resposta que falta precisa ser dada por superficie concreta, e nao por regra global simplista. + +### Lista para voto `sim` / `nao` + +Responda `sim` ou `nao` para cada grupo abaixo. + +1. **Banco concreto e modulo base** + Inclui `TileBank`, `tile_bank.rs`, comentarios e docs especificos desse tipo. + +2. **Enum e contrato de asset** + Inclui `BankType::TILES -> BankType::GLYPH`, mensagens de erro, metadata helpers e referencias equivalentes no path de assets. + +3. **Pools e interfaces de memoria** + Inclui `TileBankPoolAccess`, `TileBankPoolInstaller`, `tile_bank_slot`, `install_tile_bank`, campos internos como `tile_bank_pool`. + +4. **Asset manager e loader path** + Inclui funcoes como `decode_tile_bank_*`, `perform_load_tile_bank`, variaveis locais, testes e mensagens associadas ao load do banco grafico. + +5. **Renderer e hardware references ao banco** + Inclui usos em `gfx.rs` e `hardware.rs` que referenciam o banco concreto, por exemplo campos `tile_banks`, docs sobre VRAM `TileBanks`, parametros `bank: &TileBank`. + +6. **Modulo e tipos de layer/mapa** + Inclui `tile_layer.rs`, `TileMap`, `TileLayer`, `ScrollableTileLayer`, `HudTileLayer`. + Este grupo e o mais sensivel porque pode nao ser rename de banco concreto, e sim outro dominio. + +7. **TileSize** + Inclui `TileSize` e referencias ao tamanho de tile como unidade geometrica. + Este grupo pode continuar como `TileSize` mesmo se o banco virar `GlyphBank`. + +8. **Strings, fixtures e test names** + Inclui nomes de testes, helper names, snapshots, mensagens e dados de teste que ainda falam em tile bank. + +9. **Lessons e docs historicas** + Inclui lessons ja publicadas e material de discussao/mental model que hoje falam em `TileBank`. + +### Votos registrados + +1. **Banco concreto e modulo base**: `sim` + `TileBank`, `tile_bank.rs`, modulo base e documentacao associada entram na migracao. + +2. **Enum e contrato de asset**: `sim` + `BankType::TILES` e o contrato textual equivalente entram na migracao para `GLYPH`. + +3. **Pools e interfaces de memoria**: `sim` + `TileBankPool*`, `tile_bank_slot` e derivados entram na migracao. + +4. **Asset manager e loader path**: `sim` + Helpers, decode path, mensagens e testes associados ao banco grafico concreto entram na migracao. + +5. **Renderer e hardware references ao banco**: `sim` + Referencias ao banco concreto em renderer e hardware entram na migracao. + +6. **Modulo e tipos de layer/mapa**: `nao` + `TileLayer`, `TileMap`, `ScrollableTileLayer` e `HudTileLayer` ficam fora desta rodada. + +7. **TileSize**: `nao` + `TileSize` permanece como conceito geometrico e nao entra automaticamente no rename. + +8. **Strings, fixtures e test names**: `sim` + Helpers, fixtures, mensagens e nomes de testes entram na migracao para evitar residuos do vocabulario antigo. + +9. **Lessons e docs historicas**: `sim` + A migracao documental e historica entra no escopo, seguindo a mesma fronteira desta agenda: renomear o banco grafico concreto sem arrastar automaticamente o dominio de layer/mapa. + +## Resolucao Provisoria + +Ha consenso provisoriamente estabelecido sobre os seguintes pontos: + +- a mudanca e `rename-only`; +- o vocabulario canonico do banco grafico concreto passa de `TileBank` para `GlyphBank`; +- `BankType::TILES` deve migrar para `BankType::GLYPH`; +- pools, contrato de asset, loader path, renderer, hardware references, fixtures e nomes de teste entram na migracao; +- `TileLayer`/`TileMap` e derivados ficam fora desta rodada; +- `TileSize` fica fora desta rodada; +- docs e lessons historicas entram no escopo, respeitando essa mesma fronteira. + +O principal ponto restante para encerrar a agenda e confirmar se essa resolucao ja e suficiente para virar decisao normativa, ou se ainda falta detalhar como a migracao documental deve tratar referencias historicas em texto corrido quando `tile` continuar correto para layer/mapa. + +## Resolucao + +A agenda fica encerrada com a seguinte orientacao: + +- a mudanca e `rename-only`; +- o banco grafico concreto passa a se chamar `GlyphBank`; +- `BankType::TILES` passa a `BankType::GLYPH`; +- pools, contrato de asset, loader path, renderer, hardware references, fixtures, nomes de teste, docs e lessons entram na migracao; +- `TileLayer`, `TileMap` e derivados ficam fora; +- `TileSize` fica fora; +- docs e lessons antigas devem migrar referencias ao banco grafico concreto para `GlyphBank`, mantendo `tile` quando o assunto for layer, mapa, grade ou geometria. + +## Criterio para Encerrar + +Esta agenda pode ser encerrada quando houver consenso escrito sobre: + +- se a mudanca e rename-only ou nao; +- qual vocabulario passa a ser canonico; +- quais contratos externos entram no rename; +- quais grupos concretos entram ou nao na migracao; +- como evitar que `TileLayer`/`TileMap` sejam arrastados automaticamente sem decisao propria. diff --git a/discussion/workflow/decisions/DEC-0006-glyph-bank-domain-naming-contract.md b/discussion/workflow/decisions/DEC-0006-glyph-bank-domain-naming-contract.md new file mode 100644 index 00000000..65a66ebf --- /dev/null +++ b/discussion/workflow/decisions/DEC-0006-glyph-bank-domain-naming-contract.md @@ -0,0 +1,93 @@ +--- +id: DEC-0006 +ticket: tile-bank-vs-glyph-bank-domain-naming +title: Glyph Bank Domain Naming Contract +status: accepted +created: 2026-04-10 +accepted: 2026-04-10 +agenda: AGD-0020 +plans: [PLN-0005] +tags: [gfx, runtime, naming, domain-model] +--- + +## Status + +Accepted on 2026-04-10. + +## Contexto + +The runtime currently uses `TileBank` and related names for the concrete graphical bank consumed by the asset pipeline and renderer. At the same time, the project wants `Tile` to remain available as a broader domain concept for grid, layer, map, and geometric tile semantics. + +This decision closes the naming contract by separating: + +- the concrete graphical bank artifact; +- the logical tile/layer/map domain; +- the migration boundary for code, tests, docs, and historical lessons. + +The change is explicitly rename-only. It does not alter format, runtime behavior, memory layout, payload structure, or renderer algorithms. + +## Decisao + +1. The concrete graphical bank currently named `TileBank` MUST be renamed to `GlyphBank`. +2. This rename MUST be treated as nomenclature-only. It MUST NOT change payload format, runtime behavior, memory layout, codec behavior, metadata structure, or rendering semantics. +3. `BankType::TILES` MUST be renamed to `BankType::GLYPH`. +4. The asset contract and runtime code that refer to the concrete graphical bank MUST migrate to `Glyph*` naming consistently. +5. The following groups MUST be included in the rename: + - concrete bank type and module; + - asset contract and asset-facing terminology; + - memory pools and bank installation/access interfaces; + - asset manager decode/load path; + - renderer and hardware references to the concrete bank; + - fixtures, test names, helper names, and user-facing/runtime-facing strings for the concrete bank; + - documentation and historical lessons, subject to the editorial boundary below. +6. The following groups MUST NOT be renamed as part of this decision: + - `TileLayer`, `TileMap`, `ScrollableTileLayer`, `HudTileLayer`, and related layer/map structures; + - `TileSize` and geometric tile-size concepts. +7. Documentation and historical lessons MUST be updated with the following editorial rule: + - references to the concrete graphical bank artifact MUST migrate to `GlyphBank` and equivalent `Glyph*` naming; + - references to layer, map, grid, or geometric tile concepts MUST remain `tile` when that is the correct domain meaning. +8. The project MUST NOT adopt a partial mixed vocabulary where the same concrete bank artifact is simultaneously described as both `TileBank` and `GlyphBank` in active code or maintained documentation. +9. `glyph` is treated as a new canonical artifact name for this stage and does not conflict with an existing canonical artifact that would block adoption. + +## Rationale + +- The current name overloads `tile` across both logical map/layer concepts and the concrete graphical bank artifact. +- Renaming only the concrete bank artifact improves domain clarity without reopening graphics architecture. +- Excluding `TileLayer`, `TileMap`, and `TileSize` preserves established semantics where `tile` still means the logical or geometric concept. +- A full migration across code, tests, docs, and lessons avoids the unstable mixed-language state that tends to follow partial renames. + +## Invariantes / Contrato + +- `GlyphBank` is the canonical name for the concrete graphical bank artifact. +- `tile` remains canonical for layer/map/geometric concepts unless a later decision explicitly changes that. +- `BankType::GLYPH` is the canonical asset-bank enum variant for the concrete graphical bank. +- This decision is a rename boundary, not a behavior-change boundary. +- Documentation must follow artifact meaning, not mechanical string replacement. + +## Impactos + +- `prometeu-hal` will need type/module/enum renames for the concrete graphical bank path. +- `prometeu-drivers` will need renderer, memory-pool, hardware, and asset-manager naming migration for bank-specific references. +- Tests, fixtures, and helper names need coordinated updates to avoid mixed terminology. +- Docs and lessons need targeted rewriting rather than blind search-and-replace, because `tile` remains correct in map/layer contexts. + +## Referencias + +- AGD-0020: Tile Bank vs Glyph Bank Domain Naming +- LSN-0022: Tilemap Empty Cell Convergence +- `crates/console/prometeu-hal/src/tile_bank.rs` +- `crates/console/prometeu-hal/src/tile_layer.rs` +- `crates/console/prometeu-drivers/src/gfx.rs` +- `crates/console/prometeu-drivers/src/memory_banks.rs` +- `crates/console/prometeu-drivers/src/asset.rs` + +## Propagacao Necessaria + +- Write an execution plan before code and documentation migration. +- Rename the concrete bank surface consistently across runtime crates. +- Preserve `tile` naming in layer/map/geometric surfaces excluded by this decision. +- Update docs and lessons according to artifact meaning, not blanket replacement. + +## Revision Log + +- 2026-04-10: Initial accepted decision from AGD-0020. diff --git a/discussion/workflow/plans/PLN-0005-glyph-bank-domain-naming-execution.md b/discussion/workflow/plans/PLN-0005-glyph-bank-domain-naming-execution.md new file mode 100644 index 00000000..77465faf --- /dev/null +++ b/discussion/workflow/plans/PLN-0005-glyph-bank-domain-naming-execution.md @@ -0,0 +1,161 @@ +--- +id: PLN-0005 +ticket: tile-bank-vs-glyph-bank-domain-naming +title: Glyph Bank Domain Naming Execution +status: accepted +created: 2026-04-10 +completed: +tags: [gfx, runtime, naming, domain-model] +--- + +## Briefing + +Implement DEC-0006 by renaming the concrete graphical bank artifact from `TileBank` to `GlyphBank` across runtime code, tests, documentation, and historical lessons, while preserving behavior and keeping the excluded tile-domain surfaces untouched. This is a rename-only migration. It must not alter payload format, rendering semantics, codec behavior, metadata structure, or memory layout. + +## Decisions de Origem + +- DEC-0006: Glyph Bank Domain Naming Contract + +## Alvo + +Land a consistent runtime-and-docs migration that: + +- renames the concrete bank artifact and its module/type surface to `GlyphBank`; +- renames `BankType::TILES` to `BankType::GLYPH`; +- renames pool and loader/runtime bank-specific APIs accordingly; +- preserves `TileLayer`, `TileMap`, `ScrollableTileLayer`, `HudTileLayer`, and `TileSize`; +- rewrites maintained docs and lessons according to artifact meaning rather than blanket token replacement. + +## Escopo + +- `prometeu-hal` concrete bank type/module/enum renames. +- `prometeu-drivers` renderer, memory-bank, hardware, and asset-manager bank-specific renames. +- `prometeu-system` callsites and dispatch paths that refer to the asset-bank enum variant. +- Tests, helpers, fixtures, and strings that still describe the concrete bank as `TileBank`. +- Discussion lessons and maintained discussion artifacts that refer to the concrete graphical bank artifact. + +## Fora de Escopo + +- Any format, algorithm, codec, metadata, or layout change. +- Renaming `TileLayer`, `TileMap`, `ScrollableTileLayer`, `HudTileLayer`, or related layer/map structures. +- Renaming `TileSize` or geometric tile-size concepts. +- Redesigning tile/glyph semantics beyond the naming boundary established by DEC-0006. + +## Plano de Execucao + +### Step 1 - Rename the HAL concrete bank surface + +**What:** +Rename the concrete bank module and type surface in `prometeu-hal` from `TileBank` to `GlyphBank`. + +**How:** +Rename `tile_bank.rs` to `glyph_bank.rs`, update `lib.rs` exports/import paths, rename `TileBank` to `GlyphBank`, and rename concrete-bank-specific constants if they are artifact-specific rather than geometric. Keep `TileSize` unchanged even if it continues to live near the bank implementation or must be re-exported from the renamed module in a compatibility-preserving layout inside this refactor. + +**File(s):** +- `crates/console/prometeu-hal/src/tile_bank.rs` +- `crates/console/prometeu-hal/src/lib.rs` +- any direct imports of `prometeu_hal::tile_bank::*` in runtime crates + +### Step 2 - Rename the asset-bank contract + +**What:** +Migrate the asset-bank enum and related asset-facing terminology from `TILES` to `GLYPH`. + +**How:** +Rename `BankType::TILES` to `BankType::GLYPH`, update any JSON/serde expectations and validation paths, and rename asset-facing metadata helpers only where they describe the concrete bank artifact rather than the geometric tile model. Preserve behavior and fail-fast semantics unchanged. + +**File(s):** +- `crates/console/prometeu-hal/src/asset.rs` +- `crates/console/prometeu-hal/src/cartridge_loader.rs` +- `crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs` +- `crates/console/prometeu-system/src/virtual_machine_runtime/tick.rs` +- `crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs` + +### Step 3 - Rename memory pools and concrete-bank runtime APIs + +**What:** +Rename bank-specific pool interfaces and storage paths that refer to the concrete graphical bank. + +**How:** +Rename `TileBankPoolAccess`, `TileBankPoolInstaller`, `tile_bank_slot`, `install_tile_bank`, `tile_bank_pool`, and related fields/traits/vars to `Glyph*` equivalents. Keep semantics and slot behavior unchanged. + +**File(s):** +- `crates/console/prometeu-drivers/src/memory_banks.rs` +- `crates/console/prometeu-drivers/src/hardware.rs` +- `crates/console/prometeu-drivers/src/gfx.rs` +- `crates/console/prometeu-drivers/src/asset.rs` + +### Step 4 - Rename concrete-bank decode/load path and tests + +**What:** +Update the asset manager and concrete-bank decode path to the new artifact name. + +**How:** +Rename functions such as `decode_tile_bank_*`, `perform_load_tile_bank`, and related test helpers/messages to `GlyphBank` naming. Keep payload interpretation, palette handling, and runtime materialization identical to current behavior. Do not rename `TileSize`. + +**File(s):** +- `crates/console/prometeu-drivers/src/asset.rs` +- affected tests in `crates/console/prometeu-drivers/src/asset.rs` +- any helper or stress/test utility strings such as `missing_tile_bank` + +### Step 5 - Apply renderer and hardware editorial cleanup + +**What:** +Rename only the references that describe the concrete bank artifact inside renderer and hardware documentation/comments. + +**How:** +Update comments, docs, field names, and user-facing/runtime-facing strings so that references to the bank artifact become `GlyphBank`/`glyph bank`, while preserving `tile` terminology for layers, maps, and geometric concepts. Review `gfx.rs` carefully because it contains both domains. + +**File(s):** +- `crates/console/prometeu-drivers/src/gfx.rs` +- `crates/console/prometeu-drivers/src/hardware.rs` +- any crate-level docs/comments on the bank artifact path + +### Step 6 - Rewrite maintained lessons and discussion docs by meaning + +**What:** +Migrate maintained lessons and relevant discussion text to the new canonical artifact name. + +**How:** +Update published lessons and maintained discussion artifacts so that references to the concrete graphical bank become `GlyphBank`, but references to tile layers, tilemaps, grid semantics, or tile geometry remain `tile`. Use manual review, not blanket replacement. + +**File(s):** +- `discussion/lessons/DSC-0016-tilemap-empty-cell-semantics/LSN-0022-tilemap-empty-cell-convergence.md` +- `discussion/lessons/DSC-0001-runtime-learn-legacy-import/LSN-0011-mental-model-gfx.md` +- `discussion/lessons/DSC-0001-runtime-learn-legacy-import/LSN-0009-mental-model-asset-management.md` +- any maintained agenda/decision/plan artifacts still intentionally retained and referencing the concrete bank artifact + +## Criterios de Aceite + +- The canonical concrete bank type/module name in runtime code is `GlyphBank`. +- The canonical asset-bank enum variant is `BankType::GLYPH`. +- Pool interfaces and concrete-bank runtime helpers use `Glyph*` naming consistently. +- `TileLayer`, `TileMap`, `ScrollableTileLayer`, `HudTileLayer`, and `TileSize` remain unchanged. +- No behavior, payload format, or renderer semantics change as part of this migration. +- Tests, helper names, and runtime-facing strings do not keep active mixed `TileBank`/`GlyphBank` naming for the same artifact. +- Maintained lessons and docs use `GlyphBank` for the concrete bank artifact while preserving `tile` where it refers to layer/map/geometric concepts. + +## Tests / Validacao + +### Unit Tests + +- Update and run unit tests that directly construct or decode the concrete graphical bank path. +- Confirm renamed bank/pool/helper symbols compile without compatibility shims. + +### Integration Tests + +- Run `prometeu-hal`, `prometeu-drivers`, and `prometeu-system` test targets affected by the rename. +- Verify asset loading still succeeds for the renamed `BankType::GLYPH` path. + +### Manual Verification + +- Review representative docs and lessons to confirm `GlyphBank` only replaces the concrete bank artifact meaning. +- Review `gfx.rs` and `tile_layer.rs` boundaries to confirm tile-domain structures were not accidentally renamed. +- Search the repository for residual `TileBank` references and classify any remaining occurrence as either acceptable historical residue outside scope or a migration miss. + +## Riscos + +- The repository uses `tile` in two different domains, so blind search-and-replace is likely to over-rename excluded surfaces. +- Renaming the enum variant from `TILES` to `GLYPH` touches serialization contracts and can break test fixtures or external packer assumptions if not coordinated. +- Moving `tile_bank.rs` to `glyph_bank.rs` may cause broad import churn across crates and tests. +- Historical lessons can easily drift into editorial inconsistency if the rewrite is done mechanically instead of semantically. -- 2.47.2 From a16edcb2572dd3f23d1f3d1311474d52dd93963c Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Fri, 10 Apr 2026 06:04:25 +0100 Subject: [PATCH 09/10] clean up --- discussion/index.ndjson | 4 +- ...rename-artifact-by-meaning-not-by-token.md | 45 ++++ ...0-tile-bank-vs-glyph-bank-domain-naming.md | 221 ------------------ ...-0006-glyph-bank-domain-naming-contract.md | 93 -------- ...0005-glyph-bank-domain-naming-execution.md | 161 ------------- 5 files changed, 47 insertions(+), 477 deletions(-) create mode 100644 discussion/lessons/DSC-0022-glyph-bank-domain-naming/LSN-0025-rename-artifact-by-meaning-not-by-token.md delete mode 100644 discussion/workflow/agendas/AGD-0020-tile-bank-vs-glyph-bank-domain-naming.md delete mode 100644 discussion/workflow/decisions/DEC-0006-glyph-bank-domain-naming-contract.md delete mode 100644 discussion/workflow/plans/PLN-0005-glyph-bank-domain-naming-execution.md diff --git a/discussion/index.ndjson b/discussion/index.ndjson index eb065250..cc0aa485 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -1,7 +1,7 @@ -{"type":"meta","next_id":{"DSC":23,"AGD":21,"DEC":7,"PLN":6,"LSN":25,"CLSN":1}} +{"type":"meta","next_id":{"DSC":23,"AGD":21,"DEC":7,"PLN":6,"LSN":26,"CLSN":1}} {"type":"discussion","id":"DSC-0020","status":"done","ticket":"jenkins-gitea-integration","title":"Jenkins Gitea Integration and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins","gitea"],"agendas":[{"id":"AGD-0018","file":"workflow/agendas/AGD-0018-jenkins-gitea-integration-and-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0003","file":"workflow/decisions/DEC-0003-jenkins-gitea-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0003","file":"workflow/plans/PLN-0003-jenkins-gitea-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0021","file":"lessons/DSC-0020-jenkins-gitea-integration/LSN-0021-jenkins-gitea-integration.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]} {"type":"discussion","id":"DSC-0021","status":"done","ticket":"asset-entry-codec-enum-with-metadata","title":"Asset Entry Codec Enum Contract","created_at":"2026-04-09","updated_at":"2026-04-09","tags":["asset","runtime","codec","metadata"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0024","file":"lessons/DSC-0021-asset-entry-codec-enum-contract/LSN-0024-string-on-the-wire-enum-in-runtime.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]} -{"type":"discussion","id":"DSC-0022","status":"open","ticket":"tile-bank-vs-glyph-bank-domain-naming","title":"Glyph Bank Domain Naming Contract","created_at":"2026-04-09","updated_at":"2026-04-10","tags":["gfx","runtime","naming","domain-model"],"agendas":[{"id":"AGD-0020","file":"AGD-0020-tile-bank-vs-glyph-bank-domain-naming.md","status":"accepted","created_at":"2026-04-09","updated_at":"2026-04-10"}],"decisions":[{"id":"DEC-0006","file":"DEC-0006-glyph-bank-domain-naming-contract.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10","ref_agenda":"AGD-0020"}],"plans":[{"id":"PLN-0005","file":"PLN-0005-glyph-bank-domain-naming-execution.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10","ref_decisions":["DEC-0006"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0022","status":"done","ticket":"tile-bank-vs-glyph-bank-domain-naming","title":"Glyph Bank Domain Naming Contract","created_at":"2026-04-09","updated_at":"2026-04-10","tags":["gfx","runtime","naming","domain-model"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0025","file":"lessons/DSC-0022-glyph-bank-domain-naming/LSN-0025-rename-artifact-by-meaning-not-by-token.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} {"type":"discussion","id":"DSC-0001","status":"done","ticket":"legacy-runtime-learn-import","title":"Import legacy runtime learn into discussion lessons","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["migration","tech-debt"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0001","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0001-prometeu-learn-index.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0002","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0002-historical-asset-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0003","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0003-historical-audio-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0004","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0004-historical-cartridge-boot-protocol-and-manifest-authority.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0005","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0005-historical-game-memcard-slots-surface-and-semantics.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0006","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0006-historical-gfx-status-first-fault-and-return-contract.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0007","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0007-historical-retired-fault-and-input-decisions.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0008","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0008-historical-vm-core-and-assets.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0009","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0009-mental-model-asset-management.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0010","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0010-mental-model-audio.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0011","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0011-mental-model-gfx.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0012","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0012-mental-model-input.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0013","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0013-mental-model-observability-and-debugging.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0014","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0014-mental-model-portability-and-cross-platform.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0015","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0015-mental-model-save-memory-and-memcard.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0016","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0016-mental-model-status-first-and-fault-thinking.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0017","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0017-mental-model-time-and-cycles.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"},{"id":"LSN-0018","file":"lessons/DSC-0001-runtime-learn-legacy-import/LSN-0018-mental-model-touch.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]} {"type":"discussion","id":"DSC-0002","status":"open","ticket":"runtime-edge-test-plan","title":"Agenda - Runtime Edge Test Plan","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0001","file":"workflow/agendas/AGD-0001-runtime-edge-test-plan.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0003","status":"open","ticket":"packed-cartridge-loader-pmc","title":"Agenda - Packed Cartridge Loader PMC","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0002","file":"workflow/agendas/AGD-0002-packed-cartridge-loader-pmc.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]} diff --git a/discussion/lessons/DSC-0022-glyph-bank-domain-naming/LSN-0025-rename-artifact-by-meaning-not-by-token.md b/discussion/lessons/DSC-0022-glyph-bank-domain-naming/LSN-0025-rename-artifact-by-meaning-not-by-token.md new file mode 100644 index 00000000..07a8c3d6 --- /dev/null +++ b/discussion/lessons/DSC-0022-glyph-bank-domain-naming/LSN-0025-rename-artifact-by-meaning-not-by-token.md @@ -0,0 +1,45 @@ +--- +id: LSN-0025 +ticket: tile-bank-vs-glyph-bank-domain-naming +title: Rename Artifact by Meaning, Not by Token +created: 2026-04-10 +tags: [gfx, runtime, naming, refactor, documentation] +--- + +## Context + +The runtime used `TileBank` as the name of the concrete graphical bank artifact while also using `tile` for map, layer, and geometric concepts such as `TileLayer`, `TileMap`, and `TileSize`. This overloaded the term `tile` across two different meanings. + +The implemented migration renamed only the concrete bank artifact to `GlyphBank` and renamed the corresponding asset-bank contract from `BankType::TILES` to `BankType::GLYPH`, while explicitly preserving `TileLayer`, `TileMap`, and `TileSize`. + +## Key Decisions + +### Glyph Bank Domain Naming Contract + +**What:** +Rename the concrete graphical bank artifact and its runtime interfaces to `GlyphBank`, but keep tile-domain structures unchanged where `tile` still correctly describes layers, maps, grids, and geometry. + +**Why:** +The goal was domain clarity, not behavior change. The project needed a more precise name for the concrete graphical bank without reopening rendering architecture or erasing valid uses of `tile`. + +**Trade-offs:** +This requires more care than a blind search-and-replace. The migration is broader editorially, but safer semantically, because it avoids damaging concepts that still belong in the tile domain. + +## Patterns and Algorithms + +- Rename by artifact meaning, not by string token frequency. +- When a term is overloaded, split the rename along domain boundaries instead of trying to enforce total lexical uniformity. +- Let the runtime contract adopt the new canonical artifact name (`GlyphBank`, `BankType::GLYPH`) while preserving existing terms for excluded concepts (`TileLayer`, `TileMap`, `TileSize`). +- Apply the same semantic rule to docs and lessons, not only to code. + +## Pitfalls + +- Blind replacement would have incorrectly renamed tile-layer and tile-size concepts that were intentionally out of scope. +- Partial renames create mixed vocabulary, which is worse than either old or new terminology used consistently. +- Historical documentation is especially easy to corrupt if rewritten mechanically instead of by artifact meaning. + +## Takeaways + +- A naming refactor can be rename-only in behavior while still requiring architectural discipline in wording. +- Use one canonical name per artifact, but do not force unrelated domains to share that rename. +- Documentation migrations should follow the same semantic boundary as code migrations, or the codebase will drift back into conceptual ambiguity. diff --git a/discussion/workflow/agendas/AGD-0020-tile-bank-vs-glyph-bank-domain-naming.md b/discussion/workflow/agendas/AGD-0020-tile-bank-vs-glyph-bank-domain-naming.md deleted file mode 100644 index 7eda50a4..00000000 --- a/discussion/workflow/agendas/AGD-0020-tile-bank-vs-glyph-bank-domain-naming.md +++ /dev/null @@ -1,221 +0,0 @@ ---- -id: AGD-0020 -ticket: tile-bank-vs-glyph-bank-domain-naming -title: Agenda - Tile Bank vs Glyph Bank Domain Naming -status: accepted -created: 2026-04-09 -resolved: 2026-04-10 -decision: DEC-0006 -tags: [gfx, runtime, naming, domain-model] ---- - -# Agenda - Tile Bank vs Glyph Bank Domain Naming - -## Contexto - -Hoje o runtime usa `TileBank`, `tile_bank.rs`, `TileBankPool*` e termos derivados para nomear o banco grafico consumido pelo renderer e pelo pipeline de assets. - -Ao mesmo tempo, existe a vontade de elevar `Tile` para um conceito mais amplo do dominio, nao restrito ao sheet grafico concreto. Nessa leitura: - -- `Tile` passa a ser uma ideia mais geral da grade/elemento logico; -- o que hoje e o artefato grafico concreto chamado `TileBank` deveria passar a se chamar `GlyphBank`; -- a intencao inicial nao e mudar formato, memoria, payload ou algoritmo, e sim alinhar a linguagem do projeto. - -Esse tema ja encosta em documentacao e lessons existentes, porque o vocabulario atual mistura: - -- tile como unidade de composicao visual; -- tile bank como sheet grafico concreto; -- referencias esparsas a glyph em lições e agendas. - -## Problema - -Se o projeto mudar o centro semantico de `tile` e `glyph` sem uma decisao explicita, o repositorio tende a ficar com vocabulário hibrido: - -- tipos antigos com nome legado; -- docs novas com nome novo; -- renderer e assets falando uma lingua; -- lessons e discussoes falando outra. - -O problema principal nao e tecnico-algoritmico. E semantico e operacional: qual vocabulario o projeto quer tornar canonico para o banco grafico concreto, e qual parte dessa mudanca e apenas rename mecanico versus mudanca real de modelo. - -## Pontos Criticos - -1. Escopo da mudanca. - Precisamos separar rename de dominio de qualquer mudanca de formato ou comportamento. - -2. Contrato externo. - Precisamos fechar quais contratos externos tambem entram no rename. Ja existe direcao para migrar `BankType::TILES` para `BankType::GLYPH`. - -3. Consistencia de linguagem. - A mudanca so vale a pena se atingir codigo, tests, docs e lessons de forma coordenada. - -4. Custo de churn. - Mesmo sem mudar comportamento, o rename atravessa muitos modulos (`hal`, `drivers`, renderer, pools, mensagens de erro, tests e docs). - -5. Fronteira conceitual. - Precisamos definir o que `Tile` passa a significar depois do rename, para evitar trocar um overload semantico por outro. - -## Opcoes - -### Opcao A - Manter `TileBank` como esta - -- **Abordagem:** preservar `TileBank` como nome canonico do banco grafico concreto e aceitar que `tile` continue carregando tanto o lado logico quanto o lado visual. -- **Pro:** zero churn nominal imediato e nenhuma migracao de docs/codigo. -- **Con:** o overload conceitual de `tile` permanece e pode continuar poluindo a linguagem de dominio. -- **Tradeoff:** economiza trabalho agora ao custo de clareza futura. - -### Opcao B - Renomear `TileBank` para `GlyphBank` como refactor semantico - -- **Abordagem:** tratar a mudanca como rename consistente de tipos, modulos, docs, testes e mensagens, sem alterar formato de payload, layout em memoria ou renderer. -- **Pro:** melhora a linguagem do projeto sem reabrir a arquitetura grafica. -- **Con:** exige disciplina para manter a promessa de “rename only” e nao misturar isso com redesign. -- **Tradeoff:** churn mecanico relativamente alto para ganhar clareza conceitual. - -### Opcao C - Fazer rename parcial - -- **Abordagem:** adotar `glyph` apenas em docs novas ou em algumas camadas, preservando nomes antigos em APIs e modulos centrais. -- **Pro:** menor custo inicial. -- **Con:** cria o pior estado intermediario: dois vocabulários concorrentes para o mesmo conceito. -- **Tradeoff:** parece barato, mas deixa a linguagem do projeto menos confiavel. - -## Sugestao / Recomendacao - -Seguir inicialmente com a **Opcao B**, desde que a discussao confirme que a mudanca e de nomenclatura e nao de semantica operacional. - -A recomendacao provisoria e: - -- `GlyphBank` se torna o nome do artefato grafico concreto que hoje chamamos de `TileBank`; -- `Tile` fica livre para representar um conceito mais geral do dominio; -- `BankType::TILES` passa a `BankType::GLYPH`; -- a migracao deve ser consistente em codigo, tests, docs e lessons; -- `TileLayer` e derivados nao entram automaticamente no rename, porque ja pertencem a outra fronteira de dominio e precisam de triagem separada; -- a discussao deve explicitar quais superfícies mudam juntas para impedir vocabulario hibrido sem reabrir a arquitetura grafica. - -## Perguntas em Aberto - -- Confirmar a superficie exata do rename para evitar misturar banco grafico concreto com conceitos de layer/mapa. - -## Discussao - -### Direcao fechada ate aqui - -1. **Natureza da mudanca** - A mudanca e `rename-only`. Nao ha intencao de alterar formato, algoritmo, layout em memoria ou comportamento. - -2. **Contrato externo** - `BankType::TILES` nao deve permanecer. O contrato deve migrar para `BankType::GLYPH`. - -3. **Escopo documental** - A migracao deve ser completa, inclusive em documentacao e lessons historicas. - -4. **Colisao semantica** - `glyph` nao colide com outro artefato canonico relevante nesta etapa. - -### Fronteira importante - -Nem todo `Tile*` deve migrar automaticamente para `Glyph*`. - -Existe uma fronteira entre: - -- nomes que descrevem o banco grafico concreto e seu circuito de carga/uso; e -- nomes que ja descrevem dominio de layer, mapa, grade ou composicao. - -Por isso, a proxima resposta que falta precisa ser dada por superficie concreta, e nao por regra global simplista. - -### Lista para voto `sim` / `nao` - -Responda `sim` ou `nao` para cada grupo abaixo. - -1. **Banco concreto e modulo base** - Inclui `TileBank`, `tile_bank.rs`, comentarios e docs especificos desse tipo. - -2. **Enum e contrato de asset** - Inclui `BankType::TILES -> BankType::GLYPH`, mensagens de erro, metadata helpers e referencias equivalentes no path de assets. - -3. **Pools e interfaces de memoria** - Inclui `TileBankPoolAccess`, `TileBankPoolInstaller`, `tile_bank_slot`, `install_tile_bank`, campos internos como `tile_bank_pool`. - -4. **Asset manager e loader path** - Inclui funcoes como `decode_tile_bank_*`, `perform_load_tile_bank`, variaveis locais, testes e mensagens associadas ao load do banco grafico. - -5. **Renderer e hardware references ao banco** - Inclui usos em `gfx.rs` e `hardware.rs` que referenciam o banco concreto, por exemplo campos `tile_banks`, docs sobre VRAM `TileBanks`, parametros `bank: &TileBank`. - -6. **Modulo e tipos de layer/mapa** - Inclui `tile_layer.rs`, `TileMap`, `TileLayer`, `ScrollableTileLayer`, `HudTileLayer`. - Este grupo e o mais sensivel porque pode nao ser rename de banco concreto, e sim outro dominio. - -7. **TileSize** - Inclui `TileSize` e referencias ao tamanho de tile como unidade geometrica. - Este grupo pode continuar como `TileSize` mesmo se o banco virar `GlyphBank`. - -8. **Strings, fixtures e test names** - Inclui nomes de testes, helper names, snapshots, mensagens e dados de teste que ainda falam em tile bank. - -9. **Lessons e docs historicas** - Inclui lessons ja publicadas e material de discussao/mental model que hoje falam em `TileBank`. - -### Votos registrados - -1. **Banco concreto e modulo base**: `sim` - `TileBank`, `tile_bank.rs`, modulo base e documentacao associada entram na migracao. - -2. **Enum e contrato de asset**: `sim` - `BankType::TILES` e o contrato textual equivalente entram na migracao para `GLYPH`. - -3. **Pools e interfaces de memoria**: `sim` - `TileBankPool*`, `tile_bank_slot` e derivados entram na migracao. - -4. **Asset manager e loader path**: `sim` - Helpers, decode path, mensagens e testes associados ao banco grafico concreto entram na migracao. - -5. **Renderer e hardware references ao banco**: `sim` - Referencias ao banco concreto em renderer e hardware entram na migracao. - -6. **Modulo e tipos de layer/mapa**: `nao` - `TileLayer`, `TileMap`, `ScrollableTileLayer` e `HudTileLayer` ficam fora desta rodada. - -7. **TileSize**: `nao` - `TileSize` permanece como conceito geometrico e nao entra automaticamente no rename. - -8. **Strings, fixtures e test names**: `sim` - Helpers, fixtures, mensagens e nomes de testes entram na migracao para evitar residuos do vocabulario antigo. - -9. **Lessons e docs historicas**: `sim` - A migracao documental e historica entra no escopo, seguindo a mesma fronteira desta agenda: renomear o banco grafico concreto sem arrastar automaticamente o dominio de layer/mapa. - -## Resolucao Provisoria - -Ha consenso provisoriamente estabelecido sobre os seguintes pontos: - -- a mudanca e `rename-only`; -- o vocabulario canonico do banco grafico concreto passa de `TileBank` para `GlyphBank`; -- `BankType::TILES` deve migrar para `BankType::GLYPH`; -- pools, contrato de asset, loader path, renderer, hardware references, fixtures e nomes de teste entram na migracao; -- `TileLayer`/`TileMap` e derivados ficam fora desta rodada; -- `TileSize` fica fora desta rodada; -- docs e lessons historicas entram no escopo, respeitando essa mesma fronteira. - -O principal ponto restante para encerrar a agenda e confirmar se essa resolucao ja e suficiente para virar decisao normativa, ou se ainda falta detalhar como a migracao documental deve tratar referencias historicas em texto corrido quando `tile` continuar correto para layer/mapa. - -## Resolucao - -A agenda fica encerrada com a seguinte orientacao: - -- a mudanca e `rename-only`; -- o banco grafico concreto passa a se chamar `GlyphBank`; -- `BankType::TILES` passa a `BankType::GLYPH`; -- pools, contrato de asset, loader path, renderer, hardware references, fixtures, nomes de teste, docs e lessons entram na migracao; -- `TileLayer`, `TileMap` e derivados ficam fora; -- `TileSize` fica fora; -- docs e lessons antigas devem migrar referencias ao banco grafico concreto para `GlyphBank`, mantendo `tile` quando o assunto for layer, mapa, grade ou geometria. - -## Criterio para Encerrar - -Esta agenda pode ser encerrada quando houver consenso escrito sobre: - -- se a mudanca e rename-only ou nao; -- qual vocabulario passa a ser canonico; -- quais contratos externos entram no rename; -- quais grupos concretos entram ou nao na migracao; -- como evitar que `TileLayer`/`TileMap` sejam arrastados automaticamente sem decisao propria. diff --git a/discussion/workflow/decisions/DEC-0006-glyph-bank-domain-naming-contract.md b/discussion/workflow/decisions/DEC-0006-glyph-bank-domain-naming-contract.md deleted file mode 100644 index 65a66ebf..00000000 --- a/discussion/workflow/decisions/DEC-0006-glyph-bank-domain-naming-contract.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -id: DEC-0006 -ticket: tile-bank-vs-glyph-bank-domain-naming -title: Glyph Bank Domain Naming Contract -status: accepted -created: 2026-04-10 -accepted: 2026-04-10 -agenda: AGD-0020 -plans: [PLN-0005] -tags: [gfx, runtime, naming, domain-model] ---- - -## Status - -Accepted on 2026-04-10. - -## Contexto - -The runtime currently uses `TileBank` and related names for the concrete graphical bank consumed by the asset pipeline and renderer. At the same time, the project wants `Tile` to remain available as a broader domain concept for grid, layer, map, and geometric tile semantics. - -This decision closes the naming contract by separating: - -- the concrete graphical bank artifact; -- the logical tile/layer/map domain; -- the migration boundary for code, tests, docs, and historical lessons. - -The change is explicitly rename-only. It does not alter format, runtime behavior, memory layout, payload structure, or renderer algorithms. - -## Decisao - -1. The concrete graphical bank currently named `TileBank` MUST be renamed to `GlyphBank`. -2. This rename MUST be treated as nomenclature-only. It MUST NOT change payload format, runtime behavior, memory layout, codec behavior, metadata structure, or rendering semantics. -3. `BankType::TILES` MUST be renamed to `BankType::GLYPH`. -4. The asset contract and runtime code that refer to the concrete graphical bank MUST migrate to `Glyph*` naming consistently. -5. The following groups MUST be included in the rename: - - concrete bank type and module; - - asset contract and asset-facing terminology; - - memory pools and bank installation/access interfaces; - - asset manager decode/load path; - - renderer and hardware references to the concrete bank; - - fixtures, test names, helper names, and user-facing/runtime-facing strings for the concrete bank; - - documentation and historical lessons, subject to the editorial boundary below. -6. The following groups MUST NOT be renamed as part of this decision: - - `TileLayer`, `TileMap`, `ScrollableTileLayer`, `HudTileLayer`, and related layer/map structures; - - `TileSize` and geometric tile-size concepts. -7. Documentation and historical lessons MUST be updated with the following editorial rule: - - references to the concrete graphical bank artifact MUST migrate to `GlyphBank` and equivalent `Glyph*` naming; - - references to layer, map, grid, or geometric tile concepts MUST remain `tile` when that is the correct domain meaning. -8. The project MUST NOT adopt a partial mixed vocabulary where the same concrete bank artifact is simultaneously described as both `TileBank` and `GlyphBank` in active code or maintained documentation. -9. `glyph` is treated as a new canonical artifact name for this stage and does not conflict with an existing canonical artifact that would block adoption. - -## Rationale - -- The current name overloads `tile` across both logical map/layer concepts and the concrete graphical bank artifact. -- Renaming only the concrete bank artifact improves domain clarity without reopening graphics architecture. -- Excluding `TileLayer`, `TileMap`, and `TileSize` preserves established semantics where `tile` still means the logical or geometric concept. -- A full migration across code, tests, docs, and lessons avoids the unstable mixed-language state that tends to follow partial renames. - -## Invariantes / Contrato - -- `GlyphBank` is the canonical name for the concrete graphical bank artifact. -- `tile` remains canonical for layer/map/geometric concepts unless a later decision explicitly changes that. -- `BankType::GLYPH` is the canonical asset-bank enum variant for the concrete graphical bank. -- This decision is a rename boundary, not a behavior-change boundary. -- Documentation must follow artifact meaning, not mechanical string replacement. - -## Impactos - -- `prometeu-hal` will need type/module/enum renames for the concrete graphical bank path. -- `prometeu-drivers` will need renderer, memory-pool, hardware, and asset-manager naming migration for bank-specific references. -- Tests, fixtures, and helper names need coordinated updates to avoid mixed terminology. -- Docs and lessons need targeted rewriting rather than blind search-and-replace, because `tile` remains correct in map/layer contexts. - -## Referencias - -- AGD-0020: Tile Bank vs Glyph Bank Domain Naming -- LSN-0022: Tilemap Empty Cell Convergence -- `crates/console/prometeu-hal/src/tile_bank.rs` -- `crates/console/prometeu-hal/src/tile_layer.rs` -- `crates/console/prometeu-drivers/src/gfx.rs` -- `crates/console/prometeu-drivers/src/memory_banks.rs` -- `crates/console/prometeu-drivers/src/asset.rs` - -## Propagacao Necessaria - -- Write an execution plan before code and documentation migration. -- Rename the concrete bank surface consistently across runtime crates. -- Preserve `tile` naming in layer/map/geometric surfaces excluded by this decision. -- Update docs and lessons according to artifact meaning, not blanket replacement. - -## Revision Log - -- 2026-04-10: Initial accepted decision from AGD-0020. diff --git a/discussion/workflow/plans/PLN-0005-glyph-bank-domain-naming-execution.md b/discussion/workflow/plans/PLN-0005-glyph-bank-domain-naming-execution.md deleted file mode 100644 index 77465faf..00000000 --- a/discussion/workflow/plans/PLN-0005-glyph-bank-domain-naming-execution.md +++ /dev/null @@ -1,161 +0,0 @@ ---- -id: PLN-0005 -ticket: tile-bank-vs-glyph-bank-domain-naming -title: Glyph Bank Domain Naming Execution -status: accepted -created: 2026-04-10 -completed: -tags: [gfx, runtime, naming, domain-model] ---- - -## Briefing - -Implement DEC-0006 by renaming the concrete graphical bank artifact from `TileBank` to `GlyphBank` across runtime code, tests, documentation, and historical lessons, while preserving behavior and keeping the excluded tile-domain surfaces untouched. This is a rename-only migration. It must not alter payload format, rendering semantics, codec behavior, metadata structure, or memory layout. - -## Decisions de Origem - -- DEC-0006: Glyph Bank Domain Naming Contract - -## Alvo - -Land a consistent runtime-and-docs migration that: - -- renames the concrete bank artifact and its module/type surface to `GlyphBank`; -- renames `BankType::TILES` to `BankType::GLYPH`; -- renames pool and loader/runtime bank-specific APIs accordingly; -- preserves `TileLayer`, `TileMap`, `ScrollableTileLayer`, `HudTileLayer`, and `TileSize`; -- rewrites maintained docs and lessons according to artifact meaning rather than blanket token replacement. - -## Escopo - -- `prometeu-hal` concrete bank type/module/enum renames. -- `prometeu-drivers` renderer, memory-bank, hardware, and asset-manager bank-specific renames. -- `prometeu-system` callsites and dispatch paths that refer to the asset-bank enum variant. -- Tests, helpers, fixtures, and strings that still describe the concrete bank as `TileBank`. -- Discussion lessons and maintained discussion artifacts that refer to the concrete graphical bank artifact. - -## Fora de Escopo - -- Any format, algorithm, codec, metadata, or layout change. -- Renaming `TileLayer`, `TileMap`, `ScrollableTileLayer`, `HudTileLayer`, or related layer/map structures. -- Renaming `TileSize` or geometric tile-size concepts. -- Redesigning tile/glyph semantics beyond the naming boundary established by DEC-0006. - -## Plano de Execucao - -### Step 1 - Rename the HAL concrete bank surface - -**What:** -Rename the concrete bank module and type surface in `prometeu-hal` from `TileBank` to `GlyphBank`. - -**How:** -Rename `tile_bank.rs` to `glyph_bank.rs`, update `lib.rs` exports/import paths, rename `TileBank` to `GlyphBank`, and rename concrete-bank-specific constants if they are artifact-specific rather than geometric. Keep `TileSize` unchanged even if it continues to live near the bank implementation or must be re-exported from the renamed module in a compatibility-preserving layout inside this refactor. - -**File(s):** -- `crates/console/prometeu-hal/src/tile_bank.rs` -- `crates/console/prometeu-hal/src/lib.rs` -- any direct imports of `prometeu_hal::tile_bank::*` in runtime crates - -### Step 2 - Rename the asset-bank contract - -**What:** -Migrate the asset-bank enum and related asset-facing terminology from `TILES` to `GLYPH`. - -**How:** -Rename `BankType::TILES` to `BankType::GLYPH`, update any JSON/serde expectations and validation paths, and rename asset-facing metadata helpers only where they describe the concrete bank artifact rather than the geometric tile model. Preserve behavior and fail-fast semantics unchanged. - -**File(s):** -- `crates/console/prometeu-hal/src/asset.rs` -- `crates/console/prometeu-hal/src/cartridge_loader.rs` -- `crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs` -- `crates/console/prometeu-system/src/virtual_machine_runtime/tick.rs` -- `crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs` - -### Step 3 - Rename memory pools and concrete-bank runtime APIs - -**What:** -Rename bank-specific pool interfaces and storage paths that refer to the concrete graphical bank. - -**How:** -Rename `TileBankPoolAccess`, `TileBankPoolInstaller`, `tile_bank_slot`, `install_tile_bank`, `tile_bank_pool`, and related fields/traits/vars to `Glyph*` equivalents. Keep semantics and slot behavior unchanged. - -**File(s):** -- `crates/console/prometeu-drivers/src/memory_banks.rs` -- `crates/console/prometeu-drivers/src/hardware.rs` -- `crates/console/prometeu-drivers/src/gfx.rs` -- `crates/console/prometeu-drivers/src/asset.rs` - -### Step 4 - Rename concrete-bank decode/load path and tests - -**What:** -Update the asset manager and concrete-bank decode path to the new artifact name. - -**How:** -Rename functions such as `decode_tile_bank_*`, `perform_load_tile_bank`, and related test helpers/messages to `GlyphBank` naming. Keep payload interpretation, palette handling, and runtime materialization identical to current behavior. Do not rename `TileSize`. - -**File(s):** -- `crates/console/prometeu-drivers/src/asset.rs` -- affected tests in `crates/console/prometeu-drivers/src/asset.rs` -- any helper or stress/test utility strings such as `missing_tile_bank` - -### Step 5 - Apply renderer and hardware editorial cleanup - -**What:** -Rename only the references that describe the concrete bank artifact inside renderer and hardware documentation/comments. - -**How:** -Update comments, docs, field names, and user-facing/runtime-facing strings so that references to the bank artifact become `GlyphBank`/`glyph bank`, while preserving `tile` terminology for layers, maps, and geometric concepts. Review `gfx.rs` carefully because it contains both domains. - -**File(s):** -- `crates/console/prometeu-drivers/src/gfx.rs` -- `crates/console/prometeu-drivers/src/hardware.rs` -- any crate-level docs/comments on the bank artifact path - -### Step 6 - Rewrite maintained lessons and discussion docs by meaning - -**What:** -Migrate maintained lessons and relevant discussion text to the new canonical artifact name. - -**How:** -Update published lessons and maintained discussion artifacts so that references to the concrete graphical bank become `GlyphBank`, but references to tile layers, tilemaps, grid semantics, or tile geometry remain `tile`. Use manual review, not blanket replacement. - -**File(s):** -- `discussion/lessons/DSC-0016-tilemap-empty-cell-semantics/LSN-0022-tilemap-empty-cell-convergence.md` -- `discussion/lessons/DSC-0001-runtime-learn-legacy-import/LSN-0011-mental-model-gfx.md` -- `discussion/lessons/DSC-0001-runtime-learn-legacy-import/LSN-0009-mental-model-asset-management.md` -- any maintained agenda/decision/plan artifacts still intentionally retained and referencing the concrete bank artifact - -## Criterios de Aceite - -- The canonical concrete bank type/module name in runtime code is `GlyphBank`. -- The canonical asset-bank enum variant is `BankType::GLYPH`. -- Pool interfaces and concrete-bank runtime helpers use `Glyph*` naming consistently. -- `TileLayer`, `TileMap`, `ScrollableTileLayer`, `HudTileLayer`, and `TileSize` remain unchanged. -- No behavior, payload format, or renderer semantics change as part of this migration. -- Tests, helper names, and runtime-facing strings do not keep active mixed `TileBank`/`GlyphBank` naming for the same artifact. -- Maintained lessons and docs use `GlyphBank` for the concrete bank artifact while preserving `tile` where it refers to layer/map/geometric concepts. - -## Tests / Validacao - -### Unit Tests - -- Update and run unit tests that directly construct or decode the concrete graphical bank path. -- Confirm renamed bank/pool/helper symbols compile without compatibility shims. - -### Integration Tests - -- Run `prometeu-hal`, `prometeu-drivers`, and `prometeu-system` test targets affected by the rename. -- Verify asset loading still succeeds for the renamed `BankType::GLYPH` path. - -### Manual Verification - -- Review representative docs and lessons to confirm `GlyphBank` only replaces the concrete bank artifact meaning. -- Review `gfx.rs` and `tile_layer.rs` boundaries to confirm tile-domain structures were not accidentally renamed. -- Search the repository for residual `TileBank` references and classify any remaining occurrence as either acceptable historical residue outside scope or a migration miss. - -## Riscos - -- The repository uses `tile` in two different domains, so blind search-and-replace is likely to over-rename excluded surfaces. -- Renaming the enum variant from `TILES` to `GLYPH` touches serialization contracts and can break test fixtures or external packer assumptions if not coordinated. -- Moving `tile_bank.rs` to `glyph_bank.rs` may cause broad import churn across crates and tests. -- Historical lessons can easily drift into editorial inconsistency if the rewrite is done mechanically instead of semantically. -- 2.47.2 From 35c138fd86bd97dec1a40b7299c9133766d67ba6 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Fri, 10 Apr 2026 06:19:12 +0100 Subject: [PATCH 10/10] polish metadata definitions --- crates/console/prometeu-drivers/src/asset.rs | 22 ++++++++++---------- crates/console/prometeu-hal/src/asset.rs | 8 +++---- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/crates/console/prometeu-drivers/src/asset.rs b/crates/console/prometeu-drivers/src/asset.rs index 88558910..468f6bd6 100644 --- a/crates/console/prometeu-drivers/src/asset.rs +++ b/crates/console/prometeu-drivers/src/asset.rs @@ -182,7 +182,7 @@ impl AssetManager { fn decode_glyph_bank_layout( entry: &AssetEntry, ) -> Result<(TileSize, usize, usize, usize), String> { - let meta = entry.metadata_as_glyphs()?; + let meta = entry.metadata_as_glyph_bank()?; let tile_size = match meta.tile_size { 8 => TileSize::Size8, @@ -624,7 +624,7 @@ impl AssetManager { entry: &AssetEntry, buffer: &[u8], ) -> Result { - let meta = entry.metadata_as_sounds()?; + let meta = entry.metadata_as_sound_bank()?; let sample_rate = meta.sample_rate; let mut data = Vec::with_capacity(buffer.len() / 2); @@ -913,7 +913,7 @@ mod tests { #[test] fn test_decode_glyph_bank_unpacks_packed_pixels_and_reads_palette_colors() { - let entry = test_glyph_asset_entry("tiles", 2, 2); + let entry = test_glyph_asset_entry("glyphs", 2, 2); let mut data = vec![0x10, 0x23]; data.extend_from_slice(&[0u8; GLYPH_BANK_PALETTE_BYTES_V1]); data[2] = 0x34; @@ -928,7 +928,7 @@ mod tests { #[test] fn test_decode_glyph_bank_rejects_short_packed_buffer() { - let entry = test_glyph_asset_entry("tiles", 16, 16); + let entry = test_glyph_asset_entry("glyphs", 16, 16); let data = vec![0u8; expected_glyph_payload_size(16, 16) - 1]; let err = match AssetManager::decode_glyph_bank_from_buffer(&entry, &data) { @@ -941,7 +941,7 @@ mod tests { #[test] fn test_decode_glyph_bank_requires_palette_count_64() { - let mut entry = test_glyph_asset_entry("tiles", 16, 16); + let mut entry = test_glyph_asset_entry("glyphs", 16, 16); entry.metadata["palette_count"] = serde_json::json!(32); let err = @@ -955,14 +955,14 @@ mod tests { #[test] fn test_op_mode_for_glyphs_none_stages_in_memory() { - let entry = test_glyph_asset_entry("tiles", 16, 16); + let entry = test_glyph_asset_entry("glyphs", 16, 16); assert_eq!(AssetManager::op_mode_for(&entry), Ok(AssetOpMode::StageInMemory)); } #[test] fn test_op_mode_for_glyphs_none_uses_typed_codec() { - let entry = test_glyph_asset_entry("tiles", 16, 16); + let entry = test_glyph_asset_entry("glyphs", 16, 16); assert_eq!(AssetManager::op_mode_for(&entry), Ok(AssetOpMode::StageInMemory)); } @@ -992,7 +992,7 @@ mod tests { let sound_installer = Arc::clone(&banks) as Arc; let data = test_glyph_asset_data(); - let asset_entry = test_glyph_asset_entry("test_tiles", 16, 16); + let asset_entry = test_glyph_asset_entry("test_glyphs", 16, 16); let am = AssetManager::new( vec![asset_entry], @@ -1030,7 +1030,7 @@ mod tests { let sound_installer = Arc::clone(&banks) as Arc; let data = test_glyph_asset_data(); - let asset_entry = test_glyph_asset_entry("test_tiles", 16, 16); + let asset_entry = test_glyph_asset_entry("test_glyphs", 16, 16); let am = AssetManager::new( vec![asset_entry], @@ -1160,7 +1160,7 @@ mod tests { let sound_installer = Arc::clone(&banks) as Arc; let data = test_glyph_asset_data(); let am = AssetManager::new( - vec![test_glyph_asset_entry("test_tiles", 16, 16)], + vec![test_glyph_asset_entry("test_glyphs", 16, 16)], AssetsPayloadSource::from_bytes(data), gfx_installer, sound_installer, @@ -1189,7 +1189,7 @@ mod tests { let sound_installer = Arc::clone(&banks) as Arc; let data = test_glyph_asset_data(); let am = AssetManager::new( - vec![test_glyph_asset_entry("test_tiles", 16, 16)], + vec![test_glyph_asset_entry("test_glyphs", 16, 16)], AssetsPayloadSource::from_bytes(data), gfx_installer, sound_installer, diff --git a/crates/console/prometeu-hal/src/asset.rs b/crates/console/prometeu-hal/src/asset.rs index 443595f3..e67b49c0 100644 --- a/crates/console/prometeu-hal/src/asset.rs +++ b/crates/console/prometeu-hal/src/asset.rs @@ -31,7 +31,7 @@ pub struct AssetEntry { } #[derive(Debug, Clone, Deserialize, Serialize)] -pub struct GlyphsMetadata { +pub struct GlyphBankMetadata { pub tile_size: u32, pub width: u32, pub height: u32, @@ -40,13 +40,13 @@ pub struct GlyphsMetadata { } #[derive(Debug, Clone, Deserialize, Serialize)] -pub struct SoundsMetadata { +pub struct SoundBankMetadata { pub sample_rate: u32, pub channels: u32, } impl AssetEntry { - pub fn metadata_as_glyphs(&self) -> Result { + pub fn metadata_as_glyph_bank(&self) -> Result { if self.bank_type != BankType::GLYPH { return Err(format!( "Asset {} is not a GLYPH bank (type: {:?})", @@ -57,7 +57,7 @@ impl AssetEntry { .map_err(|e| format!("Invalid GLYPH metadata for asset {}: {}", self.asset_id, e)) } - pub fn metadata_as_sounds(&self) -> Result { + pub fn metadata_as_sound_bank(&self) -> Result { if self.bank_type != BankType::SOUNDS { return Err(format!( "Asset {} is not a SOUNDS bank (type: {:?})", -- 2.47.2