Actionable invitations and orderly cross-lobby handoff
23 October 2026 - v1.0.9-spectator-playtest.3

This optional Patch Pipeline layer composes after the accepted spectator
layer. It stages three first-party C++ copies: runtime_ui, game_main and
runtime_platform, plus the final online UI include. No checked-out source,
submodule, RecompiledFuncs or RecompiledPatches is edited.

The UI-owned inbox contains invitation IDs and friend identities, not
credentials. One private in-memory intent retains the admission capability
through the ImGui-context switch. Toast, Open Lobbies and the active-lobby
inbox use the same acceptance handler. Ordinary friend-presence toasts keep
their existing passive rendering.

The existing configurable Open Menu shortcut focuses an invitation; A is
never a global invitation accelerator. A modal waits for opening controls
to be released. Modal input is excluded from gameplay, including owned
online input. A separate raw-input release fence prevents a back/confirm
press from leaking into gameplay when the prompt closes. SDL2 and the
existing SDL3 controller snapshots are supported.

The UI publishes StopGame, not a process kill or a renderer destruction.
The pending return reason suppresses the unrelated old match-ended modal.
Only the main/event owner, after runtime/renderer/save producer retirement,
finishes the cross-lobby return. EEPROM, records and owned-runtime failures
block the new join. The old DirectSession remains alive until this point.
Ordinary Stop Game behavior is unchanged without an invitation intent.

The launcher resumes the intent only after its normal ROM/manifest setup.
Old mod preparation is cancelled and allowed to finish asynchronously before
the new join starts. Existing authenticated friend admission, host spectator
policy, mod verification and code consent remain authoritative. The existing
friend service marks acceptance only after actual lobby admission.

Expired, cancelled, blocked, incompatible and malformed invitations fail
closed. Cancelling during retirement does not resurrect a stopped runtime;
it cancels the subsequent join. Network failure leaves the launcher open.
No admission capability is placed in new settings, logs or on-disk queues.

configure.py composes this layer; audit.py checks scope, source receipts,
retirement ordering and the retained spectator/rollback guards. package.py
uses the accepted allowlisted Windows ZIP/Linux AppImage packager. No target
executable is launched. PLAYTEST.txt records owner acceptance checks.
