dev/perf-runtime-introspection-syscalls #17

Merged
bquarkz merged 6 commits from dev/perf-runtime-introspection-syscalls into master 2026-04-19 08:20:10 +00:00
22 changed files with 499 additions and 222 deletions

View File

@ -68,6 +68,8 @@ pub enum DebugEvent {
glyph_slots_total: u32, glyph_slots_total: u32,
sound_slots_used: u32, sound_slots_used: u32,
sound_slots_total: u32, sound_slots_total: u32,
scene_slots_used: u32,
scene_slots_total: u32,
}, },
#[serde(rename = "fault")] #[serde(rename = "fault")]
Fault { Fault {
@ -99,6 +101,8 @@ mod tests {
glyph_slots_total: 16, glyph_slots_total: 16,
sound_slots_used: 2, sound_slots_used: 2,
sound_slots_total: 16, sound_slots_total: 16,
scene_slots_used: 3,
scene_slots_total: 16,
}; };
let json = serde_json::to_string(&event).unwrap(); let json = serde_json::to_string(&event).unwrap();

View File

@ -64,7 +64,6 @@ pub enum Syscall {
AssetCommit = 0x6003, AssetCommit = 0x6003,
AssetCancel = 0x6004, AssetCancel = 0x6004,
BankInfo = 0x6101, BankInfo = 0x6101,
BankSlotInfo = 0x6102,
} }
/// Canonical metadata describing a syscall using the unified slot-based ABI. /// Canonical metadata describing a syscall using the unified slot-based ABI.

View File

@ -1,12 +1,7 @@
use crate::syscalls::{Syscall, SyscallRegistryEntry, caps}; use crate::syscalls::{Syscall, SyscallRegistryEntry, caps};
pub(crate) const ENTRIES: &[SyscallRegistryEntry] = &[ pub(crate) const ENTRIES: &[SyscallRegistryEntry] =
SyscallRegistryEntry::builder(Syscall::BankInfo, "bank", "info") &[SyscallRegistryEntry::builder(Syscall::BankInfo, "bank", "info")
.args(1) .args(1)
.rets(1) .rets(2)
.caps(caps::BANK), .caps(caps::BANK)];
SyscallRegistryEntry::builder(Syscall::BankSlotInfo, "bank", "slot_info")
.args(2)
.rets(1)
.caps(caps::BANK),
];

View File

@ -48,7 +48,6 @@ impl Syscall {
0x6003 => Some(Self::AssetCommit), 0x6003 => Some(Self::AssetCommit),
0x6004 => Some(Self::AssetCancel), 0x6004 => Some(Self::AssetCancel),
0x6101 => Some(Self::BankInfo), 0x6101 => Some(Self::BankInfo),
0x6102 => Some(Self::BankSlotInfo),
_ => None, _ => None,
} }
} }
@ -99,7 +98,6 @@ impl Syscall {
Self::AssetCommit => "AssetCommit", Self::AssetCommit => "AssetCommit",
Self::AssetCancel => "AssetCancel", Self::AssetCancel => "AssetCancel",
Self::BankInfo => "BankInfo", Self::BankInfo => "BankInfo",
Self::BankSlotInfo => "BankSlotInfo",
} }
} }
} }

View File

