Files
browser/openspec/changes/make-run-runtime-state-authoritative/design.md
T

2.7 KiB

Context

Platform currently stores serverInstances.state as both desired state and observed runtime state. CompleteRunJob projects successful lifecycle jobs directly into that field, so a previous process.start success can leave a server as running even after the actual Run-managed process is gone. A manually started generated Run can register and heartbeat, but platform will not dispatch another start job because it trusts the stale stored state.

Run already has the safer primitive: plugin-declared process.status executes inside the generated Run workspace and returns a redacted processState. This change uses that existing channel as the observed runtime source.

Goals / Non-Goals

Goals:

  • Make generated Run startup reconcile stale platform lifecycle state through a platform-dispatched, Run-executed process.status job.
  • Project server state from Run processState for status/start/stop lifecycle results.
  • Preserve platform ownership of authorization, command dispatch, leases, and audit.

Non-Goals:

  • Add a new live telemetry protocol or raw process list to heartbeat.
  • Hardcode SCUM-specific executable names, Steam app IDs, paths, ports, or health checks in platform or Run.
  • Change plugin create wizard requirements or distribution-build ownership.

Decisions

  • Use process.status rather than adding heartbeat fields. This keeps state reconciliation inside the existing job lease, capability, audit, and plugin-declared action model.
  • Queue status reconciliation on generated Run registration when stored state is running or failed. Those states are the ones most likely to be stale after a manually restarted Run or process crash.
  • Skip reconciliation when an active lifecycle job already exists for the server. The active job is already the current control operation and should not be raced by a status probe.
  • Project process.status into lifecycle state with conservative mapping: running => running, stopped/not-started => stopped, unexpected exited => failed, operator-stopped exited => stopped.

Risks / Trade-offs

  • A plugin start wrapper can be running while still installing dependencies. This change does not solve game readiness; it only prevents platform from preserving stale process state when Run reports no managed process.
  • Status reconciliation is asynchronous. A manual Run may briefly appear with stale state until it claims and completes the status job.
  • If a server process was started outside the active generated Run workspace, the registering Run will report not-started and platform will mark stopped. That is intentional: unmanaged external processes are not authoritative for platform lifecycle.