Foreground Stack, Game Pause, and VM-Backed Shell Coexistence

This commit is contained in:
bQUARKz 2026-07-04 18:48:11 +01:00
parent a22215392d
commit 3eeda25e97
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
5 changed files with 416 additions and 4 deletions

View File

@ -39,8 +39,8 @@ impl<'a> LifecycleFacade<'a> {
pub fn set_foreground_task(&mut self, task_id: TaskId) -> Result<(), LifecycleError> {
match task_kind_for_task(self.os, task_id)? {
TaskKind::Game => return self.set_game_foreground_task(task_id),
TaskKind::Shell => return self.set_shell_foreground_task(task_id),
TaskKind::Game => self.set_game_foreground_task(task_id),
TaskKind::Shell => self.set_shell_foreground_task(task_id),
}
}

View File

@ -1,8 +1,8 @@
{"type":"meta","next_id":{"DSC":44,"AGD":46,"DEC":38,"PLN":145,"LSN":52,"CLSN":1}}
{"type":"meta","next_id":{"DSC":44,"AGD":46,"DEC":38,"PLN":148,"LSN":52,"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-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-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"}],"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"}],"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"]}],"lessons":[]}
{"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"}],"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"}],"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":"open","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":"open","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":"open","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0037"]}],"lessons":[]}
{"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-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"}]}

View File

@ -0,0 +1,149 @@
---
id: PLN-0145
ticket: foreground-stack-game-pause-shell-vm-backed
title: Centralize Resident Game Resume Transition
status: open
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.

View File

@ -0,0 +1,119 @@
---
id: PLN-0146
ticket: foreground-stack-game-pause-shell-vm-backed
title: Consolidate Lifecycle Process Lookup Helpers
status: open
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.

View File

@ -0,0 +1,144 @@
---
id: PLN-0147
ticket: foreground-stack-game-pause-shell-vm-backed
title: Move Native Shell Profile Update Into Hub
status: open
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.