@ -249,6 +249,22 @@ fn status_first_syscall_signatures_are_pinned() {
let asset_cancel = meta_for(Syscall::AssetCancel); let asset_cancel = meta_for(Syscall::AssetCancel);
assert_eq!(asset_cancel.arg_slots, 1); assert_eq!(asset_cancel.arg_slots, 1);
assert_eq!(asset_cancel.ret_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] #[test]

View File

@ -18,6 +18,8 @@ pub struct TelemetryFrame {
pub glyph_slots_total: u32, pub glyph_slots_total: u32,
pub sound_slots_used: u32, pub sound_slots_used: u32,
pub sound_slots_total: u32, pub sound_slots_total: u32,
pub scene_slots_used: u32,
pub scene_slots_total: u32,
// RAM (Heap) // RAM (Heap)
pub heap_used_bytes: usize, pub heap_used_bytes: usize,
@ -45,6 +47,8 @@ pub struct AtomicTelemetry {
pub glyph_slots_total: AtomicU32, pub glyph_slots_total: AtomicU32,
pub sound_slots_used: AtomicU32, pub sound_slots_used: AtomicU32,
pub sound_slots_total: AtomicU32, pub sound_slots_total: AtomicU32,
pub scene_slots_used: AtomicU32,
pub scene_slots_total: AtomicU32,
// RAM (Heap) // RAM (Heap)
pub heap_used_bytes: AtomicUsize, pub heap_used_bytes: AtomicUsize,
@ -75,6 +79,8 @@ impl AtomicTelemetry {
glyph_slots_total: self.glyph_slots_total.load(Ordering::Relaxed), glyph_slots_total: self.glyph_slots_total.load(Ordering::Relaxed),
sound_slots_used: self.sound_slots_used.load(Ordering::Relaxed), sound_slots_used: self.sound_slots_used.load(Ordering::Relaxed),
sound_slots_total: self.sound_slots_total.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_used_bytes: self.heap_used_bytes.load(Ordering::Relaxed),
heap_max_bytes: self.heap_max_bytes.load(Ordering::Relaxed), heap_max_bytes: self.heap_max_bytes.load(Ordering::Relaxed),
logs_count: self.logs_count.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.glyph_slots_total.store(0, Ordering::Relaxed);
self.sound_slots_used.store(0, Ordering::Relaxed); self.sound_slots_used.store(0, Ordering::Relaxed);
self.sound_slots_total.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.heap_used_bytes.store(0, Ordering::Relaxed);
self.vm_steps.store(0, Ordering::Relaxed); self.vm_steps.store(0, Ordering::Relaxed);
self.logs_count.store(0, Ordering::Relaxed); self.logs_count.store(0, Ordering::Relaxed);

View File

@ -567,19 +567,8 @@ impl NativeInterface for VirtualMachineRuntime {
used_slots: 0, used_slots: 0,
total_slots: 0, total_slots: 0,
}); });
let json = serde_json::to_string(&telemetry).unwrap_or_default(); ret.push_int(telemetry.used_slots as i64);
ret.push_string(json); ret.push_int(telemetry.total_slots as i64);
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);
Ok(()) Ok(())
} }
} }

View File

