From e01b463e1f89f6a01f07107ca6a6f62147bc77bc Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Sat, 4 Jul 2026 19:42:59 +0100 Subject: [PATCH] implements PLN-0153 --- discussion/index.ndjson | 2 +- ...nership-and-background-ready-scheduling.md | 2 +- .../runtime/09-events-and-concurrency.md | 38 +++++++++++++++++-- .../12-firmware-pos-and-prometeuhub.md | 28 ++++++++++++-- docs/specs/runtime/14-boot-profiles.md | 5 +++ .../specs/runtime/16-host-abi-and-syscalls.md | 5 +++ 6 files changed, 71 insertions(+), 9 deletions(-) diff --git a/discussion/index.ndjson b/discussion/index.ndjson index fb7c23a1..751f9fc4 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -2,7 +2,7 @@ {"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"},{"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":"open","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0038"]}],"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"},{"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-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"}]} diff --git a/discussion/workflow/plans/PLN-0153-specify-vm-session-ownership-and-background-ready-scheduling.md b/discussion/workflow/plans/PLN-0153-specify-vm-session-ownership-and-background-ready-scheduling.md index 2d0f0e81..a5e6cd85 100644 --- a/discussion/workflow/plans/PLN-0153-specify-vm-session-ownership-and-background-ready-scheduling.md +++ b/discussion/workflow/plans/PLN-0153-specify-vm-session-ownership-and-background-ready-scheduling.md @@ -2,7 +2,7 @@ id: PLN-0153 ticket: foreground-stack-game-pause-shell-vm-backed title: Specify VM Session Ownership and Background-Ready Scheduling -status: open +status: done created: 2026-07-04 ref_decisions: [DEC-0038] tags: [runtime, os, lifecycle, shell, game, vm, foreground, architecture] diff --git a/docs/specs/runtime/09-events-and-concurrency.md b/docs/specs/runtime/09-events-and-concurrency.md index 0e9a1606..4ec0e44a 100644 --- a/docs/specs/runtime/09-events-and-concurrency.md +++ b/docs/specs/runtime/09-events-and-concurrency.md @@ -133,7 +133,37 @@ time. A suspended Game receives no normal gameplay ticks, no normal Game input, and no frame pacing. Real background execution is outside the v1 foreground contract. -## 8 Render Worker Concurrency +## 8 VM Sessions and Execution Eligibility + +Every VM-backed process MUST own a distinct VM session. A VM session owns the +mutable execution context for that process, including the VM instance, +per-session runtime counters, lifecycle delivery bookkeeping, debug/pause state, +and transient handles opened through VM-facing services. + +Foreground ownership and execution eligibility are related but separate +concepts: + +- foreground ownership selects the visual/navigation owner; +- execution eligibility selects which sessions may receive VM ticks; +- v1 grants normal VM execution only to the foreground VM-backed task, plus the + bounded Game pause handoff tick defined above; +- a suspended resident Game preserves its VM session but MUST NOT receive + normal gameplay ticks, normal Game input, or frame pacing; +- a VM-backed Shell owns its own VM session and MUST NOT overwrite or advance + the resident Game session while the Shell is foreground; +- a native Shell does not own a VM session. + +The model is intentionally compatible with future background execution. Future +service or media processes may become execution-eligible without changing the +rule that mutable VM context belongs to the owning session. This chapter does +not grant any v1 background progress guarantee. + +Durable app data remains keyed by `app_id` where the relevant storage contract +requires it. Transient VM handles, open files, lifecycle delivery queues, and +staging state are session-scoped unless another spec explicitly makes them +durable app data. + +## 9 Render Worker Concurrency The render worker is not a machine-visible event source and does not introduce guest callbacks. It is an implementation-side consumer of closed render @@ -160,7 +190,7 @@ Shutdown is explicit and bounded. A shutdown request wakes a waiting worker, causes pending work that will not be consumed to be discarded, and reports a typed failure if the worker cannot join within the configured timeout. -## 9 Async Asset and IO Work Lane +## 10 Async Asset and IO Work Lane The asset/IO async work lane is not a machine-visible event source and does not introduce guest callbacks. It is an implementation-side lane for asset @@ -184,7 +214,7 @@ does not publish resident graphics/audio/scene state directly. FS and game persistence services may consume this lane for IO-style work, but public FS API shape is defined by the FS/app-home contract, not by this chapter. -## 10 Determinism and Best Practices +## 11 Determinism and Best Practices PROMETEU encourages: @@ -199,7 +229,7 @@ PROMETEU discourages: - hidden timing channels; - ambiguous out-of-band execution. -## 11 Relationship to Other Specs +## 12 Relationship to Other Specs - [`09a-coroutines-and-cooperative-scheduling.md`](09a-coroutines-and-cooperative-scheduling.md) defines coroutine lifecycle and scheduling behavior. - [`10-debug-inspection-and-profiling.md`](10-debug-inspection-and-profiling.md) defines observability and diagnostics surfaces. diff --git a/docs/specs/runtime/12-firmware-pos-and-prometeuhub.md b/docs/specs/runtime/12-firmware-pos-and-prometeuhub.md index d4c5782a..b75246c9 100644 --- a/docs/specs/runtime/12-firmware-pos-and-prometeuhub.md +++ b/docs/specs/runtime/12-firmware-pos-and-prometeuhub.md @@ -50,13 +50,16 @@ The VM does not own the machine lifecycle. Firmware does. - **Suspended**: an OS-owned scheduler/runtime state. A suspended Game remains resident but does not receive normal gameplay ticks, normal Game input, or frame pacing. +- **VM session**: the POS-owned mutable execution context for one VM-backed + process. It contains the VM instance and per-session runtime state. ## 3 POS Responsibilities POS is responsible for: - deterministic reset into known machine state; -- VM initialization and teardown for cartridge execution; +- VM session creation, initialization, activation, and teardown for cartridge + execution; - input latching and logical-frame budgeting; - peripheral reset/orchestration; - fault capture and crash-flow transition; @@ -68,6 +71,10 @@ At the VM boundary, POS must preserve: - `FRAME_SYNC` as the canonical frame boundary; - deterministic transition from host tick to VM slice execution. +Firmware must not treat a single global VM object as the canonical execution +owner for all cartridges. The firmware state machine orchestrates macro-state; +POS/session services own VM-backed process context. + ## 4 PrometeuHub Responsibilities PrometeuHub is responsible for: @@ -204,19 +211,31 @@ boundaries. Game audio pauses with the Game VM in v1. Independent background audio, real background execution, multiple resident Games, multiple foreground Shells, and direct Game-to-Game switching are outside this contract. +The resident Game VM session remains allocated while Hub or Shell is foreground. +It preserves Game VM state but is not execution-eligible except for the bounded +pause handoff and later foreground resume. A VM-backed Shell has a separate VM +session. A native Shell has no VM session. + ## 8 Cartridge Load Flow Current high-level flow: 1. POS receives a cartridge to load; 2. asset manager is initialized from the cartridge asset table, preload list, and packed asset bytes; -3. POS initializes the VM/runtime for the cartridge; -4. launch behavior branches by `AppMode`: +3. POS creates or selects the VM session for the cartridge; +4. POS initializes the session-owned VM/runtime for the cartridge; +5. launch behavior branches by `AppMode`: - `Game` -> transition to the game-running firmware state; - `System` -> create/focus a Hub window and return to the Hub state. If VM initialization fails, firmware transitions to the crash path. +For v1, POS must enforce at most one resident Game session. Selecting the same +resident Game from Home resumes that session. Selecting a different Game while a +resident Game exists is outside the v1 foreground contract and must not create a +second resident Game session. VM-backed Shell loading creates a distinct Shell +session and must not overwrite resident Game VM state. + POS selects the cartridge and its execution context, but the cartridge's initial callable is not chosen by firmware metadata. Execution starts from the cartridge boot protocol defined in [`13-cartridge.md`](13-cartridge.md), currently `func_id = 0`. When Home launches a discovered Game entry, the request is a system-owned action @@ -239,6 +258,7 @@ The current firmware state model includes: - `HubHome` - `LoadCartridge` - `GameRunning` +- `ShellRunning` - `AppCrashes` These states express machine orchestration above the VM. They are not guest-visible bytecode states. @@ -286,6 +306,8 @@ Expected behavior: - Home remains foreground while the Game is resident; - launching and closing a Shell app returns to Home without losing the resident Game; +- a VM-backed Shell advances its own VM session and does not tick the suspended + resident Game session; - returning to the same Game resumes it through the foreground lifecycle and waits for current-epoch Game rendering before visual Game presentation. diff --git a/docs/specs/runtime/14-boot-profiles.md b/docs/specs/runtime/14-boot-profiles.md index 285763c9..a420804c 100644 --- a/docs/specs/runtime/14-boot-profiles.md +++ b/docs/specs/runtime/14-boot-profiles.md @@ -50,6 +50,8 @@ For a cartridge boot target: - `Game` cartridges transition into the game-running pipeline; - `System` cartridges transition into a Runtime/Hub pipeline dedicated to system UI and app hosting. +- VM-backed cartridges are initialized in a POS-owned VM session associated with + the created task/process. This preserves the distinction between machine firmware state and app execution mode. @@ -104,6 +106,9 @@ Game is already running. It does not change direct `--run`, debugger direct boot, or `--games-root` Home library startup semantics. Home/SystemOS requests are host/system controls, not guest cartridge boot APIs. +Direct boot creates the initial VM session for the selected cartridge. It does +not create a reusable global VM shared by later Game or Shell cartridges. + ## 5 Firmware State Relationship Boot target selection feeds into firmware states such as: diff --git a/docs/specs/runtime/16-host-abi-and-syscalls.md b/docs/specs/runtime/16-host-abi-and-syscalls.md index c759a232..88a4749a 100644 --- a/docs/specs/runtime/16-host-abi-and-syscalls.md +++ b/docs/specs/runtime/16-host-abi-and-syscalls.md @@ -187,6 +187,11 @@ The game memcard profile uses module `mem` with status-first return shapes. The `mem` module is a game profile surface. It is not the public storage model for `System` profile apps. +Durable memcard data is keyed by `app_id` according to the save-memory +contract. Runtime handles, open file tables, pending lifecycle delivery, and +other mutable VM-facing syscall context are scoped to the owning VM session and +MUST NOT be used as durable app identity. + Canonical operations in v1 are: - `mem.slot_count() -> (status, count)`