Compare commits

...

7 Commits

Author SHA1 Message Date
2bfbe3b2be
implements PLN-0050 system pipeline evidence
All checks were successful
Intrepid/Prometeu/Runtime/pipeline/head This commit looks good
Intrepid/Prometeu/Runtime/pipeline/pr-master This commit looks good
2026-05-14 13:30:25 +01:00
6de583c9d4
implements PLN-0049 system profile abi gates 2026-05-14 13:28:34 +01:00
20e8439416
implements PLN-0048 minimal system hub pipeline 2026-05-14 13:27:05 +01:00
bea3f76aa7
implements PLN-0047 firmware profile dispatch 2026-05-14 13:25:24 +01:00
da908ffcb4
implements PLN-0046 profile separation spec 2026-05-14 13:24:09 +01:00
e3ac3dfd06
Runtime Mode Separation: Game and System 2026-05-14 13:22:34 +01:00
4d34e08c6c
Runtime Mode Separation: Game and System 2026-05-14 08:41:40 +01:00
14 changed files with 444 additions and 63 deletions

View File

@ -108,6 +108,7 @@ impl Firmware {
FirmwareState::HubHome(s) => s.on_enter(&mut req), FirmwareState::HubHome(s) => s.on_enter(&mut req),
FirmwareState::LoadCartridge(s) => s.on_enter(&mut req), FirmwareState::LoadCartridge(s) => s.on_enter(&mut req),
FirmwareState::GameRunning(s) => s.on_enter(&mut req), FirmwareState::GameRunning(s) => s.on_enter(&mut req),
FirmwareState::SystemRunning(s) => s.on_enter(&mut req),
FirmwareState::AppCrashes(s) => s.on_enter(&mut req), FirmwareState::AppCrashes(s) => s.on_enter(&mut req),
} }
} }
@ -134,6 +135,7 @@ impl Firmware {
FirmwareState::HubHome(s) => s.on_update(&mut req), FirmwareState::HubHome(s) => s.on_update(&mut req),
FirmwareState::LoadCartridge(s) => s.on_update(&mut req), FirmwareState::LoadCartridge(s) => s.on_update(&mut req),
FirmwareState::GameRunning(s) => s.on_update(&mut req), FirmwareState::GameRunning(s) => s.on_update(&mut req),
FirmwareState::SystemRunning(s) => s.on_update(&mut req),
FirmwareState::AppCrashes(s) => s.on_update(&mut req), FirmwareState::AppCrashes(s) => s.on_update(&mut req),
} }
} }
@ -155,6 +157,7 @@ impl Firmware {
FirmwareState::HubHome(s) => s.on_exit(&mut req), FirmwareState::HubHome(s) => s.on_exit(&mut req),
FirmwareState::LoadCartridge(s) => s.on_exit(&mut req), FirmwareState::LoadCartridge(s) => s.on_exit(&mut req),
FirmwareState::GameRunning(s) => s.on_exit(&mut req), FirmwareState::GameRunning(s) => s.on_exit(&mut req),
FirmwareState::SystemRunning(s) => s.on_exit(&mut req),
FirmwareState::AppCrashes(s) => s.on_exit(&mut req), FirmwareState::AppCrashes(s) => s.on_exit(&mut req),
} }
} }
@ -327,7 +330,7 @@ mod tests {
} }
#[test] #[test]
fn load_cartridge_routes_system_apps_back_to_hub_home() { fn load_cartridge_routes_system_apps_to_system_running() {
let mut firmware = Firmware::new(None); let mut firmware = Firmware::new(None);
let mut hardware = Hardware::new(); let mut hardware = Hardware::new();
let signals = InputSignals::default(); let signals = InputSignals::default();
@ -335,7 +338,25 @@ mod tests {
firmware.load_cartridge(valid_cartridge(AppMode::System)); firmware.load_cartridge(valid_cartridge(AppMode::System));
firmware.tick(&signals, &mut hardware); firmware.tick(&signals, &mut hardware);
assert!(matches!(firmware.state, FirmwareState::HubHome(_))); assert!(matches!(firmware.state, FirmwareState::SystemRunning(_)));
assert!(firmware.hub.window_manager.focused.is_some());
assert_eq!(firmware.hub.window_manager.windows.len(), 1);
}
#[test]
fn system_running_ticks_focused_system_app_through_hub_pipeline() {
let mut firmware = Firmware::new(None);
let mut hardware = Hardware::new();
let signals = InputSignals::default();
firmware.load_cartridge(valid_cartridge(AppMode::System));
firmware.tick(&signals, &mut hardware);
let tick_index_before_system_update = firmware.os.tick_index;
firmware.tick(&signals, &mut hardware);
assert!(matches!(firmware.state, FirmwareState::SystemRunning(_)));
assert_eq!(firmware.os.tick_index, tick_index_before_system_update + 1);
} }
#[test] #[test]

View File

@ -5,6 +5,7 @@ pub use crate::firmware::firmware_step_launch_hub::LaunchHubStep;
pub use crate::firmware::firmware_step_load_cartridge::LoadCartridgeStep; pub use crate::firmware::firmware_step_load_cartridge::LoadCartridgeStep;
pub use crate::firmware::firmware_step_reset::ResetStep; pub use crate::firmware::firmware_step_reset::ResetStep;
pub use crate::firmware::firmware_step_splash_screen::SplashScreenStep; pub use crate::firmware::firmware_step_splash_screen::SplashScreenStep;
pub use crate::firmware::firmware_step_system_running::SystemRunningStep;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum FirmwareState { pub enum FirmwareState {
@ -14,5 +15,6 @@ pub enum FirmwareState {
HubHome(HubHomeStep), HubHome(HubHomeStep),
LoadCartridge(LoadCartridgeStep), LoadCartridge(LoadCartridgeStep),
GameRunning(GameRunningStep), GameRunning(GameRunningStep),
SystemRunning(SystemRunningStep),
AppCrashes(AppCrashesStep), AppCrashes(AppCrashesStep),
} }

View File

@ -11,26 +11,8 @@ impl HubHomeStep {
} }
pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option<FirmwareState> { pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option<FirmwareState> {
let mut error = None; let outcome = ctx.hub.update_system_profile(ctx.os, ctx.vm, ctx.signals, ctx.hw);
if let Some(report) = outcome.crash {
// Always updates the Hub GUI (clears screen and processes input if no focus)
ctx.hub.gui_update(ctx.os, ctx.hw);
if let Some(focused_id) = ctx.hub.window_manager.focused {
if ctx.hw.pad().start().down {
ctx.hub.window_manager.remove_window(focused_id);
} else {
// System App runs here, drawing over the Hub background
error = ctx.os.tick(ctx.vm, ctx.signals, ctx.hw);
}
}
// Renders the System App window borders
ctx.hub.render(ctx.os, ctx.hw);
ctx.hw.gfx_mut().present();
if let Some(report) = error {
return Some(FirmwareState::AppCrashes(AppCrashesStep { report })); return Some(FirmwareState::AppCrashes(AppCrashesStep { report }));
} }

View File

@ -1,5 +1,5 @@
use crate::firmware::firmware_state::{ use crate::firmware::firmware_state::{
AppCrashesStep, FirmwareState, GameRunningStep, HubHomeStep, AppCrashesStep, FirmwareState, GameRunningStep, SystemRunningStep,
}; };
use crate::firmware::prometeu_context::PrometeuContext; use crate::firmware::prometeu_context::PrometeuContext;
use prometeu_hal::cartridge::{AppMode, Cartridge}; use prometeu_hal::cartridge::{AppMode, Cartridge};
@ -50,9 +50,7 @@ impl LoadCartridgeStep {
); );
ctx.hub.window_manager.set_focus(id); ctx.hub.window_manager.set_focus(id);
// System apps do not change the firmware state to GameRunning. return Some(FirmwareState::SystemRunning(SystemRunningStep));
// They run in the background or via windows in the Hub.
return Some(FirmwareState::HubHome(HubHomeStep));
} }
Some(FirmwareState::GameRunning(GameRunningStep)) Some(FirmwareState::GameRunning(GameRunningStep))

