Make generated run lifecycle autonomous

This commit is contained in:
npc0-hue
2026-08-06 18:57:42 +08:00
parent acec5e4367
commit 4e78957a60
22 changed files with 583 additions and 236 deletions
@@ -2,14 +2,14 @@
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.
Run already has the safer primitive: it owns the generated package startup path and can report redacted `processState` facts from inside the generated Run workspace. This change uses Run reports as the observed runtime source and avoids Platform registration-time probes.
## Goals / Non-Goals
**Goals:**
- Make generated Run startup reconcile stale platform lifecycle state through a platform-dispatched, Run-executed `process.status` job.
- Stop generated Run startup from relying on a platform-dispatched registration-time `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.
- Preserve Platform ownership of authorization, leases, and audit for explicit operator requests while treating generated Run bootstrap as Run-owned.
**Non-Goals:**
- Add a new live telemetry protocol or raw process list to heartbeat.
@@ -18,9 +18,8 @@ Run already has the safer primitive: plugin-declared `process.status` executes i
## 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.
- Do not queue status reconciliation on generated Run registration. Registration confirms identity and session only; Run-owned lifecycle/status reports correct stale Platform projections.
- Keep explicit `process.status` result projection for operator-requested or Run-reported status flows that are not registration bootstrap side effects.
- 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
@@ -4,10 +4,10 @@ Manual generated Run execution exposed a stale lifecycle design: platform persis
## What Changes
- Add runtime-state reconciliation for generated Run registration so a Run endpoint can report the actual managed process state for its server after reconnect/startup.
- Remove registration-time runtime-state reconciliation jobs for generated Run; Run reports the actual managed process state from its own lifecycle authority instead of waiting for Platform probes.
- Project `process.status` results into server lifecycle state using Run-reported `processState` values such as `running`, `stopped`, `not-started`, and `exited`.
- Prevent stale platform `running` from surviving when the active Run reports no managed process for that server.
- Keep lifecycle command authorization and job dispatch platform-owned; only observed runtime/process state becomes Run-authoritative.
- Keep Platform authorization/audit for explicit operator requests while making observed runtime/process state and generated Run bootstrap Run-authoritative.
## Capabilities
@@ -1,15 +1,15 @@
## ADDED Requirements
### Requirement: Generated Run registration reconciles observed process state
When a generated Run registers for a bound server instance, the platform SHALL enqueue a scoped `process.status` reconciliation job when the stored server state says the game process is running or failed and no active lifecycle job already covers that server. The reconciliation job SHALL use the plugin-declared status action and the same scoped workspace metadata as normal lifecycle jobs.
### Requirement: Generated Run registration does not dispatch observed-state probes
When a generated Run registers for a bound server instance, the platform SHALL NOT enqueue a scoped `process.status` reconciliation job merely because the stored server state says the game process is running or failed. Registration SHALL confirm identity, binding, and session state only; observed process state SHALL come from Run-owned lifecycle/status reports.
#### Scenario: Stale running state is checked after manual Run startup
#### Scenario: Stale running state waits for Run report after manual Run startup
- **WHEN** a generated Run registers for a server whose stored state is `running`
- **THEN** the platform enqueues one `process.status` job for that server and Run endpoint
- **THEN** the platform does not enqueue a `process.status` job solely from registration
#### Scenario: Existing active lifecycle job avoids duplicate status checks
#### Scenario: Existing active lifecycle job remains untouched
- **WHEN** a generated Run registers while the same server already has an active lifecycle job
- **THEN** the platform does not enqueue an additional status reconciliation job
- **THEN** the platform leaves the existing job unchanged and does not add a registration-time status probe
### Requirement: Run process status is authoritative for observed lifecycle state
The platform SHALL project terminal `process.status` results from Run into the server instance state. A Run-reported `processState` of `running` SHALL mark the server `running`; `stopped` or `not-started` SHALL mark it `stopped`; `exited` SHALL mark it `failed` unless the exit classification is an operator stop such as `requested-stop`, `forced-stop`, or `already-stopped`, in which case it SHALL mark the server `stopped`.
@@ -1,9 +1,9 @@
## 1. Runtime State Reconciliation
- [x] 1.1 Queue generated Run status reconciliation on registration when stored server state may be stale
- [x] 1.1 Prevent generated Run registration from queuing status reconciliation when stored server state may be stale
- [x] 1.2 Project `process.status` execution results into server lifecycle state using Run `processState`
## 2. Verification
- [x] 2.1 Add service tests for stale running correction and active-job dedupe
- [x] 2.1 Add service tests for no registration-time status dispatch and Run-fact state projection
- [x] 2.2 Run targeted Go tests, `openspec validate make-run-runtime-state-authoritative --strict`, and `scripts/check-structure.sh`