diff --git a/crates/console/prometeu-hal/src/debugger_protocol.rs b/crates/console/prometeu-hal/src/debugger_protocol.rs index 033c0cc2..cc6e8950 100644 --- a/crates/console/prometeu-hal/src/debugger_protocol.rs +++ b/crates/console/prometeu-hal/src/debugger_protocol.rs @@ -68,6 +68,8 @@ pub enum DebugEvent { glyph_slots_total: u32, sound_slots_used: u32, sound_slots_total: u32, + scene_slots_used: u32, + scene_slots_total: u32, }, #[serde(rename = "fault")] Fault { @@ -99,6 +101,8 @@ mod tests { glyph_slots_total: 16, sound_slots_used: 2, sound_slots_total: 16, + scene_slots_used: 3, + scene_slots_total: 16, }; let json = serde_json::to_string(&event).unwrap(); diff --git a/crates/console/prometeu-hal/src/syscalls.rs b/crates/console/prometeu-hal/src/syscalls.rs index 3e9b269a..d63173a1 100644 --- a/crates/console/prometeu-hal/src/syscalls.rs +++ b/crates/console/prometeu-hal/src/syscalls.rs @@ -64,7 +64,6 @@ pub enum Syscall { AssetCommit = 0x6003, AssetCancel = 0x6004, BankInfo = 0x6101, - BankSlotInfo = 0x6102, } /// Canonical metadata describing a syscall using the unified slot-based ABI. diff --git a/crates/console/prometeu-hal/src/syscalls/domains/bank.rs b/crates/console/prometeu-hal/src/syscalls/domains/bank.rs index 87a7ef1b..2fda20d9 100644 --- a/crates/console/prometeu-hal/src/syscalls/domains/bank.rs +++ b/crates/console/prometeu-hal/src/syscalls/domains/bank.rs @@ -1,12 +1,7 @@ use crate::syscalls::{Syscall, SyscallRegistryEntry, caps}; -pub(crate) const ENTRIES: &[SyscallRegistryEntry] = &[ - SyscallRegistryEntry::builder(Syscall::BankInfo, "bank", "info") +pub(crate) const ENTRIES: &[SyscallRegistryEntry] = + &[SyscallRegistryEntry::builder(Syscall::BankInfo, "bank", "info") .args(1) - .rets(1) - .caps(caps::BANK), - SyscallRegistryEntry::builder(Syscall::BankSlotInfo, "bank", "slot_info") - .args(2) - .rets(1) - .caps(caps::BANK), -]; + .rets(2) + .caps(caps::BANK)]; diff --git a/crates/console/prometeu-hal/src/syscalls/registry.rs b/crates/console/prometeu-hal/src/syscalls/registry.rs index b8007c71..7b353b1a 100644 --- a/crates/console/prometeu-hal/src/syscalls/registry.rs +++ b/crates/console/prometeu-hal/src/syscalls/registry.rs @@ -48,7 +48,6 @@ impl Syscall { 0x6003 => Some(Self::AssetCommit), 0x6004 => Some(Self::AssetCancel), 0x6101 => Some(Self::BankInfo), - 0x6102 => Some(Self::BankSlotInfo), _ => None, } } @@ -99,7 +98,6 @@ impl Syscall { Self::AssetCommit => "AssetCommit", Self::AssetCancel => "AssetCancel", Self::BankInfo => "BankInfo", - Self::BankSlotInfo => "BankSlotInfo", } } } diff --git a/crates/console/prometeu-hal/src/syscalls/tests.rs b/crates/console/prometeu-hal/src/syscalls/tests.rs index 3129d813..362a6687 100644 --- a/crates/console/prometeu-hal/src/syscalls/tests.rs +++ b/crates/console/prometeu-hal/src/syscalls/tests.rs @@ -249,6 +249,22 @@ fn status_first_syscall_signatures_are_pinned() { let asset_cancel = meta_for(Syscall::AssetCancel); assert_eq!(asset_cancel.arg_slots, 1); assert_eq!(asset_cancel.ret_slots, 1); + + let bank_info = meta_for(Syscall::BankInfo); + assert_eq!(bank_info.arg_slots, 1); + assert_eq!(bank_info.ret_slots, 2); +} + +#[test] +fn resolver_rejects_removed_bank_slot_info_identity() { + assert!(resolve_syscall("bank", "slot_info", 1).is_none()); + + let requested = [SyscallIdentity { module: "bank", name: "slot_info", version: 1 }]; + let err = resolve_program_syscalls(&requested, caps::ALL).unwrap_err(); + assert_eq!( + err, + LoadError::UnknownSyscall { module: "bank".into(), name: "slot_info".into(), version: 1 } + ); } #[test] diff --git a/crates/console/prometeu-hal/src/telemetry.rs b/crates/console/prometeu-hal/src/telemetry.rs index 0965a95d..8dd9ea75 100644 --- a/crates/console/prometeu-hal/src/telemetry.rs +++ b/crates/console/prometeu-hal/src/telemetry.rs @@ -18,6 +18,8 @@ pub struct TelemetryFrame { pub glyph_slots_total: u32, pub sound_slots_used: u32, pub sound_slots_total: u32, + pub scene_slots_used: u32, + pub scene_slots_total: u32, // RAM (Heap) pub heap_used_bytes: usize, @@ -45,6 +47,8 @@ pub struct AtomicTelemetry { pub glyph_slots_total: AtomicU32, pub sound_slots_used: AtomicU32, pub sound_slots_total: AtomicU32, + pub scene_slots_used: AtomicU32, + pub scene_slots_total: AtomicU32, // RAM (Heap) pub heap_used_bytes: AtomicUsize, @@ -75,6 +79,8 @@ impl AtomicTelemetry { glyph_slots_total: self.glyph_slots_total.load(Ordering::Relaxed), sound_slots_used: self.sound_slots_used.load(Ordering::Relaxed), sound_slots_total: self.sound_slots_total.load(Ordering::Relaxed), + scene_slots_used: self.scene_slots_used.load(Ordering::Relaxed), + scene_slots_total: self.scene_slots_total.load(Ordering::Relaxed), heap_used_bytes: self.heap_used_bytes.load(Ordering::Relaxed), heap_max_bytes: self.heap_max_bytes.load(Ordering::Relaxed), logs_count: self.logs_count.load(Ordering::Relaxed), @@ -93,6 +99,8 @@ impl AtomicTelemetry { self.glyph_slots_total.store(0, Ordering::Relaxed); self.sound_slots_used.store(0, Ordering::Relaxed); self.sound_slots_total.store(0, Ordering::Relaxed); + self.scene_slots_used.store(0, Ordering::Relaxed); + self.scene_slots_total.store(0, Ordering::Relaxed); self.heap_used_bytes.store(0, Ordering::Relaxed); self.vm_steps.store(0, Ordering::Relaxed); self.logs_count.store(0, Ordering::Relaxed); 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 2a0fb39b..fdf0c963 100644 --- a/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs +++ b/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs @@ -567,19 +567,8 @@ impl NativeInterface for VirtualMachineRuntime { used_slots: 0, total_slots: 0, }); - let json = serde_json::to_string(&telemetry).unwrap_or_default(); - ret.push_string(json); - Ok(()) - } - Syscall::BankSlotInfo => { - let asset_type = match expect_int(args, 0)? as u32 { - 0 => BankType::GLYPH, - 1 => BankType::SOUNDS, - _ => return Err(VmFault::Trap(TRAP_TYPE, "Invalid asset type".to_string())), - }; - let slot = SlotRef { asset_type, index: expect_int(args, 1)? as usize }; - let json = serde_json::to_string(&hw.assets().slot_info(slot)).unwrap_or_default(); - ret.push_string(json); + ret.push_int(telemetry.used_slots as i64); + ret.push_int(telemetry.total_slots as i64); Ok(()) } } 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 6ca08d2b..df491343 100644 --- a/crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs +++ b/crates/console/prometeu-system/src/virtual_machine_runtime/tests.rs @@ -937,6 +937,60 @@ fn tick_asset_status_unknown_handle_returns_status_not_crash() { assert_eq!(vm.operand_stack_top(1), vec![Value::Int64(LoadStatus::UnknownHandle as i64)]); } +#[test] +fn tick_bank_info_returns_slot_summary_not_json() { + let mut runtime = VirtualMachineRuntime::new(None); + let mut vm = VirtualMachine::default(); + let mut hardware = Hardware::new(); + let signals = InputSignals::default(); + let asset_data = test_glyph_asset_data(); + hardware.assets.initialize_for_cartridge( + 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), + ); + let code = assemble("PUSH_I32 0\nHOSTCALL 0\nHALT").expect("assemble"); + let program = serialized_single_function_module( + code, + vec![SyscallDecl { + module: "bank".into(), + name: "info".into(), + version: 1, + arg_slots: 1, + ret_slots: 2, + }], + ); + let cartridge = cartridge_with_program(program, caps::BANK); + + runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize"); + let report = runtime.tick(&mut vm, &signals, &mut hardware); + assert!(report.is_none(), "bank summary must not crash"); + assert!(vm.is_halted()); + assert_eq!(vm.operand_stack_top(2), vec![Value::Int64(16), Value::Int64(1)]); +} + +#[test] +fn initialize_vm_rejects_removed_bank_slot_info_syscall_identity() { + let mut runtime = VirtualMachineRuntime::new(None); + let mut vm = VirtualMachine::default(); + let code = assemble("PUSH_I32 0\nPUSH_I32 0\nHOSTCALL 0\nHALT").expect("assemble"); + let program = serialized_single_function_module( + code, + vec![SyscallDecl { + module: "bank".into(), + name: "slot_info".into(), + version: 1, + arg_slots: 2, + ret_slots: 1, + }], + ); + let cartridge = cartridge_with_program(program, caps::BANK); + + let res = runtime.initialize_vm(&mut vm, &cartridge); + + assert!(matches!(res, Err(CrashReport::VmInit { error: VmInitError::LoaderPatchFailed(_) }))); +} + #[test] fn tick_asset_commit_invalid_transition_returns_status_not_crash() { let mut runtime = VirtualMachineRuntime::new(None); 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 4a281f66..72c67b1d 100644 --- a/crates/console/prometeu-system/src/virtual_machine_runtime/tick.rs +++ b/crates/console/prometeu-system/src/virtual_machine_runtime/tick.rs @@ -7,7 +7,9 @@ use prometeu_vm::LogicalFrameEndingReason; use std::sync::atomic::Ordering; impl VirtualMachineRuntime { - fn bank_telemetry_summary(hw: &dyn HardwareBridge) -> (BankTelemetry, BankTelemetry) { + fn bank_telemetry_summary( + hw: &dyn HardwareBridge, + ) -> (BankTelemetry, BankTelemetry, BankTelemetry) { let telemetry = hw.assets().bank_telemetry(); let glyph = telemetry.iter().find(|entry| entry.bank_type == BankType::GLYPH).cloned().unwrap_or( @@ -17,8 +19,12 @@ impl VirtualMachineRuntime { telemetry.iter().find(|entry| entry.bank_type == BankType::SOUNDS).cloned().unwrap_or( BankTelemetry { bank_type: BankType::SOUNDS, used_slots: 0, total_slots: 0 }, ); + let scenes = + telemetry.iter().find(|entry| entry.bank_type == BankType::SCENE).cloned().unwrap_or( + BankTelemetry { bank_type: BankType::SCENE, used_slots: 0, total_slots: 0 }, + ); - (glyph, sounds) + (glyph, sounds, scenes) } pub fn debug_step_instruction( @@ -148,7 +154,7 @@ impl VirtualMachineRuntime { hw.render_frame(); // 1. Snapshot full telemetry at logical frame end - let (glyph_bank, sound_bank) = Self::bank_telemetry_summary(hw); + let (glyph_bank, sound_bank, scene_bank) = Self::bank_telemetry_summary(hw); self.atomic_telemetry .glyph_slots_used .store(glyph_bank.used_slots as u32, Ordering::Relaxed); @@ -161,6 +167,12 @@ impl VirtualMachineRuntime { self.atomic_telemetry .sound_slots_total .store(sound_bank.total_slots as u32, Ordering::Relaxed); + self.atomic_telemetry + .scene_slots_used + .store(scene_bank.used_slots as u32, Ordering::Relaxed); + self.atomic_telemetry + .scene_slots_total + .store(scene_bank.total_slots as u32, Ordering::Relaxed); self.atomic_telemetry .heap_used_bytes @@ -218,7 +230,7 @@ impl VirtualMachineRuntime { // 2. High-frequency telemetry update (only if inspection is active) if self.inspection_active { - let (glyph_bank, sound_bank) = Self::bank_telemetry_summary(hw); + let (glyph_bank, sound_bank, scene_bank) = Self::bank_telemetry_summary(hw); self.atomic_telemetry .glyph_slots_used .store(glyph_bank.used_slots as u32, Ordering::Relaxed); @@ -231,6 +243,12 @@ impl VirtualMachineRuntime { self.atomic_telemetry .sound_slots_total .store(sound_bank.total_slots as u32, Ordering::Relaxed); + self.atomic_telemetry + .scene_slots_used + .store(scene_bank.used_slots as u32, Ordering::Relaxed); + self.atomic_telemetry + .scene_slots_total + .store(scene_bank.total_slots as u32, Ordering::Relaxed); self.atomic_telemetry .heap_used_bytes diff --git a/crates/host/prometeu-host-desktop-winit/src/debugger.rs b/crates/host/prometeu-host-desktop-winit/src/debugger.rs index 2127459b..c698ec2e 100644 --- a/crates/host/prometeu-host-desktop-winit/src/debugger.rs +++ b/crates/host/prometeu-host-desktop-winit/src/debugger.rs @@ -2,6 +2,7 @@ use prometeu_drivers::hardware::Hardware; use prometeu_firmware::{BootTarget, Firmware}; use prometeu_hal::cartridge_loader::CartridgeLoader; use prometeu_hal::debugger_protocol::*; +use prometeu_hal::telemetry::{CertificationConfig, TelemetryFrame}; use prometeu_system::CrashReport; use std::io::{Read, Write}; use std::net::{TcpListener, TcpStream}; @@ -12,6 +13,8 @@ use std::net::{TcpListener, TcpStream}; /// Prometeu Debugger) to observe and control the execution of the virtual machine. /// /// Communication is based on JSONL (JSON lines) over TCP. +/// Detailed inspection and certification events are synthesized in the host +/// from telemetry snapshots and host/runtime integration state. pub struct HostDebugger { /// If true, the VM will not start execution until a 'start' command is received. pub waiting_for_start: bool, @@ -230,6 +233,72 @@ impl HostDebugger { } } + pub(crate) fn cert_event_from_snapshot( + tag: u16, + telemetry: TelemetryFrame, + cert_config: &CertificationConfig, + frame_index: u64, + ) -> Option { + let (rule, used, limit) = match tag { + 0xCA01 => ( + "cycles_budget".to_string(), + telemetry.cycles_used, + cert_config.cycles_budget_per_frame.unwrap_or(0), + ), + 0xCA02 => ( + "max_syscalls".to_string(), + telemetry.syscalls as u64, + cert_config.max_syscalls_per_frame.unwrap_or(0) as u64, + ), + 0xCA03 => ( + "max_host_cpu_us".to_string(), + telemetry.host_cpu_time_us, + cert_config.max_host_cpu_us_per_frame.unwrap_or(0), + ), + 0xCA04 => ( + "max_glyph_slots_used".to_string(), + telemetry.glyph_slots_used as u64, + cert_config.max_glyph_slots_used.unwrap_or(0) as u64, + ), + 0xCA05 => ( + "max_sound_slots_used".to_string(), + telemetry.sound_slots_used as u64, + cert_config.max_sound_slots_used.unwrap_or(0) as u64, + ), + 0xCA06 => ( + "max_heap_bytes".to_string(), + telemetry.heap_used_bytes as u64, + cert_config.max_heap_bytes.unwrap_or(0) as u64, + ), + 0xCA07 => ( + "max_logs_per_frame".to_string(), + telemetry.logs_count as u64, + cert_config.max_logs_per_frame.unwrap_or(0) as u64, + ), + _ => return None, + }; + + Some(DebugEvent::Cert { rule, used, limit, frame_index }) + } + + pub(crate) fn telemetry_event_from_snapshot(telemetry: TelemetryFrame) -> DebugEvent { + DebugEvent::Telemetry { + frame_index: telemetry.frame_index, + vm_steps: telemetry.vm_steps, + syscalls: telemetry.syscalls, + cycles: telemetry.cycles_used, + cycles_budget: telemetry.cycles_budget, + host_cpu_time_us: telemetry.host_cpu_time_us, + violations: telemetry.violations, + glyph_slots_used: telemetry.glyph_slots_used, + glyph_slots_total: telemetry.glyph_slots_total, + sound_slots_used: telemetry.sound_slots_used, + sound_slots_total: telemetry.sound_slots_total, + scene_slots_used: telemetry.scene_slots_used, + scene_slots_total: telemetry.scene_slots_total, + } + } + /// Scans the system for new information to push to the debugger client. fn stream_events(&mut self, firmware: &mut Firmware) { if let Some(report) = firmware.os.last_crash_report.as_ref() { @@ -251,46 +320,15 @@ impl HostDebugger { }); } - // Map Certification tags (0xCA01-0xCA03) to 'Cert' protocol events. - if event.tag >= 0xCA01 && event.tag <= 0xCA03 { - let tel = firmware.os.atomic_telemetry.snapshot(); - let cert_config = &firmware.os.certifier.config; - - let (rule, used, limit) = match event.tag { - 0xCA01 => ( - "cycles_budget".to_string(), - tel.cycles_used, - cert_config.cycles_budget_per_frame.unwrap_or(0), - ), - 0xCA02 => ( - "max_syscalls".to_string(), - tel.syscalls as u64, - cert_config.max_syscalls_per_frame.unwrap_or(0) as u64, - ), - 0xCA03 => ( - "max_host_cpu_us".to_string(), - tel.host_cpu_time_us, - cert_config.max_host_cpu_us_per_frame.unwrap_or(0), - ), - 0xCA04 => ( - "max_glyph_slots_used".to_string(), - tel.glyph_slots_used as u64, - cert_config.max_glyph_slots_used.unwrap_or(0) as u64, - ), - 0xCA05 => ( - "max_sound_slots_used".to_string(), - tel.sound_slots_used as u64, - cert_config.max_sound_slots_used.unwrap_or(0) as u64, - ), - _ => ("unknown".to_string(), 0, 0), - }; - - self.send_event(DebugEvent::Cert { - rule, - used, - limit, - frame_index: firmware.os.logical_frame_index, - }); + if (0xCA01..=0xCA07).contains(&event.tag) + && let Some(cert_event) = Self::cert_event_from_snapshot( + event.tag, + firmware.os.atomic_telemetry.snapshot(), + &firmware.os.certifier.config, + firmware.os.logical_frame_index, + ) + { + self.send_event(cert_event); } self.send_event(DebugEvent::Log { @@ -304,19 +342,7 @@ impl HostDebugger { let current_frame = firmware.os.logical_frame_index; if current_frame > self.last_telemetry_frame { let tel = firmware.os.atomic_telemetry.snapshot(); - self.send_event(DebugEvent::Telemetry { - frame_index: tel.frame_index, - vm_steps: tel.vm_steps, - syscalls: tel.syscalls, - cycles: tel.cycles_used, - cycles_budget: tel.cycles_budget, - host_cpu_time_us: tel.host_cpu_time_us, - violations: tel.violations, - glyph_slots_used: tel.glyph_slots_used, - glyph_slots_total: tel.glyph_slots_total, - sound_slots_used: tel.sound_slots_used, - sound_slots_total: tel.sound_slots_total, - }); + self.send_event(Self::telemetry_event_from_snapshot(tel)); self.last_telemetry_frame = current_frame; } } diff --git a/crates/host/prometeu-host-desktop-winit/src/overlay.rs b/crates/host/prometeu-host-desktop-winit/src/overlay.rs index f1d707a1..b55967d7 100644 --- a/crates/host/prometeu-host-desktop-winit/src/overlay.rs +++ b/crates/host/prometeu-host-desktop-winit/src/overlay.rs @@ -94,6 +94,7 @@ pub(crate) fn capture_snapshot(stats: &HostStats, firmware: &Firmware) -> Overla let heap_ratio = ratio(tel.heap_used_bytes as u64, heap_total_bytes as u64); let glyph_ratio = ratio(tel.glyph_slots_used as u64, tel.glyph_slots_total as u64); let sound_ratio = ratio(tel.sound_slots_used as u64, tel.sound_slots_total as u64); + let scene_ratio = ratio(tel.scene_slots_used as u64, tel.scene_slots_total as u64); OverlaySnapshot { rows: vec![ @@ -163,6 +164,12 @@ pub(crate) fn capture_snapshot(stats: &HostStats, firmware: &Firmware) -> Overla ratio: sound_ratio, warn: tel.sound_slots_total > 0 && tel.sound_slots_used >= tel.sound_slots_total, }, + OverlayBar { + label: "SCENE", + value: format!("{} / {} slots", tel.scene_slots_used, tel.scene_slots_total), + ratio: scene_ratio, + warn: tel.scene_slots_total > 0 && tel.scene_slots_used >= tel.scene_slots_total, + }, ], footer, } diff --git a/crates/host/prometeu-host-desktop-winit/src/runner.rs b/crates/host/prometeu-host-desktop-winit/src/runner.rs index cb1cd9cb..79f10c29 100644 --- a/crates/host/prometeu-host-desktop-winit/src/runner.rs +++ b/crates/host/prometeu-host-desktop-winit/src/runner.rs @@ -58,7 +58,8 @@ pub struct HostRunner { /// Performance metrics collector. stats: HostStats, - /// Remote debugger interface. + /// Remote debugger interface. It consumes host-owned telemetry and runtime state + /// directly instead of depending on guest-visible inspection syscalls. debugger: HostDebugger, /// Flag to enable/disable the technical telemetry display. @@ -225,7 +226,8 @@ impl ApplicationHandler for HostRunner { // 1. Process pending debug commands from the network. self.debugger.check_commands(&mut self.firmware, &mut self.hardware); - // Sync inspection mode state. + // Sync inspection mode state. This is host-owned overlay/debugger control, + // not a guest-visible debug ABI switch. self.firmware.os.inspection_active = self.overlay_enabled || self.debugger.stream.is_some(); // 2. Maintain filesystem connection if it was lost (e.g., directory removed). @@ -307,9 +309,123 @@ mod tests { use super::*; use prometeu_firmware::BootTarget; use prometeu_hal::debugger_protocol::DEVTOOLS_PROTOCOL_VERSION; + use prometeu_hal::telemetry::{CertificationConfig, TelemetryFrame}; use std::io::{Read, Write}; use std::net::TcpStream; + #[test] + fn host_debugger_maps_cert_events_from_host_owned_sources() { + let telemetry = TelemetryFrame { + glyph_slots_used: 3, + heap_used_bytes: 4096, + logs_count: 7, + ..Default::default() + }; + let config = CertificationConfig { + enabled: true, + max_glyph_slots_used: Some(2), + max_heap_bytes: Some(2048), + max_logs_per_frame: Some(5), + ..Default::default() + }; + + match HostDebugger::cert_event_from_snapshot(0xCA04, telemetry, &config, 12) { + Some(prometeu_hal::debugger_protocol::DebugEvent::Cert { + rule, + used, + limit, + frame_index, + }) => { + assert_eq!(rule, "max_glyph_slots_used"); + assert_eq!(used, 3); + assert_eq!(limit, 2); + assert_eq!(frame_index, 12); + } + other => panic!("unexpected glyph cert event: {:?}", other), + } + match HostDebugger::cert_event_from_snapshot(0xCA06, telemetry, &config, 12) { + Some(prometeu_hal::debugger_protocol::DebugEvent::Cert { + rule, + used, + limit, + frame_index, + }) => { + assert_eq!(rule, "max_heap_bytes"); + assert_eq!(used, 4096); + assert_eq!(limit, 2048); + assert_eq!(frame_index, 12); + } + other => panic!("unexpected heap cert event: {:?}", other), + } + match HostDebugger::cert_event_from_snapshot(0xCA07, telemetry, &config, 12) { + Some(prometeu_hal::debugger_protocol::DebugEvent::Cert { + rule, + used, + limit, + frame_index, + }) => { + assert_eq!(rule, "max_logs_per_frame"); + assert_eq!(used, 7); + assert_eq!(limit, 5); + assert_eq!(frame_index, 12); + } + other => panic!("unexpected logs cert event: {:?}", other), + } + } + + #[test] + fn host_debugger_maps_telemetry_from_atomic_snapshot_fields() { + let telemetry = TelemetryFrame { + frame_index: 8, + vm_steps: 123, + syscalls: 9, + cycles_used: 456, + cycles_budget: 789, + host_cpu_time_us: 321, + violations: 2, + glyph_slots_used: 1, + glyph_slots_total: 16, + sound_slots_used: 0, + sound_slots_total: 8, + scene_slots_used: 2, + scene_slots_total: 16, + ..Default::default() + }; + + match HostDebugger::telemetry_event_from_snapshot(telemetry) { + prometeu_hal::debugger_protocol::DebugEvent::Telemetry { + frame_index, + vm_steps, + syscalls, + cycles, + cycles_budget, + host_cpu_time_us, + violations, + glyph_slots_used, + glyph_slots_total, + sound_slots_used, + sound_slots_total, + scene_slots_used, + scene_slots_total, + } => { + assert_eq!(frame_index, 8); + assert_eq!(vm_steps, 123); + assert_eq!(syscalls, 9); + assert_eq!(cycles, 456); + assert_eq!(cycles_budget, 789); + assert_eq!(host_cpu_time_us, 321); + assert_eq!(violations, 2); + assert_eq!(glyph_slots_used, 1); + assert_eq!(glyph_slots_total, 16); + assert_eq!(sound_slots_used, 0); + assert_eq!(sound_slots_total, 8); + assert_eq!(scene_slots_used, 2); + assert_eq!(scene_slots_total, 16); + } + other => panic!("unexpected telemetry event: {:?}", other), + } + } + #[test] #[ignore = "requires localhost TCP bind/connect; run via `cargo test -p prometeu-host-desktop-winit --lib -- --ignored`"] fn test_debug_port_opens() { diff --git a/discussion/.backups/index.ndjson.20260419-085004.bak b/discussion/.backups/index.ndjson.20260419-085004.bak new file mode 100644 index 00000000..0b53408d --- /dev/null +++ b/discussion/.backups/index.ndjson.20260419-085004.bak @@ -0,0 +1,29 @@ +{"type":"meta","next_id":{"DSC":29,"AGD":29,"DEC":18,"PLN":33,"LSN":34,"CLSN":1}} +{"type":"discussion","id":"DSC-0023","status":"done","ticket":"perf-full-migration-to-atomic-telemetry","title":"Agenda - [PERF] Full Migration to Atomic Telemetry","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["perf","runtime","telemetry"],"agendas":[{"id":"AGD-0021","file":"workflow/agendas/AGD-0021-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"decisions":[{"id":"DEC-0008","file":"workflow/decisions/DEC-0008-full-migration-to-atomic-telemetry.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10"}],"plans":[{"id":"PLN-0007","file":"workflow/plans/PLN-0007-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"lessons":[{"id":"LSN-0028","file":"lessons/DSC-0023-perf-full-migration-to-atomic-telemetry/LSN-0028-converging-to-single-atomic-telemetry-source.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} +{"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":"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":[]} +{"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":"done","ticket":"perf-runtime-telemetry-hot-path","title":"Agenda - [PERF] Runtime Telemetry Hot Path","created_at":"2026-03-27","updated_at":"2026-04-10","tags":[],"agendas":[{"id":"AGD-0007","file":"workflow/agendas/AGD-0007-perf-runtime-telemetry-hot-path.md","status":"done","created_at":"2026-03-27","updated_at":"2026-04-10"}],"decisions":[{"id":"DEC-0005","file":"workflow/decisions/DEC-0005-perf-push-based-telemetry-model.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10"}],"plans":[{"id":"PLN-0005","file":"workflow/plans/PLN-0005-perf-push-based-telemetry-implementation.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"lessons":[{"id":"LSN-0026","file":"lessons/DSC-0008-perf-runtime-telemetry-hot-path/LSN-0026-push-based-telemetry-model.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} +{"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":"review","ticket":"perf-runtime-introspection-syscalls","title":"Agenda - [PERF] Runtime Introspection Syscalls","created_at":"2026-03-27","updated_at":"2026-04-19","tags":["perf","runtime","syscall","telemetry","debug","asset"],"agendas":[{"id":"AGD-0011","file":"workflow/agendas/AGD-0011-perf-runtime-introspection-syscalls.md","status":"accepted","created_at":"2026-03-27","updated_at":"2026-04-18"}],"decisions":[{"id":"DEC-0009","file":"workflow/decisions/DEC-0009-host-owned-debug-and-certification.md","status":"accepted","created_at":"2026-04-18","updated_at":"2026-04-19","ref_agenda":"AGD-0011"}],"plans":[{"id":"PLN-0030","file":"workflow/plans/PLN-0030-dec9-spec-boundary-propagation.md","status":"done","created_at":"2026-04-19","updated_at":"2026-04-19","ref_decisions":["DEC-0009"]},{"id":"PLN-0031","file":"workflow/plans/PLN-0031-dec9-runtime-bank-abi-cleanup.md","status":"done","created_at":"2026-04-19","updated_at":"2026-04-19","ref_decisions":["DEC-0009"]},{"id":"PLN-0032","file":"workflow/plans/PLN-0032-dec9-host-debugger-and-certification-alignment.md","status":"done","created_at":"2026-04-19","updated_at":"2026-04-19","ref_decisions":["DEC-0009"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0013","status":"done","ticket":"perf-host-debug-overlay-isolation","title":"Agenda - [PERF] Host Debug Overlay Isolation","created_at":"2026-03-27","updated_at":"2026-04-10","tags":[],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0027","file":"lessons/DSC-0013-perf-host-debug-overlay-isolation/LSN-0027-host-debug-overlay-isolation.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} +{"type":"discussion","id":"DSC-0024","status":"done","ticket":"generic-memory-bank-slot-contract","title":"Agenda - Generic Memory Bank Slot Contract","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["runtime","asset","memory-bank","slots","host"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0029","file":"lessons/DSC-0024-generic-memory-bank-slot-contract/LSN-0029-slot-first-bank-telemetry-belongs-in-asset-manager.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} +{"type":"discussion","id":"DSC-0025","status":"done","ticket":"scene-bank-and-viewport-cache-refactor","title":"Scene Bank and Viewport Cache Refactor","created_at":"2026-04-11","updated_at":"2026-04-14","tags":["gfx","tilemap","runtime","render"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0030","file":"lessons/DSC-0025-scene-bank-and-viewport-cache-refactor/LSN-0030-canonical-scene-cache-and-resolver-split.md","status":"done","created_at":"2026-04-14","updated_at":"2026-04-14"}]} +{"type":"discussion","id":"DSC-0026","status":"done","ticket":"render-all-scene-cache-and-camera-integration","title":"Integrate render_all with Scene Cache and Camera","created_at":"2026-04-14","updated_at":"2026-04-18","tags":["gfx","runtime","render","camera","scene"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0031","file":"lessons/DSC-0026-render-all-scene-cache-and-camera-integration/LSN-0031-frame-composition-belongs-above-the-render-backend.md","status":"done","created_at":"2026-04-18","updated_at":"2026-04-18"}]} +{"type":"discussion","id":"DSC-0027","status":"done","ticket":"frame-composer-public-syscall-surface","title":"Agenda - FrameComposer Public Syscall Surface","created_at":"2026-04-17","updated_at":"2026-04-18","tags":["gfx","runtime","syscall","abi","frame-composer","scene","camera","sprites"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0032","file":"lessons/DSC-0027-frame-composer-public-syscall-surface/LSN-0032-public-abi-must-follow-the-canonical-service-boundary.md","status":"done","created_at":"2026-04-18","updated_at":"2026-04-18"}]} +{"type":"discussion","id":"DSC-0028","status":"done","ticket":"deferred-overlay-and-primitive-composition","title":"Deferred Overlay and Primitive Composition over FrameComposer","created_at":"2026-04-18","updated_at":"2026-04-18","tags":["gfx","runtime","render","frame-composer","overlay","primitives","hud"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0033","file":"lessons/DSC-0028-deferred-overlay-and-primitive-composition/LSN-0033-debug-primitives-should-be-a-final-overlay-not-part-of-game-composition.md","status":"done","created_at":"2026-04-18","updated_at":"2026-04-18"}]} +{"type":"discussion","id":"DSC-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":"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/index.ndjson b/discussion/index.ndjson index 6c1c7a0d..d86c5efc 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -1,4 +1,4 @@ -{"type":"meta","next_id":{"DSC":29,"AGD":29,"DEC":17,"PLN":30,"LSN":34,"CLSN":1}} +{"type":"meta","next_id":{"DSC":29,"AGD":29,"DEC":18,"PLN":33,"LSN":35,"CLSN":1}} {"type":"discussion","id":"DSC-0023","status":"done","ticket":"perf-full-migration-to-atomic-telemetry","title":"Agenda - [PERF] Full Migration to Atomic Telemetry","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["perf","runtime","telemetry"],"agendas":[{"id":"AGD-0021","file":"workflow/agendas/AGD-0021-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"decisions":[{"id":"DEC-0008","file":"workflow/decisions/DEC-0008-full-migration-to-atomic-telemetry.md","status":"accepted","created_at":"2026-04-10","updated_at":"2026-04-10"}],"plans":[{"id":"PLN-0007","file":"workflow/plans/PLN-0007-full-migration-to-atomic-telemetry.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}],"lessons":[{"id":"LSN-0028","file":"lessons/DSC-0023-perf-full-migration-to-atomic-telemetry/LSN-0028-converging-to-single-atomic-telemetry-source.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} {"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"}]} @@ -14,7 +14,7 @@ {"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-0012","status":"done","ticket":"perf-runtime-introspection-syscalls","title":"Agenda - [PERF] Runtime Introspection Syscalls","created_at":"2026-03-27","updated_at":"2026-04-19","tags":["perf","runtime","syscall","telemetry","debug","asset"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0034","file":"lessons/DSC-0012-perf-runtime-introspection-syscalls/LSN-0034-host-owned-debug-boundaries.md","status":"done","created_at":"2026-04-19","updated_at":"2026-04-19"}]} {"type":"discussion","id":"DSC-0013","status":"done","ticket":"perf-host-debug-overlay-isolation","title":"Agenda - [PERF] Host Debug Overlay Isolation","created_at":"2026-03-27","updated_at":"2026-04-10","tags":[],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0027","file":"lessons/DSC-0013-perf-host-debug-overlay-isolation/LSN-0027-host-debug-overlay-isolation.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} {"type":"discussion","id":"DSC-0024","status":"done","ticket":"generic-memory-bank-slot-contract","title":"Agenda - Generic Memory Bank Slot Contract","created_at":"2026-04-10","updated_at":"2026-04-10","tags":["runtime","asset","memory-bank","slots","host"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0029","file":"lessons/DSC-0024-generic-memory-bank-slot-contract/LSN-0029-slot-first-bank-telemetry-belongs-in-asset-manager.md","status":"done","created_at":"2026-04-10","updated_at":"2026-04-10"}]} {"type":"discussion","id":"DSC-0025","status":"done","ticket":"scene-bank-and-viewport-cache-refactor","title":"Scene Bank and Viewport Cache Refactor","created_at":"2026-04-11","updated_at":"2026-04-14","tags":["gfx","tilemap","runtime","render"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0030","file":"lessons/DSC-0025-scene-bank-and-viewport-cache-refactor/LSN-0030-canonical-scene-cache-and-resolver-split.md","status":"done","created_at":"2026-04-14","updated_at":"2026-04-14"}]} diff --git a/discussion/lessons/DSC-0012-perf-runtime-introspection-syscalls/LSN-0034-host-owned-debug-boundaries.md b/discussion/lessons/DSC-0012-perf-runtime-introspection-syscalls/LSN-0034-host-owned-debug-boundaries.md new file mode 100644 index 00000000..8234b304 --- /dev/null +++ b/discussion/lessons/DSC-0012-perf-runtime-introspection-syscalls/LSN-0034-host-owned-debug-boundaries.md @@ -0,0 +1,55 @@ +--- +id: LSN-0034 +ticket: perf-runtime-introspection-syscalls +title: Host-Owned Debug Boundaries and Cheap Machine Diagnostics +created: 2026-04-19 +tags: [runtime, host, debug, certification, telemetry, abi] +--- + +## Context + +`DSC-0012` resolved a recurring ambiguity in PROMETEU: whether development diagnostics should be treated as part of the guest/runtime contract or as host tooling concerns. The codebase already had host overlay rendering, atomic telemetry snapshots, and slot-first bank telemetry, but the runtime still exposed JSON-formatted bank inspection syscalls. + +The completed work converged specs, runtime ABI, and host debugger behavior around a single boundary: rich diagnostics belong to the host, while the runtime exports only bounded machine-facing summaries. + +## Key Decisions + +### Host-Owned Debug and Certification (`DEC-0009`) + +**What:** +Debug tooling and certification are host-owned concerns. Guest-visible debug convenience APIs are not part of the long-term runtime ABI. + +**Why:** +Development inspection, profiling, and certification analysis happen on desktop host environments, not on production handheld/runtime targets. Keeping those concerns in the guest ABI forces production execution to carry tooling-oriented surface area and cost. + +**Trade-offs:** +The runtime gives up general introspection syscalls, so host tooling must consume telemetry and internal host/runtime integration points directly. In exchange, the public ABI becomes cheaper, narrower, and easier to reason about. + +## Patterns and Algorithms + +- Treat diagnostics as a layered boundary: + The runtime owns deterministic counters, bounded summaries, and machine-facing operational signals. + The host owns rendering, protocol projection, certification output, and rich inspection UX. + +- Prefer cheap structured values over textual debug payloads: + If a machine-facing diagnostic survives, it should return small canonical stack values such as `(used_slots, total_slots)`, not JSON blobs. + +- Use one telemetry source for all host consumers: + Atomic telemetry snapshots should feed debugger streaming, overlays, and certification evaluation so the platform does not fork multiple diagnostic pipelines. + +- Keep telemetry slot-first when the real machine contract is slot-first: + Bank occupancy was correctly modeled as slot usage, so the surviving public summary stayed aligned with that semantic model instead of byte-oriented totals or slot-detail dumps. + +## Pitfalls + +- Do not let host-only needs backdoor themselves into the guest ABI as "temporary" debug syscalls. They tend to linger and become accidental contract. + +- Do not use JSON-on-the-wire as a fallback ABI for runtime inspection. It is easy to add, but it creates unbounded cost and weakens the canonical machine model. + +- Do not update only specs or only code. This boundary needed spec propagation, runtime ABI cleanup, and host debugger alignment together to avoid split-brain behavior. + +## Takeaways + +- Debug and certification pipelines should be modeled as host products that consume runtime telemetry, not as guest features. +- Surviving machine diagnostics must be justified by operational need and expressed as bounded structured values. +- The clean boundary is: runtime produces telemetry, host interprets and presents it. diff --git a/discussion/workflow/agendas/AGD-0011-perf-runtime-introspection-syscalls.md b/discussion/workflow/agendas/AGD-0011-perf-runtime-introspection-syscalls.md deleted file mode 100644 index ab1e508c..00000000 --- a/discussion/workflow/agendas/AGD-0011-perf-runtime-introspection-syscalls.md +++ /dev/null @@ -1,72 +0,0 @@ ---- -id: AGD-0011 -ticket: perf-runtime-introspection-syscalls -title: Agenda - [PERF] Runtime Introspection Syscalls -status: open -created: 2026-03-27 -resolved: -decision: -tags: [] ---- - -# Agenda - [PERF] Runtime Introspection Syscalls - -## Problema - -As syscalls de introspecao ainda carregam custo de serializacao e agregacao demais para algo que deveria ser observabilidade sob demanda. - -Hoje `BankInfo` e `BankSlotInfo` montam JSON por chamada e puxam dados potencialmente caros do asset manager. - -## Dor - -- tooling de debug pode contaminar custo percebido da runtime surface. -- serializacao de string/JSON vira alocacao no meio do dispatch. -- sem fronteira clara, apps podem abusar de syscalls de introspecao como se fossem baratas. - -## Hotspots Atuais - -- [dispatch.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/console/prometeu-system/src/virtual_machine_runtime/dispatch.rs#L481) -- [asset.rs](/Users/niltonconstantino/personal/workspace.personal/intrepid/prometeu/runtime/crates/console/prometeu-drivers/src/asset.rs#L618) - -## Alvo da Discussao - -Separar claramente custo de introspecao/debug do custo da superficie operacional normal. - -## O Que Precisa Ser Definido - -1. Papel dessas syscalls. - Decidir se `BankInfo`/`BankSlotInfo` sao: - - superficie publica normal; - - superficie de debug; - - superficie host/devtools apenas. - -2. Shape de retorno. - Definir se JSON continua existindo ou se v1 exige shape mais barato/canonico. - -3. Caching. - Delimitar se snapshots de introspecao podem ser cacheados por frame. - -4. Limites de uso. - Decidir se o runtime deve impor throttling, budget ou feature gate de debug. - -## Open Questions de Arquitetura - -1. Vale manter JSON na ABI do guest ou isso deveria ficar restrito ao host/debugger? -2. Existe um subconjunto de dados numericos suficiente para carts sem tooling externo? -3. O certifier deve observar essas chamadas como custo anormal de debug? - -## Dependencias - -- `../specs/10-debug-inspection-and-profiling.md` -- `../specs/15-asset-management.md` -- `../specs/16-host-abi-and-syscalls.md` -- `../specs/16a-syscall-policies.md` - -## Criterio de Saida Desta Agenda - -Pode virar PR quando houver decisao escrita sobre: - -- papel normativo de `BankInfo`/`BankSlotInfo`; -- permanencia ou remocao de JSON como shape de retorno; -- politica de cache/throttling; -- custo aceitavel de introspecao no dispatch. diff --git a/docs/specs/runtime/09-events-and-concurrency.md b/docs/specs/runtime/09-events-and-concurrency.md index bfba647f..200ad704 100644 --- a/docs/specs/runtime/09-events-and-concurrency.md +++ b/docs/specs/runtime/09-events-and-concurrency.md @@ -98,7 +98,7 @@ Important properties: - events are processed at known points; - no execution occurs outside the frame loop; -- frame structure remains observable for tooling and certification. +- frame structure remains observable for host tooling and host-owned certification. ## 7 Determinism and Best Practices diff --git a/docs/specs/runtime/10-debug-inspection-and-profiling.md b/docs/specs/runtime/10-debug-inspection-and-profiling.md index 50db8446..4f140f23 100644 --- a/docs/specs/runtime/10-debug-inspection-and-profiling.md +++ b/docs/specs/runtime/10-debug-inspection-and-profiling.md @@ -7,19 +7,18 @@ Didactic companion: [`../learn/mental-model-observability-and-debugging.md`](../ ## 1 Scope -This chapter defines the machine-visible debugging, inspection, and profiling surface of PROMETEU. +This chapter defines the machine-visible diagnostics and profiling surface of PROMETEU after `DEC-0009`. + +`DEC-0009` locks debug tooling and certification output as host-owned concerns. The runtime machine contract therefore exposes only bounded operational diagnostics and deterministic telemetry production, not a general-purpose guest-visible debug surface. It covers: -- execution modes; -- pause and stepping; -- state inspection; -- graphics inspection; +- runtime-visible execution control used by host tooling; +- bounded operational telemetry; - profiling; -- breakpoints and watchpoints; - event and fault visibility; -- certification-facing diagnostics; -- Host-side debug overlay (HUD) isolation. +- certification input data; +- host-side debug overlay (HUD) isolation. ## 2 Execution Modes @@ -34,18 +33,18 @@ PROMETEU operates in three main modes: ### 2.2 Debug Mode - controlled execution -- access to internal state +- host-mediated access to internal state - pauses and stepping ### 2.3 Certification Mode - deterministic execution - collected metrics -- report generation +- host-generated report output No mode alters the logical result of the program. -## 3 Execution Debug +## 3 Execution Control ### 3.1 Pause and Resume @@ -64,24 +63,26 @@ During pause: ### 3.2 Step-by-Step -PROMETEU allows stepping at different levels: +PROMETEU allows host tooling to step execution at different levels: - **by frame** - **by function** - **by VM instruction** -Stepping by instruction reveals: +When the host activates instruction-level inspection, it may observe: - Program Counter (PC) - current instruction - operand stack - call stack -## 4 State Inspection +## 4 State Inspection Boundary + +Detailed state inspection is a host-owned concern. The runtime may expose deterministic machine state to host tooling, but this chapter does not define a general guest ABI for introspection convenience. ### 4.1 Stacks -PROMETEU allows inspecting: +Host tooling may inspect: - **Operand Stack** - **Call Stack** @@ -94,7 +95,7 @@ For each frame: ### 4.2 Heap -The heap can be inspected in real time: +The host may inspect heap state in real time: - total size - current usage @@ -109,22 +110,22 @@ The programmer can observe: ### 4.3 Global Space -Global variables: +Host-owned inspection may observe global variables, including: - current values - references - initialization -## 5 Graphics Debug +## 5 Graphics Inspection Boundary -PROMETEU allows inspecting the graphics system: +Host tooling may inspect the graphics system: - front buffer - back buffer - palette state - active sprites -It is possible to: +The host may: - freeze the image - observe buffers separately @@ -153,7 +154,7 @@ SYSTEM:612 ### 6.2 Per-Function Profiling -PROMETEU can associate cycles with: +Host tooling may associate cycles with: - functions - methods @@ -161,7 +162,7 @@ PROMETEU can associate cycles with: ### 6.3 Per-Instruction Profiling -At the lowest level, the system can display: +At the lowest level, host tooling can display: - executed instructions - individual cost @@ -185,13 +186,13 @@ Peak:34KB❌ Limit:32KB ``` -These data directly feed the certification. +These data feed host-owned certification. ### 7.1 Bank Occupancy Profiling Bank occupancy diagnostics are slot-first. -The visible per-bank telemetry used by host inspection surfaces and certification is: +The visible per-bank telemetry used by host inspection surfaces and host-owned certification is: - `bank_type` - `used_slots` @@ -206,9 +207,11 @@ Byte-oriented bank occupancy is not the canonical visible profiling contract. ## 8 Breakpoints and Watchpoints +Breakpoints and watchpoints are host-mediated tooling controls. They are not part of the gameplay-facing cartridge contract. + ### 8.1 Breakpoints -PROMETEU supports breakpoints in: +The platform supports host-owned breakpoints in: - specific frames - functions @@ -222,7 +225,7 @@ Breakpoints: ### 8.2 Watchpoints -Watchpoints monitor: +Host tooling may monitor watchpoints over: - variables - heap addresses @@ -233,10 +236,14 @@ Execution can pause when: - a value changes - a limit is exceeded -## 9 Event and Fault Debugging +## 9 Event and Fault Diagnostics ## 10 Certification Diagnostics +Certification diagnostics are produced by the host from deterministic runtime telemetry. + +The runtime does not generate the final certification artifact. It only maintains the machine-facing counters and summaries required for host-owned certification. + Certification diagnostics may enforce bank occupancy ceilings. For bank residency, certification uses slot-based limits, such as: @@ -246,7 +253,7 @@ For bank residency, certification uses slot-based limits, such as: Bank certification MUST NOT depend on `max_gfx_bytes` or `max_audio_bytes`. -PROMETEU allows observing: +Host tooling may observe: - event queue - active timers @@ -259,7 +266,7 @@ Each event has: - cost - consequence -## 10 Host-Side Debug Overlay (HUD) Isolation +## 11 Host-Side Debug Overlay (HUD) Isolation The visual Debug Overlay (HUD) for technical inspection is not part of the emulated machine pipeline. @@ -270,7 +277,7 @@ The visual Debug Overlay (HUD) for technical inspection is not part of the emula 3. **Host overlay module:** The Desktop Host owns a dedicated overlay module that performs host-side text, panel, and simple bar composition. 4. **Composition boundary:** The overlay is composed on the Host presentation surface after the emulated frame is ready. Overlay pixels must not be written back into the emulated framebuffer. 5. **Host control:** Overlay visibility and presentation policy remain under Host control. -2. **Host (Desktop):** Responsible for collecting telemetry from the runtime and rendering the HUD as a native, transparent layer. +6. **Host (Desktop):** Responsible for collecting telemetry from the runtime and rendering the HUD as a native, transparent layer. ### 10.2 Principles @@ -287,11 +294,11 @@ To ensure zero-impact synchronization between the VM and the Host Debug Overlay, 3. **Single Source of Truth:** This model is the exclusive source of truth for both real-time inspection and frame-end certification, replacing legacy per-frame buffered fields. 4. **Frame-Closed Log Metric:** `logs_count` in the snapshot represents the number of logs emitted in the last completed logical frame, not a transient in-flight counter. -## 11 Integration with CAP and Certification +## 12 Integration with CAP and Certification -All debug and profiling data: +All machine diagnostics and profiling data: -- feed the certification report -- are collected deterministically -- do not depend on external tools +- feed the host-owned certification report; +- are collected deterministically; +- do not require a guest-visible debug ABI; - are consistent regardless of whether the Host HUD is active or not. diff --git a/docs/specs/runtime/11-portability-and-cross-platform-execution.md b/docs/specs/runtime/11-portability-and-cross-platform-execution.md index e745eef9..dac986f4 100644 --- a/docs/specs/runtime/11-portability-and-cross-platform-execution.md +++ b/docs/specs/runtime/11-portability-and-cross-platform-execution.md @@ -162,7 +162,7 @@ Without changing semantics. ## 11 Certification and Portability -The **PROMETEU Certification** is valid for all platforms. +The host-owned **PROMETEU Certification** is valid for all platforms. If a cartridge: diff --git a/docs/specs/runtime/15-asset-management.md b/docs/specs/runtime/15-asset-management.md index 90c56683..9de63331 100644 --- a/docs/specs/runtime/15-asset-management.md +++ b/docs/specs/runtime/15-asset-management.md @@ -110,44 +110,44 @@ This table describes content identity and storage layout, not live residency. ### 4.1 `TILES` asset contract in v1 -Para `BankType::TILES`, o contrato v1 voltado para o runtime é: +For `BankType::TILES`, the v1 runtime-facing contract is: - `codec = NONE` -- pixels serializados usam índices de paleta `u4` empacotados -- paletas serializadas usam `RGB565` (`u16`, little-endian) +- serialized pixels use packed `u4` palette indices +- serialized palettes use `RGB565` (`u16`, little-endian) - `palette_count = 64` -- a materialização em runtime pode expandir índices de pixel para um `u8` por pixel +- runtime materialization may expand pixel indices to one `u8` per pixel -`NONE` para `TILES` significa que não há camada de codec genérica adicional além do próprio contrato do banco. +For `TILES`, `NONE` means there is no additional generic codec layer beyond the bank contract itself. -Para a janela de transição atual: +For the current transition window: -- `RAW` é um alias legado e depreciado de `NONE` -- novos materiais publicados devem usar `NONE` como valor canônico +- `RAW` is a legacy deprecated alias of `NONE` +- newly published material must use `NONE` as the canonical value -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. +Even with `codec = NONE`, `TILES` still requires bank-specific decode from its serialized payload. The serialized byte layout therefore does not need to match the in-memory layout. #### 4.1.1 Metadata Normalization -Seguindo a `DEC-0004`, o campo `AssetEntry.metadata` deve ser estruturado de forma segmentada para evitar ambiguidades. +Following `DEC-0004`, the `AssetEntry.metadata` field must be structured in segmented form to avoid ambiguity. -Campos de metadados obrigatórios (efetivos) para `TILES` no nível raiz: +Required effective metadata fields for `TILES` at the root level: -- `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 +- `tile_size`: tile edge in pixels; valid values are `8`, `16`, or `32` +- `width`: total sheet width in pixels +- `height`: total sheet height in pixels +- `palette_count`: number of serialized palettes for the bank -Subárvores opcionais e informativas: +Optional informative subtrees: -- `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). +- `metadata.codec`: codec/compressor-specific configuration (for example dictionaries or compression flags). +- `metadata.pipeline`: informative metadata from the Studio build process (for example source hashes, timestamps, or tool versions). -Regras de validação para `TILES` v1: +Validation rules for `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). +- `palette_count` must be `64` +- `width * height` defines the number of logical indexed pixels in the decoded sheet +- extra metadata may exist, but the runtime contract must not depend on it to reconstruct the in-memory bank unless that data is defined at the root as an effective field. #### 4.1.2 Payload Layout @@ -202,7 +202,8 @@ Rules: - the visible contract MUST NOT expose byte-oriented bank occupancy as the canonical summary; - canonical bank names are `GLYPH` and `SOUNDS`; -- detailed occupancy inspection remains slot-based through slot references, not bank byte totals; +- detailed occupancy inspection remains host-owned and slot-based through slot references, not bank byte totals; +- public runtime specs MUST NOT treat `bank.slot_info` or JSON bank inspection payloads as the long-term guest ABI after `DEC-0009`; - any residual byte accounting MAY exist internally, but it is not part of the visible bank telemetry contract. ## 6 Load Lifecycle @@ -255,9 +256,9 @@ Therefore: - shutting down a cartridge can release bank residency independently of VM heap behavior. - the runtime must not keep the full `assets.pa` payload resident in RAM as a baseline requirement. -## 8 Bank Telemetry +## 8 Bank Telemetry and Inspection Boundary -The runtime surfaces bank and slot statistics such as: +The runtime may maintain bank and slot statistics such as: - total bytes; - used bytes; @@ -266,7 +267,11 @@ The runtime surfaces bank and slot statistics such as: - slot occupancy; - resident asset identity per slot. -These metrics support debugging, telemetry, and certification-oriented inspection. +These metrics support host-owned debugging, telemetry, and certification-oriented inspection. + +Only bounded operational summaries belong to the public runtime contract. + +Detailed per-slot inspection is valid for host tooling and runtime internals, but it must not be treated as a general guest-visible debug convenience API. ## 9 Preload diff --git a/docs/specs/runtime/16-host-abi-and-syscalls.md b/docs/specs/runtime/16-host-abi-and-syscalls.md index 0e2b4c1b..77d66115 100644 --- a/docs/specs/runtime/16-host-abi-and-syscalls.md +++ b/docs/specs/runtime/16-host-abi-and-syscalls.md @@ -15,6 +15,8 @@ It focuses on: Operational policies such as capabilities, fault classes, determinism, GC interaction, budgeting, and blocking are split into a companion chapter. +Per `DEC-0009`, debug tooling and certification are host-owned concerns. This chapter therefore excludes general-purpose guest-visible debug syscalls from the canonical public ABI. + ## 1 Design Principles The syscall ABI follows these rules: @@ -199,6 +201,16 @@ For `asset.load`: - `slot` is the target slot index; - bank kind is resolved from `asset_table` by `asset_id`, not supplied by the caller. +### Bank diagnostics surface (`bank`, v1) + +`DEC-0009` narrows the public bank contract: + +- `bank.info(bank_type) -> (used_slots, total_slots)` is the only surviving public bank diagnostic shape in v1; +- `bank.info` exists only as a cheap deterministic operational summary aligned with the slot-first bank telemetry contract from [`15-asset-management.md`](15-asset-management.md); +- `bank.slot_info` is host/debug tooling surface and is not part of the canonical public guest ABI; +- JSON-on-the-wire bank inspection payloads are not valid public ABI; +- `bank.info` returns stack values, not textual structured payloads. + ### Composition surface (`composer`, v1) The canonical frame-orchestration public ABI uses module `composer`. diff --git a/docs/specs/runtime/16a-syscall-policies.md b/docs/specs/runtime/16a-syscall-policies.md index fb940b1f..1efb10ef 100644 --- a/docs/specs/runtime/16a-syscall-policies.md +++ b/docs/specs/runtime/16a-syscall-policies.md @@ -9,6 +9,8 @@ It complements [`16-host-abi-and-syscalls.md`](16-host-abi-and-syscalls.md), whi Unless a domain chapter explicitly narrows behavior further, this chapter is the transversal policy for all syscalls in v1. +Per `DEC-0009`, guest-visible syscalls must not exist merely for debug convenience or certification plumbing. Those concerns belong to host-owned tooling. + ## 1 Error Model: Faults vs Status Returns Syscalls use a status-first hybrid model. @@ -61,6 +63,7 @@ Practical rule: - `void` is allowed only for operations whose non-fault path is effectively unconditional success; - any operation with a meaningful operational non-success outcome must surface that outcome through `status`, not through absence of return values. +- debug-only or certification-only convenience calls are not justified public ABI even if they could be given a valid return shape. ### No-op policy @@ -77,6 +80,14 @@ This means operational problems must not be reclassified as: - `Trap`, when the failure is not structural; - `Panic`, when the failure is not an internal invariant break. +### Diagnostics boundary + +These policy constraints also apply to observability surfaces: + +- host-owned inspection and certification consumers may use runtime telemetry and internal host/runtime integration paths; +- guest-visible syscalls must be justified by machine-facing operational needs, not by tooling convenience; +- textual JSON diagnostics are not valid long-term public syscall ABI. + ## 2 Capability System Each syscall requires a declared capability. @@ -141,7 +152,7 @@ The system may account for: - cycles spent in syscalls; - allocations triggered by syscalls. -This keeps host interaction visible in certification, telemetry, and profiling. +This keeps host interaction visible in host-owned certification, telemetry, and profiling. ## 6 Blocking and Long Operations