View File

@ -0,0 +1,27 @@
use crate::firmware::firmware_state::{AppCrashesStep, FirmwareState, HubHomeStep};
use crate::firmware::prometeu_context::PrometeuContext;
use prometeu_hal::log::{LogLevel, LogSource};
#[derive(Debug, Clone)]
pub struct SystemRunningStep;
impl SystemRunningStep {
pub fn on_enter(&mut self, ctx: &mut PrometeuContext) {
ctx.os.log(LogLevel::Info, LogSource::Hub, 0, "Entering SystemRunning".to_string());
}
pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option<FirmwareState> {
let outcome = ctx.hub.update_system_profile(ctx.os, ctx.vm, ctx.signals, ctx.hw);
if let Some(report) = outcome.crash {
return Some(FirmwareState::AppCrashes(AppCrashesStep { report }));
}
if !outcome.focused_window_active {
return Some(FirmwareState::HubHome(HubHomeStep));
}
None
}
pub fn on_exit(&mut self, _ctx: &mut PrometeuContext) {}
}

View File

@ -10,6 +10,7 @@ pub(crate) mod firmware_step_launch_hub;
pub(crate) mod firmware_step_load_cartridge; pub(crate) mod firmware_step_load_cartridge;
pub(crate) mod firmware_step_reset; pub(crate) mod firmware_step_reset;
pub(crate) mod firmware_step_splash_screen; pub(crate) mod firmware_step_splash_screen;
pub(crate) mod firmware_step_system_running;
mod prometeu_context; mod prometeu_context;
pub use boot_target::BootTarget; pub use boot_target::BootTarget;

View File

