Make generated run lifecycle autonomous
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-08-06
|
||||
@@ -0,0 +1,3 @@
|
||||
# make-run-autonomous-lifecycle-owner
|
||||
|
||||
Make generated Run own autonomous plugin-declared lifecycle bootstrap and make Platform follow Run-reported state.
|
||||
@@ -0,0 +1,46 @@
|
||||
## Context
|
||||
|
||||
Generated Run packages currently register with Platform and then wait for Platform to enqueue lifecycle work before any game bootstrap can happen. That makes Platform the practical lifecycle starter even though the machine-side Run is the only component that can observe process truth, supervise local execution, and safely decide whether install/update/start work is needed.
|
||||
|
||||
The corrected ownership model is: Platform builds and authenticates a server-scoped Run package, the package contains plugin-declared lifecycle assets plus a bounded autonomous lifecycle plan, Run executes that plan locally on startup, and Platform updates persisted projections from Run-reported lifecycle facts. Plugin manifests remain the source of game-specific instructions; Platform and Run remain generic.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Stop generated Run registration from enqueueing `process.start` or `process.status` jobs as a bootstrap side effect.
|
||||
- Add a generated-package autonomous lifecycle plan containing plugin lifecycle action refs, dependency declarations, safe deployment inputs, log sources, and selected runtime profile metadata.
|
||||
- Keep Platform responsible for server records, registration binding, auth, distribution builds, audit, and visible projections from Run reports.
|
||||
- Update governance and protocol docs so future work treats Run as the lifecycle authority.
|
||||
|
||||
**Non-Goals:**
|
||||
- Implement the independent `run` repository's plan executor in this repo.
|
||||
- Add game-specific SCUM install/start behavior to Platform.
|
||||
- Move distribution builds to machine-side Run endpoints or expose distribution-build authority to generated Runs.
|
||||
- Remove explicit operator lifecycle command APIs in this change.
|
||||
|
||||
## Decisions
|
||||
|
||||
- **Embed lifecycle intent at package-build time.** Platform already has the server instance, plugin manifest, selected profile, lifecycle assets, and deployment definition when it builds a generated Run package. Encoding those into the package avoids waiting on `/run/jobs/claim` after registration and keeps the startup path deterministic.
|
||||
- **Use plugin declarations, not Platform logic, for game behavior.** The plan references action files, dependency probes/install plans, process log sources, DLL extension declarations, and sanitized deployment inputs already declared by the plugin. It does not include SCUM executable names, Steam app IDs, ports, or platform-side command synthesis.
|
||||
- **Make registration binding-only for generated Run bootstrap.** `RegisterRunHello` continues to authenticate the component, upsert endpoint metadata, and issue a session token. It does not dispatch lifecycle or reconciliation work merely because a generated Run appeared.
|
||||
- **Keep Platform projections report-driven.** Existing terminal job/result projection can remain for explicit lifecycle commands, but generated Run startup state must converge through Run reports rather than Platform's stale stored state or registration-time probes.
|
||||
- **Preserve builder security boundaries.** The platform-owned builder receives the plaintext component auth key internally and the autonomous plan as build input. Machine-side run endpoints still cannot claim `distribution.build` jobs or fetch plaintext build input.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Run repository lag** -> The generated package can carry the plan before the independent Run executable consumes it. Mitigation: protocol and build input are explicit, and this repo does not re-add Run source.
|
||||
- **Stored Platform state may look stale until Run reports** -> Registration no longer paper-over probes with Platform jobs. Mitigation: UI/API must treat persisted lifecycle state as projection, not observed process truth.
|
||||
- **Plan drift between build and execution** -> A package carries the plugin declarations and deployment revision available at build time. Mitigation: include plugin version, profile key, deployment revision, and target release so Run and Platform can report stale-plan evidence.
|
||||
- **Operator command APIs still dispatch jobs** -> This change fixes generated Run autonomous startup first. Explicit commands remain auditable Platform requests until a later change redesigns command transport around Run-owned intent handling.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
- Stop queuing registration-time lifecycle/status jobs for generated Runs.
|
||||
- Extend run distribution build input and DTOs with `autonomousLifecycle` for Run packages only.
|
||||
- Update platform builder input materialization so the generated package has a serialized plan alongside the existing workspace seed.
|
||||
- Update service tests to assert registration does not enqueue bootstrap/reconciliation jobs and build input includes the plan.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- The independent `run` repository must define exactly how it consumes `autonomousLifecycle`, persists local bootstrap state, and reports lifecycle phases back to Platform.
|
||||
- A future change may replace explicit Platform-dispatched start/stop jobs with Run-owned desired-intent handling for all lifecycle commands.
|
||||
@@ -0,0 +1,28 @@
|
||||
## Why
|
||||
|
||||
Generated Run currently comes online, registers, and then waits for Platform to assign lifecycle jobs before a game process can exist. That inverts the intended ownership model: Run is the machine-side lifecycle owner, Platform should follow Run-reported facts, and plugins should only declare how Run initializes, installs dependencies, verifies readiness, and starts the game.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **BREAKING** Treat generated Run startup as an autonomous lifecycle bootstrap driven by plugin-declared lifecycle assets and the server deployment definition embedded in the generated package.
|
||||
- Stop using accepted generated Run registration as a Platform trigger to enqueue `process.start` or `process.status` reconciliation jobs.
|
||||
- Add a bounded autonomous lifecycle plan to generated Run distribution build input so the independent Run package can self-bootstrap without waiting for `/run/jobs/claim` work.
|
||||
- Keep Platform as the registry, authorization, audit, and projection surface: Platform receives Run heartbeats, logs, lifecycle reports, and process facts, then updates visible server state from those Run-owned facts.
|
||||
- Preserve Platform-owned distribution builds and component key security; generated Runs still never receive distribution-build authority.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `run-autonomous-lifecycle-owner`: Generated Run owns plugin-declared bootstrap/start behavior and Platform follows Run-reported observed state.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `platform-side-distribution-builds`: Generated Run packages must include the bounded autonomous lifecycle plan required for Run to bootstrap itself.
|
||||
|
||||
## Impact
|
||||
|
||||
- `AGENTS.md`, `platform/protocol/*`, and OpenSpec contracts must stop describing Platform as the lifecycle bootstrap dispatcher for generated Runs.
|
||||
- `platform/` distribution build input and tests gain an autonomous lifecycle plan sourced from plugin lifecycle declarations and server deployment settings.
|
||||
- `platform/service/control.go` stops enqueueing registration-time lifecycle/status jobs for generated Runs.
|
||||
- The independent `run` repository must implement plan consumption and autonomous execution; this repository must not re-add a `run/` source tree.
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Generated Run build input includes autonomous lifecycle plan
|
||||
The platform-owned distribution builder SHALL receive a generated Run autonomous lifecycle plan for `run` component packages and SHALL keep that plan inside platform-side build input rather than requiring a machine-side Run endpoint to claim lifecycle bootstrap work.
|
||||
|
||||
#### Scenario: Platform builder assembles Run package input
|
||||
- **WHEN** an owner requests Run generation for a server instance
|
||||
- **THEN** the platform builder input and generated workspace seed include plugin lifecycle action refs, selected profile key, dependency probes, install plans, process log sources, deployment revision, and redacted deployment execution inputs for that server
|
||||
|
||||
#### Scenario: Client-manager build input
|
||||
- **WHEN** an owner requests client-manager generation
|
||||
- **THEN** the build input does not include a server Run autonomous lifecycle plan
|
||||
|
||||
#### Scenario: Build input remains platform-owned
|
||||
- **WHEN** a machine-side Run endpoint attempts to claim or read a platform-owned distribution build
|
||||
- **THEN** Platform denies that access and does not expose the plaintext component auth key or autonomous lifecycle plan through the machine job channel
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Generated Run startup is autonomous
|
||||
Generated Run packages SHALL carry a bounded autonomous lifecycle plan that lets Run bootstrap the server from plugin-declared lifecycle assets without waiting for Platform to enqueue `process.start`, `process.install`, or `process.status` work after registration.
|
||||
|
||||
#### Scenario: Generated Run registers after startup
|
||||
- **WHEN** a server-scoped generated Run registers with valid component authentication
|
||||
- **THEN** Platform accepts the registration and does not enqueue lifecycle or status jobs solely because the registration occurred
|
||||
|
||||
#### Scenario: Generated Run package starts locally
|
||||
- **WHEN** the generated Run executable starts on its host
|
||||
- **THEN** Run can read the embedded autonomous lifecycle plan and execute plugin-declared init, dependency verification/install, install-if-needed, readiness/status, and start behavior locally
|
||||
|
||||
### Requirement: Platform follows Run-reported lifecycle facts
|
||||
Platform SHALL treat persisted server lifecycle state as a projection of Run-reported lifecycle facts, heartbeats, logs, and terminal process reports rather than as authoritative observed process truth.
|
||||
|
||||
#### Scenario: Run reports no managed process
|
||||
- **WHEN** Run reports that the server process is stopped, not started, or exited
|
||||
- **THEN** Platform updates the visible server projection from that Run-owned fact instead of preserving stale `running` state
|
||||
|
||||
#### Scenario: Run reports a live managed process
|
||||
- **WHEN** Run reports that the managed process is running
|
||||
- **THEN** Platform projects the server as running based on the Run report
|
||||
|
||||
### Requirement: Plugins declare game-specific lifecycle behavior
|
||||
Plugins SHALL declare lifecycle action refs, dependency probes, install plans, runtime profiles, log sources, and deployment templates needed by Run, and Platform SHALL NOT hardcode game-specific install, update, status, or startup behavior.
|
||||
|
||||
#### Scenario: SCUM lifecycle bootstrap
|
||||
- **WHEN** a SCUM generated Run package is built
|
||||
- **THEN** Platform packages the plugin-declared lifecycle refs and deployment inputs without hardcoding SCUM executable names, Steam app IDs, ports, or install directories in Platform code
|
||||
|
||||
### Requirement: Run registration is binding and authentication only
|
||||
Generated Run registration SHALL authenticate the component, bind or confirm the dedicated endpoint identity, upsert endpoint metadata, and issue a control session, but SHALL NOT be used as a Platform-side lifecycle bootstrap dispatcher.
|
||||
|
||||
#### Scenario: Guided draft generated Run registers
|
||||
- **WHEN** a guided draft server's generated Run registers
|
||||
- **THEN** the server remains awaiting Run-owned lifecycle reports and Platform does not create a bootstrap start job
|
||||
|
||||
#### Scenario: Stale running generated Run registers
|
||||
- **WHEN** a generated Run registers for a server whose persisted state is `running`
|
||||
- **THEN** Platform does not create a registration-time `process.status` reconciliation job and instead waits for Run-owned status/lifecycle reporting
|
||||
@@ -0,0 +1,22 @@
|
||||
## Prompt Boundaries
|
||||
|
||||
- [x] 0.1 正向提示词: Make generated Run packages self-bootstrap from plugin-declared lifecycle plans so the first-party server management area projects state from Run-owned facts.
|
||||
- [x] 0.2 方向提示词: Update `platform/` build input, registration handling, tests, and protocol docs while preserving existing domain/service/DTO separation; verify with Go tests, OpenSpec validation, and `scripts/check-structure.sh`.
|
||||
- [x] 0.3 任务边界: Do not add a `run/` source tree, hardcode SCUM behavior in Platform, expose component keys to machine endpoints, add cloud/billing/provider workflows, or touch unrelated frontend styling.
|
||||
|
||||
## 1. OpenSpec Contract
|
||||
|
||||
- [x] 1.1 Add design and delta specs for Run-owned autonomous lifecycle startup.
|
||||
- [x] 1.2 Validate the OpenSpec change strictly before completion.
|
||||
|
||||
## 2. Platform Implementation
|
||||
|
||||
- [x] 2.1 Add autonomous lifecycle plan domain/build-input structures without exposing the plan through machine job-channel DTOs.
|
||||
- [x] 2.2 Populate the plan from plugin lifecycle declarations, runtime profile data, dependency declarations, log sources, DLL extensions, and deployment definition.
|
||||
- [x] 2.3 Stop generated Run registration from enqueueing bootstrap start or status reconciliation jobs.
|
||||
- [x] 2.4 Update protocol and governance docs to make Run the lifecycle authority.
|
||||
|
||||
## 3. Verification
|
||||
|
||||
- [x] 3.1 Update service tests for autonomous build input and no registration-time lifecycle dispatch.
|
||||
- [x] 3.2 Run targeted Go tests plus repository structure checks.
|
||||
@@ -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
|
||||
|
||||
|
||||
+6
-6
@@ -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`
|
||||
|
||||
Reference in New Issue
Block a user