@ -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)]); 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] #[test]
fn tick_asset_commit_invalid_transition_returns_status_not_crash() { fn tick_asset_commit_invalid_transition_returns_status_not_crash() {
let mut runtime = VirtualMachineRuntime::new(None); let mut runtime = VirtualMachineRuntime::new(None);

View File

@ -7,7 +7,9 @@ use prometeu_vm::LogicalFrameEndingReason;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
impl VirtualMachineRuntime { 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 telemetry = hw.assets().bank_telemetry();
let glyph = let glyph =
telemetry.iter().find(|entry| entry.bank_type == BankType::GLYPH).cloned().unwrap_or( 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( telemetry.iter().find(|entry| entry.bank_type == BankType::SOUNDS).cloned().unwrap_or(
BankTelemetry { bank_type: BankType::SOUNDS, used_slots: 0, total_slots: 0 }, 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( pub fn debug_step_instruction(
@ -148,7 +154,7 @@ impl VirtualMachineRuntime {
hw.render_frame(); hw.render_frame();
// 1. Snapshot full telemetry at logical frame end // 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 self.atomic_telemetry
.glyph_slots_used .glyph_slots_used
.store(glyph_bank.used_slots as u32, Ordering::Relaxed); .store(glyph_bank.used_slots as u32, Ordering::Relaxed);
@ -161,6 +167,12 @@ impl VirtualMachineRuntime {
self.atomic_telemetry self.atomic_telemetry
.sound_slots_total .sound_slots_total
.store(sound_bank.total_slots as u32, Ordering::Relaxed); .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 self.atomic_telemetry
.heap_used_bytes .heap_used_bytes
@ -218,7 +230,7 @@ impl VirtualMachineRuntime {
// 2. High-frequency telemetry update (only if inspection is active) // 2. High-frequency telemetry update (only if inspection is active)
if self.inspection_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 self.atomic_telemetry
.glyph_slots_used .glyph_slots_used
.store(glyph_bank.used_slots as u32, Ordering::Relaxed); .store(glyph_bank.used_slots as u32, Ordering::Relaxed);
@ -231,6 +243,12 @@ impl VirtualMachineRuntime {
self.atomic_telemetry self.atomic_telemetry
.sound_slots_total .sound_slots_total
.store(sound_bank.total_slots as u32, Ordering::Relaxed); .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 self.atomic_telemetry
.heap_used_bytes .heap_used_bytes

View File

@ -2,6 +2,7 @@ use prometeu_drivers::hardware::Hardware;
use prometeu_firmware::{BootTarget, Firmware}; use prometeu_firmware::{BootTarget, Firmware};
use prometeu_hal::cartridge_loader::CartridgeLoader; use prometeu_hal::cartridge_loader::CartridgeLoader;
use prometeu_hal::debugger_protocol::*; use prometeu_hal::debugger_protocol::*;
use prometeu_hal::telemetry::{CertificationConfig, TelemetryFrame};
use prometeu_system::CrashReport; use prometeu_system::CrashReport;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream}; 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. /// Prometeu Debugger) to observe and control the execution of the virtual machine.
/// ///
/// Communication is based on JSONL (JSON lines) over TCP. /// 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 { pub struct HostDebugger {
/// If true, the VM will not start execution until a 'start' command is received. /// If true, the VM will not start execution until a 'start' command is received.
pub waiting_for_start: bool, 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<DebugEvent> {
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. /// Scans the system for new information to push to the debugger client.
fn stream_events(&mut self, firmware: &mut Firmware) { fn stream_events(&mut self, firmware: &mut Firmware) {
if let Some(report) = firmware.os.last_crash_report.as_ref() { 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 (0xCA01..=0xCA07).contains(&event.tag)
if event.tag >= 0xCA01 && event.tag <= 0xCA03 { && let Some(cert_event) = Self::cert_event_from_snapshot(
let tel = firmware.os.atomic_telemetry.snapshot(); event.tag,
let cert_config = &firmware.os.certifier.config; firmware.os.atomic_telemetry.snapshot(),
&firmware.os.certifier.config,
let (rule, used, limit) = match event.tag { firmware.os.logical_frame_index,
0xCA01 => ( )
"cycles_budget".to_string(), {
tel.cycles_used, self.send_event(cert_event);
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,
});
} }
self.send_event(DebugEvent::Log { self.send_event(DebugEvent::Log {
@ -304,19 +342,7 @@ impl HostDebugger {
let current_frame = firmware.os.logical_frame_index; let current_frame = firmware.os.logical_frame_index;
if current_frame > self.last_telemetry_frame { if current_frame > self.last_telemetry_frame {
let tel = firmware.os.atomic_telemetry.snapshot(); let tel = firmware.os.atomic_telemetry.snapshot();
self.send_event(DebugEvent::Telemetry { self.send_event(Self::telemetry_event_from_snapshot(tel));
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.last_telemetry_frame = current_frame; self.last_telemetry_frame = current_frame;
} }
} }

View File

@ -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 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 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 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 { OverlaySnapshot {
rows: vec![ rows: vec![
@ -163,6 +164,12 @@ pub(crate) fn capture_snapshot(stats: &HostStats, firmware: &Firmware) -> Overla
ratio: sound_ratio, ratio: sound_ratio,
warn: tel.sound_slots_total > 0 && tel.sound_slots_used >= tel.sound_slots_total, 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, footer,
} }

View File

@ -58,7 +58,8 @@ pub struct HostRunner {
/// Performance metrics collector. /// Performance metrics collector.
stats: HostStats, 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, debugger: HostDebugger,
/// Flag to enable/disable the technical telemetry display. /// Flag to enable/disable the technical telemetry display.
@ -225,7 +226,8 @@ impl ApplicationHandler for HostRunner {
// 1. Process pending debug commands from the network. // 1. Process pending debug commands from the network.
self.debugger.check_commands(&mut self.firmware, &mut self.hardware); 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(); 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). // 2. Maintain filesystem connection if it was lost (e.g., directory removed).
@ -307,9 +309,123 @@ mod tests {
use super::*; use super::*;
use prometeu_firmware::BootTarget; use prometeu_firmware::BootTarget;
use prometeu_hal::debugger_protocol::DEVTOOLS_PROTOCOL_VERSION; use prometeu_hal::debugger_protocol::DEVTOOLS_PROTOCOL_VERSION;
use prometeu_hal::telemetry::{CertificationConfig, TelemetryFrame};
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::net::TcpStream; 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] #[test]
#[ignore = "requires localhost TCP bind/connect; run via `cargo test -p prometeu-host-desktop-winit --lib -- --ignored`"] #[ignore = "requires localhost TCP bind/connect; run via `cargo test -p prometeu-host-desktop-winit --lib -- --ignored`"]
fn test_debug_port_opens() { fn test_debug_port_opens() {

View File

@ -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"}]}

View File

@ -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-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-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-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-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-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-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-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-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-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"}]}

View File

@ -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.

View File

@ -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.

View File

@ -98,7 +98,7 @@ Important properties:
- events are processed at known points; - events are processed at known points;
- no execution occurs outside the frame loop; - 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 ## 7 Determinism and Best Practices

View File

@ -7,19 +7,18 @@ Didactic companion: [`../learn/mental-model-observability-and-debugging.md`](../
## 1 Scope ## 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: It covers:
- execution modes; - runtime-visible execution control used by host tooling;
- pause and stepping; - bounded operational telemetry;
- state inspection;
- graphics inspection;
- profiling; - profiling;
- breakpoints and watchpoints;
- event and fault visibility; - event and fault visibility;
- certification-facing diagnostics; - certification input data;
- Host-side debug overlay (HUD) isolation. - host-side debug overlay (HUD) isolation.
## 2 Execution Modes ## 2 Execution Modes
@ -34,18 +33,18 @@ PROMETEU operates in three main modes:
### 2.2 Debug Mode ### 2.2 Debug Mode
- controlled execution - controlled execution
- access to internal state - host-mediated access to internal state
- pauses and stepping - pauses and stepping
### 2.3 Certification Mode ### 2.3 Certification Mode
- deterministic execution - deterministic execution
- collected metrics - collected metrics
- report generation - host-generated report output
No mode alters the logical result of the program. No mode alters the logical result of the program.
## 3 Execution Debug ## 3 Execution Control
### 3.1 Pause and Resume ### 3.1 Pause and Resume
@ -64,24 +63,26 @@ During pause:
### 3.2 Step-by-Step ### 3.2 Step-by-Step
PROMETEU allows stepping at different levels: PROMETEU allows host tooling to step execution at different levels:
- **by frame** - **by frame**
- **by function** - **by function**
- **by VM instruction** - **by VM instruction**
Stepping by instruction reveals: When the host activates instruction-level inspection, it may observe:
- Program Counter (PC) - Program Counter (PC)
- current instruction - current instruction
- operand stack - operand stack
- call 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 ### 4.1 Stacks
PROMETEU allows inspecting: Host tooling may inspect:
- **Operand Stack** - **Operand Stack**
- **Call Stack** - **Call Stack**
@ -94,7 +95,7 @@ For each frame:
### 4.2 Heap ### 4.2 Heap
The heap can be inspected in real time: The host may inspect heap state in real time:
- total size - total size
- current usage - current usage
@ -109,22 +110,22 @@ The programmer can observe:
### 4.3 Global Space ### 4.3 Global Space
Global variables: Host-owned inspection may observe global variables, including:
- current values - current values
- references - references
- initialization - initialization
## 5 Graphics Debug ## 5 Graphics Inspection Boundary
PROMETEU allows inspecting the graphics system: Host tooling may inspect the graphics system:
- front buffer - front buffer
- back buffer - back buffer
- palette state - palette state
- active sprites - active sprites
It is possible to: The host may:
- freeze the image - freeze the image
- observe buffers separately - observe buffers separately
@ -153,7 +154,7 @@ SYSTEM:612
### 6.2 Per-Function Profiling ### 6.2 Per-Function Profiling
PROMETEU can associate cycles with: Host tooling may associate cycles with:
- functions - functions
- methods - methods
@ -161,7 +162,7 @@ PROMETEU can associate cycles with:
### 6.3 Per-Instruction Profiling ### 6.3 Per-Instruction Profiling
At the lowest level, the system can display: At the lowest level, host tooling can display:
- executed instructions - executed instructions
- individual cost - individual cost
@ -185,13 +186,13 @@ Peak:34KB❌
Limit:32KB Limit:32KB
``` ```
These data directly feed the certification. These data feed host-owned certification.
### 7.1 Bank Occupancy Profiling ### 7.1 Bank Occupancy Profiling
Bank occupancy diagnostics are slot-first. 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` - `bank_type`
- `used_slots` - `used_slots`
@ -206,9 +207,11 @@ Byte-oriented bank occupancy is not the canonical visible profiling contract.
## 8 Breakpoints and Watchpoints ## 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 ### 8.1 Breakpoints
PROMETEU supports breakpoints in: The platform supports host-owned breakpoints in:
- specific frames - specific frames
- functions - functions
@ -222,7 +225,7 @@ Breakpoints:
### 8.2 Watchpoints ### 8.2 Watchpoints
Watchpoints monitor: Host tooling may monitor watchpoints over:
- variables - variables
- heap addresses - heap addresses
@ -233,10 +236,14 @@ Execution can pause when:
- a value changes - a value changes
- a limit is exceeded - a limit is exceeded
## 9 Event and Fault Debugging ## 9 Event and Fault Diagnostics
## 10 Certification 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. Certification diagnostics may enforce bank occupancy ceilings.
For bank residency, certification uses slot-based limits, such as: 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`. Bank certification MUST NOT depend on `max_gfx_bytes` or `max_audio_bytes`.
PROMETEU allows observing: Host tooling may observe:
- event queue - event queue
- active timers - active timers
@ -259,7 +266,7 @@ Each event has:
- cost - cost
- consequence - 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. 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. 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. 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. 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 ### 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. 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. 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 - feed the host-owned certification report;
- are collected deterministically - are collected deterministically;
- do not depend on external tools - do not require a guest-visible debug ABI;
- are consistent regardless of whether the Host HUD is active or not. - are consistent regardless of whether the Host HUD is active or not.

View File

@ -162,7 +162,7 @@ Without changing semantics.
## 11 Certification and Portability ## 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: If a cartridge:

View File

@ -110,44 +110,44 @@ This table describes content identity and storage layout, not live residency.
### 4.1 `TILES` asset contract in v1 ### 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` - `codec = NONE`
- pixels serializados usam índices de paleta `u4` empacotados - serialized pixels use packed `u4` palette indices
- paletas serializadas usam `RGB565` (`u16`, little-endian) - serialized palettes use `RGB565` (`u16`, little-endian)
- `palette_count = 64` - `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` - `RAW` is a legacy deprecated alias of `NONE`
- novos materiais publicados devem usar `NONE` como valor canônico - 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 #### 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` - `tile_size`: tile edge in pixels; valid values are `8`, `16`, or `32`
- `width`: largura total da folha do banco em pixels - `width`: total sheet width in pixels
- `height`: altura total da folha do banco em pixels - `height`: total sheet height in pixels
- `palette_count`: número de paletas serializadas para o banco - `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.codec`: codec/compressor-specific configuration (for example dictionaries or compression flags).
- `metadata.pipeline`: Metadados informativos do processo de build do Studio (ex: source hashes, timestamps, tool versions). - `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` - `palette_count` must be `64`
- `width * height` define o número de pixels indexados lógicos na folha decodificada - `width * height` defines the number of logical indexed pixels in the decoded sheet
- 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). - 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 #### 4.1.2 Payload Layout
@ -202,7 +202,8 @@ Rules:
- the visible contract MUST NOT expose byte-oriented bank occupancy as the canonical summary; - the visible contract MUST NOT expose byte-oriented bank occupancy as the canonical summary;
- canonical bank names are `GLYPH` and `SOUNDS`; - 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. - any residual byte accounting MAY exist internally, but it is not part of the visible bank telemetry contract.
## 6 Load Lifecycle ## 6 Load Lifecycle
@ -255,9 +256,9 @@ Therefore:
- shutting down a cartridge can release bank residency independently of VM heap behavior. - 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. - 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; - total bytes;
- used bytes; - used bytes;
@ -266,7 +267,11 @@ The runtime surfaces bank and slot statistics such as:
- slot occupancy; - slot occupancy;
- resident asset identity per slot. - 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 ## 9 Preload

View File

@ -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. 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 ## 1 Design Principles
The syscall ABI follows these rules: The syscall ABI follows these rules:
@ -199,6 +201,16 @@ For `asset.load`:
- `slot` is the target slot index; - `slot` is the target slot index;
- bank kind is resolved from `asset_table` by `asset_id`, not supplied by the caller. - 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) ### Composition surface (`composer`, v1)
The canonical frame-orchestration public ABI uses module `composer`. The canonical frame-orchestration public ABI uses module `composer`.

View File

@ -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. 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 ## 1 Error Model: Faults vs Status Returns
Syscalls use a status-first hybrid model. 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; - `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. - 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 ### No-op policy
@ -77,6 +80,14 @@ This means operational problems must not be reclassified as:
- `Trap`, when the failure is not structural; - `Trap`, when the failure is not structural;
- `Panic`, when the failure is not an internal invariant break. - `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 ## 2 Capability System
Each syscall requires a declared capability. Each syscall requires a declared capability.
@ -141,7 +152,7 @@ The system may account for:
- cycles spent in syscalls; - cycles spent in syscalls;
- allocations triggered by 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 ## 6 Blocking and Long Operations