@ -1,15 +1,21 @@
use crate::VirtualMachineRuntime;
use crate::programs::prometeu_hub::window_manager::WindowManager; use crate::programs::prometeu_hub::window_manager::WindowManager;
use prometeu_hal::HardwareBridge; use crate::{CrashReport, VirtualMachineRuntime};
use prometeu_hal::color::Color; use prometeu_hal::color::Color;
use prometeu_hal::log::{LogLevel, LogSource}; use prometeu_hal::log::{LogLevel, LogSource};
use prometeu_hal::window::Rect; use prometeu_hal::window::Rect;
use prometeu_hal::{HardwareBridge, InputSignals};
use prometeu_vm::VirtualMachine;
/// PrometeuHub: Launcher and system UI environment. /// PrometeuHub: Launcher and system UI environment.
pub struct PrometeuHub { pub struct PrometeuHub {
pub window_manager: WindowManager, pub window_manager: WindowManager,
} }
pub struct SystemProfileUpdate {
pub crash: Option<CrashReport>,
pub focused_window_active: bool,
}
impl Default for PrometeuHub { impl Default for PrometeuHub {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
@ -75,4 +81,29 @@ impl PrometeuHub {
); );
} }
} }
pub fn update_system_profile(
&mut self,
os: &mut VirtualMachineRuntime,
vm: &mut VirtualMachine,
signals: &InputSignals,
hw: &mut dyn HardwareBridge,
) -> SystemProfileUpdate {
let mut crash = None;
self.gui_update(os, hw);
if let Some(focused_id) = self.window_manager.focused {
if hw.pad().start().down {
self.window_manager.remove_window(focused_id);
} else {
crash = os.tick(vm, signals, hw);
}
}
self.render(os, hw);
hw.gfx_mut().present();
SystemProfileUpdate { crash, focused_window_active: self.window_manager.focused.is_some() }
}
} }

View File

