Compare commits
5 Commits
e01b463e1f
...
6dc8cb5372
| Author | SHA1 | Date | |
|---|---|---|---|
| 6dc8cb5372 | |||
| c34bbaf936 | |||
| e613ab43c9 | |||
| 4e05daf280 | |||
| 8098dbd7f4 |
@ -9,7 +9,6 @@ use prometeu_hal::log::LogSource;
|
|||||||
use prometeu_hal::telemetry::CertificationConfig;
|
use prometeu_hal::telemetry::CertificationConfig;
|
||||||
use prometeu_hal::{InputSignals, RuntimePlatform};
|
use prometeu_hal::{InputSignals, RuntimePlatform};
|
||||||
use prometeu_system::{PrometeuHub, ResidentGameState, SystemOS};
|
use prometeu_system::{PrometeuHub, ResidentGameState, SystemOS};
|
||||||
use prometeu_vm::VirtualMachine;
|
|
||||||
|
|
||||||
/// PROMETEU Firmware.
|
/// PROMETEU Firmware.
|
||||||
///
|
///
|
||||||
@ -27,8 +26,6 @@ use prometeu_vm::VirtualMachine;
|
|||||||
/// 2. Delegates the logic update to the current active state.
|
/// 2. Delegates the logic update to the current active state.
|
||||||
/// 3. Handles state transitions (e.g., from Loading to Playing).
|
/// 3. Handles state transitions (e.g., from Loading to Playing).
|
||||||
pub struct Firmware {
|
pub struct Firmware {
|
||||||
/// The execution engine (PVM) for user applications.
|
|
||||||
pub vm: VirtualMachine,
|
|
||||||
/// The underlying OS services (Syscalls, Filesystem, Telemetry).
|
/// The underlying OS services (Syscalls, Filesystem, Telemetry).
|
||||||
pub os: SystemOS,
|
pub os: SystemOS,
|
||||||
/// The internal state of the system launcher (Hub).
|
/// The internal state of the system launcher (Hub).
|
||||||
@ -46,7 +43,6 @@ impl Firmware {
|
|||||||
/// Initializes the firmware in the `Reset` state.
|
/// Initializes the firmware in the `Reset` state.
|
||||||
pub fn new(cap_config: Option<CertificationConfig>) -> Self {
|
pub fn new(cap_config: Option<CertificationConfig>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
vm: VirtualMachine::default(),
|
|
||||||
os: SystemOS::new(cap_config),
|
os: SystemOS::new(cap_config),
|
||||||
hub: PrometeuHub::new(),
|
hub: PrometeuHub::new(),
|
||||||
state: FirmwareState::Reset(ResetStep),
|
state: FirmwareState::Reset(ResetStep),
|
||||||
@ -122,7 +118,6 @@ impl Firmware {
|
|||||||
/// Dispatches the `on_enter` event to the current state implementation.
|
/// Dispatches the `on_enter` event to the current state implementation.
|
||||||
fn on_enter(&mut self, signals: &InputSignals, platform: &mut dyn RuntimePlatform) {
|
fn on_enter(&mut self, signals: &InputSignals, platform: &mut dyn RuntimePlatform) {
|
||||||
let mut req = PrometeuContext {
|
let mut req = PrometeuContext {
|
||||||
vm: &mut self.vm,
|
|
||||||
os: &mut self.os,
|
os: &mut self.os,
|
||||||
hub: &mut self.hub,
|
hub: &mut self.hub,
|
||||||
boot_target: &self.boot_target,
|
boot_target: &self.boot_target,
|
||||||
@ -150,7 +145,6 @@ impl Firmware {
|
|||||||
platform: &mut dyn RuntimePlatform,
|
platform: &mut dyn RuntimePlatform,
|
||||||
) -> Option<FirmwareState> {
|
) -> Option<FirmwareState> {
|
||||||
let mut req = PrometeuContext {
|
let mut req = PrometeuContext {
|
||||||
vm: &mut self.vm,
|
|
||||||
os: &mut self.os,
|
os: &mut self.os,
|
||||||
hub: &mut self.hub,
|
hub: &mut self.hub,
|
||||||
boot_target: &self.boot_target,
|
boot_target: &self.boot_target,
|
||||||
@ -173,7 +167,6 @@ impl Firmware {
|
|||||||
/// Dispatches the `on_exit` event to the current state implementation.
|
/// Dispatches the `on_exit` event to the current state implementation.
|
||||||
fn on_exit(&mut self, signals: &InputSignals, platform: &mut dyn RuntimePlatform) {
|
fn on_exit(&mut self, signals: &InputSignals, platform: &mut dyn RuntimePlatform) {
|
||||||
let mut req = PrometeuContext {
|
let mut req = PrometeuContext {
|
||||||
vm: &mut self.vm,
|
|
||||||
os: &mut self.os,
|
os: &mut self.os,
|
||||||
hub: &mut self.hub,
|
hub: &mut self.hub,
|
||||||
boot_target: &self.boot_target,
|
boot_target: &self.boot_target,
|
||||||
@ -532,6 +525,24 @@ mod tests {
|
|||||||
assert!(matches!(firmware.state, FirmwareState::LaunchHub(_)));
|
assert!(matches!(firmware.state, FirmwareState::LaunchHub(_)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reset_clears_vm_sessions_and_runtime_identity() {
|
||||||
|
let mut firmware = Firmware::new(None);
|
||||||
|
let mut platform = TestPlatform::new();
|
||||||
|
let signals = InputSignals::default();
|
||||||
|
|
||||||
|
firmware.load_cartridge(valid_cartridge(AppMode::Game));
|
||||||
|
firmware.tick(&signals, &mut platform);
|
||||||
|
|
||||||
|
assert_eq!(firmware.os.sessions().vm_session_count(), 1);
|
||||||
|
assert_eq!(firmware.os.vm().current_cartridge_title(), "Valid Cart");
|
||||||
|
|
||||||
|
firmware.change_state(FirmwareState::Reset(ResetStep), &signals, &mut platform);
|
||||||
|
|
||||||
|
assert_eq!(firmware.os.sessions().vm_session_count(), 0);
|
||||||
|
assert_eq!(firmware.os.vm().current_cartridge_title(), "");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn load_cartridge_routes_system_apps_to_system_running() {
|
fn load_cartridge_routes_system_apps_to_system_running() {
|
||||||
let mut firmware = Firmware::new(None);
|
let mut firmware = Firmware::new(None);
|
||||||
|
|||||||
@ -20,14 +20,29 @@ 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 outcome = ctx.hub.update_shell_profile(ctx.os, ctx.vm, ctx.signals, ctx.platform);
|
let outcome = ctx.hub.update_shell_profile(ctx.os, ctx.platform);
|
||||||
if let Some(report) = outcome.crash {
|
if let Some(report) = outcome.crash {
|
||||||
return Some(FirmwareState::AppCrashes(AppCrashesStep { report }));
|
return Some(FirmwareState::AppCrashes(AppCrashesStep { report }));
|
||||||
}
|
}
|
||||||
|
|
||||||
match outcome.action {
|
match outcome.action {
|
||||||
Some(SystemProfileAction::LaunchNativeShell(app)) => {
|
Some(SystemProfileAction::LaunchNativeShell(app)) => {
|
||||||
let task_id = ctx.os.sessions().create_native_shell_task(app.app_id(), app.title());
|
let task_id = match ctx
|
||||||
|
.os
|
||||||
|
.sessions()
|
||||||
|
.try_create_native_shell_task(app.app_id(), app.title())
|
||||||
|
{
|
||||||
|
Ok(task_id) => task_id,
|
||||||
|
Err(error) => {
|
||||||
|
ctx.os.log(
|
||||||
|
LogLevel::Warn,
|
||||||
|
LogSource::Hub,
|
||||||
|
0,
|
||||||
|
format!("Failed to launch native shell {}: {:?}", app.title(), error),
|
||||||
|
);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
let id = ctx.os.windows().add_window(
|
let id = ctx.os.windows().add_window(
|
||||||
app.title().to_string(),
|
app.title().to_string(),
|
||||||
WindowOwner::Task(task_id),
|
WindowOwner::Task(task_id),
|
||||||
|
|||||||
@ -38,13 +38,13 @@ impl LoadCartridgeStep {
|
|||||||
self.cartridge.assets.clone(),
|
self.cartridge.assets.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
match ctx.os.sessions().load_vm_cartridge(ctx.vm, &self.cartridge) {
|
match ctx.os.sessions().load_vm_cartridge(&self.cartridge) {
|
||||||
Ok(loaded) => {
|
Ok(loaded) => {
|
||||||
self.loaded_task_id = Some(loaded.task_id);
|
self.loaded_task_id = Some(loaded.task_id);
|
||||||
self.loaded_app_mode = Some(loaded.app_mode);
|
self.loaded_app_mode = Some(loaded.app_mode);
|
||||||
}
|
}
|
||||||
Err(report) => {
|
Err(report) => {
|
||||||
self.init_error = Some(report);
|
self.init_error = Some(report.into_crash_report("load VM cartridge into session"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,8 @@ pub struct ResetStep;
|
|||||||
impl ResetStep {
|
impl ResetStep {
|
||||||
pub fn on_enter(&mut self, ctx: &mut PrometeuContext) {
|
pub fn on_enter(&mut self, ctx: &mut PrometeuContext) {
|
||||||
ctx.os.log(LogLevel::Info, LogSource::Pos, 0, "Firmware Reset".to_string());
|
ctx.os.log(LogLevel::Info, LogSource::Pos, 0, "Firmware Reset".to_string());
|
||||||
ctx.os.vm().reset(ctx.vm);
|
ctx.os.sessions().clear_vm_sessions();
|
||||||
|
ctx.os.vm().reset_global_runtime_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option<FirmwareState> {
|
pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option<FirmwareState> {
|
||||||
|
|||||||
@ -1,10 +1,8 @@
|
|||||||
use crate::firmware::boot_target::BootTarget;
|
use crate::firmware::boot_target::BootTarget;
|
||||||
use prometeu_hal::{InputSignals, RuntimePlatform};
|
use prometeu_hal::{InputSignals, RuntimePlatform};
|
||||||
use prometeu_system::{PrometeuHub, SystemOS};
|
use prometeu_system::{PrometeuHub, SystemOS};
|
||||||
use prometeu_vm::VirtualMachine;
|
|
||||||
|
|
||||||
pub struct PrometeuContext<'a> {
|
pub struct PrometeuContext<'a> {
|
||||||
pub vm: &'a mut VirtualMachine,
|
|
||||||
pub os: &'a mut SystemOS,
|
pub os: &'a mut SystemOS,
|
||||||
pub hub: &'a mut PrometeuHub,
|
pub hub: &'a mut PrometeuHub,
|
||||||
pub boot_target: &'a BootTarget,
|
pub boot_target: &'a BootTarget,
|
||||||
|
|||||||
@ -40,7 +40,7 @@ pub enum DebugResponse {
|
|||||||
Breakpoints { pcs: Vec<usize> },
|
Breakpoints { pcs: Vec<usize> },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct HandshakeCartridge {
|
pub struct HandshakeCartridge {
|
||||||
pub app_id: u32,
|
pub app_id: u32,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
|||||||
@ -6,7 +6,7 @@ mod services;
|
|||||||
pub use crash_report::CrashReport;
|
pub use crash_report::CrashReport;
|
||||||
pub use os::{
|
pub use os::{
|
||||||
DEFAULT_GAME_PAUSE_BUDGET_TICKS, GameLifecycleEvent, GameLifecycleEventKind, LifecycleError,
|
DEFAULT_GAME_PAUSE_BUDGET_TICKS, GameLifecycleEvent, GameLifecycleEventKind, LifecycleError,
|
||||||
LifecycleOperation, SystemOS,
|
LifecycleOperation, SessionError, SystemOS,
|
||||||
};
|
};
|
||||||
pub use programs::{NativeShellApp, PrometeuHub, SystemProfileAction, SystemProfileUpdate};
|
pub use programs::{NativeShellApp, PrometeuHub, SystemProfileAction, SystemProfileUpdate};
|
||||||
pub use prometeu_hal::{RenderWorkerBackend, RenderWorkerFrameSink};
|
pub use prometeu_hal::{RenderWorkerBackend, RenderWorkerFrameSink};
|
||||||
|
|||||||
@ -6,6 +6,6 @@ mod window;
|
|||||||
|
|
||||||
pub use fs::FsFacade;
|
pub use fs::FsFacade;
|
||||||
pub use lifecycle::LifecycleFacade;
|
pub use lifecycle::LifecycleFacade;
|
||||||
pub use sessions::SessionsFacade;
|
pub use sessions::{SessionError, SessionsFacade};
|
||||||
pub use vm::VmFacade;
|
pub use vm::VmFacade;
|
||||||
pub use window::WindowFacade;
|
pub use window::WindowFacade;
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
use crate::CrashReport;
|
use crate::CrashReport;
|
||||||
use crate::os::LifecycleError;
|
use crate::os::LifecycleError;
|
||||||
use crate::os::SystemOS;
|
use crate::os::SystemOS;
|
||||||
|
use crate::process::ProcessId;
|
||||||
use crate::task::TaskId;
|
use crate::task::TaskId;
|
||||||
use crate::vm_session::{VmSession, VmSessionError};
|
use crate::vm_session::{VmSession, VmSessionError};
|
||||||
use prometeu_hal::app_mode::AppMode;
|
use prometeu_hal::app_mode::AppMode;
|
||||||
use prometeu_hal::cartridge::Cartridge;
|
use prometeu_hal::cartridge::Cartridge;
|
||||||
use prometeu_vm::VirtualMachine;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
@ -15,21 +15,55 @@ pub struct LoadedVmCartridge {
|
|||||||
pub reused_existing_session: bool,
|
pub reused_existing_session: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum SessionError {
|
||||||
|
Lifecycle(LifecycleError),
|
||||||
|
VmSession(VmSessionError),
|
||||||
|
TaskNotFound(TaskId),
|
||||||
|
ProcessNotFound(ProcessId),
|
||||||
|
MissingVmSession(TaskId),
|
||||||
|
Initialization(CrashReport),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SessionError {
|
||||||
|
pub fn into_crash_report(self, operation: &str) -> CrashReport {
|
||||||
|
match self {
|
||||||
|
Self::Initialization(report) => report,
|
||||||
|
error => {
|
||||||
|
CrashReport::VmPanic { message: format!("{operation} failed: {error:?}"), pc: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LifecycleError> for SessionError {
|
||||||
|
fn from(error: LifecycleError) -> Self {
|
||||||
|
Self::Lifecycle(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<VmSessionError> for SessionError {
|
||||||
|
fn from(error: VmSessionError) -> Self {
|
||||||
|
Self::VmSession(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct SessionsFacade<'a> {
|
pub struct SessionsFacade<'a> {
|
||||||
pub(in crate::os) os: &'a mut SystemOS,
|
pub(in crate::os) os: &'a mut SystemOS,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SessionsFacade<'a> {
|
impl<'a> SessionsFacade<'a> {
|
||||||
|
#[cfg(test)]
|
||||||
pub fn create_vm_game_task(&mut self, app_id: u32, title: impl Into<String>) -> TaskId {
|
pub fn create_vm_game_task(&mut self, app_id: u32, title: impl Into<String>) -> TaskId {
|
||||||
self.try_create_vm_game_task(app_id, title)
|
self.try_create_vm_game_task(app_id, title)
|
||||||
.expect("newly created game task should have an associated process")
|
.expect("test/setup VM game creation should succeed")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_create_vm_game_task(
|
pub fn try_create_vm_game_task(
|
||||||
&mut self,
|
&mut self,
|
||||||
app_id: u32,
|
app_id: u32,
|
||||||
title: impl Into<String>,
|
title: impl Into<String>,
|
||||||
) -> Result<TaskId, LifecycleError> {
|
) -> Result<TaskId, SessionError> {
|
||||||
if let Some(session) = self.os.vm_sessions.resident_game_for_app(app_id) {
|
if let Some(session) = self.os.vm_sessions.resident_game_for_app(app_id) {
|
||||||
return Ok(session.task_id);
|
return Ok(session.task_id);
|
||||||
}
|
}
|
||||||
@ -38,43 +72,54 @@ impl<'a> SessionsFacade<'a> {
|
|||||||
let process_id = self.os.process_manager.spawn_vm_game(app_id, title.clone());
|
let process_id = self.os.process_manager.spawn_vm_game(app_id, title.clone());
|
||||||
let task_id = self.os.task_manager.create_game_task(process_id, app_id, title);
|
let task_id = self.os.task_manager.create_game_task(process_id, app_id, title);
|
||||||
self.os.lifecycle().set_game_foreground_task(task_id)?;
|
self.os.lifecycle().set_game_foreground_task(task_id)?;
|
||||||
self.create_vm_session_for_task(task_id)
|
self.try_create_vm_session_for_task(task_id)?;
|
||||||
.expect("newly created game task should create a VM session");
|
|
||||||
|
|
||||||
Ok(task_id)
|
Ok(task_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
pub fn create_vm_shell_task(&mut self, app_id: u32, title: impl Into<String>) -> TaskId {
|
pub fn create_vm_shell_task(&mut self, app_id: u32, title: impl Into<String>) -> TaskId {
|
||||||
|
self.try_create_vm_shell_task(app_id, title)
|
||||||
|
.expect("test/setup VM shell creation should succeed")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_create_vm_shell_task(
|
||||||
|
&mut self,
|
||||||
|
app_id: u32,
|
||||||
|
title: impl Into<String>,
|
||||||
|
) -> Result<TaskId, SessionError> {
|
||||||
let title = title.into();
|
let title = title.into();
|
||||||
let process_id = self.os.process_manager.spawn_vm_shell(app_id, title.clone());
|
let process_id = self.os.process_manager.spawn_vm_shell(app_id, title.clone());
|
||||||
let task_id = self.os.task_manager.create_shell_task(process_id, app_id, title);
|
let task_id = self.os.task_manager.create_shell_task(process_id, app_id, title);
|
||||||
self.os
|
self.os.lifecycle().set_shell_foreground_task(task_id)?;
|
||||||
.lifecycle()
|
self.try_create_vm_session_for_task(task_id)?;
|
||||||
.set_shell_foreground_task(task_id)
|
|
||||||
.expect("newly created shell task should have an associated process");
|
|
||||||
self.create_vm_session_for_task(task_id)
|
|
||||||
.expect("newly created shell task should create a VM session");
|
|
||||||
|
|
||||||
task_id
|
Ok(task_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
pub fn create_native_shell_task(&mut self, app_id: u32, title: impl Into<String>) -> TaskId {
|
pub fn create_native_shell_task(&mut self, app_id: u32, title: impl Into<String>) -> TaskId {
|
||||||
|
self.try_create_native_shell_task(app_id, title)
|
||||||
|
.expect("test/setup native shell creation should succeed")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_create_native_shell_task(
|
||||||
|
&mut self,
|
||||||
|
app_id: u32,
|
||||||
|
title: impl Into<String>,
|
||||||
|
) -> Result<TaskId, SessionError> {
|
||||||
let title = title.into();
|
let title = title.into();
|
||||||
let process_id = self.os.process_manager.spawn_native_shell(app_id, title.clone());
|
let process_id = self.os.process_manager.spawn_native_shell(app_id, title.clone());
|
||||||
let task_id = self.os.task_manager.create_shell_task(process_id, app_id, title);
|
let task_id = self.os.task_manager.create_shell_task(process_id, app_id, title);
|
||||||
self.os
|
self.os.lifecycle().set_shell_foreground_task(task_id)?;
|
||||||
.lifecycle()
|
|
||||||
.set_shell_foreground_task(task_id)
|
|
||||||
.expect("newly created native shell task should have an associated process");
|
|
||||||
|
|
||||||
task_id
|
Ok(task_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_vm_cartridge(
|
pub fn load_vm_cartridge(
|
||||||
&mut self,
|
&mut self,
|
||||||
legacy_vm: &mut VirtualMachine,
|
|
||||||
cartridge: &Cartridge,
|
cartridge: &Cartridge,
|
||||||
) -> Result<LoadedVmCartridge, CrashReport> {
|
) -> Result<LoadedVmCartridge, SessionError> {
|
||||||
let existing_game_task = if cartridge.app_mode == AppMode::Game {
|
let existing_game_task = if cartridge.app_mode == AppMode::Game {
|
||||||
self.os
|
self.os
|
||||||
.vm_sessions
|
.vm_sessions
|
||||||
@ -87,20 +132,15 @@ impl<'a> SessionsFacade<'a> {
|
|||||||
let (task_id, reused_existing_session) = if let Some(task_id) = existing_game_task {
|
let (task_id, reused_existing_session) = if let Some(task_id) = existing_game_task {
|
||||||
(task_id, true)
|
(task_id, true)
|
||||||
} else if cartridge.app_mode == AppMode::Shell {
|
} else if cartridge.app_mode == AppMode::Shell {
|
||||||
(self.create_vm_shell_task(cartridge.app_id, cartridge.title.clone()), false)
|
(self.try_create_vm_shell_task(cartridge.app_id, cartridge.title.clone())?, false)
|
||||||
} else {
|
} else {
|
||||||
let task_id = self
|
let task_id =
|
||||||
.try_create_vm_game_task(cartridge.app_id, cartridge.title.clone())
|
self.try_create_vm_game_task(cartridge.app_id, cartridge.title.clone())?;
|
||||||
.map_err(|error| CrashReport::VmPanic {
|
|
||||||
message: format!("failed to create VM game task: {error:?}"),
|
|
||||||
pc: None,
|
|
||||||
})?;
|
|
||||||
(task_id, false)
|
(task_id, false)
|
||||||
};
|
};
|
||||||
|
|
||||||
if !reused_existing_session {
|
if !reused_existing_session {
|
||||||
self.initialize_session_cartridge(task_id, cartridge)?;
|
self.try_initialize_session_cartridge(task_id, cartridge)?;
|
||||||
self.initialize_legacy_active_vm(legacy_vm, cartridge)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(LoadedVmCartridge { task_id, app_mode: cartridge.app_mode, reused_existing_session })
|
Ok(LoadedVmCartridge { task_id, app_mode: cartridge.app_mode, reused_existing_session })
|
||||||
@ -122,13 +162,18 @@ impl<'a> SessionsFacade<'a> {
|
|||||||
self.os.vm_sessions.len()
|
self.os.vm_sessions.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_vm_session_for_task(&mut self, task_id: TaskId) -> Result<(), VmSessionError> {
|
pub fn clear_vm_sessions(&mut self) {
|
||||||
let task = self.os.task_manager.get(task_id).expect("session task should exist").clone();
|
self.os.vm_sessions.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_create_vm_session_for_task(&mut self, task_id: TaskId) -> Result<(), SessionError> {
|
||||||
|
let task =
|
||||||
|
self.os.task_manager.get(task_id).ok_or(SessionError::TaskNotFound(task_id))?.clone();
|
||||||
let process = self
|
let process = self
|
||||||
.os
|
.os
|
||||||
.process_manager
|
.process_manager
|
||||||
.get(task.process_id)
|
.get(task.process_id)
|
||||||
.expect("session process should exist")
|
.ok_or(SessionError::ProcessNotFound(task.process_id))?
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
let cap_config = self.os.vm_runtime.certifier.config;
|
let cap_config = self.os.vm_runtime.certifier.config;
|
||||||
@ -137,33 +182,28 @@ impl<'a> SessionsFacade<'a> {
|
|||||||
.vm_sessions
|
.vm_sessions
|
||||||
.create_for_task(&task, &process, "", Some(cap_config), logs_count)
|
.create_for_task(&task, &process, "", Some(cap_config), logs_count)
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
|
.map_err(SessionError::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_session_cartridge(
|
pub fn try_initialize_session_cartridge(
|
||||||
&mut self,
|
&mut self,
|
||||||
task_id: TaskId,
|
task_id: TaskId,
|
||||||
cartridge: &Cartridge,
|
cartridge: &Cartridge,
|
||||||
) -> Result<(), CrashReport> {
|
) -> Result<(), SessionError> {
|
||||||
let session = self
|
let session = self
|
||||||
.os
|
.os
|
||||||
.vm_sessions
|
.vm_sessions
|
||||||
.for_task_mut(task_id)
|
.for_task_mut(task_id)
|
||||||
.expect("loaded VM task should have a VM session");
|
.ok_or(SessionError::MissingVmSession(task_id))?;
|
||||||
|
|
||||||
session.app_id = cartridge.app_id;
|
session.app_id = cartridge.app_id;
|
||||||
session.title = cartridge.title.clone();
|
session.title = cartridge.title.clone();
|
||||||
session.app_version = cartridge.app_version.clone();
|
session.app_version = cartridge.app_version.clone();
|
||||||
session.app_mode = cartridge.app_mode;
|
session.app_mode = cartridge.app_mode;
|
||||||
session.clear_cartridge_service_state();
|
session.clear_cartridge_service_state();
|
||||||
session.runtime.initialize_vm(&mut self.os.log_service, &mut session.vm, cartridge)
|
session
|
||||||
}
|
.runtime
|
||||||
|
.initialize_vm(&mut self.os.log_service, &mut session.vm, cartridge)
|
||||||
fn initialize_legacy_active_vm(
|
.map_err(SessionError::Initialization)
|
||||||
&mut self,
|
|
||||||
legacy_vm: &mut VirtualMachine,
|
|
||||||
cartridge: &Cartridge,
|
|
||||||
) -> Result<(), CrashReport> {
|
|
||||||
self.os.clear_cartridge_service_state();
|
|
||||||
self.os.vm_runtime.initialize_vm(&mut self.os.log_service, legacy_vm, cartridge)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,14 +3,13 @@ use crate::os::{GameLifecycleEvent, SystemOS};
|
|||||||
use crate::task::TaskId;
|
use crate::task::TaskId;
|
||||||
use crate::vm_session::VmSession;
|
use crate::vm_session::VmSession;
|
||||||
use crate::{RenderWorkerConfig, RenderWorkerController};
|
use crate::{RenderWorkerConfig, RenderWorkerController};
|
||||||
|
use prometeu_bytecode::Value;
|
||||||
use prometeu_hal::app_mode::AppMode;
|
use prometeu_hal::app_mode::AppMode;
|
||||||
use prometeu_hal::cartridge::Cartridge;
|
|
||||||
use prometeu_hal::telemetry::{CertificationConfig, TelemetryFrame};
|
use prometeu_hal::telemetry::{CertificationConfig, TelemetryFrame};
|
||||||
use prometeu_hal::{
|
use prometeu_hal::{
|
||||||
FrameId, InputSignals, RenderOwnership, RenderWorkerBackend, RenderWorkerError,
|
FrameId, InputSignals, RenderOwnership, RenderWorkerBackend, RenderWorkerError,
|
||||||
RenderWorkerFrameSink, RuntimePlatform,
|
RenderWorkerFrameSink, RuntimePlatform,
|
||||||
};
|
};
|
||||||
use prometeu_vm::VirtualMachine;
|
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
pub struct VmFacade<'a> {
|
pub struct VmFacade<'a> {
|
||||||
@ -18,36 +17,6 @@ pub struct VmFacade<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> VmFacade<'a> {
|
impl<'a> VmFacade<'a> {
|
||||||
pub fn initialize(
|
|
||||||
&mut self,
|
|
||||||
vm: &mut VirtualMachine,
|
|
||||||
cartridge: &Cartridge,
|
|
||||||
) -> Result<(), CrashReport> {
|
|
||||||
self.os.clear_cartridge_service_state();
|
|
||||||
self.os.vm_runtime.initialize_vm(&mut self.os.log_service, vm, cartridge)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tick(
|
|
||||||
&mut self,
|
|
||||||
vm: &mut VirtualMachine,
|
|
||||||
signals: &InputSignals,
|
|
||||||
platform: &mut dyn RuntimePlatform,
|
|
||||||
) -> Option<CrashReport> {
|
|
||||||
self.deliver_pending_game_lifecycle_events();
|
|
||||||
|
|
||||||
self.os.vm_runtime.tick(
|
|
||||||
&mut self.os.log_service,
|
|
||||||
&mut self.os.fs,
|
|
||||||
&mut self.os.fs_state,
|
|
||||||
&mut self.os.memcard,
|
|
||||||
&mut self.os.open_files,
|
|
||||||
&mut self.os.next_handle,
|
|
||||||
vm,
|
|
||||||
signals,
|
|
||||||
platform,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tick_session(
|
pub fn tick_session(
|
||||||
&mut self,
|
&mut self,
|
||||||
task_id: TaskId,
|
task_id: TaskId,
|
||||||
@ -76,25 +45,6 @@ impl<'a> VmFacade<'a> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deliver_pending_game_lifecycle_events(&mut self) {
|
|
||||||
if self.os.vm_runtime.current_cartridge_app_mode == AppMode::Game {
|
|
||||||
let resident_game_task = self.os.foreground_stack.resident_game_task();
|
|
||||||
let mut retained = Vec::new();
|
|
||||||
let mut delivered = Vec::new();
|
|
||||||
|
|
||||||
for event in self.os.game_lifecycle_events.drain(..) {
|
|
||||||
if Some(event.task_id) == resident_game_task {
|
|
||||||
delivered.push(event);
|
|
||||||
} else {
|
|
||||||
retained.push(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.os.game_lifecycle_events = retained;
|
|
||||||
self.os.vm_runtime.deliver_game_lifecycle_events(delivered);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deliver_pending_game_lifecycle_events_to_session(&mut self, task_id: TaskId) {
|
fn deliver_pending_game_lifecycle_events_to_session(&mut self, task_id: TaskId) {
|
||||||
let Some(session) = self.os.vm_sessions.for_task(task_id) else {
|
let Some(session) = self.os.vm_sessions.for_task(task_id) else {
|
||||||
return;
|
return;
|
||||||
@ -121,16 +71,59 @@ impl<'a> VmFacade<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&mut self, vm: &mut VirtualMachine) {
|
pub fn reset_global_runtime_state(&mut self) {
|
||||||
self.os.vm_runtime.reset(vm);
|
self.os.clear_cartridge_service_state();
|
||||||
|
self.os.vm_runtime.clear_cartridge_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn debug_step_instruction(
|
pub fn debug_step_active_session(
|
||||||
&mut self,
|
&mut self,
|
||||||
vm: &mut VirtualMachine,
|
|
||||||
platform: &mut dyn RuntimePlatform,
|
platform: &mut dyn RuntimePlatform,
|
||||||
) -> Option<CrashReport> {
|
) -> Option<CrashReport> {
|
||||||
self.os.vm_runtime.debug_step_instruction(&mut self.os.log_service, vm, platform)
|
let Some(task_id) = self.active_session_task_id() else {
|
||||||
|
return Some(CrashReport::VmPanic {
|
||||||
|
message: "debug step requested without an active VM session".to_string(),
|
||||||
|
pc: None,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
let Some(session) = self.os.vm_sessions.for_task_mut(task_id) else {
|
||||||
|
return Some(CrashReport::VmPanic {
|
||||||
|
message: format!("active debug task {:?} has no VM session", task_id),
|
||||||
|
pc: None,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
session.runtime.debug_step_instruction(&mut self.os.log_service, &mut session.vm, platform)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn active_pc(&self) -> Option<usize> {
|
||||||
|
self.active_session().map(|session| session.vm.pc())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn active_operand_stack_top(&self, n: usize) -> Vec<Value> {
|
||||||
|
self.active_session().map(|session| session.vm.operand_stack_top(n)).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_active_breakpoint(&mut self, pc: usize) -> bool {
|
||||||
|
if let Some(session) = self.active_session_mut() {
|
||||||
|
session.vm.insert_breakpoint(pc);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_active_breakpoint(&mut self, pc: usize) -> bool {
|
||||||
|
if let Some(session) = self.active_session_mut() {
|
||||||
|
session.vm.remove_breakpoint(pc);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn active_breakpoints_list(&self) -> Vec<usize> {
|
||||||
|
self.active_session().map(|session| session.vm.breakpoints_list()).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn paused(&self) -> bool {
|
pub fn paused(&self) -> bool {
|
||||||
@ -314,11 +307,14 @@ impl<'a> VmFacade<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn active_session_mut(&mut self) -> Option<&mut VmSession> {
|
fn active_session_mut(&mut self) -> Option<&mut VmSession> {
|
||||||
let task_id = self
|
let task_id = self.active_session_task_id()?;
|
||||||
.os
|
|
||||||
.task_manager
|
|
||||||
.foreground_task()
|
|
||||||
.or_else(|| self.os.foreground_stack.resident_game_task())?;
|
|
||||||
self.os.vm_sessions.for_task_mut(task_id)
|
self.os.vm_sessions.for_task_mut(task_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn active_session_task_id(&self) -> Option<TaskId> {
|
||||||
|
self.os
|
||||||
|
.task_manager
|
||||||
|
.foreground_task()
|
||||||
|
.or_else(|| self.os.foreground_stack.resident_game_task())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,9 @@ mod facades;
|
|||||||
mod lifecycle;
|
mod lifecycle;
|
||||||
mod system_os;
|
mod system_os;
|
||||||
|
|
||||||
pub use facades::{FsFacade, LifecycleFacade, SessionsFacade, VmFacade, WindowFacade};
|
pub use facades::{
|
||||||
|
FsFacade, LifecycleFacade, SessionError, SessionsFacade, VmFacade, WindowFacade,
|
||||||
|
};
|
||||||
pub use lifecycle::{
|
pub use lifecycle::{
|
||||||
DEFAULT_GAME_PAUSE_BUDGET_TICKS, GameLifecycleEvent, GameLifecycleEventKind, LifecycleError,
|
DEFAULT_GAME_PAUSE_BUDGET_TICKS, GameLifecycleEvent, GameLifecycleEventKind, LifecycleError,
|
||||||
LifecycleOperation,
|
LifecycleOperation,
|
||||||
|
|||||||
@ -143,17 +143,33 @@ impl SystemOS {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::os::{GameLifecycleEventKind, LifecycleError, LifecycleOperation};
|
use crate::os::{GameLifecycleEventKind, LifecycleError, LifecycleOperation, SessionError};
|
||||||
use crate::process::{ProcessKind, ProcessState};
|
use crate::process::{ProcessId, ProcessKind, ProcessState};
|
||||||
use crate::services::foreground::{ForegroundOwner, ResidentGame, ResidentGameState};
|
use crate::services::foreground::{ForegroundOwner, ResidentGame, ResidentGameState};
|
||||||
use crate::task::{TaskId, TaskState};
|
use crate::task::{TaskId, TaskState};
|
||||||
use prometeu_hal::app_mode::AppMode;
|
use prometeu_hal::app_mode::AppMode;
|
||||||
|
use prometeu_hal::cartridge::{AssetsPayloadSource, Cartridge};
|
||||||
|
use prometeu_hal::syscalls::caps;
|
||||||
|
|
||||||
fn process_state_for_task(os: &SystemOS, task_id: TaskId) -> ProcessState {
|
fn process_state_for_task(os: &SystemOS, task_id: TaskId) -> ProcessState {
|
||||||
let task = os.task_manager.get(task_id).expect("task should exist");
|
let task = os.task_manager.get(task_id).expect("task should exist");
|
||||||
os.process_manager.get(task.process_id).expect("process should exist").state
|
os.process_manager.get(task.process_id).expect("process should exist").state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn session_test_cartridge(app_mode: AppMode, app_id: u32, title: &str) -> Cartridge {
|
||||||
|
Cartridge {
|
||||||
|
app_id,
|
||||||
|
title: title.to_string(),
|
||||||
|
app_version: "1.0.0".to_string(),
|
||||||
|
app_mode,
|
||||||
|
capabilities: caps::NONE,
|
||||||
|
program: vec![],
|
||||||
|
assets: AssetsPayloadSource::empty(),
|
||||||
|
asset_table: vec![],
|
||||||
|
preload: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_foreground_task_marks_task_and_process_running() {
|
fn set_foreground_task_marks_task_and_process_running() {
|
||||||
let mut os = SystemOS::new(None);
|
let mut os = SystemOS::new(None);
|
||||||
@ -295,6 +311,43 @@ mod tests {
|
|||||||
assert_eq!(shell_session.open_files.get(&1).map(String::as_str), Some("/shell.dat"));
|
assert_eq!(shell_session.open_files.get(&1).map(String::as_str), Some("/shell.dat"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vm_debug_operations_target_foreground_shell_before_resident_game() {
|
||||||
|
let mut os = SystemOS::new(None);
|
||||||
|
let game = os.sessions().create_vm_game_task(42, "Sector Crawl");
|
||||||
|
os.lifecycle().request_home_from_game(game).expect("home request should succeed");
|
||||||
|
os.lifecycle().advance_game_pause_budget(game).expect("budget should expire");
|
||||||
|
let shell = os.sessions().create_vm_shell_task(7, "Settings");
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut sessions = os.sessions();
|
||||||
|
let game_session =
|
||||||
|
sessions.vm_session_for_task_mut(game).expect("game VM session should exist");
|
||||||
|
game_session.vm.insert_breakpoint(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(os.vm().insert_active_breakpoint(20));
|
||||||
|
assert_eq!(os.vm().active_breakpoints_list(), vec![20]);
|
||||||
|
|
||||||
|
let sessions = os.sessions();
|
||||||
|
assert_eq!(
|
||||||
|
sessions
|
||||||
|
.vm_session_for_task(game)
|
||||||
|
.expect("game VM session should exist")
|
||||||
|
.vm
|
||||||
|
.breakpoints_list(),
|
||||||
|
vec![10]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
sessions
|
||||||
|
.vm_session_for_task(shell)
|
||||||
|
.expect("shell VM session should exist")
|
||||||
|
.vm
|
||||||
|
.breakpoints_list(),
|
||||||
|
vec![20]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn home_request_notifies_game_before_budget_suspends_it() {
|
fn home_request_notifies_game_before_budget_suspends_it() {
|
||||||
let mut os = SystemOS::new(None);
|
let mut os = SystemOS::new(None);
|
||||||
@ -416,6 +469,60 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vm_shell_load_returns_typed_error_when_foreground_shell_exists() {
|
||||||
|
let mut os = SystemOS::new(None);
|
||||||
|
let first = os.sessions().create_vm_shell_task(7, "Settings");
|
||||||
|
let cartridge = session_test_cartridge(AppMode::Shell, 8, "Files");
|
||||||
|
|
||||||
|
let error = os.sessions().load_vm_cartridge(&cartridge).expect_err("load should fail");
|
||||||
|
|
||||||
|
match error {
|
||||||
|
SessionError::Lifecycle(LifecycleError::ForegroundShellAlreadyExists {
|
||||||
|
existing,
|
||||||
|
requested,
|
||||||
|
}) => {
|
||||||
|
assert_eq!(existing, first);
|
||||||
|
assert_ne!(requested, first);
|
||||||
|
}
|
||||||
|
other => panic!("expected foreground shell lifecycle error, got {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vm_session_creation_returns_typed_error_for_missing_task() {
|
||||||
|
let mut os = SystemOS::new(None);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
os.sessions().try_create_vm_session_for_task(TaskId(999)),
|
||||||
|
Err(SessionError::TaskNotFound(TaskId(999)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vm_session_creation_returns_typed_error_for_missing_process() {
|
||||||
|
let mut os = SystemOS::new(None);
|
||||||
|
let task_id = os.task_manager.create_game_task(ProcessId(999), 42, "Broken");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
os.sessions().try_create_vm_session_for_task(task_id),
|
||||||
|
Err(SessionError::ProcessNotFound(ProcessId(999)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn session_cartridge_initialization_returns_typed_error_for_missing_session() {
|
||||||
|
let mut os = SystemOS::new(None);
|
||||||
|
let process_id = os.process_manager.spawn_vm_game(42, "Sector Crawl");
|
||||||
|
let task_id = os.task_manager.create_game_task(process_id, 42, "Sector Crawl");
|
||||||
|
let cartridge = session_test_cartridge(AppMode::Game, 42, "Sector Crawl");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
os.sessions().try_initialize_session_cartridge(task_id, &cartridge),
|
||||||
|
Err(SessionError::MissingVmSession(task_id))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn closing_foreground_shell_returns_to_hub() {
|
fn closing_foreground_shell_returns_to_hub() {
|
||||||
let mut os = SystemOS::new(None);
|
let mut os = SystemOS::new(None);
|
||||||
|
|||||||
@ -6,7 +6,6 @@ use prometeu_hal::{
|
|||||||
FrameId, GfxUiCommand, InputPlatform, InputSignals, RenderSubmission, RuntimePlatform,
|
FrameId, GfxUiCommand, InputPlatform, InputSignals, RenderSubmission, RuntimePlatform,
|
||||||
ShellUiFramePacket,
|
ShellUiFramePacket,
|
||||||
};
|
};
|
||||||
use prometeu_vm::VirtualMachine;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
const SHELL_A_BUTTON: Rect = Rect { x: 112, y: 172, w: 112, h: 32 };
|
const SHELL_A_BUTTON: Rect = Rect { x: 112, y: 172, w: 112, h: 32 };
|
||||||
@ -175,24 +174,17 @@ impl PrometeuHub {
|
|||||||
pub fn update_shell_profile(
|
pub fn update_shell_profile(
|
||||||
&mut self,
|
&mut self,
|
||||||
os: &mut SystemOS,
|
os: &mut SystemOS,
|
||||||
vm: &mut VirtualMachine,
|
|
||||||
signals: &InputSignals,
|
|
||||||
platform: &mut dyn RuntimePlatform,
|
platform: &mut dyn RuntimePlatform,
|
||||||
) -> SystemProfileUpdate {
|
) -> SystemProfileUpdate {
|
||||||
let mut crash = None;
|
|
||||||
let mut action = self.gui_update(os, platform);
|
let mut action = self.gui_update(os, platform);
|
||||||
|
|
||||||
if os.windows().focused_window().is_some() {
|
if os.windows().focused_window().is_some() && platform.input().pad().start().down {
|
||||||
if platform.input().pad().start().down {
|
|
||||||
action = Some(SystemProfileAction::CloseShell);
|
action = Some(SystemProfileAction::CloseShell);
|
||||||
} else if action != Some(SystemProfileAction::CloseShell) {
|
|
||||||
crash = os.vm().tick(vm, signals, platform);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.render(os, platform);
|
self.render(os, platform);
|
||||||
|
|
||||||
SystemProfileUpdate { crash, action }
|
SystemProfileUpdate { crash: None, action }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_vm_shell_profile(
|
pub fn update_vm_shell_profile(
|
||||||
|
|||||||
@ -138,6 +138,10 @@ impl VmSessionRegistry {
|
|||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.sessions.is_empty()
|
self.sessions.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.sessions.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app_mode_for_task_and_process(
|
fn app_mode_for_task_and_process(
|
||||||
|
|||||||
@ -28,6 +28,8 @@ pub struct HostDebugger {
|
|||||||
last_telemetry_frame: u64,
|
last_telemetry_frame: u64,
|
||||||
/// Last fault summary sent to the debugger client.
|
/// Last fault summary sent to the debugger client.
|
||||||
last_fault_summary: Option<String>,
|
last_fault_summary: Option<String>,
|
||||||
|
/// Debug cartridge metadata captured before the VM session exists.
|
||||||
|
debug_cartridge: Option<HandshakeCartridge>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for HostDebugger {
|
impl Default for HostDebugger {
|
||||||
@ -46,19 +48,25 @@ impl HostDebugger {
|
|||||||
last_log_seq: 0,
|
last_log_seq: 0,
|
||||||
last_telemetry_frame: 0,
|
last_telemetry_frame: 0,
|
||||||
last_fault_summary: None,
|
last_fault_summary: None,
|
||||||
|
debug_cartridge: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configures the debugger based on the boot target.
|
/// Configures the debugger based on the boot target.
|
||||||
/// If debug mode is enabled, it binds to the specified TCP port.
|
/// If debug mode is enabled, it binds to the specified TCP port.
|
||||||
pub fn setup_boot_target(&mut self, boot_target: &BootTarget, firmware: &mut Firmware) {
|
pub fn setup_boot_target(&mut self, boot_target: &BootTarget, _firmware: &mut Firmware) {
|
||||||
if let BootTarget::Cartridge { path, debug: true, debug_port } = boot_target {
|
if let BootTarget::Cartridge { path, debug: true, debug_port } = boot_target {
|
||||||
self.waiting_for_start = true;
|
self.waiting_for_start = true;
|
||||||
|
|
||||||
// Pre-load cartridge metadata so the Handshake message can contain
|
// Pre-load cartridge metadata so the Handshake message can contain
|
||||||
// valid information about the App being debugged.
|
// valid information before the VM session is created.
|
||||||
if let Ok(cartridge) = CartridgeLoader::load(path) {
|
if let Ok(cartridge) = CartridgeLoader::load(path) {
|
||||||
let _ = firmware.os.vm().initialize(&mut firmware.vm, &cartridge);
|
self.debug_cartridge = Some(HandshakeCartridge {
|
||||||
|
app_id: cartridge.app_id,
|
||||||
|
title: cartridge.title,
|
||||||
|
app_version: cartridge.app_version,
|
||||||
|
app_mode: cartridge.app_mode,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
match TcpListener::bind(format!("127.0.0.1:{}", debug_port)) {
|
match TcpListener::bind(format!("127.0.0.1:{}", debug_port)) {
|
||||||
@ -116,12 +124,7 @@ impl HostDebugger {
|
|||||||
let handshake = DebugResponse::Handshake {
|
let handshake = DebugResponse::Handshake {
|
||||||
protocol_version: DEVTOOLS_PROTOCOL_VERSION,
|
protocol_version: DEVTOOLS_PROTOCOL_VERSION,
|
||||||
runtime_version: "0.1".to_string(),
|
runtime_version: "0.1".to_string(),
|
||||||
cartridge: HandshakeCartridge {
|
cartridge: self.handshake_cartridge(firmware),
|
||||||
app_id: firmware.os.vm().current_app_id(),
|
|
||||||
title: firmware.os.vm().current_cartridge_title(),
|
|
||||||
app_version: firmware.os.vm().current_cartridge_app_version(),
|
|
||||||
app_mode: firmware.os.vm().current_cartridge_app_mode(),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
self.send_response(handshake);
|
self.send_response(handshake);
|
||||||
} else {
|
} else {
|
||||||
@ -201,7 +204,7 @@ impl HostDebugger {
|
|||||||
DebugCommand::Step => {
|
DebugCommand::Step => {
|
||||||
// Execute exactly one instruction and keep paused.
|
// Execute exactly one instruction and keep paused.
|
||||||
firmware.os.vm().set_paused(true);
|
firmware.os.vm().set_paused(true);
|
||||||
let _ = firmware.os.vm().debug_step_instruction(&mut firmware.vm, platform);
|
let _ = firmware.os.vm().debug_step_active_session(platform);
|
||||||
}
|
}
|
||||||
DebugCommand::StepFrame => {
|
DebugCommand::StepFrame => {
|
||||||
// Execute until the end of the current logical frame.
|
// Execute until the end of the current logical frame.
|
||||||
@ -210,10 +213,10 @@ impl HostDebugger {
|
|||||||
}
|
}
|
||||||
DebugCommand::GetState => {
|
DebugCommand::GetState => {
|
||||||
// Return detailed VM register and stack state.
|
// Return detailed VM register and stack state.
|
||||||
let stack_top = firmware.vm.operand_stack_top(10);
|
let stack_top = firmware.os.vm().active_operand_stack_top(10);
|
||||||
|
|
||||||
let resp = DebugResponse::GetState {
|
let resp = DebugResponse::GetState {
|
||||||
pc: firmware.vm.pc(),
|
pc: firmware.os.vm().active_pc().unwrap_or(0),
|
||||||
stack_top,
|
stack_top,
|
||||||
frame_index: firmware.os.vm().logical_frame_index(),
|
frame_index: firmware.os.vm().logical_frame_index(),
|
||||||
app_id: firmware.os.vm().current_app_id(),
|
app_id: firmware.os.vm().current_app_id(),
|
||||||
@ -221,18 +224,37 @@ impl HostDebugger {
|
|||||||
self.send_response(resp);
|
self.send_response(resp);
|
||||||
}
|
}
|
||||||
DebugCommand::SetBreakpoint { pc } => {
|
DebugCommand::SetBreakpoint { pc } => {
|
||||||
firmware.vm.insert_breakpoint(pc);
|
let _ = firmware.os.vm().insert_active_breakpoint(pc);
|
||||||
}
|
}
|
||||||
DebugCommand::ListBreakpoints => {
|
DebugCommand::ListBreakpoints => {
|
||||||
let pcs = firmware.vm.breakpoints_list();
|
let pcs = firmware.os.vm().active_breakpoints_list();
|
||||||
self.send_response(DebugResponse::Breakpoints { pcs });
|
self.send_response(DebugResponse::Breakpoints { pcs });
|
||||||
}
|
}
|
||||||
DebugCommand::ClearBreakpoint { pc } => {
|
DebugCommand::ClearBreakpoint { pc } => {
|
||||||
firmware.vm.remove_breakpoint(pc);
|
let _ = firmware.os.vm().remove_active_breakpoint(pc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handshake_cartridge(&self, firmware: &mut Firmware) -> HandshakeCartridge {
|
||||||
|
let app_id = firmware.os.vm().current_app_id();
|
||||||
|
if app_id != 0 {
|
||||||
|
return HandshakeCartridge {
|
||||||
|
app_id,
|
||||||
|
title: firmware.os.vm().current_cartridge_title(),
|
||||||
|
app_version: firmware.os.vm().current_cartridge_app_version(),
|
||||||
|
app_mode: firmware.os.vm().current_cartridge_app_mode(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
self.debug_cartridge.clone().unwrap_or_else(|| HandshakeCartridge {
|
||||||
|
app_id,
|
||||||
|
title: firmware.os.vm().current_cartridge_title(),
|
||||||
|
app_version: firmware.os.vm().current_cartridge_app_version(),
|
||||||
|
app_mode: firmware.os.vm().current_cartridge_app_mode(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn cert_event_from_snapshot(
|
pub(crate) fn cert_event_from_snapshot(
|
||||||
tag: u16,
|
tag: u16,
|
||||||
telemetry: TelemetryFrame,
|
telemetry: TelemetryFrame,
|
||||||
@ -315,7 +337,7 @@ impl HostDebugger {
|
|||||||
// Map specific internal log tags to protocol events.
|
// Map specific internal log tags to protocol events.
|
||||||
if event.tag == 0xDEB1 {
|
if event.tag == 0xDEB1 {
|
||||||
self.send_event(DebugEvent::BreakpointHit {
|
self.send_event(DebugEvent::BreakpointHit {
|
||||||
pc: firmware.vm.pc(),
|
pc: firmware.os.vm().active_pc().unwrap_or(0),
|
||||||
frame_index: firmware.os.vm().logical_frame_index(),
|
frame_index: firmware.os.vm().logical_frame_index(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -409,6 +431,52 @@ mod tests {
|
|||||||
assert!(firmware.os.vm().debug_step_requested());
|
assert!(firmware.os.vm().debug_step_requested());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn breakpoint_commands_mutate_active_vm_session() {
|
||||||
|
let mut debugger = HostDebugger::new();
|
||||||
|
let mut firmware = Firmware::new(None);
|
||||||
|
let mut platform = TestPlatform::new();
|
||||||
|
let task_id = firmware
|
||||||
|
.os
|
||||||
|
.sessions()
|
||||||
|
.try_create_vm_game_task(42, "Sector Crawl")
|
||||||
|
.expect("debugger test should create a game session");
|
||||||
|
|
||||||
|
debugger.handle_command(
|
||||||
|
DebugCommand::SetBreakpoint { pc: 123 },
|
||||||
|
&mut firmware,
|
||||||
|
&mut platform,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
firmware
|
||||||
|
.os
|
||||||
|
.sessions()
|
||||||
|
.vm_session_for_task(task_id)
|
||||||
|
.expect("game VM session should exist")
|
||||||
|
.vm
|
||||||
|
.breakpoints_list(),
|
||||||
|
vec![123]
|
||||||
|
);
|
||||||
|
|
||||||
|
debugger.handle_command(
|
||||||
|
DebugCommand::ClearBreakpoint { pc: 123 },
|
||||||
|
&mut firmware,
|
||||||
|
&mut platform,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
firmware
|
||||||
|
.os
|
||||||
|
.sessions()
|
||||||
|
.vm_session_for_task(task_id)
|
||||||
|
.expect("game VM session should exist")
|
||||||
|
.vm
|
||||||
|
.breakpoints_list()
|
||||||
|
.is_empty()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn handle_command_start_leaves_waiting_for_start_mode() {
|
fn handle_command_start_leaves_waiting_for_start_mode() {
|
||||||
let mut debugger = HostDebugger::new();
|
let mut debugger = HostDebugger::new();
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
{"type":"meta","next_id":{"DSC":44,"AGD":47,"DEC":39,"PLN":154,"LSN":52,"CLSN":1}}
|
{"type":"meta","next_id":{"DSC":44,"AGD":47,"DEC":39,"PLN":157,"LSN":53,"CLSN":1}}
|
||||||
{"type":"discussion","id":"DSC-0043","status":"open","ticket":"system-os-cartridge-switch-orchestrator","title":"SystemOS Cartridge Switch Orchestrator","created_at":"2026-07-03","updated_at":"2026-07-03","tags":["runtime","os","lifecycle","game","cartridge","architecture"],"agendas":[{"id":"AGD-0044","file":"AGD-0044-systemos-cartridge-switch-orchestrator.md","status":"open","created_at":"2026-07-03","updated_at":"2026-07-03"}],"decisions":[],"plans":[],"lessons":[]}
|
{"type":"discussion","id":"DSC-0043","status":"open","ticket":"system-os-cartridge-switch-orchestrator","title":"SystemOS Cartridge Switch Orchestrator","created_at":"2026-07-03","updated_at":"2026-07-03","tags":["runtime","os","lifecycle","game","cartridge","architecture"],"agendas":[{"id":"AGD-0044","file":"AGD-0044-systemos-cartridge-switch-orchestrator.md","status":"open","created_at":"2026-07-03","updated_at":"2026-07-03"}],"decisions":[],"plans":[],"lessons":[]}
|
||||||
{"type":"discussion","id":"DSC-0039","status":"abandoned","ticket":"render-pipeline-family-and-future-3d","title":"Render Pipeline Family and Future 3D","created_at":"2026-06-04","updated_at":"2026-06-04","tags":["gfx","renderer","runtime","architecture","pipeline"],"agendas":[{"id":"AGD-0039","file":"AGD-0039-render-pipeline-family-and-future-3d.md","status":"abandoned","created_at":"2026-06-04","updated_at":"2026-06-04","_override_reason":"User explicitly chose to close this agenda without a new decision because DSC-0038 already established enough architecture for future extension, and 3D is intentionally deferred."}],"decisions":[],"plans":[],"lessons":[],"_override_reason":"User explicitly chose to close this agenda without a new decision because DSC-0038 already established enough architecture for future extension, and 3D is intentionally deferred."}
|
{"type":"discussion","id":"DSC-0039","status":"abandoned","ticket":"render-pipeline-family-and-future-3d","title":"Render Pipeline Family and Future 3D","created_at":"2026-06-04","updated_at":"2026-06-04","tags":["gfx","renderer","runtime","architecture","pipeline"],"agendas":[{"id":"AGD-0039","file":"AGD-0039-render-pipeline-family-and-future-3d.md","status":"abandoned","created_at":"2026-06-04","updated_at":"2026-06-04","_override_reason":"User explicitly chose to close this agenda without a new decision because DSC-0038 already established enough architecture for future extension, and 3D is intentionally deferred."}],"decisions":[],"plans":[],"lessons":[],"_override_reason":"User explicitly chose to close this agenda without a new decision because DSC-0038 already established enough architecture for future extension, and 3D is intentionally deferred."}
|
||||||
{"type":"discussion","id":"DSC-0040","status":"done","ticket":"vm-render-parallel-execution-boundary","title":"VM and Render Parallel Execution Boundary","created_at":"2026-06-04","updated_at":"2026-06-06","tags":["runtime","renderer","vm","concurrency","architecture","perf"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0048","file":"discussion/lessons/DSC-0040-vm-render-parallel-execution-boundary/LSN-0048-render-workers-need-a-closed-packet-contract.md","status":"done","created_at":"2026-06-06","updated_at":"2026-06-06"}]}
|
{"type":"discussion","id":"DSC-0040","status":"done","ticket":"vm-render-parallel-execution-boundary","title":"VM and Render Parallel Execution Boundary","created_at":"2026-06-04","updated_at":"2026-06-06","tags":["runtime","renderer","vm","concurrency","architecture","perf"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0048","file":"discussion/lessons/DSC-0040-vm-render-parallel-execution-boundary/LSN-0048-render-workers-need-a-closed-packet-contract.md","status":"done","created_at":"2026-06-06","updated_at":"2026-06-06"}]}
|
||||||
{"type":"discussion","id":"DSC-0041","status":"in_progress","ticket":"foreground-stack-game-pause-shell-vm-backed","title":"Foreground Stack, Game Pause, and VM-Backed Shell Coexistence","created_at":"2026-06-05","updated_at":"2026-07-04","tags":["runtime","os","lifecycle","shell","game","vm","foreground","architecture"],"agendas":[{"id":"AGD-0041","file":"AGD-0041-foreground-stack-game-pause-shell-vm-backed.md","status":"accepted","created_at":"2026-06-05","updated_at":"2026-07-03"},{"id":"AGD-0046","file":"AGD-0046-vm-context-ownership-for-resident-game-and-vm-backed-shell.md","status":"accepted","created_at":"2026-07-04","updated_at":"2026-07-04"}],"decisions":[{"id":"DEC-0037","file":"DEC-0037-foreground-stack-and-game-pause-contract.md","status":"accepted","created_at":"2026-07-03","updated_at":"2026-07-03","ref_agenda":"AGD-0041"},{"id":"DEC-0038","file":"DEC-0038-vm-session-ownership-for-vm-backed-processes.md","status":"accepted","created_at":"2026-07-04","updated_at":"2026-07-04","ref_agenda":"AGD-0046"}],"plans":[{"id":"PLN-0136","file":"PLN-0136-route-desktop-home-key-outside-guest-input.md","status":"done","created_at":"2026-07-03","updated_at":"2026-07-03","ref_decisions":["DEC-0037"]},{"id":"PLN-0137","file":"PLN-0137-implement-systemos-foreground-stack-state.md","status":"done","created_at":"2026-07-03","updated_at":"2026-07-03","ref_decisions":["DEC-0037"]},{"id":"PLN-0138","file":"PLN-0138-specify-foreground-pause-and-home-contract.md","status":"done","created_at":"2026-07-03","updated_at":"2026-07-03","ref_decisions":["DEC-0037"]},{"id":"PLN-0139","file":"PLN-0139-validate-game-home-shell-game-end-to-end.md","status":"done","created_at":"2026-07-03","updated_at":"2026-07-03","ref_decisions":["DEC-0037"]},{"id":"PLN-0140","file":"PLN-0140-deliver-game-pause-resume-and-suspension.md","status":"done","created_at":"2026-07-03","updated_at":"2026-07-03","ref_decisions":["DEC-0037"]},{"id":"PLN-0141","file":"PLN-0141-integrate-render-audio-and-input-pause-boundaries.md","status":"done","created_at":"2026-07-03","updated_at":"2026-07-03","ref_decisions":["DEC-0037"]},{"id":"PLN-0142","file":"PLN-0142-deliver-game-lifecycle-events-to-vm-runtime.md","status":"done","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0037"]},{"id":"PLN-0143","file":"PLN-0143-guarantee-cooperative-pause-budget-tick-before-suspension.md","status":"done","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0037"]},{"id":"PLN-0144","file":"PLN-0144-suspend-resident-game-when-shell-takes-foreground.md","status":"done","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0037"]},{"id":"PLN-0145","file":"PLN-0145-centralize-resident-game-resume-transition.md","status":"done","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0037"]},{"id":"PLN-0146","file":"PLN-0146-consolidate-lifecycle-process-lookup-helpers.md","status":"done","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0037"]},{"id":"PLN-0147","file":"PLN-0147-move-native-shell-profile-update-into-hub.md","status":"done","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0037"]},{"id":"PLN-0148","file":"PLN-0148-introduce-vm-session-registry-and-identity.md","status":"done","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0038"]},{"id":"PLN-0149","file":"PLN-0149-move-vm-runtime-state-into-vm-sessions.md","status":"done","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0038"]},{"id":"PLN-0150","file":"PLN-0150-route-cartridge-loading-through-vm-sessions.md","status":"done","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0038"]},{"id":"PLN-0151","file":"PLN-0151-schedule-foreground-vm-session-ticks.md","status":"done","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0038"]},{"id":"PLN-0152","file":"PLN-0152-validate-game-and-vm-shell-session-coexistence.md","status":"done","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0038"]},{"id":"PLN-0153","file":"PLN-0153-specify-vm-session-ownership-and-background-ready-scheduling.md","status":"done","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0038"]}],"lessons":[]}
|
{"type":"discussion","id":"DSC-0041","status":"done","ticket":"foreground-stack-game-pause-shell-vm-backed","title":"Foreground Stack, Game Pause, and VM-Backed Shell Coexistence","created_at":"2026-06-05","updated_at":"2026-07-05","tags":["runtime","os","lifecycle","shell","game","vm","foreground","architecture"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0052","file":"discussion/lessons/DSC-0041-foreground-stack-game-pause-shell-vm-backed/LSN-0052-foreground-ownership-and-vm-session-ownership-are-separate.md","status":"done","created_at":"2026-07-05","updated_at":"2026-07-05"}]}
|
||||||
{"type":"discussion","id":"DSC-0042","status":"done","ticket":"real-render-worker-establishment","title":"Real Render Worker Establishment","created_at":"2026-06-06","updated_at":"2026-06-20","tags":["runtime","renderer","worker","concurrency","host","hal","architecture"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0049","file":"discussion/lessons/DSC-0042-real-render-worker-establishment/LSN-0049-render-worker-publication-boundary.md","status":"done","created_at":"2026-06-20","updated_at":"2026-06-20"}]}
|
{"type":"discussion","id":"DSC-0042","status":"done","ticket":"real-render-worker-establishment","title":"Real Render Worker Establishment","created_at":"2026-06-06","updated_at":"2026-06-20","tags":["runtime","renderer","worker","concurrency","host","hal","architecture"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0049","file":"discussion/lessons/DSC-0042-real-render-worker-establishment/LSN-0049-render-worker-publication-boundary.md","status":"done","created_at":"2026-06-20","updated_at":"2026-06-20"}]}
|
||||||
{"type":"discussion","id":"DSC-0038","status":"done","ticket":"render-frame-packet-boundary","title":"Logical Render Pipelines and Command Packets","created_at":"2026-05-25","updated_at":"2026-06-04","tags":["gfx","renderer","runtime","frame-composer","architecture","ui","pipeline"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0047","file":"discussion/lessons/DSC-0038-render-frame-packet-boundary/LSN-0047-typed-render-submissions-preserve-domain-boundaries.md","status":"done","created_at":"2026-06-04","updated_at":"2026-06-04"}]}
|
{"type":"discussion","id":"DSC-0038","status":"done","ticket":"render-frame-packet-boundary","title":"Logical Render Pipelines and Command Packets","created_at":"2026-05-25","updated_at":"2026-06-04","tags":["gfx","renderer","runtime","frame-composer","architecture","ui","pipeline"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0047","file":"discussion/lessons/DSC-0038-render-frame-packet-boundary/LSN-0047-typed-render-submissions-preserve-domain-boundaries.md","status":"done","created_at":"2026-06-04","updated_at":"2026-06-04"}]}
|
||||||
{"type":"discussion","id":"DSC-0035","status":"done","ticket":"task-owned-shell-windows","title":"Agenda - Task-Owned Shell Windows","created_at":"2026-05-15","updated_at":"2026-05-15","tags":["runtime","os","task","window-manager","shell","lifecycle"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0044","file":"discussion/lessons/DSC-0035-task-owned-shell-windows/LSN-0044-task-window-liveness-belongs-to-the-task.md","status":"done","created_at":"2026-05-15","updated_at":"2026-05-15"}]}
|
{"type":"discussion","id":"DSC-0035","status":"done","ticket":"task-owned-shell-windows","title":"Agenda - Task-Owned Shell Windows","created_at":"2026-05-15","updated_at":"2026-05-15","tags":["runtime","os","task","window-manager","shell","lifecycle"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0044","file":"discussion/lessons/DSC-0035-task-owned-shell-windows/LSN-0044-task-window-liveness-belongs-to-the-task.md","status":"done","created_at":"2026-05-15","updated_at":"2026-05-15"}]}
|
||||||
|
|||||||
@ -0,0 +1,145 @@
|
|||||||
|
---
|
||||||
|
id: LSN-0052
|
||||||
|
ticket: foreground-stack-game-pause-shell-vm-backed
|
||||||
|
title: Foreground Ownership and VM Session Ownership Are Separate
|
||||||
|
created: 2026-07-05
|
||||||
|
tags: [runtime, os, lifecycle, shell, game, vm, foreground, architecture]
|
||||||
|
---
|
||||||
|
|
||||||
|
# LSN-0052: Foreground Ownership and VM Session Ownership Are Separate
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
DSC-0041 closed the `Game -> Home/Shell -> same Game` lifecycle for the first
|
||||||
|
runtime implementation that supports both native Shell apps and VM-backed Shell
|
||||||
|
apps. The important architectural shift was not only pausing the Game. The
|
||||||
|
runtime also had to stop treating one firmware-owned VM as the place where all
|
||||||
|
guest execution state lives.
|
||||||
|
|
||||||
|
The final model separates two questions:
|
||||||
|
|
||||||
|
- Who owns the foreground visual/input authority right now?
|
||||||
|
- Which VM-backed process owns a mutable VM execution context?
|
||||||
|
|
||||||
|
Foreground ownership is global SystemOS policy. VM execution state belongs to a
|
||||||
|
session associated with a task/process. A suspended resident Game is not a saved
|
||||||
|
snapshot and not a special global VM. It is a live VM session that is temporarily
|
||||||
|
ineligible for normal foreground ticks, input, frame pacing, and render
|
||||||
|
publication.
|
||||||
|
|
||||||
|
## Key Decisions
|
||||||
|
|
||||||
|
### Foreground Stack and Game Pause Contract
|
||||||
|
|
||||||
|
**What:** PROMETEU uses a single foreground owner in v1. Hub/Home is the root
|
||||||
|
Shell owner, a Game may remain resident, and one foreground Shell app may run in
|
||||||
|
front of the resident Game. Pressing desktop `Esc` is a host/SystemOS Home
|
||||||
|
request, not guest input.
|
||||||
|
|
||||||
|
**Why:** This preserves console-like navigation without turning the runtime into
|
||||||
|
a general multitasking scheduler. It lets the system pause and suspend a Game,
|
||||||
|
run Hub or Shell, then resume the same Game deterministically.
|
||||||
|
|
||||||
|
**Trade-offs:** The model intentionally rejects multiple resident Games, direct
|
||||||
|
Shell-to-Game return, background execution, and Game-to-Game switching in v1.
|
||||||
|
Those features remain future work, but the lifecycle structure now has a place
|
||||||
|
to add them.
|
||||||
|
|
||||||
|
### VM Session Ownership for VM-Backed Processes
|
||||||
|
|
||||||
|
**What:** VM-backed mutable execution state is owned by VM sessions. A
|
||||||
|
VM-backed Game and a VM-backed Shell never share the same VM, stack, heap, PC,
|
||||||
|
open handles, cartridge identity, or VM-scoped runtime state.
|
||||||
|
|
||||||
|
**Why:** A single firmware-owned VM cannot represent a suspended resident Game
|
||||||
|
and a foreground VM Shell at the same time. Loading the Shell into that global
|
||||||
|
VM would overwrite the Game context that the lifecycle model says is resident.
|
||||||
|
|
||||||
|
**Trade-offs:** Session ownership is more explicit than one global VM, but it
|
||||||
|
keeps the model simpler than snapshot/restore. It also prepares the runtime for
|
||||||
|
future background-capable sessions without committing to background scheduling
|
||||||
|
yet.
|
||||||
|
|
||||||
|
## Patterns and Algorithms
|
||||||
|
|
||||||
|
### Pause Is Cooperative, Suspension Is OS Authority
|
||||||
|
|
||||||
|
The Game receives lifecycle events such as pause and resume, but the OS owns the
|
||||||
|
actual scheduling state. If the Game does not cooperate within the bounded pause
|
||||||
|
budget, SystemOS can suspend it anyway. On resume, the Game receives a foreground
|
||||||
|
restore event and may still keep its internal pause screen while it
|
||||||
|
synchronizes.
|
||||||
|
|
||||||
|
This distinction prevents userland from owning system navigation. The Game may
|
||||||
|
observe and react. It cannot veto Home.
|
||||||
|
|
||||||
|
### Foreground Owner Is Not the Same as Tick Eligibility
|
||||||
|
|
||||||
|
Foreground ownership controls input, visual authority, render ownership, and
|
||||||
|
normal frame pacing. Tick eligibility controls whether a VM session may execute.
|
||||||
|
In v1, only the foreground VM session receives normal ticks, and the resident
|
||||||
|
Game does not tick while Shell or Hub is foreground.
|
||||||
|
|
||||||
|
The important design detail is that the data model does not assume this must be
|
||||||
|
true forever. Sessions can later become background-eligible without changing who
|
||||||
|
owns render/input foreground.
|
||||||
|
|
||||||
|
### VM Sessions Are the Unit of Mutable Guest State
|
||||||
|
|
||||||
|
Each VM-backed task/process maps to a `VmSession`. The session owns the VM,
|
||||||
|
runtime state, session-scoped filesystem state, open file handles, next handle
|
||||||
|
allocation, cartridge identity, lifecycle delivery state, and debug state.
|
||||||
|
|
||||||
|
Cartridge loading creates or reuses a VM session through SystemOS session
|
||||||
|
services. Firmware orchestrates macro states such as `LoadCartridge`,
|
||||||
|
`GameRunning`, `HubHome`, and `ShellRunning`, but it no longer owns a canonical
|
||||||
|
guest VM.
|
||||||
|
|
||||||
|
### Debugger and Host Inspection Must Follow the Active Session
|
||||||
|
|
||||||
|
Host debugging must inspect and mutate the active session VM, not a transitional
|
||||||
|
global VM. PC, operand stack, breakpoints, debug-step execution, cartridge
|
||||||
|
identity, and breakpoint-hit events all need to resolve through active session
|
||||||
|
state.
|
||||||
|
|
||||||
|
This keeps debugger behavior aligned with actual execution. A debugger that
|
||||||
|
observes a different VM than the scheduler ticks is worse than no debugger: it
|
||||||
|
creates false confidence and hides lifecycle bugs.
|
||||||
|
|
||||||
|
### Operational Failures Should Cross Facades as Typed Errors
|
||||||
|
|
||||||
|
Session creation and loading are operational boundaries. Missing task, missing
|
||||||
|
process, duplicate VM session, missing VM session during initialization, and
|
||||||
|
foreground Shell rejection should be typed errors. Firmware can then choose a
|
||||||
|
controlled crash report, a launch rejection, or a logged no-transition outcome.
|
||||||
|
|
||||||
|
Panics are reserved for test/setup wrappers or impossible internal invariants
|
||||||
|
that have already been proven by prior checks.
|
||||||
|
|
||||||
|
## Pitfalls
|
||||||
|
|
||||||
|
- Do not use a global VM as a compatibility bridge after session ownership is
|
||||||
|
introduced. It will eventually become stale or contradictory.
|
||||||
|
- Do not let Home/SystemOS be guest input. It changes OS authority, so it must
|
||||||
|
bypass guest pad state.
|
||||||
|
- Do not present old frames after foreground ownership changes. Render ownership
|
||||||
|
and epoch validation are part of lifecycle correctness, not renderer polish.
|
||||||
|
- Do not model pause and suspension as the same state. Pause is visible to the
|
||||||
|
Game; suspension is the scheduler mechanism.
|
||||||
|
- Do not encode v1 limits such as one resident Game or one foreground Shell by
|
||||||
|
collapsing storage into one VM. Express those limits as lifecycle/session
|
||||||
|
policy.
|
||||||
|
- Do not let debugger state drift from scheduler state. The debugger must use
|
||||||
|
the same active session as normal execution.
|
||||||
|
|
||||||
|
## Takeaways
|
||||||
|
|
||||||
|
- Foreground ownership is global policy; VM execution state is session-owned.
|
||||||
|
- A suspended Game is a preserved session, not a serialized VM and not a global
|
||||||
|
VM waiting to be reused.
|
||||||
|
- Shell apps can be native or VM-backed, but only VM-backed processes need VM
|
||||||
|
sessions.
|
||||||
|
- Session ownership is the foundation for future background-capable processes,
|
||||||
|
even before background execution exists.
|
||||||
|
- Typed session/lifecycle errors make launch and recovery behavior explicit at
|
||||||
|
firmware boundaries.
|
||||||
@ -1,178 +0,0 @@
|
|||||||
---
|
|
||||||
id: AGD-0041
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: Foreground Stack, Game Pause, and VM-Backed Shell Coexistence
|
|
||||||
status: accepted
|
|
||||||
created: 2026-06-05
|
|
||||||
resolved: 2026-07-03
|
|
||||||
decision: DEC-0037
|
|
||||||
tags: [runtime, os, lifecycle, shell, game, vm, foreground, architecture]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Contexto
|
|
||||||
|
|
||||||
Durante a discussao da `DSC-0040` sobre separar VM e render worker, surgiu um caso que nao pertence somente ao render: `Game -> Shell -> Game`.
|
|
||||||
|
|
||||||
O caso minimo e:
|
|
||||||
|
|
||||||
- um Game esta rodando;
|
|
||||||
- o usuario aperta Home;
|
|
||||||
- o Game deve sair do foreground sem ser encerrado;
|
|
||||||
- o Hub/Shell assume a tela;
|
|
||||||
- o usuario pode abrir um app Shell, inclusive VM-backed;
|
|
||||||
- depois o usuario retorna ao mesmo Game.
|
|
||||||
|
|
||||||
O render worker precisa saber qual visual owner pode apresentar frames, mas a decisao principal e de lifecycle: quais VMs existem, quais podem executar, o que significa pausar um Game, e como Shell apps coexistem com um Game pausado.
|
|
||||||
|
|
||||||
Material relacionado:
|
|
||||||
|
|
||||||
- `DSC-0031`: separacao entre perfis Game e System/Shell.
|
|
||||||
- `DSC-0032`: SystemOS como autoridade de lifecycle/process/task.
|
|
||||||
- `DSC-0035`: janelas Shell pertencem a tasks e definem liveness do app.
|
|
||||||
- `DSC-0036`: Hub/Shell prova fatias de lifecycle.
|
|
||||||
- `DSC-0040`: render ownership/epoch durante transicoes e paralelismo VM/render.
|
|
||||||
|
|
||||||
## Problema
|
|
||||||
|
|
||||||
Precisamos definir o contrato de foreground para impedir ambiguidades como:
|
|
||||||
|
|
||||||
- Game pausado continuando a executar frames logicos enquanto Shell esta no foreground;
|
|
||||||
- Shell VM-backed competindo com Game por pacing, input, superficie ou render ownership;
|
|
||||||
- multiplos Shells foreground ou multiplos Games ativos sem uma politica explicita;
|
|
||||||
- retorno ao Game sem clareza sobre estado pausado, input, render invalidation e retomada da VM.
|
|
||||||
|
|
||||||
Sem esse contrato, a separacao VM/render da `DSC-0040` pode ficar tecnicamente correta, mas o SystemOS ainda pode permitir estados incoerentes.
|
|
||||||
|
|
||||||
## Pontos Criticos
|
|
||||||
|
|
||||||
- Existe apenas um foreground visual owner por vez.
|
|
||||||
- Background execution e outro assunto e deve ficar fora da primeira decisao, salvo se for necessario definir uma proibicao explicita.
|
|
||||||
- Shell VM-backed nao deve herdar politica de game loop so por usar VM.
|
|
||||||
- Game pausado provavelmente deve reter estado, mas nao deve consumir frames logicos.
|
|
||||||
- O Hub/Shell precisa ser capaz de rodar enquanto o Game esta suspenso.
|
|
||||||
- A transicao deve invalidar render submissions antigas para impedir present de frames obsoletos.
|
|
||||||
- O modelo precisa caber no SystemOS/lifecycle existente, nao diretamente no render worker.
|
|
||||||
|
|
||||||
## Opcoes
|
|
||||||
|
|
||||||
### Opcao A - Foreground unico, Game suspenso, Shell unico
|
|
||||||
|
|
||||||
- **Abordagem:** permitir no maximo um Game carregado e no maximo um Shell foreground; ao apertar Home, o Game entra em estado pausado/suspenso e Shell/Hub assume o foreground. Shell VM-backed pode executar apenas enquanto for o foreground Shell.
|
|
||||||
- **Pro:** modelo simples, previsivel e alinhado com console; reduz concorrencia real entre VMs; combina com a regra de render ownership unico.
|
|
||||||
- **Contra:** nao cobre multitarefa/background; futuras features de overlay ou apps persistentes precisarao expandir o contrato.
|
|
||||||
- **Manutenibilidade:** alta; SystemOS pode modelar uma pilha pequena de foreground/suspended sem virar scheduler generico.
|
|
||||||
|
|
||||||
### Opcao B - Multiplas VMs residentes, somente foreground executa
|
|
||||||
|
|
||||||
- **Abordagem:** permitir que Game e Shell VM-backed coexistam como VMs residentes, mas somente a entidade foreground executa. O Game pausado fica residente, sem tick; Shell VM-backed roda sob lifecycle Shell.
|
|
||||||
- **Pro:** preserva estado e permite retorno rapido; nao exige background execution.
|
|
||||||
- **Contra:** precisa contrato claro de memoria, ownership de input, foco, cancelamento e fechamento.
|
|
||||||
- **Manutenibilidade:** boa se os estados forem explicitos; ruim se a residencia for confundida com execucao em background.
|
|
||||||
|
|
||||||
### Opcao C - Encerrar Game ao entrar no Shell VM-backed
|
|
||||||
|
|
||||||
- **Abordagem:** Home fecha/salva o Game antes de abrir Shell VM-backed, evitando coexistencia.
|
|
||||||
- **Pro:** simplifica o runtime.
|
|
||||||
- **Contra:** quebra a expectativa de console moderno; torna `Game -> Shell -> Game` um reload, nao uma pausa.
|
|
||||||
- **Manutenibilidade:** simples no curto prazo, mas provavelmente gera mais excecoes e UX ruim depois.
|
|
||||||
|
|
||||||
### Opcao D - Background real desde ja
|
|
||||||
|
|
||||||
- **Abordagem:** permitir Game pausado ou Shell apps rodando em background com politicas de prioridade.
|
|
||||||
- **Pro:** cobre casos futuros ricos.
|
|
||||||
- **Contra:** amplia demais escopo: scheduler, budget, energia, audio, input, IO e memoria.
|
|
||||||
- **Manutenibilidade:** baixa para v1; mistura a decisao de foreground com multitarefa real.
|
|
||||||
|
|
||||||
## Sugestao / Recomendacao
|
|
||||||
|
|
||||||
Recomendo seguir com a combinacao das opcoes A e B:
|
|
||||||
|
|
||||||
- somente um Game residente por vez;
|
|
||||||
- somente um Shell foreground por vez;
|
|
||||||
- Game pode ficar pausado/suspenso enquanto Shell esta foreground;
|
|
||||||
- Shell VM-backed pode existir junto com Game pausado, mas segue lifecycle Shell;
|
|
||||||
- somente o owner foreground executa VM/tick/event loop conforme sua politica;
|
|
||||||
- background execution fica explicitamente fora de escopo;
|
|
||||||
- render ownership muda junto com foreground owner e deve invalidar submissions antigas via epoch/generation, conforme `DSC-0040`.
|
|
||||||
|
|
||||||
Isso preserva o ciclo `Game -> Home/Shell -> Game` sem transformar o runtime em multitarefa geral agora.
|
|
||||||
|
|
||||||
## Perguntas em Aberto
|
|
||||||
|
|
||||||
- [x] Qual e o nome canonico dos estados de Game fora do foreground: `Paused`, `Suspended`, ambos, ou outro?
|
|
||||||
- [x] Ao apertar Home, o Game deve receber um evento/interrupt/trap de pausa antes de parar de executar?
|
|
||||||
- [x] O Hub e um Shell especial sempre presente ou uma task Shell comum?
|
|
||||||
- [x] Um app Shell VM-backed pode substituir o Hub no foreground ou o Hub permanece como owner raiz?
|
|
||||||
- [x] Quando o Shell VM-backed fecha, o retorno vai sempre para Hub ou pode voltar diretamente ao Game?
|
|
||||||
- [x] O input do Game e drenado/limpo ao pausar e ao retomar?
|
|
||||||
- [x] Audio do Game pausa junto com a VM ou tem politica propria?
|
|
||||||
- [x] Existe limite de memoria/residencia para manter Game pausado enquanto Shell app roda?
|
|
||||||
- [x] O retorno ao Game exige novo frame antes de trocar a superficie, ou a tela pode manter Hub/Shell ate a primeira submission valida?
|
|
||||||
- [x] Quais eventos de lifecycle precisam ser visiveis para firmware, OS, VM e host?
|
|
||||||
|
|
||||||
## Resolucao
|
|
||||||
|
|
||||||
Fica aceito o modelo `Game -> Home/Shell -> mesmo Game` com foreground unico e
|
|
||||||
residencia limitada:
|
|
||||||
|
|
||||||
- somente um Game pode estar residente por vez;
|
|
||||||
- somente um Shell pode ser foreground por vez;
|
|
||||||
- Hub e o Shell root/especial e permanece owner raiz/back-stack;
|
|
||||||
- um Shell VM-backed pode assumir o foreground visual, mas segue lifecycle de
|
|
||||||
Shell e nao herda politica de game loop;
|
|
||||||
- fechar um Shell VM-backed retorna ao Hub em v1;
|
|
||||||
- background execution real fica fora de escopo;
|
|
||||||
- no host desktop, `Esc` e a tecla principal para solicitar Home/SystemOS e a
|
|
||||||
tecla fisica `Home` pode existir como alias;
|
|
||||||
- a acao Home/SystemOS e interceptada pelo host/SystemOS e nao entra no pad
|
|
||||||
logico visivel ao Game;
|
|
||||||
- `Paused` e o estado/evento cooperativo visivel ao Game;
|
|
||||||
- `Suspended` e o estado operacional imposto pelo OS/scheduler;
|
|
||||||
- ao sair do foreground, o OS notifica o Game para pausar e concede um budget
|
|
||||||
curto de reacao;
|
|
||||||
- se o Game nao reagir ao pause dentro do budget, o OS suspende a VM mesmo
|
|
||||||
assim;
|
|
||||||
- ao retornar, o OS reativa a VM, notifica o Game para resume/foreground
|
|
||||||
restore e permite que o Game sincronize antes de voltar ao gameplay;
|
|
||||||
- input pendente do Game e drenado/limpo nas fronteiras de pause/resume;
|
|
||||||
- audio do Game pausa junto com a VM em v1;
|
|
||||||
- retorno ao Game deve manter Hub/Shell visivel ate a primeira render submission
|
|
||||||
valida do Game retomado;
|
|
||||||
- render ownership muda junto com o foreground owner e invalida submissions
|
|
||||||
antigas via epoch/generation;
|
|
||||||
- firmware observa transicoes macro; OS observa lifecycle completo; VM observa
|
|
||||||
pause/resume; host observa owner/render epoch e estado de apresentacao.
|
|
||||||
|
|
||||||
## Criterio para Encerrar
|
|
||||||
|
|
||||||
A agenda pode ser encerrada quando tivermos uma decisao clara sobre:
|
|
||||||
|
|
||||||
- cardinalidade de Games e Shells residentes/foreground;
|
|
||||||
- semantica de Game pausado/suspenso;
|
|
||||||
- politica de execucao para Shell VM-backed durante Game pausado;
|
|
||||||
- regra de foreground visual owner e render invalidation;
|
|
||||||
- comportamento minimo do ciclo `Game -> Shell -> Game`;
|
|
||||||
- itens explicitamente fora de escopo, especialmente background execution real.
|
|
||||||
|
|
||||||
## Discussao
|
|
||||||
|
|
||||||
- 2026-06-05: Agenda criada a partir da `DSC-0040`, quando o caso de Shell VM-backed aberto sobre um Game pausado mostrou que a discussao pertence ao lifecycle/SystemOS, nao apenas ao render worker.
|
|
||||||
- 2026-07-03: Direcao provisoria aceita para as perguntas 2-10:
|
|
||||||
Game deve receber evento de pausa com budget curto antes da suspensao; Hub e
|
|
||||||
Shell root/especial; Shell VM-backed pode assumir foreground visual, mas Hub
|
|
||||||
permanece como owner raiz/back-stack; fechar Shell VM-backed retorna ao Hub em
|
|
||||||
v1; input do Game deve ser drenado/limpo em pause/resume; audio do Game pausa
|
|
||||||
junto com VM em v1; apenas um Game pausado residente e permitido; retorno ao
|
|
||||||
Game deve manter Hub/Shell visivel ate a primeira submission valida; firmware
|
|
||||||
ve transicoes macro, OS ve lifecycle completo, VM ve pause/resume, host ve
|
|
||||||
owner/render epoch e estado de apresentacao.
|
|
||||||
- 2026-07-03: Pergunta 1 permanece aberta para esclarecer se `Paused` e
|
|
||||||
`Suspended` sao estados distintos, nomes para camadas diferentes, ou se v1
|
|
||||||
deve expor somente um deles.
|
|
||||||
- 2026-07-03: Direcao proposta para pergunta 1: `Paused` e o estado/evento
|
|
||||||
cooperativo visivel ao Game; `Suspended` e o estado operacional imposto pelo
|
|
||||||
OS. Ao sair do foreground, o OS notifica o Game para pausar e concede um
|
|
||||||
budget curto de reacao; se o Game nao reagir, o OS suspende a VM mesmo assim.
|
|
||||||
Ao retornar, o OS notifica o Game antes de recoloca-lo no foreground visual,
|
|
||||||
permitindo sincronizacao/revalidacao. O Game nao controla a suspensao, mas
|
|
||||||
pode observar `pause`/`resume` para ajustar seu estado interno.
|
|
||||||
@ -1,186 +0,0 @@
|
|||||||
---
|
|
||||||
id: AGD-0046
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: VM Context Ownership for Resident Game and VM-Backed Shell
|
|
||||||
status: accepted
|
|
||||||
created: 2026-07-04
|
|
||||||
resolved:
|
|
||||||
decision: DEC-0038
|
|
||||||
tags: [runtime, os, lifecycle, shell, game, vm, foreground, architecture]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Contexto
|
|
||||||
|
|
||||||
`DEC-0037` define que o v1 deve suportar o fluxo `Game -> Home/Shell -> same
|
|
||||||
Game`, com o Game residente pausado/suspenso enquanto Hub ou Shell possuem o
|
|
||||||
foreground. A implementacao atual ja impede que Shell nativo ticke o Game
|
|
||||||
suspenso, porque Shell nativo nao usa a VM.
|
|
||||||
|
|
||||||
A lacuna aparece quando o Shell tambem eh VM-backed. Hoje `Firmware` possui um
|
|
||||||
unico `VirtualMachine` global (`Firmware.vm`). O Game residente e um Shell
|
|
||||||
VM-backed competem por esse mesmo contexto. Se o Shell VM-backed inicializa uma
|
|
||||||
nova cartridge nesse objeto, ele sobrescreve o contexto do Game que deveria
|
|
||||||
permanecer residente/suspenso.
|
|
||||||
|
|
||||||
Isso significa que o modelo de foreground/task/process ja avancou, mas o modelo
|
|
||||||
de contexto VM ainda esta acoplado ao firmware global.
|
|
||||||
|
|
||||||
## Problema
|
|
||||||
|
|
||||||
Precisamos decidir onde vive o contexto de execucao VM quando existem, ao mesmo
|
|
||||||
tempo, um Game residente suspenso e um Shell VM-backed foreground.
|
|
||||||
|
|
||||||
O problema nao eh apenas pausar ticks. Para coexistencia real, cada VM-backed
|
|
||||||
process precisa preservar seu proprio estado: PC, stack, heap, globals,
|
|
||||||
cartridge identity, runtime services escopados, render ownership metadata e
|
|
||||||
eventos lifecycle pendentes.
|
|
||||||
|
|
||||||
Enquanto `Firmware.vm` for unico, "suspender" um Game VM nao preserva o
|
|
||||||
contexto se outra cartridge VM-backed for carregada.
|
|
||||||
|
|
||||||
## Pontos Criticos
|
|
||||||
|
|
||||||
- `DEC-0037` diz que um Game suspenso nao recebe ticks/input/frame pacing, mas
|
|
||||||
continua residente para voltar ao mesmo Game.
|
|
||||||
- Shell nativo nao precisa de VM context proprio, mas Shell VM-backed precisa.
|
|
||||||
- `SystemOS` ja eh autoridade de lifecycle/process/task, entao eh candidato
|
|
||||||
natural para possuir ou localizar contextos VM por sessao.
|
|
||||||
- `Firmware` deve orquestrar macro estados, mas nao deveria ser dono unico do
|
|
||||||
contexto de todos os processos VM-backed.
|
|
||||||
- `VirtualMachineRuntime` contem estado atualmente misto: identidade da
|
|
||||||
cartridge atual, render manager, lifecycle delivery, fs/memcard/open handles
|
|
||||||
e telemetria. Precisamos separar o que eh global do OS do que eh por sessao
|
|
||||||
VM-backed.
|
|
||||||
- A solucao deve preservar a restricao v1: no maximo um Game residente e no
|
|
||||||
maximo um Shell foreground. Ela nao deve abrir multitasking geral.
|
|
||||||
- Game-to-Game switching continua fora desta agenda e pertence a `DSC-0043`.
|
|
||||||
|
|
||||||
## Opcoes
|
|
||||||
|
|
||||||
### Opcao A - Manter VM global no Firmware e bloquear Shell VM-backed com Game residente
|
|
||||||
|
|
||||||
- **Abordagem:** documentar que Shell VM-backed nao pode abrir enquanto um Game
|
|
||||||
VM esta residente; apenas Shell nativo coexiste com Game suspenso.
|
|
||||||
- **Pro:** menor implementacao imediata.
|
|
||||||
- **Contra:** contradiz a intencao de coexistencia de VM-backed Shell em
|
|
||||||
`DEC-0037` e deixa a arquitetura incompleta.
|
|
||||||
- **Manutenibilidade:** baixa, porque o sistema teria regras diferentes para
|
|
||||||
Shell nativo e Shell VM-backed sem uma fronteira clara de sessao.
|
|
||||||
|
|
||||||
### Opcao B - Serializar o Game VM ao suspender e restaurar ao resumir
|
|
||||||
|
|
||||||
- **Abordagem:** ao suspender Game, extrair snapshot do `VirtualMachine` e do
|
|
||||||
runtime associado; ao resumir, restaurar snapshot depois do Shell VM-backed.
|
|
||||||
- **Pro:** preserva a ideia de um unico VM executor ativo.
|
|
||||||
- **Contra:** exige serializacao profunda e correta de todo o estado VM/runtime,
|
|
||||||
incluindo handles, assets, render state e side effects. Isso eh muito mais
|
|
||||||
complexo que a necessidade v1.
|
|
||||||
- **Manutenibilidade:** baixa a media; snapshots viram contrato dificil e
|
|
||||||
fragil antes de termos necessidade de hibernacao real.
|
|
||||||
|
|
||||||
### Opcao C - Tornar o contexto VM propriedade da sessao/processo VM-backed
|
|
||||||
|
|
||||||
- **Abordagem:** cada processo VM-backed possui seu proprio `VirtualMachine` e
|
|
||||||
seu contexto runtime VM-scoped. O scheduler/firmware seleciona qual sessao
|
|
||||||
recebe tick conforme foreground/lifecycle. Game suspenso simplesmente deixa
|
|
||||||
de ser tickado, mas seu contexto permanece armazenado na sessao Game.
|
|
||||||
- **Pro:** alinha com task/process/lifecycle, suporta Game residente + Shell
|
|
||||||
VM-backed sem sobrescrever contexto, e prepara o terreno para futuras sessoes
|
|
||||||
sem implementar multitasking geral agora.
|
|
||||||
- **Contra:** exige separar estado hoje concentrado em `Firmware.vm` e
|
|
||||||
`SystemOS.vm_runtime`, decidindo o que eh global do OS e o que eh por sessao.
|
|
||||||
- **Manutenibilidade:** alta, porque a posse do contexto acompanha o processo
|
|
||||||
que executa a VM.
|
|
||||||
|
|
||||||
### Opcao D - Criar apenas dois slots VM fixos: Game VM e Shell VM
|
|
||||||
|
|
||||||
- **Abordagem:** manter um slot `game_vm` e um slot `shell_vm`, em vez de uma VM
|
|
||||||
por processo/sessao.
|
|
||||||
- **Pro:** mais simples que uma tabela geral de sessoes.
|
|
||||||
- **Contra:** codifica uma limitacao acidental. Ainda precisariamos decidir
|
|
||||||
ownership, runtime scoped state e lifecycle por slot. Pode virar refactor
|
|
||||||
duplo quando mais de um Shell ou Game switching aparecer.
|
|
||||||
- **Manutenibilidade:** media; aceitavel como stepping stone somente se modelado
|
|
||||||
internamente como sessao, nao como campos especiais permanentes.
|
|
||||||
|
|
||||||
## Sugestao / Recomendacao
|
|
||||||
|
|
||||||
Seguir a Opcao C: contexto VM deve pertencer a uma sessao/processo VM-backed,
|
|
||||||
nao ao `Firmware` global.
|
|
||||||
|
|
||||||
Para v1, a implementacao pode continuar limitada a uma sessao Game residente e
|
|
||||||
uma sessao Shell foreground, mas o modelo deve ser "session-owned VM context".
|
|
||||||
Isso permite que Shell VM-backed rode enquanto o Game fica suspenso sem perder
|
|
||||||
PC/stack/heap/estado runtime.
|
|
||||||
|
|
||||||
O firmware deve deixar de possuir a VM como estado global de todos os apps e
|
|
||||||
passar a pedir ao SystemOS/runtime scheduler o contexto VM ativo para o owner
|
|
||||||
foreground. `SystemOS` deve continuar sendo a autoridade de lifecycle; o
|
|
||||||
runtime deve separar estado VM-scoped de estado global de OS/render/telemetria
|
|
||||||
conforme necessario.
|
|
||||||
|
|
||||||
## Perguntas em Aberto
|
|
||||||
|
|
||||||
- [ ] Qual objeto deve representar a sessao VM-backed: `Process`, novo
|
|
||||||
`VmSession`, ou uma tabela separada ligada a `ProcessId`/`TaskId`?
|
|
||||||
- [ ] Quais campos de `VirtualMachineRuntime` sao por sessao VM-backed e quais
|
|
||||||
devem permanecer globais em `SystemOS`?
|
|
||||||
- [ ] O render manager fica global por foreground owner ou dentro de cada
|
|
||||||
contexto VM com handoff global?
|
|
||||||
- [ ] Handles de FS, memcard, assets e open files devem ser por sessao,
|
|
||||||
por app_id, ou globais com namespace por processo?
|
|
||||||
- [ ] Como `LoadCartridgeStep` deve mudar: criar sessao VM nova, ativar sessao
|
|
||||||
existente, ou delegar tudo para uma facade de sessions?
|
|
||||||
- [ ] Como preservar os testes e invariantes de `DEC-0037` durante a migracao
|
|
||||||
incremental sem abrir multitasking geral?
|
|
||||||
- [ ] Como processos background-capable, como um player de musica, entram no
|
|
||||||
modelo sem confundir foreground visual ownership com elegibilidade de
|
|
||||||
execucao?
|
|
||||||
- [ ] Cada Shell VM-backed deve ter uma VM propria, mesmo quando existe mais de
|
|
||||||
um Shell em memoria no futuro?
|
|
||||||
|
|
||||||
## Criterio para Encerrar
|
|
||||||
|
|
||||||
Esta agenda pode virar decision quando definirmos:
|
|
||||||
|
|
||||||
- o proprietario canonico do contexto VM;
|
|
||||||
- a fronteira entre estado VM-scoped e estado global do OS;
|
|
||||||
- como firmware seleciona/ticka a sessao VM ativa;
|
|
||||||
- como Game residente suspenso e Shell VM-backed foreground coexistem;
|
|
||||||
- quais restricoes v1 permanecem explicitamente fora de escopo.
|
|
||||||
|
|
||||||
## Discussion
|
|
||||||
|
|
||||||
- 2026-07-04: Aberta a partir da observacao de que Shell nativo coexiste com
|
|
||||||
Game suspenso, mas Shell VM-backed ainda conflita com o `Firmware.vm` unico.
|
|
||||||
- 2026-07-04: Discussao adicional separou foreground ownership de background
|
|
||||||
execution. A recomendacao atual eh que todo processo VM-backed tenha sua
|
|
||||||
propria `VmSession`; processos background-capable devem ser modelados como
|
|
||||||
sessoes com classe/politica de scheduling propria, nao como foreground owners.
|
|
||||||
|
|
||||||
## Resolution
|
|
||||||
|
|
||||||
Resolvido em 2026-07-04:
|
|
||||||
|
|
||||||
- O modelo deve seguir a Opcao C: todo processo VM-backed deve possuir uma
|
|
||||||
`VmSession` propria, ligada ao lifecycle/task/process, em vez de depender de
|
|
||||||
um `Firmware.vm` global.
|
|
||||||
- Cada Shell VM-backed deve ter sua propria sessao VM. Ele pode compartilhar a
|
|
||||||
engine/runtime e caches imutaveis, mas nao stack, heap, PC, globals, handles
|
|
||||||
ou identidade mutavel de execucao.
|
|
||||||
- O Game residente suspenso preserva seu contexto porque sua `VmSession`
|
|
||||||
continua viva; ele apenas deixa de ser elegivel para ticks normais enquanto
|
|
||||||
Hub/Shell possui foreground.
|
|
||||||
- Foreground ownership e background execution sao conceitos separados.
|
|
||||||
Foreground ownership decide input visual, render ownership e frame pacing
|
|
||||||
normal. Execution eligibility decide quais sessoes podem receber ticks sem
|
|
||||||
serem foreground.
|
|
||||||
- Background execution, como player de musica ou background services, nao entra
|
|
||||||
na primeira implementacao desta agenda. Mesmo assim, o modelo de `VmSession`
|
|
||||||
deve ser obrigatoriamente compativel com essa evolucao em um futuro proximo.
|
|
||||||
- Render ownership permanece autoridade global do SystemOS. Sessoes VM-backed
|
|
||||||
produzem frames, mas nao decidem se podem apresentar.
|
|
||||||
- `LoadCartridgeStep` nao deve inicializar uma VM global diretamente. Ele deve
|
|
||||||
delegar criacao/ativacao de sessao para uma facade de sessions/scheduler.
|
|
||||||
|
|
||||||
Esta agenda esta pronta para virar decision normativa.
|
|
||||||
@ -1,191 +0,0 @@
|
|||||||
---
|
|
||||||
id: DEC-0037
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: Foreground Stack and Game Pause Contract
|
|
||||||
status: accepted
|
|
||||||
created: 2026-07-03
|
|
||||||
ref_agenda: AGD-0041
|
|
||||||
tags: [runtime, os, lifecycle, shell, game, vm, foreground, architecture]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Status
|
|
||||||
|
|
||||||
Accepted.
|
|
||||||
|
|
||||||
## Contexto
|
|
||||||
|
|
||||||
`AGD-0041` defines the minimum lifecycle contract for `Game -> Home/Shell ->
|
|
||||||
same Game`.
|
|
||||||
|
|
||||||
The issue is not only rendering. The render worker needs a single valid visual
|
|
||||||
owner, but SystemOS must decide which VMs may exist, which owner receives input,
|
|
||||||
which owner may tick, what happens to a Game when Home takes foreground, and how
|
|
||||||
a VM-backed Shell app coexists with a paused Game.
|
|
||||||
|
|
||||||
This decision builds on:
|
|
||||||
|
|
||||||
- `DSC-0031`: runtime mode separation between Game and System/Shell;
|
|
||||||
- `DSC-0032`: SystemOS lifecycle/process/task authority;
|
|
||||||
- `DSC-0035`: task-owned Shell windows and Shell liveness;
|
|
||||||
- `DSC-0036`: Hub/Shell as a lifecycle proving surface;
|
|
||||||
- `DSC-0040`: render ownership/epoch and stale submission invalidation.
|
|
||||||
|
|
||||||
## Decisao
|
|
||||||
|
|
||||||
PROMETEU MUST use a single-foreground-owner model for v1.
|
|
||||||
|
|
||||||
At most one Game MAY be resident at a time. At most one Shell app MAY be the
|
|
||||||
foreground visual owner at a time.
|
|
||||||
|
|
||||||
The Hub MUST be treated as the root/special Shell owner for Home. A VM-backed
|
|
||||||
Shell app MAY replace the Hub as the foreground visual owner, but the Hub
|
|
||||||
remains the root/back-stack controller. In v1, closing a VM-backed Shell app
|
|
||||||
MUST return to Hub/Home, not directly to the Game.
|
|
||||||
|
|
||||||
When the user requests Home while a Game is running, SystemOS MUST start a
|
|
||||||
cooperative pause transition:
|
|
||||||
|
|
||||||
- the Game receives a pause lifecycle notification;
|
|
||||||
- the Game is given a short bounded budget to react;
|
|
||||||
- after that budget, SystemOS MAY suspend the Game VM even if the Game did not
|
|
||||||
cooperate;
|
|
||||||
- the Game remains resident but MUST NOT receive normal gameplay ticks, normal
|
|
||||||
game input, or frame pacing while suspended;
|
|
||||||
- the Shell/Hub foreground owner MAY execute according to Shell lifecycle rules.
|
|
||||||
|
|
||||||
`Paused` is the Game-visible lifecycle state/event. `Suspended` is the
|
|
||||||
OS/scheduler operational state. A Game may observe pause/resume, but it MUST NOT
|
|
||||||
control whether the OS suspends or resumes its VM.
|
|
||||||
|
|
||||||
When returning to the same Game, SystemOS MUST reactivate the Game VM and send a
|
|
||||||
resume or foreground-restore lifecycle notification before restoring visual
|
|
||||||
foreground. The Game MAY remain internally paused while it synchronizes. The
|
|
||||||
system MUST keep Hub/Shell visible until the resumed Game produces a valid new
|
|
||||||
render submission for the current ownership epoch.
|
|
||||||
|
|
||||||
Input pending for the Game MUST be drained or cleared across pause/resume
|
|
||||||
boundaries. Game audio MUST pause with the Game VM in v1. Independent background
|
|
||||||
audio policy is out of scope.
|
|
||||||
|
|
||||||
Render ownership MUST move with the foreground owner. Foreground transitions
|
|
||||||
MUST invalidate stale render submissions through ownership epoch/generation so
|
|
||||||
old Game frames cannot be presented after Shell takes foreground, and old Shell
|
|
||||||
frames cannot be presented after the Game resumes.
|
|
||||||
|
|
||||||
On the desktop host, `Esc` MUST be the primary keyboard request for Home/SystemOS.
|
|
||||||
The physical `Home` key MAY be supported as an alias. This Home request is a
|
|
||||||
host/SystemOS control action and MUST NOT be exposed as a normal guest pad
|
|
||||||
button or userland input capability.
|
|
||||||
|
|
||||||
Real background execution, multiple resident Games, multiple foreground Shells,
|
|
||||||
Game-to-Game switching, Shell app persistence policy, memory eviction policy,
|
|
||||||
and rich multitasking are out of scope for this decision.
|
|
||||||
|
|
||||||
## Rationale
|
|
||||||
|
|
||||||
The chosen model preserves the expected console-like flow: a Game can be
|
|
||||||
paused, Home can run, Shell apps can open, and the same Game can resume without
|
|
||||||
turning the runtime into a general multitasking scheduler.
|
|
||||||
|
|
||||||
Separating `Paused` from `Suspended` keeps authority clear. `Paused` is the
|
|
||||||
contract the Game can observe and react to. `Suspended` is the OS mechanism that
|
|
||||||
prevents the paused Game from continuing to consume ticks, input, audio, and
|
|
||||||
render ownership while another owner is foreground.
|
|
||||||
|
|
||||||
Keeping Hub as root/back-stack prevents VM-backed Shell apps from becoming
|
|
||||||
independent lifecycle roots. Returning to Hub before resuming Game keeps v1
|
|
||||||
navigation deterministic and leaves more complex direct-return behavior for a
|
|
||||||
future decision.
|
|
||||||
|
|
||||||
Keeping Hub/Shell visible until the Game submits a new valid frame avoids
|
|
||||||
presenting stale pixels from the previous Game epoch. This aligns lifecycle
|
|
||||||
state with the render ownership model from `DSC-0040`.
|
|
||||||
|
|
||||||
Using `Esc` as desktop Home makes the control available on compact keyboards.
|
|
||||||
Supporting the physical `Home` key as an alias is acceptable, but the portable
|
|
||||||
contract cannot depend on it.
|
|
||||||
|
|
||||||
## Invariantes / Contrato
|
|
||||||
|
|
||||||
- There is exactly one foreground visual owner at a time.
|
|
||||||
- V1 supports at most one resident Game.
|
|
||||||
- V1 supports at most one foreground Shell app.
|
|
||||||
- Hub/Home is the root Shell owner and back-stack controller.
|
|
||||||
- VM-backed Shell apps follow Shell lifecycle, not Game loop lifecycle.
|
|
||||||
- A paused Game may remain resident, but a suspended Game does not receive
|
|
||||||
normal gameplay ticks, normal game input, or frame pacing.
|
|
||||||
- Pause/resume are cooperative Game-visible lifecycle events.
|
|
||||||
- Suspend/resume scheduling authority belongs to SystemOS.
|
|
||||||
- SystemOS may suspend a Game after the pause budget even if the Game does not
|
|
||||||
cooperate.
|
|
||||||
- Input is cleared or barriered across Game pause/resume boundaries.
|
|
||||||
- Game audio pauses with the Game VM in v1.
|
|
||||||
- Foreground transitions advance render ownership epoch/generation.
|
|
||||||
- Visual foreground returns to Game only after a valid current-epoch Game render
|
|
||||||
submission exists.
|
|
||||||
- Desktop `Esc` is a host/SystemOS Home request; physical `Home` may alias it.
|
|
||||||
- Home/SystemOS is not a guest pad button and is not userland input.
|
|
||||||
- Background execution and general multitasking are explicitly excluded.
|
|
||||||
|
|
||||||
## Impactos
|
|
||||||
|
|
||||||
- **Spec:** runtime lifecycle, firmware/Hub, input, and render ownership specs
|
|
||||||
must document foreground ownership, pause/suspend semantics, Home key mapping,
|
|
||||||
and stale frame invalidation.
|
|
||||||
- **Runtime / SystemOS:** SystemOS needs explicit foreground owner, Hub root,
|
|
||||||
Shell foreground, Game paused, and Game suspended state.
|
|
||||||
- **VM:** VM execution must support pause/resume lifecycle delivery and OS-owned
|
|
||||||
suspension of ticks.
|
|
||||||
- **Firmware:** firmware should observe macro transitions such as GameRunning,
|
|
||||||
Hub/Home, Shell foreground, pause request, and resume request without owning
|
|
||||||
every internal OS detail.
|
|
||||||
- **Host:** host must route desktop `Esc` and optional physical `Home` to a
|
|
||||||
SystemOS Home request instead of guest pad input.
|
|
||||||
- **Input:** Game input must be drained or barriered during pause/resume and
|
|
||||||
Home/SystemOS must stay outside the guest-visible pad ABI.
|
|
||||||
- **Audio:** v1 audio follows Game pause/suspend and does not continue as
|
|
||||||
independent background audio.
|
|
||||||
- **Renderer:** render publication must reject stale submissions after ownership
|
|
||||||
epoch changes and keep previous foreground visible until the resumed Game
|
|
||||||
publishes a valid current-epoch frame.
|
|
||||||
- **Tests:** coverage must prove pause budget behavior, non-foreground VM
|
|
||||||
non-execution, input clearing, host Home routing, Shell return-to-Hub, and
|
|
||||||
stale render rejection.
|
|
||||||
|
|
||||||
## Alternativas Descartadas
|
|
||||||
|
|
||||||
- **Terminate Game when entering Shell:** rejected because it makes
|
|
||||||
`Game -> Shell -> Game` a reload rather than a pause/resume flow.
|
|
||||||
- **Real background execution now:** rejected because it pulls in scheduler,
|
|
||||||
budget, energy, audio, input, IO, memory, and persistence policy beyond v1.
|
|
||||||
- **Multiple resident Games:** rejected for v1 because Game-to-Game switching is
|
|
||||||
owned by `DSC-0043 / AGD-0044`.
|
|
||||||
- **Expose Home as guest input:** rejected because Home changes system
|
|
||||||
foreground authority and must not be handled as ordinary game input.
|
|
||||||
- **Return directly from Shell app to Game in v1:** rejected to keep Hub as the
|
|
||||||
deterministic root/back-stack owner.
|
|
||||||
|
|
||||||
## Referencias
|
|
||||||
|
|
||||||
- Source agenda: `AGD-0041`
|
|
||||||
- Runtime mode separation: `DSC-0031`
|
|
||||||
- SystemOS lifecycle authority: `DSC-0032`
|
|
||||||
- Shell task/window lifecycle: `DSC-0035`
|
|
||||||
- Hub UI direction: `DSC-0036`
|
|
||||||
- Render ownership and stale submission handling: `DSC-0040`
|
|
||||||
- Future Game A -> Home -> Game B switching: `DSC-0043 / AGD-0044`
|
|
||||||
|
|
||||||
## Propagacao Necessaria
|
|
||||||
|
|
||||||
- Update canonical runtime specs for foreground ownership and pause/suspend.
|
|
||||||
- Specify desktop Home request mapping in host/input documentation.
|
|
||||||
- Add plan(s) for SystemOS foreground stack state and Game pause/suspend.
|
|
||||||
- Add plan(s) for VM lifecycle event delivery and forced suspension budget.
|
|
||||||
- Add plan(s) for host `Esc`/`Home` routing outside guest pad input.
|
|
||||||
- Add plan(s) for render epoch integration during Game/Shell transitions.
|
|
||||||
- Add tests covering lifecycle, input, host routing, and render publication
|
|
||||||
invariants.
|
|
||||||
|
|
||||||
## Revision Log
|
|
||||||
|
|
||||||
- 2026-07-03: Initial decision from `AGD-0041`.
|
|
||||||
@ -1,199 +0,0 @@
|
|||||||
---
|
|
||||||
id: DEC-0038
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: VM Session Ownership for VM-Backed Processes
|
|
||||||
status: accepted
|
|
||||||
created: 2026-07-04
|
|
||||||
ref_agenda: AGD-0046
|
|
||||||
tags: [runtime, os, lifecycle, shell, game, vm, foreground, architecture]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Status
|
|
||||||
|
|
||||||
Open for review.
|
|
||||||
|
|
||||||
## Contexto
|
|
||||||
|
|
||||||
`DEC-0037` established the foreground stack and Game pause contract for
|
|
||||||
`Game -> Home/Shell -> same Game`. It requires a suspended resident Game to stop
|
|
||||||
receiving normal gameplay ticks, input, and frame pacing while remaining
|
|
||||||
resident for later resume.
|
|
||||||
|
|
||||||
The current implementation works for native Shell apps because native Shell
|
|
||||||
apps do not use the VM. It does not fully support VM-backed Shell coexistence:
|
|
||||||
`Firmware` owns a single global `VirtualMachine` (`Firmware.vm`), so loading a
|
|
||||||
VM-backed Shell can overwrite the suspended Game context that should remain
|
|
||||||
resident.
|
|
||||||
|
|
||||||
`AGD-0046` closes this gap by separating VM execution context ownership from
|
|
||||||
foreground ownership. A foreground owner decides who receives visual/input
|
|
||||||
authority. A VM execution context decides where PC, stack, heap, globals,
|
|
||||||
handles, cartridge identity, lifecycle delivery state, and VM-scoped runtime
|
|
||||||
state live.
|
|
||||||
|
|
||||||
## Decisao
|
|
||||||
|
|
||||||
PROMETEU MUST make VM execution context session-owned for VM-backed processes.
|
|
||||||
|
|
||||||
Every VM-backed process MUST have its own `VmSession` or equivalent
|
|
||||||
session-owned context. A VM-backed Game and a VM-backed Shell MUST NOT share the
|
|
||||||
same mutable `VirtualMachine` instance, stack, heap, PC, globals, open handles,
|
|
||||||
cartridge identity, or VM-scoped lifecycle state.
|
|
||||||
|
|
||||||
`Firmware.vm` MUST NOT remain the canonical owner of all VM-backed app context.
|
|
||||||
Firmware MAY continue to orchestrate macro states such as loading, Home, Shell,
|
|
||||||
and GameRunning, but it MUST delegate VM session creation, activation, lookup,
|
|
||||||
and ticking to SystemOS/session services.
|
|
||||||
|
|
||||||
For v1, the system MAY still enforce:
|
|
||||||
|
|
||||||
- at most one resident Game;
|
|
||||||
- at most one foreground Shell;
|
|
||||||
- no Game-to-Game switching;
|
|
||||||
- no general background execution.
|
|
||||||
|
|
||||||
Those restrictions MUST NOT be implemented by relying on a single global VM
|
|
||||||
context. They must be expressed as lifecycle/session policy.
|
|
||||||
|
|
||||||
Each VM-backed Shell MUST receive its own VM session. If future Shell
|
|
||||||
persistence allows more than one Shell app to remain in memory, each retained
|
|
||||||
VM-backed Shell MUST still have distinct mutable VM context.
|
|
||||||
|
|
||||||
A suspended resident Game MUST preserve its VM session. Suspension means the
|
|
||||||
session is not eligible for normal foreground ticks, normal Game input, or
|
|
||||||
normal frame pacing. It does not mean serializing, deleting, or overwriting the
|
|
||||||
VM context.
|
|
||||||
|
|
||||||
Foreground ownership and execution eligibility MUST be separate concepts:
|
|
||||||
|
|
||||||
- foreground ownership controls visual owner, primary input, render ownership,
|
|
||||||
and normal foreground frame pacing;
|
|
||||||
- execution eligibility controls which sessions may receive ticks or restricted
|
|
||||||
callbacks while not foreground.
|
|
||||||
|
|
||||||
Background execution, including music players or background services, is out of
|
|
||||||
scope for the first implementation of this decision. However, the session model
|
|
||||||
MUST be compatible with adding background-capable scheduling soon. The initial
|
|
||||||
model MUST NOT bake in assumptions that only the foreground session can ever be
|
|
||||||
eligible to execute.
|
|
||||||
|
|
||||||
Render ownership MUST remain a global SystemOS authority. VM sessions may
|
|
||||||
produce render submissions, but they MUST NOT decide whether those submissions
|
|
||||||
can be presented. SystemOS/render ownership policy remains responsible for
|
|
||||||
active owner, epoch/generation validity, stale submission rejection, and final
|
|
||||||
publication authority.
|
|
||||||
|
|
||||||
`LoadCartridgeStep` MUST stop initializing a global firmware VM directly as the
|
|
||||||
long-term model. Cartridge load/activation MUST be delegated to a sessions or
|
|
||||||
VM-session facade that can create a new VM session, activate an existing VM
|
|
||||||
session, or reject an unsupported transition according to lifecycle policy.
|
|
||||||
|
|
||||||
## Rationale
|
|
||||||
|
|
||||||
The foreground stack is already task/process-oriented. VM execution context must
|
|
||||||
follow the same ownership model. Otherwise the system can mark a Game as
|
|
||||||
resident and suspended while the only real VM context has already been reused
|
|
||||||
for a Shell.
|
|
||||||
|
|
||||||
Session ownership solves the actual coexistence problem without requiring VM
|
|
||||||
snapshot/restore. A suspended Game is simply a live session that is not scheduled
|
|
||||||
for normal ticks. A VM-backed Shell is another session with its own mutable
|
|
||||||
state.
|
|
||||||
|
|
||||||
Keeping background execution out of the first implementation avoids expanding
|
|
||||||
this work into a general scheduler. At the same time, making execution
|
|
||||||
eligibility separate from foreground ownership prevents an immediate future
|
|
||||||
background audio/player feature from requiring another foundational refactor.
|
|
||||||
|
|
||||||
Keeping render ownership global preserves the contract from `DEC-0037` and the
|
|
||||||
render ownership model from `DSC-0040`: sessions can generate frames, but only
|
|
||||||
the current foreground/render owner can publish valid current-epoch output.
|
|
||||||
|
|
||||||
## Invariantes / Contrato
|
|
||||||
|
|
||||||
- VM-backed mutable execution state is owned by a VM session, not by a global
|
|
||||||
firmware VM.
|
|
||||||
- A VM-backed Game and VM-backed Shell never share mutable VM context.
|
|
||||||
- A suspended resident Game retains its VM session.
|
|
||||||
- Suspension prevents normal foreground Game ticks/input/frame pacing; it does
|
|
||||||
not destroy or overwrite VM state.
|
|
||||||
- Native Shell apps do not require VM sessions.
|
|
||||||
- VM-backed Shell apps always require VM sessions.
|
|
||||||
- Each VM-backed Shell gets its own mutable VM session.
|
|
||||||
- Foreground ownership and execution eligibility are distinct.
|
|
||||||
- General background execution is out of scope for the initial implementation,
|
|
||||||
but the model must support adding it without replacing session ownership.
|
|
||||||
- SystemOS remains lifecycle and render ownership authority.
|
|
||||||
- Render ownership and stale frame rejection remain global policy, not per-VM
|
|
||||||
session policy.
|
|
||||||
- Cartridge load/activation must go through session services rather than
|
|
||||||
initializing one global firmware VM.
|
|
||||||
- V1 restrictions on one resident Game and one foreground Shell remain policy,
|
|
||||||
not a consequence of single-VM storage.
|
|
||||||
|
|
||||||
## Impactos
|
|
||||||
|
|
||||||
- **Runtime / SystemOS:** add a VM session model or equivalent service that
|
|
||||||
stores VM-backed process execution context and maps it to task/process
|
|
||||||
lifecycle.
|
|
||||||
- **Firmware:** remove `Firmware.vm` as the canonical VM context owner for all
|
|
||||||
apps. Firmware should request session creation/activation/tick from SystemOS.
|
|
||||||
- **VM:** support multiple distinct `VirtualMachine` instances or equivalent
|
|
||||||
isolated VM contexts in one OS lifetime.
|
|
||||||
- **Lifecycle:** task/process states must control session scheduling
|
|
||||||
eligibility without conflating foreground ownership with future background
|
|
||||||
execution.
|
|
||||||
- **Hub / Shell:** VM-backed Shell load must create or activate a Shell VM
|
|
||||||
session without overwriting a resident Game session.
|
|
||||||
- **Game:** Game pause/suspend/resume must preserve the Game VM session and
|
|
||||||
deliver lifecycle events to that session.
|
|
||||||
- **Renderer:** render manager stays global and validates ownership/epoch for
|
|
||||||
submissions produced by sessions.
|
|
||||||
- **FS / Memcard / Assets:** mutable handles and per-app runtime handles must
|
|
||||||
be evaluated for session scoping. Durable memcard/app data can remain keyed by
|
|
||||||
`app_id`; open handles and staging state should not accidentally leak across
|
|
||||||
sessions.
|
|
||||||
- **Tests:** add coverage proving VM-backed Shell creation does not overwrite a
|
|
||||||
suspended Game session, and that only eligible sessions tick.
|
|
||||||
|
|
||||||
## Alternativas Descartadas
|
|
||||||
|
|
||||||
- **Keep one global firmware VM and block VM-backed Shell when a Game is
|
|
||||||
resident:** rejected because it leaves `DEC-0037` incomplete and creates
|
|
||||||
divergent Shell behavior.
|
|
||||||
- **Serialize/snapshot the Game VM on suspension:** rejected because snapshot
|
|
||||||
correctness is harder than needed for v1 and would include VM, handles,
|
|
||||||
render state, assets, and side effects.
|
|
||||||
- **Hard-code two VM slots (`game_vm`, `shell_vm`):** rejected as a permanent
|
|
||||||
model because it encodes a short-term limit as architecture. It may only be
|
|
||||||
acceptable as an internal stepping stone if represented through the same
|
|
||||||
session abstraction.
|
|
||||||
- **Treat background-capable apps as hidden foreground owners:** rejected
|
|
||||||
because it would violate foreground ownership semantics and make render/input
|
|
||||||
authority ambiguous.
|
|
||||||
|
|
||||||
## Referencias
|
|
||||||
|
|
||||||
- Source agenda: `AGD-0046`
|
|
||||||
- Foreground stack and Game pause contract: `DEC-0037`
|
|
||||||
- Runtime mode separation: `DSC-0031`
|
|
||||||
- SystemOS lifecycle/process/task authority: `DSC-0032`
|
|
||||||
- VM render parallel execution boundary: `DSC-0040`
|
|
||||||
- Future Game-to-Game switching: `DSC-0043`
|
|
||||||
|
|
||||||
## Propagacao Necessaria
|
|
||||||
|
|
||||||
- Add implementation plans for introducing VM sessions and moving VM context
|
|
||||||
out of `Firmware`.
|
|
||||||
- Add implementation plans for separating VM-scoped runtime state from global
|
|
||||||
SystemOS/render state.
|
|
||||||
- Update runtime/firmware specs to describe session-owned VM context and the
|
|
||||||
distinction between foreground ownership and execution eligibility.
|
|
||||||
- Update tests for Game resident suspension, VM-backed Shell creation, and
|
|
||||||
session isolation.
|
|
||||||
- Keep future background execution as out of scope for first implementation,
|
|
||||||
but document that the model must support background-capable scheduling.
|
|
||||||
|
|
||||||
## Revision Log
|
|
||||||
|
|
||||||
- 2026-07-04: Initial decision draft from `AGD-0046`.
|
|
||||||
@ -1,96 +0,0 @@
|
|||||||
---
|
|
||||||
id: PLN-0136
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: Route Desktop Home Key Outside Guest Input
|
|
||||||
status: done
|
|
||||||
created: 2026-07-03
|
|
||||||
ref_decisions: [DEC-0037]
|
|
||||||
tags: [host, input, systemos, keyboard, lifecycle]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
`DEC-0037` makes desktop `Esc` the primary Home/SystemOS request and allows the
|
|
||||||
physical `Home` key as an alias. This request must be intercepted by the
|
|
||||||
host/SystemOS and must not appear as a guest pad button or input intrinsic.
|
|
||||||
|
|
||||||
This plan adds the host input route for the Home request without changing the
|
|
||||||
guest-visible pad surface.
|
|
||||||
|
|
||||||
## Decisions of Origin
|
|
||||||
|
|
||||||
- `DEC-0037` - Foreground Stack and Game Pause Contract.
|
|
||||||
|
|
||||||
## Target
|
|
||||||
|
|
||||||
Route desktop `Esc` and optional physical `Home` key to a SystemOS Home request
|
|
||||||
outside `InputSignals`.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
- Extend `crates/host/prometeu-host-desktop-winit/src/input.rs` or a nearby
|
|
||||||
host input module with a host/system control signal separate from
|
|
||||||
`prometeu_hal::InputSignals`.
|
|
||||||
- Map `KeyCode::Escape` to Home/SystemOS request.
|
|
||||||
- Map `KeyCode::Home` as an alias.
|
|
||||||
- Ensure neither key mutates `start_signal`, `select_signal`, or any pad field.
|
|
||||||
- Route the request through `crates/host/prometeu-host-desktop-winit/src/runner.rs`
|
|
||||||
into firmware/SystemOS.
|
|
||||||
- Add tests proving `Esc` and `Home` do not affect guest pad state.
|
|
||||||
|
|
||||||
## Out of Scope
|
|
||||||
|
|
||||||
- No pause/resume lifecycle implementation.
|
|
||||||
- No render ownership gating.
|
|
||||||
- No guest-visible Home button.
|
|
||||||
- No gamepad hardware Home mapping unless a future plan defines controller
|
|
||||||
system controls separately.
|
|
||||||
|
|
||||||
## Execution Plan
|
|
||||||
|
|
||||||
1. Define host control state.
|
|
||||||
Add a small host-only type such as `HostSystemControls` with a
|
|
||||||
`home_requested` edge or event. Store it in `HostInputHandler` or in the
|
|
||||||
runner next to the existing input handler.
|
|
||||||
|
|
||||||
2. Update keyboard handling.
|
|
||||||
In `HostInputHandler::handle_event`, recognize `KeyCode::Escape` and
|
|
||||||
`KeyCode::Home` on press. Set the host control event and return without
|
|
||||||
modifying `InputSignals`.
|
|
||||||
|
|
||||||
3. Preserve pad mapping.
|
|
||||||
Keep existing pad mappings unchanged:
|
|
||||||
arrows, `A/D/W/S`, `Q/E`, `Z`, and Shift. Do not reuse `Start` or `Select`
|
|
||||||
for Home.
|
|
||||||
|
|
||||||
4. Route to firmware/SystemOS.
|
|
||||||
In `crates/host/prometeu-host-desktop-winit/src/runner.rs`, consume the
|
|
||||||
host Home event before or during `about_to_wait` and call the firmware or
|
|
||||||
SystemOS method introduced by `PLN-0137` / `PLN-0140`.
|
|
||||||
|
|
||||||
5. Add tests.
|
|
||||||
Add host input tests for `Esc`, physical `Home`, and existing pad mappings.
|
|
||||||
Tests must prove Home produces a system event and no guest pad mutation.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- Pressing desktop `Esc` emits a Home/SystemOS host control event.
|
|
||||||
- Pressing physical `Home` emits the same event.
|
|
||||||
- Neither key affects `InputSignals`.
|
|
||||||
- Existing guest pad keyboard mappings still work.
|
|
||||||
- The runner consumes the Home request as a system action.
|
|
||||||
- The implementation does not add a guest syscall, intrinsic, or pad button.
|
|
||||||
|
|
||||||
## Tests / Validation
|
|
||||||
|
|
||||||
- Add unit tests in `prometeu-host-desktop-winit` for host input mapping.
|
|
||||||
- Run `cargo test -p prometeu-host-desktop-winit input`.
|
|
||||||
- Run `cargo test -p prometeu-hal input` if pad signal behavior changes.
|
|
||||||
- Run `discussion validate`.
|
|
||||||
|
|
||||||
## Risks
|
|
||||||
|
|
||||||
- `Esc` may conflict with debugger or window-close expectations. Keep debugger
|
|
||||||
start handling on `D` unchanged and do not map `Esc` to process exit.
|
|
||||||
- Physical `Home` is not available on every keyboard, so it must remain an
|
|
||||||
alias rather than the primary portable mapping.
|
|
||||||
@ -1,116 +0,0 @@
|
|||||||
---
|
|
||||||
id: PLN-0137
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: Implement SystemOS Foreground Stack State
|
|
||||||
status: done
|
|
||||||
created: 2026-07-03
|
|
||||||
ref_decisions: [DEC-0037]
|
|
||||||
tags: [runtime, os, lifecycle, foreground, shell, game]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
`DEC-0037` requires SystemOS to own foreground authority. The system must know
|
|
||||||
which owner is foreground, keep Hub/Home as root, allow only one resident Game,
|
|
||||||
allow only one foreground Shell, and return from VM-backed Shell apps to Hub in
|
|
||||||
v1.
|
|
||||||
|
|
||||||
This plan implements the SystemOS state model only. It does not yet deliver
|
|
||||||
Game pause/resume events, host keyboard routing, or render/audio/input boundary
|
|
||||||
changes.
|
|
||||||
|
|
||||||
## Decisions of Origin
|
|
||||||
|
|
||||||
- `DEC-0037` - Foreground Stack and Game Pause Contract.
|
|
||||||
|
|
||||||
## Target
|
|
||||||
|
|
||||||
Add an explicit SystemOS foreground stack model that can represent Hub, a
|
|
||||||
foreground Shell task, and a paused/suspended resident Game.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
- Add or extend SystemOS-owned foreground state under
|
|
||||||
`crates/console/prometeu-system/src/os/` or
|
|
||||||
`crates/console/prometeu-system/src/services/`.
|
|
||||||
- Represent v1 foreground owners:
|
|
||||||
- Hub/Home root;
|
|
||||||
- Shell task foreground;
|
|
||||||
- Game foreground;
|
|
||||||
- Game paused/suspended resident.
|
|
||||||
- Enforce at most one resident Game and at most one foreground Shell.
|
|
||||||
- Keep Hub as root/back-stack controller.
|
|
||||||
- Ensure Shell close returns to Hub/Home in v1.
|
|
||||||
- Expose focused queries through existing or new facades, likely
|
|
||||||
`crates/console/prometeu-system/src/os/facades/lifecycle.rs` and
|
|
||||||
`crates/console/prometeu-system/src/os/facades/sessions.rs`.
|
|
||||||
|
|
||||||
## Out of Scope
|
|
||||||
|
|
||||||
- No VM pause/resume event delivery.
|
|
||||||
- No host `Esc`/`Home` keyboard mapping.
|
|
||||||
- No render epoch implementation.
|
|
||||||
- No Game-to-Game switching.
|
|
||||||
- No background execution.
|
|
||||||
- No memory eviction for the resident Game.
|
|
||||||
|
|
||||||
## Execution Plan
|
|
||||||
|
|
||||||
1. Define foreground model types.
|
|
||||||
Add enums/structs for foreground owner and resident Game state in the most
|
|
||||||
local SystemOS module. The model must distinguish Game foreground, Game
|
|
||||||
paused/suspended, Hub foreground, and Shell task foreground.
|
|
||||||
|
|
||||||
2. Add SystemOS storage.
|
|
||||||
Store the foreground model in `SystemOS` from
|
|
||||||
`crates/console/prometeu-system/src/os/system_os.rs` with initialization to
|
|
||||||
Hub/Home root.
|
|
||||||
|
|
||||||
3. Wire lifecycle facade operations.
|
|
||||||
Extend lifecycle/session facades so firmware can request:
|
|
||||||
- set Game foreground;
|
|
||||||
- request Home from Game;
|
|
||||||
- set Shell foreground from Hub;
|
|
||||||
- close Shell and return to Hub;
|
|
||||||
- query current foreground owner;
|
|
||||||
- query resident Game task.
|
|
||||||
|
|
||||||
4. Enforce cardinality.
|
|
||||||
Add checks that reject a second resident Game and a second foreground Shell
|
|
||||||
with typed lifecycle errors. Use existing `LifecycleError` style or add
|
|
||||||
precise variants.
|
|
||||||
|
|
||||||
5. Integrate existing Shell close behavior.
|
|
||||||
Update `crates/console/prometeu-firmware/src/firmware/firmware_step_shell_running.rs`
|
|
||||||
only where needed so closing a Shell task routes through the foreground model
|
|
||||||
and ends in `FirmwareState::HubHome`.
|
|
||||||
|
|
||||||
6. Add tests.
|
|
||||||
Cover initial Hub foreground, Game foreground registration, Home request
|
|
||||||
moving Game to resident paused/suspended state, Shell foreground activation,
|
|
||||||
Shell close returning to Hub, and cardinality rejection.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- SystemOS has an explicit foreground owner model.
|
|
||||||
- Hub/Home is the initialized root foreground owner.
|
|
||||||
- Only one resident Game can exist in v1.
|
|
||||||
- Only one Shell task can be foreground in v1.
|
|
||||||
- VM-backed Shell close returns to Hub/Home.
|
|
||||||
- Foreground queries are available to firmware and later plans.
|
|
||||||
- No implementation path enables background execution.
|
|
||||||
|
|
||||||
## Tests / Validation
|
|
||||||
|
|
||||||
- Add focused unit tests in `prometeu-system` around the new model and facade.
|
|
||||||
- Run `cargo test -p prometeu-system lifecycle foreground`.
|
|
||||||
- Run `cargo test -p prometeu-firmware shell`.
|
|
||||||
- Run `discussion validate`.
|
|
||||||
|
|
||||||
## Risks
|
|
||||||
|
|
||||||
- Existing `TaskState::Foreground` and `TaskState::Suspended` may look like the
|
|
||||||
complete model. Do not overload them until foreground ownership and Hub root
|
|
||||||
authority are explicit.
|
|
||||||
- Shell windows already have focus semantics. Keep window focus as visual/task
|
|
||||||
evidence, not the sole source of SystemOS foreground truth.
|
|
||||||
@ -1,114 +0,0 @@
|
|||||||
---
|
|
||||||
id: PLN-0138
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: Specify Foreground Pause and Home Contract
|
|
||||||
status: done
|
|
||||||
created: 2026-07-03
|
|
||||||
ref_decisions: [DEC-0037]
|
|
||||||
tags: [runtime, specs, lifecycle, input, renderer, foreground]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
`DEC-0037` establishes the v1 foreground stack contract: one foreground visual
|
|
||||||
owner, one resident Game, one foreground Shell, Hub as root, Game-visible
|
|
||||||
pause/resume, OS-owned suspension, desktop `Esc` as Home/SystemOS request, and
|
|
||||||
render epoch invalidation across foreground transitions.
|
|
||||||
|
|
||||||
This plan updates the canonical runtime specs before implementation so code
|
|
||||||
plans cannot drift into guest-owned Home input, background execution, or stale
|
|
||||||
frame presentation.
|
|
||||||
|
|
||||||
## Decisions of Origin
|
|
||||||
|
|
||||||
- `DEC-0037` - Foreground Stack and Game Pause Contract.
|
|
||||||
|
|
||||||
## Target
|
|
||||||
|
|
||||||
Document the foreground, pause/suspend, Home request, input, audio, and render
|
|
||||||
ownership contract in the canonical runtime specs.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
- Update `docs/specs/runtime/12-firmware-pos-and-prometeuhub.md` with:
|
|
||||||
- Hub as root/special Shell owner;
|
|
||||||
- one foreground visual owner;
|
|
||||||
- one resident Game;
|
|
||||||
- one foreground Shell;
|
|
||||||
- `Game -> Home/Shell -> same Game` lifecycle;
|
|
||||||
- VM-backed Shell app return-to-Hub behavior.
|
|
||||||
- Update `docs/specs/runtime/06-input-peripheral.md` with:
|
|
||||||
- Home/SystemOS as host/system control, not pad input;
|
|
||||||
- desktop `Esc` as primary Home request;
|
|
||||||
- physical `Home` key as optional alias;
|
|
||||||
- input clear/barrier requirements across Game pause/resume.
|
|
||||||
- Update `docs/specs/runtime/09-events-and-concurrency.md` with:
|
|
||||||
- pause notification budget;
|
|
||||||
- OS-owned suspension after the budget;
|
|
||||||
- resume/foreground-restore notification before visual return.
|
|
||||||
- Update `docs/specs/runtime/14-boot-profiles.md` only to clarify that this
|
|
||||||
foreground contract is not Game-to-Game switching and does not change direct
|
|
||||||
`--run` or `--games-root` boot semantics.
|
|
||||||
- Update renderer-related spec text in the most local existing runtime spec if
|
|
||||||
present, otherwise add a short subsection to `12-firmware-pos-and-prometeuhub.md`
|
|
||||||
describing ownership epoch invalidation and first-valid-frame gating.
|
|
||||||
|
|
||||||
## Out of Scope
|
|
||||||
|
|
||||||
- No Rust implementation.
|
|
||||||
- No new guest ABI or pad button.
|
|
||||||
- No background execution policy.
|
|
||||||
- No Game A -> Home -> Game B switching.
|
|
||||||
- No memory eviction policy for resident Games.
|
|
||||||
|
|
||||||
## Execution Plan
|
|
||||||
|
|
||||||
1. Edit `12-firmware-pos-and-prometeuhub.md`.
|
|
||||||
Define foreground owner cardinality, Hub root ownership, Shell foreground
|
|
||||||
behavior, Game resident/suspended behavior, return-to-Hub from Shell, and the
|
|
||||||
requirement to keep Hub/Shell visible until a resumed Game submits a valid
|
|
||||||
current-epoch frame.
|
|
||||||
|
|
||||||
2. Edit `06-input-peripheral.md`.
|
|
||||||
State that Home/SystemOS is outside the guest-visible pad and input
|
|
||||||
intrinsic surface. Document desktop `Esc` as the primary host mapping and
|
|
||||||
physical `Home` as an optional alias.
|
|
||||||
|
|
||||||
3. Edit `09-events-and-concurrency.md`.
|
|
||||||
Add the pause/resume event ordering: Home request, pause notification,
|
|
||||||
bounded reaction budget, OS suspension, resume/foreground-restore
|
|
||||||
notification, first-valid-frame visual return.
|
|
||||||
|
|
||||||
4. Edit `14-boot-profiles.md`.
|
|
||||||
Cross-reference the foreground contract and explicitly keep direct boot,
|
|
||||||
games-root Home launch, and future Game-to-Game switching separate.
|
|
||||||
|
|
||||||
5. Run text checks.
|
|
||||||
Search for wording that implies Home is a guest input button, a guest ABI, or
|
|
||||||
background execution. Replace misleading text with the `DEC-0037` contract.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- Specs define `Paused` as Game-visible and `Suspended` as OS-owned.
|
|
||||||
- Specs state that desktop `Esc` is the primary Home request and is not guest
|
|
||||||
pad input.
|
|
||||||
- Specs state that the Game receives pause/resume notifications but does not
|
|
||||||
control OS suspension.
|
|
||||||
- Specs state that Hub is the root Shell owner and Shell app close returns to
|
|
||||||
Hub in v1.
|
|
||||||
- Specs state that visual return to Game waits for a valid current-epoch render
|
|
||||||
submission.
|
|
||||||
- Specs explicitly exclude background execution and Game-to-Game switching.
|
|
||||||
|
|
||||||
## Tests / Validation
|
|
||||||
|
|
||||||
- Run `discussion validate`.
|
|
||||||
- Run `rg -n "Home|Esc|Suspended|Paused|foreground|background execution" docs/specs/runtime`.
|
|
||||||
- Verify no edited spec describes Home/SystemOS as a guest-visible pad button.
|
|
||||||
|
|
||||||
## Risks
|
|
||||||
|
|
||||||
- The spec could accidentally reopen Game-to-Game switching; keep that scoped to
|
|
||||||
`DSC-0043 / AGD-0044`.
|
|
||||||
- The input spec could confuse physical keyboard mapping with VM input; keep
|
|
||||||
host/system controls outside the guest input surface.
|
|
||||||
@ -1,106 +0,0 @@
|
|||||||
---
|
|
||||||
id: PLN-0139
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: Validate Game Home Shell Game End To End
|
|
||||||
status: done
|
|
||||||
created: 2026-07-03
|
|
||||||
ref_decisions: [DEC-0037]
|
|
||||||
tags: [runtime, validation, firmware, host, lifecycle]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
`DEC-0037` is not complete until the product flow is proven end to end:
|
|
||||||
a Game runs, the user requests Home, the Game is paused/suspended, Hub or a
|
|
||||||
VM-backed Shell takes foreground, closing Shell returns to Hub, and the same
|
|
||||||
Game can resume only after a valid current-epoch frame exists.
|
|
||||||
|
|
||||||
This plan adds final validation after the spec, SystemOS, VM, host input, and
|
|
||||||
render/audio/input boundary plans are implemented.
|
|
||||||
|
|
||||||
## Decisions of Origin
|
|
||||||
|
|
||||||
- `DEC-0037` - Foreground Stack and Game Pause Contract.
|
|
||||||
|
|
||||||
## Target
|
|
||||||
|
|
||||||
Prove the complete `Game -> Home/Shell -> same Game` lifecycle and prevent
|
|
||||||
regressions around guest input, foreground authority, and stale rendering.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
- Add integration-style tests for:
|
|
||||||
- Game running to Home request;
|
|
||||||
- pause notification and forced suspension;
|
|
||||||
- Hub foreground while Game is resident;
|
|
||||||
- VM-backed Shell foreground while Game is suspended;
|
|
||||||
- Shell close returning to Hub;
|
|
||||||
- resume request returning to same Game;
|
|
||||||
- first-valid-frame visual restore;
|
|
||||||
- host `Esc`/`Home` not entering guest input.
|
|
||||||
- Add manual smoke instructions for desktop host.
|
|
||||||
- Confirm direct `--run` and games-root Home launch remain separate.
|
|
||||||
|
|
||||||
## Out of Scope
|
|
||||||
|
|
||||||
- No new implementation beyond tests and developer-facing validation notes.
|
|
||||||
- No Game A -> Home -> Game B switching.
|
|
||||||
- No performance tuning.
|
|
||||||
- No polished UI.
|
|
||||||
|
|
||||||
## Execution Plan
|
|
||||||
|
|
||||||
1. Add firmware/SystemOS integration tests.
|
|
||||||
Use the most stable existing seams in `crates/console/prometeu-firmware/src/firmware/firmware.rs`
|
|
||||||
and `crates/console/prometeu-system` tests to drive Game foreground, Home
|
|
||||||
request, Shell foreground, Shell close, and Game resume.
|
|
||||||
|
|
||||||
2. Add host input integration tests.
|
|
||||||
Ensure `Esc` and physical `Home` create a host Home event and do not mutate
|
|
||||||
guest `InputSignals`.
|
|
||||||
|
|
||||||
3. Add render publication validation.
|
|
||||||
Assert stale submissions from the pre-pause Game epoch are rejected after
|
|
||||||
Home takes foreground and that the Game is not visually restored until a new
|
|
||||||
current-epoch submission exists.
|
|
||||||
|
|
||||||
4. Add audio/input boundary validation.
|
|
||||||
Assert Game input is cleared or barriered across pause/resume. Assert Game
|
|
||||||
audio pause follows Game suspension.
|
|
||||||
|
|
||||||
5. Add direct boot and games-root regression checks.
|
|
||||||
Ensure direct `--run <cart>` remains independent from Home request handling
|
|
||||||
and that games-root Home launch still enters the Game path.
|
|
||||||
|
|
||||||
6. Document manual smoke commands.
|
|
||||||
Add or update developer-facing spec text with release-mode commands:
|
|
||||||
- `cargo run --release -p prometeu-host-desktop-winit -- --run test-cartridges/stress-console`
|
|
||||||
- `cargo run --release -p prometeu-host-desktop-winit -- --games-root test-cartridges`
|
|
||||||
Include the expected `Esc -> Home -> Shell -> Hub -> resume Game` behavior
|
|
||||||
once implemented.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- Automated tests cover `Game -> Home -> same Game`.
|
|
||||||
- Automated tests cover `Game -> Home -> Shell -> Hub -> same Game`.
|
|
||||||
- Tests prove Home request is host/SystemOS control, not guest pad input.
|
|
||||||
- Tests prove suspended Game does not tick.
|
|
||||||
- Tests prove stale render submissions are not presented.
|
|
||||||
- Tests prove visual return waits for a current-epoch Game frame.
|
|
||||||
- Direct `--run` and games-root Home launch regressions pass.
|
|
||||||
|
|
||||||
## Tests / Validation
|
|
||||||
|
|
||||||
- Run `cargo test -p prometeu-system`.
|
|
||||||
- Run `cargo test -p prometeu-firmware`.
|
|
||||||
- Run `cargo test -p prometeu-host-desktop-winit`.
|
|
||||||
- Run any targeted render worker tests affected by ownership epoch changes.
|
|
||||||
- Run `discussion validate`.
|
|
||||||
|
|
||||||
## Risks
|
|
||||||
|
|
||||||
- The current test seams may not expose enough host-driven lifecycle behavior.
|
|
||||||
If needed, add narrow test-only helpers rather than testing through sleeps or
|
|
||||||
real-time window loops.
|
|
||||||
- End-to-end validation can become flaky if it depends on wall-clock timing.
|
|
||||||
Use deterministic ticks and explicit state transitions.
|
|
||||||
@ -1,110 +0,0 @@
|
|||||||
---
|
|
||||||
id: PLN-0140
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: Deliver Game Pause Resume and Suspension
|
|
||||||
status: done
|
|
||||||
created: 2026-07-03
|
|
||||||
ref_decisions: [DEC-0037]
|
|
||||||
tags: [runtime, os, vm, lifecycle, pause, suspension]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
`DEC-0037` separates Game-visible `Paused` from OS-owned `Suspended`. When Home
|
|
||||||
is requested during Game execution, SystemOS must notify the Game, give it a
|
|
||||||
short bounded budget, then suspend the VM even if the Game does not cooperate.
|
|
||||||
When returning, SystemOS must reactivate the VM and send a resume or
|
|
||||||
foreground-restore notification before the visual owner returns to Game.
|
|
||||||
|
|
||||||
This plan implements the pause/resume and suspension mechanics after the
|
|
||||||
foreground state model exists.
|
|
||||||
|
|
||||||
## Decisions of Origin
|
|
||||||
|
|
||||||
- `DEC-0037` - Foreground Stack and Game Pause Contract.
|
|
||||||
|
|
||||||
## Target
|
|
||||||
|
|
||||||
Add Game pause/resume lifecycle delivery and OS-enforced VM suspension for the
|
|
||||||
same resident Game.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
- Add a Game lifecycle event model for pause and resume/foreground-restore.
|
|
||||||
- Add a bounded pause reaction budget owned by SystemOS.
|
|
||||||
- Suspend the Game VM after the budget even if the Game does not cooperate.
|
|
||||||
- Ensure a suspended Game receives no normal gameplay ticks.
|
|
||||||
- Reactivate the VM and deliver resume/foreground-restore before Game visual
|
|
||||||
foreground restoration.
|
|
||||||
- Integrate with `GameRunningStep` and VM runtime tick control.
|
|
||||||
|
|
||||||
## Out of Scope
|
|
||||||
|
|
||||||
- No render first-valid-frame gating; that is in `PLN-0141`.
|
|
||||||
- No host keyboard routing; that is in `PLN-0136`.
|
|
||||||
- No new guest syscall.
|
|
||||||
- No background execution.
|
|
||||||
- No Game-to-Game switching.
|
|
||||||
|
|
||||||
## Execution Plan
|
|
||||||
|
|
||||||
1. Add lifecycle event representation.
|
|
||||||
Define pause/resume events in the most local runtime lifecycle module,
|
|
||||||
likely under `crates/console/prometeu-system/src/os/lifecycle.rs` or a new
|
|
||||||
SystemOS lifecycle submodule. Events must be observable by the VM/runtime
|
|
||||||
path without giving the Game authority over suspension.
|
|
||||||
|
|
||||||
2. Add pause transition state.
|
|
||||||
Extend the foreground state from `PLN-0137` with a `PauseRequested` or
|
|
||||||
equivalent transition state that records the target Game task and the
|
|
||||||
remaining pause budget.
|
|
||||||
|
|
||||||
3. Gate VM ticks.
|
|
||||||
Update `crates/console/prometeu-firmware/src/firmware/firmware_step_game_running.rs`
|
|
||||||
and `crates/console/prometeu-system/src/services/vm_runtime/tick.rs` so a
|
|
||||||
suspended Game does not receive normal ticks, normal input, or frame pacing.
|
|
||||||
|
|
||||||
4. Deliver pause notification.
|
|
||||||
Add the mechanism that makes the pause event visible to the running Game
|
|
||||||
before suspension. Use an existing VM event channel if present; otherwise add
|
|
||||||
a narrow lifecycle-event queue owned by SystemOS/VM runtime.
|
|
||||||
|
|
||||||
5. Enforce the budget.
|
|
||||||
Advance the pause budget on firmware ticks. When it expires, transition the
|
|
||||||
Game task/process to suspended through SystemOS lifecycle regardless of Game
|
|
||||||
cooperation.
|
|
||||||
|
|
||||||
6. Deliver resume notification.
|
|
||||||
On return to the resident Game, transition from suspended to resume-requested,
|
|
||||||
reactivate VM execution, deliver resume/foreground-restore, and leave visual
|
|
||||||
restoration to the render boundary plan.
|
|
||||||
|
|
||||||
7. Add tests.
|
|
||||||
Cover cooperative pause, non-cooperative forced suspension, no tick while
|
|
||||||
suspended, resume notification delivery, and no Game-owned ability to cancel
|
|
||||||
OS suspension.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- Game receives pause before suspension.
|
|
||||||
- SystemOS suspends after a bounded budget even if the Game does not cooperate.
|
|
||||||
- Suspended Game does not receive normal gameplay ticks.
|
|
||||||
- Resume/foreground-restore is delivered before visual return to Game.
|
|
||||||
- Game cannot prevent OS suspension through guest-visible behavior.
|
|
||||||
- Existing debugger pause behavior remains distinct from lifecycle suspension.
|
|
||||||
|
|
||||||
## Tests / Validation
|
|
||||||
|
|
||||||
- Add unit tests in `prometeu-system` for lifecycle transition state.
|
|
||||||
- Add firmware tests around `GameRunningStep` pause request and suspension.
|
|
||||||
- Add VM runtime tests proving suspended state suppresses normal ticks.
|
|
||||||
- Run `cargo test -p prometeu-system`.
|
|
||||||
- Run `cargo test -p prometeu-firmware`.
|
|
||||||
- Run `discussion validate`.
|
|
||||||
|
|
||||||
## Risks
|
|
||||||
|
|
||||||
- Debugger pause already exists in VM runtime. Keep debug pause separate from
|
|
||||||
lifecycle pause/suspend.
|
|
||||||
- A lifecycle event queue could become a general app event system. Keep this
|
|
||||||
plan limited to pause/resume required by `DEC-0037`.
|
|
||||||
@ -1,104 +0,0 @@
|
|||||||
---
|
|
||||||
id: PLN-0141
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: Integrate Render Audio and Input Pause Boundaries
|
|
||||||
status: done
|
|
||||||
created: 2026-07-03
|
|
||||||
ref_decisions: [DEC-0037]
|
|
||||||
tags: [runtime, renderer, audio, input, foreground, lifecycle]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
`DEC-0037` requires foreground transitions to invalidate stale render
|
|
||||||
submissions, keep Hub/Shell visible until the resumed Game publishes a valid
|
|
||||||
current-epoch frame, clear or barrier Game input across pause/resume, and pause
|
|
||||||
Game audio with the Game VM in v1.
|
|
||||||
|
|
||||||
This plan integrates those boundary effects after SystemOS foreground state and
|
|
||||||
Game pause/resume mechanics exist.
|
|
||||||
|
|
||||||
## Decisions of Origin
|
|
||||||
|
|
||||||
- `DEC-0037` - Foreground Stack and Game Pause Contract.
|
|
||||||
|
|
||||||
## Target
|
|
||||||
|
|
||||||
Make foreground transitions safe at the render, input, and audio boundaries.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
- Advance render ownership epoch/generation whenever foreground owner changes.
|
|
||||||
- Discard stale Game frames after Shell/Hub takes foreground.
|
|
||||||
- Discard stale Shell frames after Game resumes.
|
|
||||||
- Keep Hub/Shell visible until a resumed Game publishes a valid current-epoch
|
|
||||||
submission.
|
|
||||||
- Clear or barrier Game input across pause/resume boundaries.
|
|
||||||
- Pause Game audio with the Game VM in v1.
|
|
||||||
- Add telemetry/tests for stale discards and first-valid-frame gating.
|
|
||||||
|
|
||||||
## Out of Scope
|
|
||||||
|
|
||||||
- No independent background audio policy.
|
|
||||||
- No overlay rendering policy.
|
|
||||||
- No Game-to-Game switch rendering.
|
|
||||||
- No full memory eviction behavior.
|
|
||||||
|
|
||||||
## Execution Plan
|
|
||||||
|
|
||||||
1. Extend render ownership transition calls.
|
|
||||||
Update `crates/console/prometeu-system/src/services/vm_runtime/render_manager.rs`
|
|
||||||
and call sites in `tick.rs` so foreground owner changes explicitly advance
|
|
||||||
ownership epoch/generation, not only app mode/app id changes.
|
|
||||||
|
|
||||||
2. Add foreground restore gating.
|
|
||||||
Add state in SystemOS or firmware that records when visual return to Game is
|
|
||||||
pending. The state must remain in Hub/Shell presentation until a
|
|
||||||
current-epoch Game submission is observed.
|
|
||||||
|
|
||||||
3. Update host presentation selection.
|
|
||||||
Update `crates/host/prometeu-host-desktop-winit/src/runner.rs` if necessary
|
|
||||||
so `should_present_worker_frame` and fallback framebuffer presentation obey
|
|
||||||
the foreground restore gate.
|
|
||||||
|
|
||||||
4. Add stale discard coverage.
|
|
||||||
Extend render manager and render worker tests to prove stale submissions
|
|
||||||
from the previous owner are discarded after epoch change.
|
|
||||||
|
|
||||||
5. Add input boundary clearing.
|
|
||||||
Add a SystemOS or firmware input barrier when Game pause starts and when
|
|
||||||
Game resume starts. The barrier must prevent held or pressed Game input from
|
|
||||||
leaking across the lifecycle boundary.
|
|
||||||
|
|
||||||
6. Add audio pause integration.
|
|
||||||
Ensure Game pause/suspend produces the existing host audio pause command path
|
|
||||||
or a new explicit lifecycle audio pause signal. Keep this separate from
|
|
||||||
debugger pause if the observable cause matters in tests.
|
|
||||||
|
|
||||||
7. Add tests.
|
|
||||||
Cover stale frame discard, first-valid-frame visual return, input clearing,
|
|
||||||
and Game audio pause/resume behavior.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- Foreground owner changes advance render ownership epoch/generation.
|
|
||||||
- Old Game frames cannot present after Hub/Shell takes foreground.
|
|
||||||
- Old Hub/Shell frames cannot present after Game resumes.
|
|
||||||
- Visual return to Game waits for a valid current-epoch Game submission.
|
|
||||||
- Game input does not leak across pause/resume boundaries.
|
|
||||||
- Game audio pauses with Game suspension in v1.
|
|
||||||
|
|
||||||
## Tests / Validation
|
|
||||||
|
|
||||||
- Run `cargo test -p prometeu-system render foreground`.
|
|
||||||
- Run `cargo test -p prometeu-firmware pause`.
|
|
||||||
- Run `cargo test -p prometeu-host-desktop-winit presentation`.
|
|
||||||
- Run `discussion validate`.
|
|
||||||
|
|
||||||
## Risks
|
|
||||||
|
|
||||||
- Existing render epoch logic may already key off app mode/app id. Foreground
|
|
||||||
owner changes must be explicit so Shell/Hub and resumed Game cannot share a
|
|
||||||
stale epoch by accident.
|
|
||||||
- Audio pause currently follows VM paused/debugger state in the host. Keep Game
|
|
||||||
lifecycle pause distinct enough that tests can prove the decision contract.
|
|
||||||
@ -1,127 +0,0 @@
|
|||||||
---
|
|
||||||
id: PLN-0142
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: Deliver Game Lifecycle Events To VM Runtime
|
|
||||||
status: done
|
|
||||||
created: 2026-07-04
|
|
||||||
ref_decisions: [DEC-0037]
|
|
||||||
tags: [runtime, os, lifecycle, shell, game, vm, foreground, architecture]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
`DEC-0037` requires pause/resume to be Game-visible lifecycle events. The
|
|
||||||
current implementation records `GameLifecycleEvent` values in SystemOS, but no
|
|
||||||
runtime or VM execution path consumes them. Tests currently prove that the OS
|
|
||||||
queue contains events, not that the Game/runtime can observe them before
|
|
||||||
suspension or after resume.
|
|
||||||
|
|
||||||
## Objective
|
|
||||||
|
|
||||||
Deliver Game lifecycle pause/resume events from SystemOS into the VM runtime
|
|
||||||
boundary in a deterministic, testable way, without exposing Home as guest pad
|
|
||||||
input or a userland syscall.
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
- Source decision: `DEC-0037`.
|
|
||||||
- Fixes the lifecycle event delivery gap found after `PLN-0140` and
|
|
||||||
`PLN-0141`.
|
|
||||||
- `PLN-0143` depends on this plan if it needs to prove that the pause event is
|
|
||||||
consumed before the pause budget expires.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
Included:
|
|
||||||
|
|
||||||
- Define the runtime-facing lifecycle event delivery API for `Pause` and
|
|
||||||
`ResumeForeground`.
|
|
||||||
- Move events from the SystemOS queue into the VM/runtime tick boundary.
|
|
||||||
- Make delivered events observable in tests without creating a broad app event
|
|
||||||
system.
|
|
||||||
- Preserve the distinction between lifecycle pause and debugger pause.
|
|
||||||
- Ensure events are delivered only to the resident Game task they target.
|
|
||||||
|
|
||||||
Excluded:
|
|
||||||
|
|
||||||
- No guest Home input, pad field, intrinsic, or syscall.
|
|
||||||
- No general userland event bus.
|
|
||||||
- No background execution while suspended.
|
|
||||||
- No new Shell lifecycle event model.
|
|
||||||
|
|
||||||
## Non-Goals
|
|
||||||
|
|
||||||
- Do not implement Game-to-Game switching.
|
|
||||||
- Do not change renderer ownership policy except where tests need to assert
|
|
||||||
lifecycle event ordering.
|
|
||||||
- Do not add wall-clock timers or asynchronous callbacks.
|
|
||||||
|
|
||||||
## Execution Method
|
|
||||||
|
|
||||||
1. Add a narrow VM runtime lifecycle inbox.
|
|
||||||
- What changes: introduce a small data structure for pending/delivered Game
|
|
||||||
lifecycle events in `VirtualMachineRuntime`.
|
|
||||||
- How it changes: add methods such as `queue_game_lifecycle_events`,
|
|
||||||
`take_delivered_game_lifecycle_events`, or a more idiomatic local name
|
|
||||||
that preserves deterministic frame-boundary delivery.
|
|
||||||
- File(s): `crates/console/prometeu-system/src/services/vm_runtime/mod.rs`,
|
|
||||||
`crates/console/prometeu-system/src/services/vm_runtime/tick.rs`, and
|
|
||||||
focused tests under `crates/console/prometeu-system/src/services/vm_runtime/`.
|
|
||||||
|
|
||||||
2. Route SystemOS events into the VM facade before ticking.
|
|
||||||
- What changes: `VmFacade::tick` must drain lifecycle events from SystemOS
|
|
||||||
and pass only events targeted at the currently running resident Game into
|
|
||||||
the VM runtime lifecycle inbox.
|
|
||||||
- How it changes: use the existing `LifecycleFacade::take_game_lifecycle_events`
|
|
||||||
or replace it with an equivalent internal method if borrowing requires a
|
|
||||||
cleaner API. Do not leave events only observable through
|
|
||||||
`pending_game_lifecycle_events`.
|
|
||||||
- File(s): `crates/console/prometeu-system/src/os/facades/vm.rs`,
|
|
||||||
`crates/console/prometeu-system/src/os/facades/lifecycle.rs`.
|
|
||||||
|
|
||||||
3. Expose deterministic test visibility.
|
|
||||||
- What changes: tests must be able to assert that `Pause` and
|
|
||||||
`ResumeForeground` reached the VM runtime boundary.
|
|
||||||
- How it changes: add test-only or public diagnostic accessors on
|
|
||||||
`VmFacade`/`VirtualMachineRuntime` for delivered lifecycle events, scoped
|
|
||||||
to lifecycle validation rather than a guest ABI.
|
|
||||||
- File(s): `crates/console/prometeu-system/src/os/facades/vm.rs`,
|
|
||||||
`crates/console/prometeu-system/src/services/vm_runtime/tests.rs`.
|
|
||||||
|
|
||||||
4. Update firmware tests to assert delivery, not only queue presence.
|
|
||||||
- What changes: existing firmware tests that inspect
|
|
||||||
`pending_game_lifecycle_events` should assert VM-runtime delivery at the
|
|
||||||
tick boundary.
|
|
||||||
- How it changes: after a Game pause/resume flow, verify that the VM/runtime
|
|
||||||
observed the lifecycle event in order.
|
|
||||||
- File(s): `crates/console/prometeu-firmware/src/firmware/firmware.rs`.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- [ ] `Pause` is moved from SystemOS into the VM runtime boundary before
|
|
||||||
suspension is allowed to complete.
|
|
||||||
- [ ] `ResumeForeground` is moved into the VM runtime boundary before the Game
|
|
||||||
is considered visually restored.
|
|
||||||
- [ ] Tests fail if events remain only in `SystemOS.game_lifecycle_events`.
|
|
||||||
- [ ] Lifecycle event delivery does not mutate `InputSignals`.
|
|
||||||
- [ ] Debugger pause remains separate from lifecycle pause.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
- Unit tests in `prometeu-system` proving VM runtime lifecycle event delivery
|
|
||||||
and drain semantics.
|
|
||||||
- Firmware tests proving Home request delivers `Pause` to the VM/runtime before
|
|
||||||
suspension.
|
|
||||||
- Firmware tests proving resident Game resume delivers `ResumeForeground`
|
|
||||||
before Game foreground restoration.
|
|
||||||
- Run `cargo test -p prometeu-system`.
|
|
||||||
- Run `cargo test -p prometeu-firmware`.
|
|
||||||
- Run `discussion validate`.
|
|
||||||
|
|
||||||
## Affected Artifacts
|
|
||||||
|
|
||||||
- `crates/console/prometeu-system/src/os/facades/vm.rs`
|
|
||||||
- `crates/console/prometeu-system/src/os/facades/lifecycle.rs`
|
|
||||||
- `crates/console/prometeu-system/src/services/vm_runtime/mod.rs`
|
|
||||||
- `crates/console/prometeu-system/src/services/vm_runtime/tick.rs`
|
|
||||||
- `crates/console/prometeu-firmware/src/firmware/firmware.rs`
|
|
||||||
@ -1,122 +0,0 @@
|
|||||||
---
|
|
||||||
id: PLN-0143
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: Guarantee Cooperative Pause Budget Tick Before Suspension
|
|
||||||
status: done
|
|
||||||
created: 2026-07-04
|
|
||||||
ref_decisions: [DEC-0037]
|
|
||||||
tags: [runtime, os, lifecycle, shell, game, vm, foreground, architecture]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
`DEC-0037` requires a Game-visible `Paused` event followed by a bounded
|
|
||||||
cooperative reaction budget before SystemOS imposes `Suspended`. The current
|
|
||||||
implementation uses a one-tick budget and advances it before `vm.tick` in
|
|
||||||
`GameRunningStep`, so the Game can be suspended without any runtime tick in
|
|
||||||
which it could observe or react to pause.
|
|
||||||
|
|
||||||
## Objective
|
|
||||||
|
|
||||||
Guarantee at least one deterministic VM/runtime delivery point for the Game to
|
|
||||||
observe pause before forced suspension, while preserving SystemOS authority to
|
|
||||||
suspend the Game after the budget.
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
- Source decision: `DEC-0037`.
|
|
||||||
- Should be executed after or together with `PLN-0142`, because budget tests
|
|
||||||
must prove delivered lifecycle events, not only queued OS events.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
Included:
|
|
||||||
|
|
||||||
- Fix pause budget ordering so the pause budget cannot expire before the VM
|
|
||||||
runtime has a delivery point for `Pause`.
|
|
||||||
- Define the budget in deterministic machine ticks/frames, not wall-clock time.
|
|
||||||
- Ensure a Game receives no normal gameplay ticks after it reaches
|
|
||||||
`PausedSuspended`.
|
|
||||||
- Preserve the existing forced-suspension authority when the budget expires.
|
|
||||||
- Update tests that currently assert no VM tick occurs immediately after Home.
|
|
||||||
|
|
||||||
Excluded:
|
|
||||||
|
|
||||||
- No guest-controlled cancellation of suspension.
|
|
||||||
- No background execution after suspension.
|
|
||||||
- No extension of the pause budget based on host timing.
|
|
||||||
- No direct visual-return behavior; render restore gating remains covered by
|
|
||||||
the existing render boundary work.
|
|
||||||
|
|
||||||
## Non-Goals
|
|
||||||
|
|
||||||
- Do not add a general scheduler.
|
|
||||||
- Do not make pause cooperation mandatory for suspension.
|
|
||||||
- Do not conflate lifecycle pause with debugger pause.
|
|
||||||
|
|
||||||
## Execution Method
|
|
||||||
|
|
||||||
1. Redefine pause budget state transitions.
|
|
||||||
- What changes: `ResidentGameState::PauseRequested` must represent a state
|
|
||||||
in which one or more delivery ticks remain before `PausedSuspended`.
|
|
||||||
- How it changes: adjust `ForegroundStack::advance_pause_budget` or split it
|
|
||||||
into explicit methods such as `mark_pause_delivered` and
|
|
||||||
`advance_pause_budget_after_delivery`.
|
|
||||||
- File(s): `crates/console/prometeu-system/src/services/foreground.rs`.
|
|
||||||
|
|
||||||
2. Move budget advancement after lifecycle delivery.
|
|
||||||
- What changes: `GameRunningStep::on_update` must not suspend before the VM
|
|
||||||
runtime has a chance to receive and process the pause lifecycle event.
|
|
||||||
- How it changes: reorder the GameRunning update so the event delivery path
|
|
||||||
runs first, then the budget is advanced, and only a subsequent eligible
|
|
||||||
boundary can force suspension.
|
|
||||||
- File(s): `crates/console/prometeu-firmware/src/firmware/firmware_step_game_running.rs`,
|
|
||||||
`crates/console/prometeu-system/src/os/facades/vm.rs`.
|
|
||||||
|
|
||||||
3. Preserve no-tick-after-suspended behavior.
|
|
||||||
- What changes: once `ResidentGameState::PausedSuspended` and
|
|
||||||
`TaskState::Suspended` are reached, the Game must not receive normal ticks,
|
|
||||||
normal input, or frame pacing.
|
|
||||||
- How it changes: keep the existing early transition to Hub after suspension
|
|
||||||
expires, but ensure it occurs after the cooperative pause window rather
|
|
||||||
than before it.
|
|
||||||
- File(s): `crates/console/prometeu-firmware/src/firmware/firmware_step_game_running.rs`.
|
|
||||||
|
|
||||||
4. Update tests to match the corrected semantics.
|
|
||||||
- What changes: tests must stop asserting that the first tick after Home does
|
|
||||||
zero VM work purely because Home was requested.
|
|
||||||
- How it changes: assert that the pause event was delivered during the
|
|
||||||
cooperative window, then assert that a later deterministic tick forces
|
|
||||||
suspension and no further Game ticks occur.
|
|
||||||
- File(s): `crates/console/prometeu-firmware/src/firmware/firmware.rs`,
|
|
||||||
`crates/console/prometeu-system/src/os/system_os.rs`.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- [ ] A Game receives a deterministic pause delivery point before SystemOS
|
|
||||||
suspends it.
|
|
||||||
- [ ] SystemOS still suspends the Game after the bounded budget even if the
|
|
||||||
Game does not cooperate.
|
|
||||||
- [ ] A suspended Game receives no normal gameplay ticks, normal Game input, or
|
|
||||||
frame pacing.
|
|
||||||
- [ ] Tests prove the order: Home request -> pause delivery -> budget expiry ->
|
|
||||||
suspension.
|
|
||||||
- [ ] Debugger pause behavior is unchanged.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
- Unit tests in `prometeu-system` for pause budget transitions.
|
|
||||||
- Firmware tests for non-cooperative forced suspension after at least one
|
|
||||||
delivered pause boundary.
|
|
||||||
- Regression tests proving suspended Game tick count does not advance.
|
|
||||||
- Run `cargo test -p prometeu-system foreground lifecycle`.
|
|
||||||
- Run `cargo test -p prometeu-firmware pause`.
|
|
||||||
- Run `discussion validate`.
|
|
||||||
|
|
||||||
## Affected Artifacts
|
|
||||||
|
|
||||||
- `crates/console/prometeu-system/src/services/foreground.rs`
|
|
||||||
- `crates/console/prometeu-system/src/os/facades/lifecycle.rs`
|
|
||||||
- `crates/console/prometeu-system/src/os/facades/vm.rs`
|
|
||||||
- `crates/console/prometeu-firmware/src/firmware/firmware_step_game_running.rs`
|
|
||||||
- `crates/console/prometeu-firmware/src/firmware/firmware.rs`
|
|
||||||
@ -1,124 +0,0 @@
|
|||||||
---
|
|
||||||
id: PLN-0144
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: Suspend Resident Game When Shell Takes Foreground
|
|
||||||
status: done
|
|
||||||
created: 2026-07-04
|
|
||||||
ref_decisions: [DEC-0037]
|
|
||||||
tags: [runtime, os, lifecycle, shell, game, vm, foreground, architecture]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
`DEC-0037` says a suspended resident Game must not receive normal gameplay
|
|
||||||
ticks, normal Game input, or frame pacing while Hub/Shell owns foreground. The
|
|
||||||
current `set_shell_foreground` path can mark the resident Game as
|
|
||||||
`PausedSuspended` in the foreground stack while `TaskManager::set_foreground`
|
|
||||||
only demotes the previous foreground Game task to `Background`. This creates a
|
|
||||||
state mismatch if a Shell is opened while the Game has not already gone through
|
|
||||||
the Home suspension path.
|
|
||||||
|
|
||||||
## Objective
|
|
||||||
|
|
||||||
Make Shell foreground acquisition either require an already suspended resident
|
|
||||||
Game or perform the necessary OS-owned suspension consistently across
|
|
||||||
foreground stack, task manager, and process manager.
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
- Source decision: `DEC-0037`.
|
|
||||||
- Can be implemented independently of `PLN-0142` and `PLN-0143`, but tests
|
|
||||||
should be rerun with those fixes if they are executed first.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
Included:
|
|
||||||
|
|
||||||
- Close the mismatch between `ResidentGameState::PausedSuspended` and
|
|
||||||
`TaskState::Background`.
|
|
||||||
- Ensure Shell foreground cannot leave a resident Game running or backgrounded.
|
|
||||||
- Cover both `create_vm_shell_task` and `create_native_shell_task` paths.
|
|
||||||
- Preserve the rule that only one foreground Shell may exist.
|
|
||||||
- Keep the normal Home -> Shell flow working.
|
|
||||||
|
|
||||||
Excluded:
|
|
||||||
|
|
||||||
- No direct Shell -> Game return behavior; v1 still returns Shell close to Hub.
|
|
||||||
- No background Game execution.
|
|
||||||
- No multiple resident Games.
|
|
||||||
- No Shell persistence policy changes.
|
|
||||||
|
|
||||||
## Non-Goals
|
|
||||||
|
|
||||||
- Do not broaden Shell lifecycle semantics.
|
|
||||||
- Do not implement Game-to-Game switching.
|
|
||||||
- Do not add UI controls for resume.
|
|
||||||
|
|
||||||
## Execution Method
|
|
||||||
|
|
||||||
1. Define the legal Shell foreground precondition.
|
|
||||||
- What changes: decide in code whether Shell foreground requires the
|
|
||||||
resident Game to already be `PausedSuspended`, or whether
|
|
||||||
`set_shell_foreground_task` is responsible for suspending it.
|
|
||||||
- How it changes: prefer centralizing the invariant in
|
|
||||||
`LifecycleFacade::set_shell_foreground_task`, because it owns task/process
|
|
||||||
transitions.
|
|
||||||
- File(s): `crates/console/prometeu-system/src/os/facades/lifecycle.rs`,
|
|
||||||
`crates/console/prometeu-system/src/services/foreground.rs`.
|
|
||||||
|
|
||||||
2. Synchronize task/process state when Shell takes foreground.
|
|
||||||
- What changes: if a resident Game exists and is not already suspended,
|
|
||||||
mark the Game task and process `Suspended` before or during Shell
|
|
||||||
foreground transition.
|
|
||||||
- How it changes: use `process_id_for_task` for the resident Game and call
|
|
||||||
`task_manager.mark_suspended` and `process_manager.mark_suspended` in the
|
|
||||||
same lifecycle operation that sets Shell foreground.
|
|
||||||
- File(s): `crates/console/prometeu-system/src/os/facades/lifecycle.rs`.
|
|
||||||
|
|
||||||
3. Tighten foreground stack behavior.
|
|
||||||
- What changes: `ForegroundStack::set_shell_foreground` must not silently
|
|
||||||
hide invalid state transitions.
|
|
||||||
- How it changes: either reject Shell foreground when the resident Game is
|
|
||||||
still `Foreground`/`PauseRequested`, or return enough information for the
|
|
||||||
lifecycle facade to suspend the resident Game explicitly.
|
|
||||||
- File(s): `crates/console/prometeu-system/src/services/foreground.rs`.
|
|
||||||
|
|
||||||
4. Add regression tests for reachable session paths.
|
|
||||||
- What changes: create tests that call `create_vm_shell_task` and
|
|
||||||
`create_native_shell_task` while a Game task is foreground.
|
|
||||||
- How it changes: assert foreground owner is Shell, resident Game remains
|
|
||||||
recorded, Game task state is `Suspended`, and Game process state is
|
|
||||||
`Suspended`.
|
|
||||||
- File(s): `crates/console/prometeu-system/src/os/system_os.rs`,
|
|
||||||
`crates/console/prometeu-firmware/src/firmware/firmware.rs` if the
|
|
||||||
firmware Home/Shell path needs coverage.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- [ ] A Shell foreground transition cannot leave the resident Game in
|
|
||||||
`TaskState::Background`.
|
|
||||||
- [ ] Foreground stack state, task state, and process state agree that the
|
|
||||||
resident Game is suspended while Shell is foreground.
|
|
||||||
- [ ] `create_vm_shell_task` and `create_native_shell_task` both preserve the
|
|
||||||
invariant.
|
|
||||||
- [ ] Opening a second foreground Shell remains rejected.
|
|
||||||
- [ ] Closing Shell still returns to Hub/Home, not directly to Game.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
- Unit tests in `prometeu-system` for `set_shell_foreground_task` with a
|
|
||||||
resident foreground Game.
|
|
||||||
- Session facade tests for VM Shell and native Shell creation while a Game is
|
|
||||||
resident.
|
|
||||||
- Existing firmware Shell close tests must continue to pass.
|
|
||||||
- Run `cargo test -p prometeu-system foreground lifecycle shell`.
|
|
||||||
- Run `cargo test -p prometeu-firmware shell`.
|
|
||||||
- Run `discussion validate`.
|
|
||||||
|
|
||||||
## Affected Artifacts
|
|
||||||
|
|
||||||
- `crates/console/prometeu-system/src/services/foreground.rs`
|
|
||||||
- `crates/console/prometeu-system/src/os/facades/lifecycle.rs`
|
|
||||||
- `crates/console/prometeu-system/src/os/facades/sessions.rs`
|
|
||||||
- `crates/console/prometeu-system/src/os/system_os.rs`
|
|
||||||
- `crates/console/prometeu-firmware/src/firmware/firmware.rs`
|
|
||||||
@ -1,149 +0,0 @@
|
|||||||
---
|
|
||||||
id: PLN-0145
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: Centralize Resident Game Resume Transition
|
|
||||||
status: done
|
|
||||||
created: 2026-07-04
|
|
||||||
ref_decisions: [DEC-0037]
|
|
||||||
tags: [runtime, os, lifecycle, shell, game, vm, foreground, architecture]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
`DEC-0037` requires returning to the same resident Game to reactivate the Game
|
|
||||||
VM, restore render ownership, send a resume/foreground lifecycle notification,
|
|
||||||
and clear or barrier pending Game input. That transition currently exists in
|
|
||||||
more than one firmware path: `Firmware::resume_resident_game_from_home` and the
|
|
||||||
Hub library click path that resumes the selected resident Game by `app_id`.
|
|
||||||
|
|
||||||
The behavior is correct, but the duplication is fragile. Any future change to
|
|
||||||
resume ordering, input barriers, render ownership, or lifecycle event delivery
|
|
||||||
could fix one path while leaving the other path stale.
|
|
||||||
|
|
||||||
## Objective
|
|
||||||
|
|
||||||
Create one firmware-owned resident Game resume helper and route every
|
|
||||||
Home/Hub-to-Game resume path through it.
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
- Source decision: `DEC-0037`.
|
|
||||||
- Depends on the accepted v1 identity rule that `app_id` is unique for all
|
|
||||||
games and may be used to decide whether a Hub game selection refers to the
|
|
||||||
current resident Game.
|
|
||||||
- Should preserve the behavior introduced by `PLN-0142`, `PLN-0143`, and
|
|
||||||
`PLN-0144`.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
Included:
|
|
||||||
|
|
||||||
- Centralize resident Game resume orchestration in `Firmware`.
|
|
||||||
- Preserve resume lifecycle event queuing through `LifecycleFacade::resume_task`.
|
|
||||||
- Preserve render ownership transition back to `AppMode::Game`.
|
|
||||||
- Preserve input barrier behavior across all Home/Hub-to-Game resume paths.
|
|
||||||
- Preserve the Hub rule that selecting the same resident Game resumes it, while
|
|
||||||
selecting a different Game remains blocked by v1 scope.
|
|
||||||
- Keep tests proving same-task resume, lifecycle event delivery, and input
|
|
||||||
barriering.
|
|
||||||
|
|
||||||
Excluded:
|
|
||||||
|
|
||||||
- No Game-to-Game switching.
|
|
||||||
- No multiple resident Games.
|
|
||||||
- No change to `app_id` uniqueness policy.
|
|
||||||
- No direct Shell-to-Game close behavior.
|
|
||||||
|
|
||||||
## Non-Goals
|
|
||||||
|
|
||||||
- Do not move foreground authority from SystemOS into firmware.
|
|
||||||
- Do not change pause budget or suspend behavior.
|
|
||||||
- Do not add UI affordances beyond the existing Hub game row selection.
|
|
||||||
|
|
||||||
## Execution Method
|
|
||||||
|
|
||||||
1. Introduce a single resident Game resume helper.
|
|
||||||
- What changes: create a private firmware method that performs the complete
|
|
||||||
resident Game resume operation and returns the target `TaskId` or a typed
|
|
||||||
failure.
|
|
||||||
- How it changes: move the shared sequence currently duplicated across
|
|
||||||
`resume_resident_game_from_home` and `HubHomeStep`: read resident task,
|
|
||||||
call `LifecycleFacade::resume_task`, transition VM render ownership to
|
|
||||||
`AppMode::Game`, install `GameRunningStep`, clear state initialization,
|
|
||||||
and arm the input barrier.
|
|
||||||
- File(s): `crates/console/prometeu-firmware/src/firmware/firmware.rs`.
|
|
||||||
|
|
||||||
2. Route explicit resume through the helper.
|
|
||||||
- What changes: `Firmware::resume_resident_game_from_home` becomes a thin
|
|
||||||
wrapper around the centralized helper.
|
|
||||||
- How it changes: preserve its public `bool` return contract for existing
|
|
||||||
tests and call sites.
|
|
||||||
- File(s): `crates/console/prometeu-firmware/src/firmware/firmware.rs`.
|
|
||||||
|
|
||||||
3. Route Hub same-game selection through the helper.
|
|
||||||
- What changes: `HubHomeStep` must no longer hand-roll resume task, render
|
|
||||||
ownership, and input clearing.
|
|
||||||
- How it changes: expose a state transition request that lets the firmware
|
|
||||||
perform the centralized helper, or restructure the Hub action handling so
|
|
||||||
the parent `Firmware` owns this transition. Keep `HubHomeStep` responsible
|
|
||||||
only for recognizing that the selected cartridge matches the resident
|
|
||||||
`app_id`.
|
|
||||||
- File(s): `crates/console/prometeu-firmware/src/firmware/firmware.rs`,
|
|
||||||
`crates/console/prometeu-firmware/src/firmware/firmware_step_hub_home.rs`,
|
|
||||||
`crates/console/prometeu-firmware/src/firmware/firmware_state.rs` if a new
|
|
||||||
transition variant is needed.
|
|
||||||
|
|
||||||
4. Keep different-game launch blocked under v1.
|
|
||||||
- What changes: selecting a different `app_id` while a resident Game exists
|
|
||||||
continues to log and remain in Hub.
|
|
||||||
- How it changes: keep the existing warning behavior, but ensure it does not
|
|
||||||
duplicate resume side effects.
|
|
||||||
- File(s): `crates/console/prometeu-firmware/src/firmware/firmware_step_hub_home.rs`.
|
|
||||||
|
|
||||||
5. Update regression tests.
|
|
||||||
- What changes: tests should prove both explicit resume and Hub same-game
|
|
||||||
selection use the same observable transition.
|
|
||||||
- How it changes: assert same `TaskId`, foreground owner restored to Game,
|
|
||||||
pending resume lifecycle event exists before the next Game tick, delivered
|
|
||||||
resume event after the Game tick, and input barrier suppresses held input.
|
|
||||||
- File(s): `crates/console/prometeu-firmware/src/firmware/firmware.rs`.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- [ ] There is exactly one implementation of the resident Game resume sequence.
|
|
||||||
- [ ] `Firmware::resume_resident_game_from_home` preserves its external
|
|
||||||
behavior.
|
|
||||||
- [ ] Hub selection of the same resident Game resumes the existing `TaskId`
|
|
||||||
without creating or loading a new Game task.
|
|
||||||
- [ ] The resume path always queues and delivers the Game lifecycle
|
|
||||||
`ResumeForeground` event.
|
|
||||||
- [ ] The resume path always barriers held Game input.
|
|
||||||
- [ ] Selecting a different Game while one Game is resident remains blocked in
|
|
||||||
v1.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
- Unit/integration tests in `prometeu-firmware` for explicit resume from Home.
|
|
||||||
- Unit/integration tests in `prometeu-firmware` for Hub same-game selection
|
|
||||||
after Home suspension.
|
|
||||||
- Regression assertion that held input does not leak into the Game immediately
|
|
||||||
after resume.
|
|
||||||
- Run `cargo test -p prometeu-firmware resident_game_resume`.
|
|
||||||
- Run `cargo test -p prometeu-firmware hub_home_clicking_resident_game`.
|
|
||||||
- Run `cargo test -p prometeu-system`.
|
|
||||||
- Run `discussion validate`.
|
|
||||||
|
|
||||||
## Affected Artifacts
|
|
||||||
|
|
||||||
- `crates/console/prometeu-firmware/src/firmware/firmware.rs`
|
|
||||||
- `crates/console/prometeu-firmware/src/firmware/firmware_step_hub_home.rs`
|
|
||||||
- `crates/console/prometeu-firmware/src/firmware/firmware_state.rs`
|
|
||||||
|
|
||||||
## Risks
|
|
||||||
|
|
||||||
- The current step/state architecture gives `HubHomeStep` a `PrometeuContext`
|
|
||||||
but not direct access to private `Firmware` fields. If a new transition
|
|
||||||
variant is introduced, it must stay narrow and must not turn state steps into
|
|
||||||
owners of lifecycle authority.
|
|
||||||
- Duplicating less logic must not accidentally weaken the input barrier that was
|
|
||||||
added for Hub-to-Game transitions.
|
|
||||||
@ -1,119 +0,0 @@
|
|||||||
---
|
|
||||||
id: PLN-0146
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: Consolidate Lifecycle Process Lookup Helpers
|
|
||||||
status: done
|
|
||||||
created: 2026-07-04
|
|
||||||
ref_decisions: [DEC-0037]
|
|
||||||
tags: [runtime, os, lifecycle, shell, game, vm, foreground, architecture]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
`DEC-0037` depends on SystemOS lifecycle authority being internally
|
|
||||||
consistent. The lifecycle facade now exposes both process state and process kind
|
|
||||||
for a task so firmware can distinguish VM-backed Shell tasks from native Shell
|
|
||||||
tasks. Both accessors repeat the same task-to-process lookup pattern.
|
|
||||||
|
|
||||||
The duplication is small, but this facade is now part of the foreground
|
|
||||||
contract surface. Keeping lookup and error mapping in one place reduces the
|
|
||||||
chance that future lifecycle accessors drift in behavior.
|
|
||||||
|
|
||||||
## Objective
|
|
||||||
|
|
||||||
Consolidate task-to-process lookup in `LifecycleFacade` behind one private
|
|
||||||
helper, while preserving the public lifecycle facade behavior and errors.
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
- Source decision: `DEC-0037`.
|
|
||||||
- Depends on the process/task authority from SystemOS lifecycle work.
|
|
||||||
- Can be implemented independently of `PLN-0145` and `PLN-0147`.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
Included:
|
|
||||||
|
|
||||||
- Add one private helper that resolves a `TaskId` to its backing process.
|
|
||||||
- Route `process_state_for_task` and `process_kind_for_task` through that
|
|
||||||
helper.
|
|
||||||
- Preserve `LifecycleError::TaskNotFound` and
|
|
||||||
`LifecycleError::ProcessNotFound` behavior exactly.
|
|
||||||
- Keep the current process/task manager ownership boundaries intact.
|
|
||||||
- Keep the current non-architectural cleanup in `set_foreground_task` if it is
|
|
||||||
still present in the working tree.
|
|
||||||
|
|
||||||
Excluded:
|
|
||||||
|
|
||||||
- No new public process manager API.
|
|
||||||
- No changes to task or process IDs.
|
|
||||||
- No changes to Shell/Game lifecycle semantics.
|
|
||||||
- No broader facade refactor outside lifecycle lookup.
|
|
||||||
|
|
||||||
## Non-Goals
|
|
||||||
|
|
||||||
- Do not expose raw process manager access through SystemOS.
|
|
||||||
- Do not change `ProcessKind` or `TaskKind` definitions.
|
|
||||||
- Do not alter foreground stack behavior.
|
|
||||||
|
|
||||||
## Execution Method
|
|
||||||
|
|
||||||
1. Add a private lifecycle lookup helper.
|
|
||||||
- What changes: add a private function near `process_id_for_task` that
|
|
||||||
returns a process reference for a `TaskId`.
|
|
||||||
- How it changes: the helper should call `process_id_for_task`, then
|
|
||||||
retrieve the process from `process_manager`, returning
|
|
||||||
`LifecycleError::ProcessNotFound(process_id)` if the process is missing.
|
|
||||||
- File(s): `crates/console/prometeu-system/src/os/facades/lifecycle.rs`.
|
|
||||||
|
|
||||||
2. Route state and kind accessors through the helper.
|
|
||||||
- What changes: `LifecycleFacade::process_state_for_task` and
|
|
||||||
`LifecycleFacade::process_kind_for_task` stop duplicating the process
|
|
||||||
manager lookup.
|
|
||||||
- How it changes: both methods call the helper and map the process reference
|
|
||||||
to `ProcessState` or `ProcessKind`.
|
|
||||||
- File(s): `crates/console/prometeu-system/src/os/facades/lifecycle.rs`.
|
|
||||||
|
|
||||||
3. Preserve existing foreground code cleanup.
|
|
||||||
- What changes: keep `set_foreground_task` as an expression-returning match
|
|
||||||
rather than using redundant `return` statements.
|
|
||||||
- How it changes: ensure formatting and clippy stay clean.
|
|
||||||
- File(s): `crates/console/prometeu-system/src/os/facades/lifecycle.rs`.
|
|
||||||
|
|
||||||
4. Tighten tests around lookup behavior.
|
|
||||||
- What changes: extend or keep tests proving VM Shell and native Shell
|
|
||||||
process kinds are observable through lifecycle.
|
|
||||||
- How it changes: add missing-task and missing-process coverage only if the
|
|
||||||
helper changes the observable path enough to justify it.
|
|
||||||
- File(s): `crates/console/prometeu-system/src/os/system_os.rs`.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- [ ] `process_state_for_task` and `process_kind_for_task` share one private
|
|
||||||
task-to-process lookup implementation.
|
|
||||||
- [ ] Missing task and missing process errors remain unchanged.
|
|
||||||
- [ ] Existing lifecycle tests continue to pass.
|
|
||||||
- [ ] Firmware can still distinguish `ProcessKind::VmShell` from
|
|
||||||
`ProcessKind::NativeShell`.
|
|
||||||
- [ ] Clippy reports no warnings for the touched crates.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
- Unit tests in `prometeu-system` for shell process kind lookup.
|
|
||||||
- Existing lifecycle missing task/process tests must continue to pass.
|
|
||||||
- Run `cargo test -p prometeu-system lifecycle process_kind`.
|
|
||||||
- Run `cargo test -p prometeu-firmware shell`.
|
|
||||||
- Run `cargo clippy -p prometeu-system -p prometeu-firmware --all-targets -- -D warnings`.
|
|
||||||
- Run `discussion validate`.
|
|
||||||
|
|
||||||
## Affected Artifacts
|
|
||||||
|
|
||||||
- `crates/console/prometeu-system/src/os/facades/lifecycle.rs`
|
|
||||||
- `crates/console/prometeu-system/src/os/system_os.rs`
|
|
||||||
|
|
||||||
## Risks
|
|
||||||
|
|
||||||
- Over-generalizing the helper could leak process manager concepts outside the
|
|
||||||
lifecycle facade. Keep it private and narrowly typed.
|
|
||||||
- This is intentionally small cleanup. It must not become a broad SystemOS
|
|
||||||
facade redesign.
|
|
||||||
@ -1,144 +0,0 @@
|
|||||||
---
|
|
||||||
id: PLN-0147
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: Move Native Shell Profile Update Into Hub
|
|
||||||
status: done
|
|
||||||
created: 2026-07-04
|
|
||||||
ref_decisions: [DEC-0037]
|
|
||||||
tags: [runtime, os, lifecycle, shell, game, vm, foreground, architecture]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
`DEC-0037` says a suspended Game must not receive normal gameplay ticks while
|
|
||||||
Hub or Shell owns foreground. A recent fix made `ShellRunningStep` branch on
|
|
||||||
`ProcessKind`: VM-backed Shell tasks use the VM-backed shell profile path, while
|
|
||||||
native Shell tasks call `PrometeuHub::gui_update` and `PrometeuHub::render`
|
|
||||||
without ticking the VM.
|
|
||||||
|
|
||||||
That behavior is correct, but the native Shell update sequence now lives in
|
|
||||||
firmware rather than in the Hub profile API. This duplicates part of
|
|
||||||
`PrometeuHub::update_shell_profile` and creates a drift risk if Shell close,
|
|
||||||
render, or input rules change later.
|
|
||||||
|
|
||||||
## Objective
|
|
||||||
|
|
||||||
Move native Shell profile updating into `PrometeuHub` so `ShellRunningStep`
|
|
||||||
chooses the correct profile API without reimplementing Hub behavior.
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
- Source decision: `DEC-0037`.
|
|
||||||
- Depends on the `ProcessKind::VmShell` vs `ProcessKind::NativeShell`
|
|
||||||
distinction available through `LifecycleFacade::process_kind_for_task`.
|
|
||||||
- May be implemented before or after `PLN-0146`, but should use the consolidated
|
|
||||||
lookup helper if `PLN-0146` has already landed.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
Included:
|
|
||||||
|
|
||||||
- Add a Hub-owned native Shell update API that does not tick the VM.
|
|
||||||
- Keep VM-backed Shell update behavior in the existing VM-backed profile path.
|
|
||||||
- Keep Shell close handling consistent between native and VM-backed Shells.
|
|
||||||
- Keep native Shell rendering in the Hub/Shell UI path.
|
|
||||||
- Keep firmware responsible for choosing which profile applies to the current
|
|
||||||
Shell task.
|
|
||||||
- Preserve regression coverage that a suspended resident Game does not advance
|
|
||||||
while native Shell is foreground.
|
|
||||||
|
|
||||||
Excluded:
|
|
||||||
|
|
||||||
- No change to VM-backed Shell execution semantics.
|
|
||||||
- No change to native Shell UI content.
|
|
||||||
- No direct Shell close to Game.
|
|
||||||
- No multiple foreground Shells.
|
|
||||||
- No background Game ticking.
|
|
||||||
|
|
||||||
## Non-Goals
|
|
||||||
|
|
||||||
- Do not move foreground lifecycle authority into `PrometeuHub`.
|
|
||||||
- Do not make native Shells VM tasks.
|
|
||||||
- Do not introduce a general scheduler abstraction.
|
|
||||||
|
|
||||||
## Execution Method
|
|
||||||
|
|
||||||
1. Add a native Shell profile update method on `PrometeuHub`.
|
|
||||||
- What changes: create `update_native_shell_profile` or an equivalent
|
|
||||||
clearly named method that returns `SystemProfileUpdate`.
|
|
||||||
- How it changes: the method calls `gui_update`, maps Start input to
|
|
||||||
`SystemProfileAction::CloseShell` when a shell window is focused, calls
|
|
||||||
`render`, and always returns `crash: None`.
|
|
||||||
- File(s):
|
|
||||||
`crates/console/prometeu-system/src/programs/prometeu_hub/prometeu_hub.rs`.
|
|
||||||
|
|
||||||
2. Keep the VM-backed Shell profile method explicitly VM-backed.
|
|
||||||
- What changes: either keep `update_shell_profile` and document/name its VM
|
|
||||||
tick behavior, or rename/split it if the codebase convention supports that
|
|
||||||
without churn.
|
|
||||||
- How it changes: ensure the VM-backed path is the only Hub profile update
|
|
||||||
method that calls `os.vm().tick(...)`.
|
|
||||||
- File(s):
|
|
||||||
`crates/console/prometeu-system/src/programs/prometeu_hub/prometeu_hub.rs`.
|
|
||||||
|
|
||||||
3. Simplify `ShellRunningStep`.
|
|
||||||
- What changes: remove manual native Shell `gui_update`, Start handling, and
|
|
||||||
render calls from firmware.
|
|
||||||
- How it changes: branch on `ProcessKind` and call the appropriate Hub
|
|
||||||
profile method; keep crash handling only for the VM-backed method.
|
|
||||||
- File(s):
|
|
||||||
`crates/console/prometeu-firmware/src/firmware/firmware_step_shell_running.rs`.
|
|
||||||
|
|
||||||
4. Add Hub-level native profile tests.
|
|
||||||
- What changes: prove native Shell profile update emits close on the close
|
|
||||||
button and on Start, renders Shell UI, and does not tick the VM.
|
|
||||||
- How it changes: use existing Hub/system test helpers where possible; assert
|
|
||||||
VM tick index remains unchanged for native profile update.
|
|
||||||
- File(s):
|
|
||||||
`crates/console/prometeu-system/src/programs/prometeu_hub/prometeu_hub.rs`
|
|
||||||
and/or `crates/console/prometeu-firmware/src/firmware/firmware.rs`.
|
|
||||||
|
|
||||||
5. Preserve end-to-end regression coverage.
|
|
||||||
- What changes: keep the Game -> Home -> native Shell test asserting that
|
|
||||||
repeated Shell frames do not advance the resident Game VM tick index.
|
|
||||||
- How it changes: rerun the firmware regression added for native Shell
|
|
||||||
foreground.
|
|
||||||
- File(s): `crates/console/prometeu-firmware/src/firmware/firmware.rs`.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- [ ] Native Shell update behavior is owned by `PrometeuHub`, not hand-coded in
|
|
||||||
`ShellRunningStep`.
|
|
||||||
- [ ] The native Shell profile update never calls `os.vm().tick(...)`.
|
|
||||||
- [ ] VM-backed Shell update remains the only Shell profile path that ticks the
|
|
||||||
VM runtime.
|
|
||||||
- [ ] Start and close-button Shell close behavior remains unchanged for native
|
|
||||||
Shells.
|
|
||||||
- [ ] A suspended resident Game receives no normal gameplay ticks while native
|
|
||||||
Shell is foreground.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
- Hub-level tests for native Shell profile close behavior.
|
|
||||||
- Hub-level or firmware-level tests proving native Shell profile update does not
|
|
||||||
tick the VM.
|
|
||||||
- Existing firmware regression for Game -> Home -> native Shell repeated frames.
|
|
||||||
- Run `cargo test -p prometeu-system native_shell shell_profile`.
|
|
||||||
- Run `cargo test -p prometeu-firmware game_home_shell_hub_same_game_flow`.
|
|
||||||
- Run `cargo test -p prometeu-firmware`.
|
|
||||||
- Run `cargo test -p prometeu-system`.
|
|
||||||
- Run `discussion validate`.
|
|
||||||
|
|
||||||
## Affected Artifacts
|
|
||||||
|
|
||||||
- `crates/console/prometeu-system/src/programs/prometeu_hub/prometeu_hub.rs`
|
|
||||||
- `crates/console/prometeu-firmware/src/firmware/firmware_step_shell_running.rs`
|
|
||||||
- `crates/console/prometeu-firmware/src/firmware/firmware.rs`
|
|
||||||
|
|
||||||
## Risks
|
|
||||||
|
|
||||||
- Renaming the existing VM-backed shell profile method may create unnecessary
|
|
||||||
churn. Prefer adding the native method and keeping call sites explicit unless
|
|
||||||
the rename is small and clearly improves readability.
|
|
||||||
- Hub APIs must remain UI/profile orchestration only; lifecycle close and task
|
|
||||||
state changes still belong to firmware/SystemOS lifecycle.
|
|
||||||
@ -1,92 +0,0 @@
|
|||||||
---
|
|
||||||
id: PLN-0148
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: Introduce VM Session Registry and Identity
|
|
||||||
status: done
|
|
||||||
created: 2026-07-04
|
|
||||||
ref_decisions: [DEC-0038]
|
|
||||||
tags: [runtime, os, lifecycle, shell, game, vm, foreground, architecture]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
DEC-0038 requires every VM-backed process to own its own mutable VM context instead of sharing `Firmware.vm`. This plan introduces the identity and registry layer required before moving runtime state or changing cartridge loading.
|
|
||||||
|
|
||||||
The implementation must make VM session ownership explicit inside `SystemOS`. A VM-backed Game task and a VM-backed Shell task must be able to coexist as distinct sessions, even before all execution paths are migrated to session-owned VMs.
|
|
||||||
|
|
||||||
## Objective
|
|
||||||
|
|
||||||
Add a `VmSession` model and registry owned by `SystemOS`, keyed by a stable session identity tied to the owning task/process. The registry must support creating, retrieving, and validating VM sessions for VM-backed Game and Shell tasks.
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
- Source decision: DEC-0038.
|
|
||||||
- Existing foreground/task/process lifecycle from DEC-0037 remains authoritative.
|
|
||||||
- Must be implemented before PLN-0149, PLN-0150, PLN-0151, and PLN-0152.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
- Add a session identity type, preferably `VmSessionId`, with a deliberate mapping from the owning `TaskId`.
|
|
||||||
- Add a `VmSession` data structure containing at least:
|
|
||||||
- owning `TaskId`;
|
|
||||||
- owning process id or process lookup path;
|
|
||||||
- `app_id`, title, version, and `AppMode`;
|
|
||||||
- initial placeholder for the session-owned `VirtualMachine`;
|
|
||||||
- initial placeholder for session-owned runtime state to be populated by PLN-0149.
|
|
||||||
- Add a registry under `crates/console/prometeu-system/src/services/` or an equivalent existing services module.
|
|
||||||
- Expose creation and lookup through `crates/console/prometeu-system/src/os/facades/sessions.rs`.
|
|
||||||
- Update `SystemOS` construction in `crates/console/prometeu-system/src/os/system_os.rs` to own the registry.
|
|
||||||
- Enforce V1 uniqueness:
|
|
||||||
- at most one resident Game session;
|
|
||||||
- one foreground Shell task at a time;
|
|
||||||
- same `app_id` resident Game relaunch maps to the existing resident session rather than creating a second Game session.
|
|
||||||
|
|
||||||
## Non-Goals
|
|
||||||
|
|
||||||
- Do not move all VM runtime fields yet; that is PLN-0149.
|
|
||||||
- Do not route cartridge loading through sessions yet; that is PLN-0150.
|
|
||||||
- Do not remove `Firmware.vm` yet.
|
|
||||||
- Do not implement general background execution.
|
|
||||||
- Do not allow Game-to-Game switching.
|
|
||||||
|
|
||||||
## Execution Method
|
|
||||||
|
|
||||||
1. Inspect `TaskId`, process ids, and `ProcessKind` definitions in `crates/console/prometeu-system/src/services/task/` and `crates/console/prometeu-system/src/services/process/`.
|
|
||||||
2. Add the VM session module with a small API:
|
|
||||||
- create session for an existing VM-backed task/process;
|
|
||||||
- get immutable/mutable session by task id;
|
|
||||||
- get resident Game session by `app_id`;
|
|
||||||
- reject incompatible duplicate Game sessions.
|
|
||||||
3. Wire the registry into `SystemOS`.
|
|
||||||
4. Extend `SessionsFacade::create_vm_game_task` and `create_vm_shell_task` so task creation also creates a VM session record.
|
|
||||||
5. Keep native Shell task creation out of the VM session registry.
|
|
||||||
6. Add focused unit tests under `prometeu-system` for registry identity and duplicate handling.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- `SystemOS` owns a VM session registry.
|
|
||||||
- Creating a VM Game task creates exactly one VM session associated with that task.
|
|
||||||
- Creating a VM Shell task creates exactly one VM session associated with that task.
|
|
||||||
- Creating a native Shell task creates no VM session.
|
|
||||||
- Attempting to create a second resident Game session for a different `app_id` is rejected or routed through the existing lifecycle rejection path.
|
|
||||||
- Relaunching the same resident Game can resolve the existing VM session by `app_id`.
|
|
||||||
- No firmware state transition depends on the new registry yet except through task/session creation.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
- `cargo test -p prometeu-system`
|
|
||||||
- Add unit tests for:
|
|
||||||
- VM Game task creates a VM session;
|
|
||||||
- VM Shell task creates a VM session;
|
|
||||||
- native Shell task does not create a VM session;
|
|
||||||
- lookup by `TaskId`;
|
|
||||||
- same resident Game `app_id` resolves to the existing session;
|
|
||||||
- different resident Game `app_id` remains blocked under V1.
|
|
||||||
|
|
||||||
## Affected Artifacts
|
|
||||||
|
|
||||||
- `crates/console/prometeu-system/src/os/system_os.rs`
|
|
||||||
- `crates/console/prometeu-system/src/os/facades/sessions.rs`
|
|
||||||
- `crates/console/prometeu-system/src/services/mod.rs`
|
|
||||||
- New or extended module under `crates/console/prometeu-system/src/services/`
|
|
||||||
- `crates/console/prometeu-system/src/lib.rs` if exports are required
|
|
||||||
@ -1,99 +0,0 @@
|
|||||||
---
|
|
||||||
id: PLN-0149
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: Move VM Runtime State Into VM Sessions
|
|
||||||
status: done
|
|
||||||
created: 2026-07-04
|
|
||||||
ref_decisions: [DEC-0038]
|
|
||||||
tags: [runtime, os, lifecycle, shell, game, vm, foreground, architecture]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
DEC-0038 states that mutable VM context must belong to the VM-backed process session, not to firmware or a single global runtime slot. The current split has `Firmware.vm: VirtualMachine` and `SystemOS.vm_runtime: VirtualMachineRuntime`, which makes a suspended Game vulnerable to VM-backed Shell execution.
|
|
||||||
|
|
||||||
This plan migrates mutable per-application VM runtime state into the session model introduced by PLN-0148 while keeping global OS services global.
|
|
||||||
|
|
||||||
## Objective
|
|
||||||
|
|
||||||
Make each VM session own the state needed to suspend and later resume that VM-backed process without being overwritten by another VM-backed process.
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
- Source decision: DEC-0038.
|
|
||||||
- Requires PLN-0148.
|
|
||||||
- Must precede full cartridge-loading and scheduler migration in PLN-0150 and PLN-0151.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
- Classify `VirtualMachineRuntime` fields into session-owned versus global OS-owned state.
|
|
||||||
- Move session-owned fields into the `VmSession` context.
|
|
||||||
- Keep global services in `SystemOS`, including:
|
|
||||||
- `LogService`;
|
|
||||||
- process/task/window managers;
|
|
||||||
- foreground stack;
|
|
||||||
- durable `MemcardService`;
|
|
||||||
- global render publication authority or worker coordination that must remain host-facing.
|
|
||||||
- Treat these as session-owned unless proven otherwise during implementation:
|
|
||||||
- `VirtualMachine`;
|
|
||||||
- current cartridge identity;
|
|
||||||
- frame and tick counters;
|
|
||||||
- lifecycle delivery bookkeeping;
|
|
||||||
- crash report for that session;
|
|
||||||
- pause/debug flags that apply to that session;
|
|
||||||
- per-session open files, handles, and staging state used by VM syscalls.
|
|
||||||
- Keep durable memcard contents keyed by `app_id`; do not turn save data into session-private durable storage.
|
|
||||||
- Update `VmFacade` and VM runtime helpers so callers can operate on a selected session.
|
|
||||||
|
|
||||||
## Non-Goals
|
|
||||||
|
|
||||||
- Do not redesign render workers beyond separating session-local render command state from global presentation authority.
|
|
||||||
- Do not implement background execution.
|
|
||||||
- Do not change syscall semantics except where required to route state through the active session.
|
|
||||||
- Do not remove host debugger features; preserve or adapt their access path.
|
|
||||||
|
|
||||||
## Execution Method
|
|
||||||
|
|
||||||
1. Add an internal field-by-field classification comment or test-only assertion near `VirtualMachineRuntime` before moving fields.
|
|
||||||
2. Introduce a session runtime/context type in the VM session module or by splitting `VirtualMachineRuntime` into:
|
|
||||||
- session-local runtime;
|
|
||||||
- global render/log/runtime services.
|
|
||||||
3. Move `VirtualMachine` ownership from firmware placeholder/session stub into the session context.
|
|
||||||
4. Move per-cartridge and per-frame state from `SystemOS.vm_runtime` into the session context.
|
|
||||||
5. Move `open_files`, `next_handle`, and VM syscall staging state out of global `SystemOS` if they are VM-session specific.
|
|
||||||
6. Update `VmRuntimeHost` in `crates/console/prometeu-system/src/services/vm_runtime/dispatch.rs` to receive session-local state plus references to required global services.
|
|
||||||
7. Update `VmFacade` in `crates/console/prometeu-system/src/os/facades/vm.rs` with explicit session-targeted methods.
|
|
||||||
8. Preserve temporary compatibility methods only where needed for existing host/debugger code, and mark them as active-session views rather than canonical global ownership.
|
|
||||||
9. Add tests proving two VM sessions can hold different app identity and counters at the same time.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- No canonical VM state lives in `Firmware`.
|
|
||||||
- `SystemOS` can hold at least two VM sessions with independent `VirtualMachine` and per-session runtime state.
|
|
||||||
- VM session A and VM session B can have different `app_id`, title, frame counters, lifecycle delivery history, crash report, open file handles, and debug pause state.
|
|
||||||
- Durable memcard contents remain shared by `app_id` according to the existing memcard contract.
|
|
||||||
- Global render ownership remains controlled by SystemOS/host-facing runtime services, not by individual sessions.
|
|
||||||
- Existing `prometeu-system` VM runtime tests still pass after adapting their setup.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
- `cargo test -p prometeu-system`
|
|
||||||
- `cargo test -p prometeu-firmware`
|
|
||||||
- Add tests for:
|
|
||||||
- independent session app identity;
|
|
||||||
- independent tick/logical-frame counters;
|
|
||||||
- independent lifecycle delivery bookkeeping;
|
|
||||||
- VM Shell session initialization does not mutate resident Game session identity;
|
|
||||||
- session-scoped file handles do not collide across sessions.
|
|
||||||
|
|
||||||
## Affected Artifacts
|
|
||||||
|
|
||||||
- `crates/console/prometeu-system/src/services/vm_runtime/`
|
|
||||||
- VM session module introduced by PLN-0148
|
|
||||||
- `crates/console/prometeu-system/src/os/facades/vm.rs`
|
|
||||||
- `crates/console/prometeu-system/src/os/system_os.rs`
|
|
||||||
- `crates/console/prometeu-system/src/os/facades/fs.rs`
|
|
||||||
- `crates/console/prometeu-firmware/src/firmware/firmware.rs`
|
|
||||||
- `crates/host/prometeu-host-desktop-winit/src/debugger.rs`
|
|
||||||
- `crates/host/prometeu-host-desktop-winit/src/runner.rs`
|
|
||||||
- `crates/host/prometeu-host-desktop-winit/src/stats.rs`
|
|
||||||
@ -1,90 +0,0 @@
|
|||||||
---
|
|
||||||
id: PLN-0150
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: Route Cartridge Loading Through VM Sessions
|
|
||||||
status: done
|
|
||||||
created: 2026-07-04
|
|
||||||
ref_decisions: [DEC-0038]
|
|
||||||
tags: [runtime, os, lifecycle, shell, game, vm, foreground, architecture]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
DEC-0038 requires cartridge loading to create or activate a VM-backed process session instead of initializing a single global VM. The current `LoadCartridgeStep` initializes `ctx.os.vm().initialize(ctx.vm, &cartridge)` before task/session ownership is selected.
|
|
||||||
|
|
||||||
This plan moves cartridge initialization behind session services so Game and VM-backed Shell loading cannot overwrite each other.
|
|
||||||
|
|
||||||
## Objective
|
|
||||||
|
|
||||||
Route Game and VM Shell cartridge loading through the VM session registry and initialize the cartridge inside the target session-owned VM context.
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
- Source decision: DEC-0038.
|
|
||||||
- Requires PLN-0148.
|
|
||||||
- Requires enough of PLN-0149 to expose session-owned VM/runtime initialization.
|
|
||||||
- Must precede the end-to-end coexistence validation in PLN-0152.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
- Replace direct global VM initialization in `LoadCartridgeStep`.
|
|
||||||
- Add a session/facade operation that:
|
|
||||||
- selects an existing resident Game session when the same `app_id` is relaunched;
|
|
||||||
- rejects or reports different resident Game launches under V1;
|
|
||||||
- creates a new VM Shell session for a Shell cartridge;
|
|
||||||
- initializes the cartridge inside the selected session VM.
|
|
||||||
- Keep asset manager initialization aligned with the foreground cartridge being launched.
|
|
||||||
- Ensure `AppCrashesStep` receives initialization failures from the target session.
|
|
||||||
- Update Hub cartridge selection logic so resume versus load is based on lifecycle/session identity, not global VM identity.
|
|
||||||
|
|
||||||
## Non-Goals
|
|
||||||
|
|
||||||
- Do not implement multiple resident Games.
|
|
||||||
- Do not implement background execution.
|
|
||||||
- Do not redesign cartridge format or asset packaging.
|
|
||||||
- Do not broaden Shell windowing behavior beyond the existing one-foreground-Shell model.
|
|
||||||
|
|
||||||
## Execution Method
|
|
||||||
|
|
||||||
1. Add a `load_vm_cartridge` or equivalent method to `SessionsFacade` or `VmFacade`.
|
|
||||||
2. Make the method return an execution target:
|
|
||||||
- existing Game task for same resident `app_id`;
|
|
||||||
- new Game task for first Game launch;
|
|
||||||
- new VM Shell task for Shell launch;
|
|
||||||
- structured error/crash report for invalid VM initialization;
|
|
||||||
- explicit lifecycle error for blocked second Game.
|
|
||||||
3. Update `crates/console/prometeu-firmware/src/firmware/firmware_step_load_cartridge.rs` to call the facade method instead of initializing `ctx.vm`.
|
|
||||||
4. Update `crates/console/prometeu-firmware/src/firmware/firmware_step_hub_home.rs` so same-Game relaunch checks session/lifecycle state rather than `ctx.os.vm().current_app_id()`.
|
|
||||||
5. Preserve AppMode-based transition:
|
|
||||||
- Game target transitions to `GameRunning`;
|
|
||||||
- Shell target creates/focuses a window and transitions to `ShellRunning`.
|
|
||||||
6. Adapt crash tests and resident-game relaunch tests in firmware.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- `LoadCartridgeStep` no longer initializes a global firmware VM.
|
|
||||||
- Loading a Shell cartridge creates/initializes a Shell VM session.
|
|
||||||
- Loading the first Game cartridge creates/initializes the resident Game VM session.
|
|
||||||
- Clicking the same resident Game from Hub resumes the existing Game session without reinitializing it.
|
|
||||||
- Attempting to load a different Game while one resident Game exists remains blocked under V1 with a controlled outcome.
|
|
||||||
- VM initialization failure still transitions to `AppCrashesStep`.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
- `cargo test -p prometeu-firmware`
|
|
||||||
- `cargo test -p prometeu-system`
|
|
||||||
- Add or update firmware tests for:
|
|
||||||
- first Game load creates one session and enters `GameRunning`;
|
|
||||||
- same Game relaunch resumes existing task/session;
|
|
||||||
- VM Shell load creates a distinct session;
|
|
||||||
- Shell load does not change resident Game session app identity;
|
|
||||||
- invalid cartridge reports `CrashReport::VmInit`.
|
|
||||||
|
|
||||||
## Affected Artifacts
|
|
||||||
|
|
||||||
- `crates/console/prometeu-firmware/src/firmware/firmware_step_load_cartridge.rs`
|
|
||||||
- `crates/console/prometeu-firmware/src/firmware/firmware_step_hub_home.rs`
|
|
||||||
- `crates/console/prometeu-firmware/src/firmware/firmware.rs`
|
|
||||||
- `crates/console/prometeu-system/src/os/facades/sessions.rs`
|
|
||||||
- `crates/console/prometeu-system/src/os/facades/vm.rs`
|
|
||||||
- VM session module introduced by PLN-0148
|
|
||||||
@ -1,92 +0,0 @@
|
|||||||
---
|
|
||||||
id: PLN-0151
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: Schedule Foreground VM Session Ticks
|
|
||||||
status: done
|
|
||||||
created: 2026-07-04
|
|
||||||
ref_decisions: [DEC-0038]
|
|
||||||
tags: [runtime, os, lifecycle, shell, game, vm, foreground, architecture]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
DEC-0038 separates session ownership from foreground ownership and states that V1 does not provide general background execution. The runtime therefore needs an explicit scheduler path that ticks the VM session eligible for foreground execution, while leaving suspended resident Game sessions intact.
|
|
||||||
|
|
||||||
## Objective
|
|
||||||
|
|
||||||
Make firmware and SystemOS tick the VM session associated with the current foreground VM-backed task, instead of ticking a global VM/runtime pair.
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
- Source decision: DEC-0038.
|
|
||||||
- Requires PLN-0148.
|
|
||||||
- Requires PLN-0149.
|
|
||||||
- Requires PLN-0150 for loaded cartridges to map to sessions.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
- Add scheduler/facade operations that tick a specific VM session by `TaskId`.
|
|
||||||
- Update `GameRunningStep` to tick the Game session identified by its `task_id`.
|
|
||||||
- Update `ShellRunningStep` to tick only VM-backed Shell sessions.
|
|
||||||
- Ensure native Shell foreground state performs native profile updates without ticking any VM session.
|
|
||||||
- Keep suspended Game sessions non-executing while Hub or Shell is foreground.
|
|
||||||
- Preserve lifecycle event delivery:
|
|
||||||
- pause/resume events go to the resident Game session;
|
|
||||||
- Shell lifecycle events, if any, remain scoped to the Shell session.
|
|
||||||
- Preserve host debugger pause/step behavior against the active VM session view.
|
|
||||||
|
|
||||||
## Non-Goals
|
|
||||||
|
|
||||||
- Do not add background execution classes yet.
|
|
||||||
- Do not allow multiple simultaneous VM ticks in one host frame.
|
|
||||||
- Do not implement general service/player processes.
|
|
||||||
- Do not change the 60 Hz firmware tick contract.
|
|
||||||
|
|
||||||
## Execution Method
|
|
||||||
|
|
||||||
1. Add `SystemOS` facade methods for:
|
|
||||||
- ticking a VM session by task id;
|
|
||||||
- querying whether a task is VM-backed;
|
|
||||||
- exposing the active VM session to host/debugger tooling.
|
|
||||||
2. Update `crates/console/prometeu-firmware/src/firmware/firmware_step_game_running.rs` to tick through the task/session facade.
|
|
||||||
3. Update `crates/console/prometeu-firmware/src/firmware/firmware_step_shell_running.rs` to branch on process kind:
|
|
||||||
- VM Shell ticks its VM session;
|
|
||||||
- native Shell runs native profile logic only.
|
|
||||||
4. Remove direct `ctx.vm` use from Game/Shell execution steps.
|
|
||||||
5. Ensure resident Game pause budget can deliver one final pause tick and then stop ticking once suspended.
|
|
||||||
6. Update host/debugger active VM access paths to resolve through the foreground session.
|
|
||||||
7. Add regression tests for tick counters across Game, Hub, native Shell, and VM Shell transitions.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- A suspended Game session does not advance while Hub is foreground.
|
|
||||||
- A suspended Game session does not advance while a Shell is foreground.
|
|
||||||
- A VM Shell session advances while that VM Shell is foreground.
|
|
||||||
- A native Shell does not tick any VM session.
|
|
||||||
- Returning from Hub to resident Game resumes ticking the same Game session.
|
|
||||||
- Host debugger and stats read the active VM session, not a stale global VM.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
- `cargo test -p prometeu-firmware`
|
|
||||||
- `cargo test -p prometeu-system`
|
|
||||||
- `cargo test -p prometeu-host-desktop-winit`
|
|
||||||
- Add tests for:
|
|
||||||
- Game tick count freezes after Home suspension;
|
|
||||||
- VM Shell tick count advances independently;
|
|
||||||
- native Shell does not advance Game tick count;
|
|
||||||
- resume returns to same Game session tick counter;
|
|
||||||
- active-session debugger snapshot uses foreground VM session.
|
|
||||||
|
|
||||||
## Affected Artifacts
|
|
||||||
|
|
||||||
- `crates/console/prometeu-firmware/src/firmware/firmware_step_game_running.rs`
|
|
||||||
- `crates/console/prometeu-firmware/src/firmware/firmware_step_shell_running.rs`
|
|
||||||
- `crates/console/prometeu-firmware/src/firmware/prometeu_context.rs`
|
|
||||||
- `crates/console/prometeu-firmware/src/firmware/firmware.rs`
|
|
||||||
- `crates/console/prometeu-system/src/os/facades/vm.rs`
|
|
||||||
- `crates/console/prometeu-system/src/os/facades/lifecycle.rs`
|
|
||||||
- VM session module introduced by PLN-0148
|
|
||||||
- `crates/host/prometeu-host-desktop-winit/src/debugger.rs`
|
|
||||||
- `crates/host/prometeu-host-desktop-winit/src/runner.rs`
|
|
||||||
- `crates/host/prometeu-host-desktop-winit/src/stats.rs`
|
|
||||||
@ -1,86 +0,0 @@
|
|||||||
---
|
|
||||||
id: PLN-0152
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: Validate Game and VM Shell Session Coexistence
|
|
||||||
status: done
|
|
||||||
created: 2026-07-04
|
|
||||||
ref_decisions: [DEC-0038]
|
|
||||||
tags: [runtime, os, lifecycle, shell, game, vm, foreground, architecture]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
DEC-0038 was motivated by the concrete failure mode where a resident Game is suspended, a Shell is opened, and Game logs or state unexpectedly continue under the wrong foreground owner. After VM sessions and scheduling are implemented, the system needs explicit regression coverage for this workflow.
|
|
||||||
|
|
||||||
## Objective
|
|
||||||
|
|
||||||
Add end-to-end and integration validation proving a resident Game VM session and a VM-backed Shell session can coexist without overwriting or ticking each other incorrectly.
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
- Source decision: DEC-0038.
|
|
||||||
- Requires PLN-0148, PLN-0149, PLN-0150, and PLN-0151.
|
|
||||||
- Should be executed before closing the broader DEC-0038 implementation thread.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
- Add firmware-level regression tests for:
|
|
||||||
- Game launch;
|
|
||||||
- Home/SystemOS request;
|
|
||||||
- Game pause/suspend;
|
|
||||||
- VM Shell launch;
|
|
||||||
- Shell foreground ticking;
|
|
||||||
- Shell exit;
|
|
||||||
- Hub return;
|
|
||||||
- resident Game resume.
|
|
||||||
- Add system-level tests for independent session state.
|
|
||||||
- Add host-level tests only where active VM/debugger/stat routing cannot be covered at firmware/system level.
|
|
||||||
- Verify logs from the stress cartridge or equivalent test cartridge do not appear while Game is suspended and Shell is foreground.
|
|
||||||
- Verify Shell logs, frames, or tick counters belong to the Shell session.
|
|
||||||
|
|
||||||
## Non-Goals
|
|
||||||
|
|
||||||
- Do not introduce new architecture in this plan.
|
|
||||||
- Do not broaden background execution.
|
|
||||||
- Do not rewrite Hub UI behavior except where required to exercise the flow.
|
|
||||||
- Do not depend on manual desktop testing as the only evidence.
|
|
||||||
|
|
||||||
## Execution Method
|
|
||||||
|
|
||||||
1. Identify or add deterministic test cartridges/helpers that can emit per-session tick/log evidence.
|
|
||||||
2. Add a firmware integration test for `Game -> Home -> VM Shell -> Hub -> same Game`.
|
|
||||||
3. Capture Game session counters before Shell launch and after Shell foreground ticks.
|
|
||||||
4. Assert the Game session counters and lifecycle delivery state remain stable while Shell is foreground.
|
|
||||||
5. Assert the Shell session counters advance while Shell is foreground.
|
|
||||||
6. Assert returning to the same Game resumes the same task/session and then advances the Game counter.
|
|
||||||
7. Add a regression specifically covering the prior symptom: Game `stress` logs must not appear during Shell foreground execution.
|
|
||||||
8. Run the relevant crate tests and record evidence in the plan completion notes when implemented.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- The failing manual scenario is covered by automated tests.
|
|
||||||
- The Game session is suspended and non-ticking while VM Shell is foreground.
|
|
||||||
- The VM Shell session ticks independently.
|
|
||||||
- Returning to Hub and selecting the same Game resumes the resident Game session instead of creating a duplicate process/session.
|
|
||||||
- No test relies on a single global `current_app_id` to infer correctness.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
- `cargo test -p prometeu-system`
|
|
||||||
- `cargo test -p prometeu-firmware`
|
|
||||||
- `cargo test -p prometeu-host-desktop-winit` if host active-session plumbing changed.
|
|
||||||
- Optional manual smoke:
|
|
||||||
- run `cargo run --bin prometeu-host-desktop-winit --profile release --manifest-path crates/host/prometeu-host-desktop-winit/Cargo.toml -- --games-root test-cartridges`;
|
|
||||||
- open Stress Console;
|
|
||||||
- press ESC/Home;
|
|
||||||
- open VM-backed Shell;
|
|
||||||
- verify no Stress logs appear while Shell is foreground;
|
|
||||||
- return and resume Stress Console.
|
|
||||||
|
|
||||||
## Affected Artifacts
|
|
||||||
|
|
||||||
- `crates/console/prometeu-firmware/src/firmware/firmware.rs` tests
|
|
||||||
- `crates/console/prometeu-system/src/os/system_os.rs` tests
|
|
||||||
- `crates/console/prometeu-system/src/services/vm_runtime/tests.rs`
|
|
||||||
- `crates/host/prometeu-host-desktop-winit/src/runner.rs` tests if needed
|
|
||||||
- `test-cartridges/` only if a deterministic VM Shell fixture is missing
|
|
||||||
@ -1,82 +0,0 @@
|
|||||||
---
|
|
||||||
id: PLN-0153
|
|
||||||
ticket: foreground-stack-game-pause-shell-vm-backed
|
|
||||||
title: Specify VM Session Ownership and Background-Ready Scheduling
|
|
||||||
status: done
|
|
||||||
created: 2026-07-04
|
|
||||||
ref_decisions: [DEC-0038]
|
|
||||||
tags: [runtime, os, lifecycle, shell, game, vm, foreground, architecture]
|
|
||||||
---
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
DEC-0038 locks the runtime direction: VM context is session-owned, firmware orchestrates macro-state, and background execution is out of V1 but must remain a near-term compatible extension. The canonical specs must describe that contract in English so future implementation and debugging do not drift back to a single global VM model.
|
|
||||||
|
|
||||||
## Objective
|
|
||||||
|
|
||||||
Update canonical runtime specifications to document VM session ownership, foreground execution eligibility, render publication authority, and the background-ready shape of the scheduler without committing to background execution in this implementation batch.
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
- Source decision: DEC-0038.
|
|
||||||
- Should be informed by PLN-0148 through PLN-0151 once implementation names settle.
|
|
||||||
- May be done before PLN-0152 if the implementation contract is already stable, but final wording must match implemented APIs.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
- Update the canonical specs under `docs/specs/runtime/` in English.
|
|
||||||
- Document:
|
|
||||||
- each VM-backed process owns one VM session;
|
|
||||||
- VM sessions own mutable VM context;
|
|
||||||
- foreground ownership and execution eligibility are separate concepts;
|
|
||||||
- V1 executes only the foreground VM-backed task, except the existing pause handoff budget;
|
|
||||||
- suspended resident Game sessions preserve VM context but do not tick;
|
|
||||||
- VM-backed Shell sessions have their own VM context;
|
|
||||||
- native Shell does not own a VM session;
|
|
||||||
- durable app data remains keyed by `app_id`;
|
|
||||||
- session-scoped handles and staging state are not durable app data;
|
|
||||||
- global render publication remains SystemOS/host authority.
|
|
||||||
- Add background-ready wording:
|
|
||||||
- scheduler model can later mark sessions or service processes as eligible without changing ownership;
|
|
||||||
- no current guarantee of background progress is introduced.
|
|
||||||
|
|
||||||
## Non-Goals
|
|
||||||
|
|
||||||
- Do not write implementation plans into specs.
|
|
||||||
- Do not document background execution as available.
|
|
||||||
- Do not move normative text into `discussion/lessons/`.
|
|
||||||
- Do not create new legacy docs paths.
|
|
||||||
|
|
||||||
## Execution Method
|
|
||||||
|
|
||||||
1. Locate the most relevant runtime specs, likely:
|
|
||||||
- `docs/specs/runtime/09-events-and-concurrency.md`;
|
|
||||||
- `docs/specs/runtime/12-firmware-pos-and-prometeuhub.md`;
|
|
||||||
- `docs/specs/runtime/14-boot-profiles.md`;
|
|
||||||
- `docs/specs/runtime/16-host-abi-and-syscalls.md` if host/debugger active VM views are affected.
|
|
||||||
2. Add concise normative sections using `MUST`, `MUST NOT`, and `SHALL` where appropriate.
|
|
||||||
3. Keep discussion history out of the spec; cite behavior, not agenda chronology.
|
|
||||||
4. Align terminology with implemented type names from PLN-0148 through PLN-0151.
|
|
||||||
5. Run discussion validation after edits and any repository tests required by touched docs tooling.
|
|
||||||
|
|
||||||
## Acceptance Criteria
|
|
||||||
|
|
||||||
- Specs explicitly state that VM context is session-owned for VM-backed processes.
|
|
||||||
- Specs explicitly state that native Shell does not own a VM session.
|
|
||||||
- Specs distinguish foreground ownership from future execution eligibility.
|
|
||||||
- Specs state that V1 does not support general background execution.
|
|
||||||
- Specs preserve a future path for background-capable sessions/services without changing VM ownership.
|
|
||||||
- No new framework artifacts are written outside `discussion/`.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
- `discussion validate`
|
|
||||||
- Manual review of touched specs for English-only normative text.
|
|
||||||
- Run repository doc checks if such a command exists; otherwise no code test is required for this plan.
|
|
||||||
|
|
||||||
## Affected Artifacts
|
|
||||||
|
|
||||||
- `docs/specs/runtime/09-events-and-concurrency.md`
|
|
||||||
- `docs/specs/runtime/12-firmware-pos-and-prometeuhub.md`
|
|
||||||
- `docs/specs/runtime/14-boot-profiles.md`
|
|
||||||
- `docs/specs/runtime/16-host-abi-and-syscalls.md` if active VM host/debugger wording is needed
|
|
||||||
Loading…
x
Reference in New Issue
Block a user