@ -84,6 +84,42 @@ impl VirtualMachineRuntime {
fn int_arg_to_u16_status(value: i64) -> Result<u16, ComposerOpStatus> { fn int_arg_to_u16_status(value: i64) -> Result<u16, ComposerOpStatus> {
u16::try_from(value).map_err(|_| ComposerOpStatus::ArgRangeInvalid) u16::try_from(value).map_err(|_| ComposerOpStatus::ArgRangeInvalid)
} }
fn is_game_profile_syscall(syscall: Syscall) -> bool {
matches!(
syscall,
Syscall::GfxClear
| Syscall::GfxFillRect
| Syscall::GfxDrawLine
| Syscall::GfxDrawCircle
| Syscall::GfxDrawDisc
| Syscall::GfxDrawSquare
| Syscall::GfxDrawText
| Syscall::GfxClear565
| Syscall::ComposerBindScene
| Syscall::ComposerUnbindScene
| Syscall::ComposerSetCamera
| Syscall::ComposerEmitSprite
| Syscall::AssetLoad
| Syscall::AssetStatus
| Syscall::AssetCommit
| Syscall::AssetCancel
| Syscall::BankInfo
)
}
fn ensure_game_profile_syscall(&self, syscall: Syscall) -> Result<(), VmFault> {
if Self::is_game_profile_syscall(syscall)
&& self.current_cartridge_app_mode != AppMode::Game
{
return Err(VmFault::Trap(
TRAP_INVALID_SYSCALL,
format!("{} is only available to the Game profile", syscall.name()),
));
}
Ok(())
}
} }
impl NativeInterface for VirtualMachineRuntime { impl NativeInterface for VirtualMachineRuntime {
@ -108,6 +144,8 @@ impl NativeInterface for VirtualMachineRuntime {
_ => {} _ => {}
} }
self.ensure_game_profile_syscall(syscall)?;
let hw = ctx.require_hw()?; let hw = ctx.require_hw()?;
match syscall { match syscall {

View File

@ -64,12 +64,16 @@ impl FsBackend for MemFsBackend {
} }
} }
fn cartridge_with_program(program: Vec<u8>, capabilities: u64) -> Cartridge { fn cartridge_with_program_and_mode(
program: Vec<u8>,
capabilities: u64,
app_mode: AppMode,
) -> Cartridge {
Cartridge { Cartridge {
app_id: 42, app_id: 42,
title: "Test Cart".into(), title: "Test Cart".into(),
app_version: "1.0.0".into(), app_version: "1.0.0".into(),
app_mode: AppMode::Game, app_mode,
capabilities, capabilities,
program, program,
assets: AssetsPayloadSource::empty(), assets: AssetsPayloadSource::empty(),
@ -78,6 +82,10 @@ fn cartridge_with_program(program: Vec<u8>, capabilities: u64) -> Cartridge {
} }
} }
fn cartridge_with_program(program: Vec<u8>, capabilities: u64) -> Cartridge {
cartridge_with_program_and_mode(program, capabilities, AppMode::Game)
}
fn serialized_single_function_module(code: Vec<u8>, syscalls: Vec<SyscallDecl>) -> Vec<u8> { fn serialized_single_function_module(code: Vec<u8>, syscalls: Vec<SyscallDecl>) -> Vec<u8> {
serialized_single_function_module_with_consts(code, vec![], syscalls) serialized_single_function_module_with_consts(code, vec![], syscalls)
} }
@ -260,6 +268,130 @@ fn tick_returns_error_when_vm_ends_slice_with_trap() {
assert!(matches!(runtime.last_crash_report, Some(CrashReport::VmTrap { .. }))); assert!(matches!(runtime.last_crash_report, Some(CrashReport::VmTrap { .. })));
} }
#[test]
fn tick_system_profile_rejects_gfx_game_surface() {
let mut runtime = VirtualMachineRuntime::new(None);
let mut vm = VirtualMachine::default();
let mut hardware = Hardware::new();
let signals = InputSignals::default();
let code = assemble("PUSH_I32 0\nHOSTCALL 0\nHALT").expect("assemble");
let program = serialized_single_function_module(
code,
vec![SyscallDecl {
module: "gfx".into(),
name: "clear".into(),
version: 1,
arg_slots: 1,
ret_slots: 0,
}],
);
let cartridge = cartridge_with_program_and_mode(program, caps::GFX, AppMode::System);
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
let report = runtime.tick(&mut vm, &signals, &mut hardware).expect("system gfx call must trap");
match report {
CrashReport::VmTrap { trap } => {
assert_eq!(trap.code, prometeu_bytecode::TRAP_INVALID_SYSCALL);
assert!(trap.message.contains("GfxClear is only available to the Game profile"));
}
other => panic!("expected VmTrap crash report, got {:?}", other),
}
}
#[test]
fn tick_system_profile_rejects_composer_game_surface() {
let mut runtime = VirtualMachineRuntime::new(None);
let mut vm = VirtualMachine::default();
let mut hardware = Hardware::new();
let signals = InputSignals::default();
let code = assemble("PUSH_I32 0\nHOSTCALL 0\nHALT").expect("assemble");
let program = serialized_single_function_module(
code,
vec![SyscallDecl {
module: "composer".into(),
name: "bind_scene".into(),
version: 1,
arg_slots: 1,
ret_slots: 1,
}],
);
let cartridge = cartridge_with_program_and_mode(program, caps::GFX, AppMode::System);
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
let report =
runtime.tick(&mut vm, &signals, &mut hardware).expect("system composer call must trap");
match report {
CrashReport::VmTrap { trap } => {
assert_eq!(trap.code, prometeu_bytecode::TRAP_INVALID_SYSCALL);
assert!(
trap.message.contains("ComposerBindScene is only available to the Game profile")
);
}
other => panic!("expected VmTrap crash report, got {:?}", other),
}
}
#[test]
fn tick_system_profile_rejects_bank_game_surface() {
let mut runtime = VirtualMachineRuntime::new(None);
let mut vm = VirtualMachine::default();
let mut hardware = Hardware::new();
let signals = InputSignals::default();
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_and_mode(program, caps::BANK, AppMode::System);
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
let report =
runtime.tick(&mut vm, &signals, &mut hardware).expect("system bank call must trap");
match report {
CrashReport::VmTrap { trap } => {
assert_eq!(trap.code, prometeu_bytecode::TRAP_INVALID_SYSCALL);
assert!(trap.message.contains("BankInfo is only available to the Game profile"));
}
other => panic!("expected VmTrap crash report, got {:?}", other),
}
}
#[test]
fn tick_system_profile_can_use_shared_log_transport() {
let mut runtime = VirtualMachineRuntime::new(None);
let mut vm = VirtualMachine::default();
let mut hardware = Hardware::new();
let signals = InputSignals::default();
let code = assemble("PUSH_I32 2\nPUSH_CONST 0\nHOSTCALL 0\nHALT").expect("assemble");
let program = serialized_single_function_module_with_consts(
code,
vec![ConstantPoolEntry::String("system log".into())],
vec![SyscallDecl {
module: "log".into(),
name: "write".into(),
version: 1,
arg_slots: 2,
ret_slots: 0,
}],
);
let cartridge = cartridge_with_program_and_mode(program, caps::LOG, AppMode::System);
runtime.initialize_vm(&mut vm, &cartridge).expect("runtime must initialize");
let report = runtime.tick(&mut vm, &signals, &mut hardware);
assert!(report.is_none(), "system log transport must remain internally shared");
assert!(vm.is_halted());
}
#[test] #[test]
fn tick_returns_panic_report_distinct_from_trap() { fn tick_returns_panic_report_distinct_from_trap() {
let mut runtime = VirtualMachineRuntime::new(None); let mut runtime = VirtualMachineRuntime::new(None);

View File

@ -1,31 +1,32 @@
{"type":"meta","next_id":{"DSC":31,"AGD":31,"DEC":23,"PLN":46,"LSN":40,"CLSN":1}} {"type":"meta","next_id":{"DSC":32,"AGD":32,"DEC":24,"PLN":51,"LSN":41,"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":"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":"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":"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":"discussion/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":"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":"DEC-0003-jenkins-gitea-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0003","file":"PLN-0003-jenkins-gitea-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0021","file":"discussion/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":"discussion/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-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":"discussion/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-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":"discussion/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":"discussion/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":"discussion/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":"discussion/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":"discussion/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":"discussion/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":"discussion/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":"discussion/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":"discussion/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":"discussion/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":"discussion/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":"discussion/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":"discussion/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":"discussion/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":"discussion/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":"discussion/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":"discussion/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":"discussion/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":"done","ticket":"runtime-edge-test-plan","title":"Agenda - Runtime Edge Test Plan","created_at":"2026-03-27","updated_at":"2026-04-21","tags":[],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0037","file":"lessons/DSC-0002-runtime-edge-test-plan/LSN-0037-domain-gates-must-be-owned-by-the-repository.md","status":"done","created_at":"2026-04-21","updated_at":"2026-04-21"}]} {"type":"discussion","id":"DSC-0002","status":"done","ticket":"runtime-edge-test-plan","title":"Agenda - Runtime Edge Test Plan","created_at":"2026-03-27","updated_at":"2026-04-21","tags":[],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0037","file":"discussion/lessons/DSC-0002-runtime-edge-test-plan/LSN-0037-domain-gates-must-be-owned-by-the-repository.md","status":"done","created_at":"2026-04-21","updated_at":"2026-04-21"}]}
{"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-0003","status":"open","ticket":"packed-cartridge-loader-pmc","title":"Agenda - Packed Cartridge Loader PMC","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0002","file":"AGD-0002-packed-cartridge-loader-pmc.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0004","status":"open","ticket":"system-run-cart","title":"Agenda - System Run Cart","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0003","file":"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-0004","status":"open","ticket":"system-run-cart","title":"Agenda - System Run Cart","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0003","file":"AGD-0003-system-run-cart.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0005","status":"open","ticket":"system-fault-semantics-and-control-surface","title":"Agenda - System Fault Semantics and Control Surface","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0004","file":"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-0005","status":"open","ticket":"system-fault-semantics-and-control-surface","title":"Agenda - System Fault Semantics and Control Surface","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0004","file":"AGD-0004-system-fault-semantics-and-control-surface.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0006","status":"open","ticket":"vm-owned-random-service","title":"Agenda - VM-Owned Random Service","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0005","file":"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-0006","status":"open","ticket":"vm-owned-random-service","title":"Agenda - VM-Owned Random Service","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0005","file":"AGD-0005-vm-owned-random-service.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0007","status":"open","ticket":"app-home-filesystem-surface-and-semantics","title":"Agenda - App Home Filesystem Surface and Semantics","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0006","file":"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-0007","status":"open","ticket":"app-home-filesystem-surface-and-semantics","title":"Agenda - App Home Filesystem Surface and Semantics","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0006","file":"AGD-0006-app-home-filesystem-surface-and-semantics.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0008","status":"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-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":"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":"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":"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":"discussion/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-0009","status":"open","ticket":"perf-async-background-work-lanes-for-assets-and-fs","title":"Agenda - [PERF] Async Background Work Lanes for Assets and FS","created_at":"2026-03-27","updated_at":"2026-03-27","tags":[],"agendas":[{"id":"AGD-0008","file":"AGD-0008-perf-async-background-work-lanes-for-assets-and-fs.md","status":"open","created_at":"2026-03-27","updated_at":"2026-03-27"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0010","status":"done","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-04-20","tags":[],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0036","file":"lessons/DSC-0010-perf-host-desktop-frame-pacing-and-presentation/LSN-0036-frame-publication-and-host-invalidation-must-be-separate.md","status":"done","created_at":"2026-04-20","updated_at":"2026-04-20"}]} {"type":"discussion","id":"DSC-0010","status":"done","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-04-20","tags":[],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0036","file":"discussion/lessons/DSC-0010-perf-host-desktop-frame-pacing-and-presentation/LSN-0036-frame-publication-and-host-invalidation-must-be-separate.md","status":"done","created_at":"2026-04-20","updated_at":"2026-04-20"}]}
{"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":"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":"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-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":"discussion/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":"discussion/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":"discussion/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":"discussion/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-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":"discussion/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-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":"discussion/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-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":"discussion/lessons/DSC-0028-deferred-overlay-and-primitive-composition/LSN-0033-debug-primitives-should-be-a-final-overlay-not-part-of-game-composition.md","status":"done","created_at":"2026-04-18","updated_at":"2026-04-18"}]}
{"type":"discussion","id":"DSC-0029","status":"done","ticket":"scene-bank-glyph-runtime-binding-leak","title":"Scene Bank Glyph Runtime Binding Leak","created_at":"2026-04-24","updated_at":"2026-04-24","tags":["gfx","runtime","asset","scene","glyph","format","architecture"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0038","file":"lessons/DSC-0029-scene-bank-glyph-runtime-binding-leak/LSN-0038-cold-scene-dependencies-must-bind-by-asset-identity.md","status":"done","created_at":"2026-04-24","updated_at":"2026-04-24"}]} {"type":"discussion","id":"DSC-0029","status":"done","ticket":"scene-bank-glyph-runtime-binding-leak","title":"Scene Bank Glyph Runtime Binding Leak","created_at":"2026-04-24","updated_at":"2026-04-24","tags":["gfx","runtime","asset","scene","glyph","format","architecture"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0038","file":"discussion/lessons/DSC-0029-scene-bank-glyph-runtime-binding-leak/LSN-0038-cold-scene-dependencies-must-bind-by-asset-identity.md","status":"done","created_at":"2026-04-24","updated_at":"2026-04-24"}]}
{"type":"discussion","id":"DSC-0014","status":"done","ticket":"perf-vm-allocation-and-copy-pressure","title":"Agenda - [PERF] VM Allocation and Copy Pressure","created_at":"2026-03-27","updated_at":"2026-04-20","tags":[],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0035","file":"lessons/DSC-0014-perf-vm-allocation-and-copy-pressure/LSN-0035-first-materialization-is-not-the-same-as-hot-path-copy-pressure.md","status":"done","created_at":"2026-04-20","updated_at":"2026-04-20"}]} {"type":"discussion","id":"DSC-0014","status":"done","ticket":"perf-vm-allocation-and-copy-pressure","title":"Agenda - [PERF] VM Allocation and Copy Pressure","created_at":"2026-03-27","updated_at":"2026-04-20","tags":[],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0035","file":"discussion/lessons/DSC-0014-perf-vm-allocation-and-copy-pressure/LSN-0035-first-materialization-is-not-the-same-as-hot-path-copy-pressure.md","status":"done","created_at":"2026-04-20","updated_at":"2026-04-20"}]}
{"type":"discussion","id":"DSC-0015","status":"abandoned","ticket":"perf-cartridge-boot-and-program-ownership","title":"Agenda - [PERF] Cartridge Boot and Program Ownership","created_at":"2026-03-27","updated_at":"2026-04-20","tags":[],"agendas":[{"id":"AGD-0014","file":"workflow/agendas/AGD-0014-perf-cartridge-boot-and-program-ownership.md","status":"abandoned","created_at":"2026-03-27","updated_at":"2026-04-20","_override_reason":"User explicitly chose to close the discussion without decision because FS->memory copy for the program is already acceptable."}],"decisions":[],"plans":[],"lessons":[],"_override_reason":"User explicitly chose to abandon the discussion without creating a decision because FS->memory copy for the program is already acceptable."} {"type":"discussion","id":"DSC-0015","status":"abandoned","ticket":"perf-cartridge-boot-and-program-ownership","title":"Agenda - [PERF] Cartridge Boot and Program Ownership","created_at":"2026-03-27","updated_at":"2026-04-20","tags":[],"agendas":[{"id":"AGD-0014","file":"AGD-0014-perf-cartridge-boot-and-program-ownership.md","status":"abandoned","created_at":"2026-03-27","updated_at":"2026-04-20","_override_reason":"User explicitly chose to close the discussion without decision because FS->memory copy for the program is already acceptable."}],"decisions":[],"plans":[],"lessons":[],"_override_reason":"User explicitly chose to abandon the discussion without creating a decision because FS->memory copy for the program is already acceptable."}
{"type":"discussion","id":"DSC-0016","status":"done","ticket":"tilemap-empty-cell-vs-tile-id-zero","title":"Tilemap Empty Cell vs Tile ID Zero","created_at":"2026-03-27","updated_at":"2026-04-09","tags":[],"agendas":[{"id":"AGD-0015","file":"workflow/agendas/AGD-0015-tilemap-empty-cell-vs-tile-id-zero.md","status":"done","created_at":"2026-03-27","updated_at":"2026-04-09"}],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0022","file":"lessons/DSC-0016-tilemap-empty-cell-semantics/LSN-0022-tilemap-empty-cell-convergence.md","status":"done","created_at":"2026-04-09","updated_at":"2026-04-09"}]} {"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":"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":"discussion/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-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":"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":"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":"discussion/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-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":"discussion/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"}]} {"type":"discussion","id":"DSC-0019","status":"done","ticket":"jenkinsfile-correction","title":"Jenkinsfile Correction and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins"],"agendas":[{"id":"AGD-0017","file":"AGD-0017-jenkinsfile-correction.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0002","file":"DEC-0002-jenkinsfile-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0002","file":"PLN-0002-jenkinsfile-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0020","file":"discussion/lessons/DSC-0019-jenkins-ci-standardization/LSN-0020-jenkins-standard-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]}
{"type":"discussion","id":"DSC-0030","status":"done","ticket":"internal-viewport-270p","title":"Agenda - Internal Viewport 270p (480x270)","created_at":"2026-04-27","updated_at":"2026-04-28","tags":["gfx","runtime","viewport","resolution","frame-composer","host"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0039","file":"lessons/DSC-0030-internal-viewport-270p/LSN-0039-resolution-baseline-changes-are-runtime-wide-contracts.md","status":"done","created_at":"2026-04-28","updated_at":"2026-04-28"}]} {"type":"discussion","id":"DSC-0030","status":"done","ticket":"internal-viewport-270p","title":"Agenda - Internal Viewport 270p (480x270)","created_at":"2026-04-27","updated_at":"2026-04-28","tags":["gfx","runtime","viewport","resolution","frame-composer","host"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0039","file":"discussion/lessons/DSC-0030-internal-viewport-270p/LSN-0039-resolution-baseline-changes-are-runtime-wide-contracts.md","status":"done","created_at":"2026-04-28","updated_at":"2026-04-28"}]}
{"type":"discussion","id":"DSC-0031","status":"done","ticket":"runtime-mode-separation-game-system","title":"Agenda - Runtime Mode Separation: Game and System","created_at":"2026-05-11","updated_at":"2026-05-14","tags":["runtime","firmware","hub","system-apps","game-mode","scheduler"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0040","file":"discussion/lessons/DSC-0031-runtime-mode-separation-game-system/LSN-0040-system-pipeline-separation.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14"}]}

View File

@ -0,0 +1,91 @@
---
id: LSN-0040
discussion: DSC-0031
decision: DEC-0023
title: System Pipeline Separation
status: done
created: 2026-05-14
tags: [runtime, firmware, hub, system-apps, game-mode, abi]
---
# System Pipeline Separation
`AppMode` is the profile discriminator. `Game` and `System` can share the VM
and syscall transport machinery internally, but they must not share the same
author-facing runtime profile.
The important boundary is not the instruction used to cross into host services.
The boundary is the profile contract selected by `manifest.json app_mode`:
- `Game` uses the game pipeline and owns game-facing surfaces such as GFX,
frame composition, asset/bank access, sprites, game input, and memcard-style
persistence.
- `System` uses a Runtime/Hub pipeline oriented around system UI and app
hosting.
- Future system APIs may use syscalls as transport, but the public contract is a
dedicated `System` ABI and stdlib/framework.
## What Changed
The cartridge and boot specs now state that `app_mode` is the discriminator and
that no separate manifest `target` field is involved.
Firmware now routes loaded `System` cartridges into `SystemRunning` instead of
`GameRunning` or an implicit HubHome fallback. That state delegates focused
system app updates to `PrometeuHub::update_system_profile`, which gives the Hub
domain a named system-profile pipeline entry point.
The game-running path remains isolated. `GameRunningStep` still owns the game
tick and presentation behavior.
Runtime syscall dispatch now rejects representative game-only surfaces when the
current cartridge profile is `System`. GFX, composer, asset/bank surfaces are
guarded as game-profile syscalls. Memcard calls keep their existing
status-first `AccessDenied` contract for non-game profiles. Log transport
remains internally shared, while future author-facing APIs are still expected to
resolve through profile-specific stdlibs.
## Evidence
The implementation evidence for this separation was:
- `cargo test -p prometeu-firmware`: 8 passed.
- `cargo test -p prometeu-system`: 48 passed.
- `cargo test -p prometeu-host-desktop-winit`: 25 passed, 5 ignored.
The firmware tests prove that:
- `AppMode::Game` still reaches `GameRunning`;
- `AppMode::System` reaches `SystemRunning`;
- a focused system app ticks through the Hub system-profile pipeline.
The runtime tests prove that:
- `System` profile cartridges cannot call representative game GFX, composer, or
bank surfaces;
- non-game memcard access remains status-first instead of crashing;
- log transport can remain shared internally.
## How To Think About It
Do not model system apps as games inside windows. Model them as apps hosted by
the Runtime/Hub environment.
The first split is deliberately small: it creates a stable place for system UI
execution without freezing the final WindowManager, lifecycle, component tree,
filesystem, or public PBS `System` ABI. Those are follow-up contracts.
When extending this area, ask which profile owns the author-facing capability
before deciding which syscall or internal runtime service carries it. Syscalls
are a transport mechanism. Profile ABI is the contract.
## References
- `DEC-0023` - System Pipeline Separation.
- `PLN-0046` - Profile Separation Specification.
- `PLN-0047` - Firmware Runtime Profile Dispatch.
- `PLN-0048` - Minimal System Hub Pipeline.
- `PLN-0049` - System Profile ABI Gates.
- `docs/specs/runtime/13-cartridge.md`
- `docs/specs/runtime/14-boot-profiles.md`
- `docs/specs/runtime/16-host-abi-and-syscalls.md`

View File

@ -75,6 +75,9 @@ If validation fails, cartridge loading is rejected before execution.
- `Game` - `Game`
- `System` - `System`
`app_mode` is the runtime profile discriminator. No separate manifest `target`
field participates in profile selection.
Cartridge boot is protocol-driven, not manifest-driven. Cartridge boot is protocol-driven, not manifest-driven.
The executable entrypoint of a valid cartridge is always `func_id = 0`. The executable entrypoint of a valid cartridge is always `func_id = 0`.
@ -85,6 +88,37 @@ This means:
- firmware/runtime loads the cartridge and starts execution from `func_id = 0`; - firmware/runtime loads the cartridge and starts execution from `func_id = 0`;
- nominal exports may exist for linking or introspection, but they do not hold boot authority. - nominal exports may exist for linking or introspection, but they do not hold boot authority.
## 5.1 Runtime Profiles
`Game` and `System` are distinct runtime profiles.
`Game` cartridges use the game pipeline. The game profile owns the public game
surfaces for frame composition, GFX, game input, banks, sprites, and
memory-card-style persistence.
`System` cartridges use a Runtime/Hub pipeline dedicated to system UI and app
hosting. The system profile must not inherit the game stdlib or game ABI as its
public authoring surface. In particular, `System` must not expose
`FrameComposer`, `Gfx`, game `Input`, banks, sprites, or game memory cards as
public profile APIs.
Future `System` APIs may be transported through syscalls, but syscall transport
does not define the conceptual ABI boundary. The author-facing contract for
system apps is a dedicated `System` profile ABI and stdlib/framework.
The following contracts are intentionally deferred from this cartridge chapter:
- complete WindowManager design;
- component tree and lifecycle semantics;
- rich invalidation and transitions;
- public PBS `System` ABI;
- multitasking and scheduler semantics;
- detailed `System` filesystem semantics.
The positive filesystem contract for `System` remains owned by the dedicated
filesystem discussion/spec work. The only profile rule fixed here is negative:
`System` does not use game memory cards as its public storage model.
## 6 Assets and Preload ## 6 Assets and Preload
`assets.pa` is the runtime-facing asset artifact consumed by the current runtime. `assets.pa` is the runtime-facing asset artifact consumed by the current runtime.

View File

@ -40,11 +40,18 @@ When the boot target is `Cartridge`:
For a cartridge boot target: For a cartridge boot target:
- `Game` cartridges transition into game-running flow; - `Game` cartridges transition into the game-running pipeline;
- `System` cartridges are initialized and integrated into the Hub/window path. - `System` cartridges transition into a Runtime/Hub pipeline dedicated to
system UI and app hosting.
This preserves the distinction between machine firmware state and app execution mode. This preserves the distinction between machine firmware state and app execution mode.
The `System` route must not be treated as the game pipeline running inside a
window. It is a separate runtime profile path oriented around Hub ownership and
WindowManager integration. The initial implementation may be minimal, but the
routing boundary is normative: `System` does not enter `GameRunning` as its
profile pipeline.
## 4 Host CLI Relationship ## 4 Host CLI Relationship
Typical host-facing boot intents are: Typical host-facing boot intents are:
@ -67,6 +74,10 @@ Boot target selection feeds into firmware states such as:
- `GameRunning` - `GameRunning`
- `AppCrashes` - `AppCrashes`
Additional firmware states may represent dedicated `System` profile execution.
Such states are part of the Runtime/Hub route and do not change the `Game`
pipeline contract.
Boot target is not itself a firmware state. It is an input to the firmware state machine. Boot target is not itself a firmware state. It is an input to the firmware state machine.
## 6 Debug Boot ## 6 Debug Boot

View File

@ -52,6 +52,12 @@ This identity is:
Input queries are VM-owned intrinsic calls in v1 and are outside the syscall identity space. Input queries are VM-owned intrinsic calls in v1 and are outside the syscall identity space.
Syscall identity is transport, not the conceptual boundary for runtime
profiles. `Game` and `System` may both use syscall transport internally or at
their ABI edge, but that does not make their public authoring surfaces the same
ABI. `System` APIs, when exposed to PBS or other guest languages, belong to a
dedicated `System` profile ABI and stdlib/framework.
## 3 Syscall Resolution ## 3 Syscall Resolution
The host maintains a registry: The host maintains a registry:
@ -173,6 +179,9 @@ Return shape must also follow the transversal status-first policy in [`16a-sysca
The game memcard profile uses module `mem` with status-first return shapes. The game memcard profile uses module `mem` with status-first return shapes.
`mem` is a domain layer backed by runtime `fs` (it is not a separate storage backend). `mem` is a domain layer backed by runtime `fs` (it is not a separate storage backend).
The `mem` module is a game profile surface. It is not the public storage model
for `System` profile apps.
Canonical operations in v1 are: Canonical operations in v1 are:
- `mem.slot_count() -> (status, count)` - `mem.slot_count() -> (status, count)`
@ -215,6 +224,9 @@ For `asset.load`:
The canonical frame-orchestration public ABI uses module `composer`. The canonical frame-orchestration public ABI uses module `composer`.
The `composer` module is a game profile surface. It is not inherited by
`System` profile apps as their public UI/composition API.
Canonical operations in v1 are: Canonical operations in v1 are:
- `composer.bind_scene(bank_id) -> (status)` - `composer.bind_scene(bank_id) -> (status)`