## Context The platform already has installed game plugin metadata, server instance records, run endpoint registration, job claim/ack/progress/result, log ingest, artifact transfer, and a console shell. The missing product path is the operator workflow that creates a server from a plugin, dispatches lifecycle jobs through the job channel, updates server state from terminal job results, and exposes those actions in the server management UI. This change must keep platform, run, plugin, and frontend ownership boundaries intact. Browser code must call platform APIs only; plugin pages and platform_web must not receive run credentials, raw host paths, raw AI keys, or direct sockets. Run-side lifecycle execution remains a bounded job executor and does not add unrestricted command execution. ## Goals / Non-Goals **Goals:** - Add platform-mediated create/install, start, and stop workflow APIs for server instances. - Dispatch install/start/stop through existing job-channel records with stable lifecycle capabilities and idempotency keys. - Validate plugin installation state, lifecycle action references, run endpoint status/capabilities, server state, and config version before dispatch. - Project terminal lifecycle job results onto `ServerInstance.State`. - Add run-side lifecycle executor code that handles install/start/stop job assignments with bounded metadata-only results. - Add server management frontend contracts, API methods, form/actions, tests, and browser walkthrough evidence. - Keep plugin manifest and SDK capability enums aligned with lifecycle install support. **Non-Goals:** - No real game process execution, installer downloads, file writes, backups, update/restart workflows, or schedulers. - No authentication/authorization route group beyond existing service validation. - No direct plugin-to-run access, run sockets, raw host path exposure, raw credentials, billing, cloud host sales, or provider marketplace behavior. - No new database persistence layer, migrations, or distributed lease storage. - No replacement of the existing hash router or introduction of a frontend router dependency. ## Decisions ### Decision 1: Add workflow action routes beside existing resource routes The existing `POST /api/v1/server-instances` resource route remains a direct server instance record creation path. Workflow creation is added as `POST /api/v1/server-instances/workflows/create`, and lifecycle commands are added as `POST /api/v1/server-instances/{id}/start` and `POST /api/v1/server-instances/{id}/stop`. Alternative considered: change `POST /api/v1/server-instances` to return a workflow response and always dispatch install. Rejected because existing resource-route tests and clients use the direct create/list/detail contract, and explicit workflow routes make dispatching side effects clear. ### Decision 2: Lifecycle jobs use fixed run capabilities Workflow dispatch maps create/install to `process.install`, start to `process.start`, and stop to `process.stop`. The plugin manifest schema, plugin SDK type union, platform validator allowlist, and run smoke/runtime capability list will include `process.install` so create workflows can be validated consistently. Alternative considered: use arbitrary plugin action JSON references as job capabilities. Rejected because claim matching already uses capability strings reported by run endpoints, and action references are plugin metadata rather than run capability names. ### Decision 3: Platform service owns lifecycle validation and dispatch `platform/service.Core` adds explicit lifecycle methods that validate dependencies and state transitions, then create queued jobs with operator-provided idempotency keys. The service rejects stale `configVersion` values for start/stop commands. Alternative considered: let the frontend create jobs directly through generic `POST /api/v1/jobs`. Rejected because lifecycle state rules, plugin lifecycle action references, and config-version checks belong in the platform service, not the browser. ### Decision 4: Terminal job results project instance state When a lifecycle job completes, the existing run job result path updates the job and then projects the terminal result onto the server instance: successful install makes the instance `ready`, successful start makes it `running`, successful stop makes it `stopped`, and failed/cancelled lifecycle jobs make the instance `failed`. Alternative considered: require a separate status polling endpoint from run before changing server state. Rejected for this first workflow because the job result is already the authoritative terminal signal in the current in-memory platform. ### Decision 5: Run executor is bounded and metadata-only The run-side lifecycle executor accepts a claimed job assignment, supports only the fixed lifecycle capabilities, and returns bounded success/failure metadata without executing arbitrary local commands or returning paths. Alternative considered: execute plugin action definitions immediately. Rejected because scoped file/process execution semantics and plugin proof behavior belong to later changes. ### Decision 6: Frontend uses platform APIs with local fallback data The server management page loads plugins, run endpoints, server instances, and jobs through typed API client methods, but retains safe seed data when the backend is not available. Create/start/stop buttons call workflow APIs and update local state from the returned instance/job. Alternative considered: keep the page as a static overview until a later acceptance suite. Rejected because this change's completion gate requires browser walkthrough of create/start/stop workflows. ## Risks / Trade-offs - [Risk] Workflow APIs create jobs but the run executor still simulates lifecycle completion. Mitigation: name this as bounded lifecycle execution and test the dispatch/result/state contract; real process orchestration stays deferred. - [Risk] Idempotency keys are caller-provided, so poor clients can create repeated lifecycle jobs. Mitigation: validators require non-empty keys and the frontend generates per-action keys; service tests cover duplicate idempotency behavior through the existing job repository. - [Risk] Direct resource creation can still create `draft` instances without workflow dispatch. Mitigation: keep direct route documented as metadata creation and make the console use workflow routes for operational create. - [Risk] Instance state can remain unchanged while start/stop jobs are active because no `starting`/`stopping` states exist. Mitigation: the UI shows pending job state separately, and terminal job result projection updates the instance state. - [Risk] In-memory job/state projection can be lost on process restart. Mitigation: this repository currently uses in-memory storage; persistence and reconciliation remain future changes. ## Migration Plan 1. Add lifecycle domain, DTO, validation, service, API route, and documentation changes in `platform/`. 2. Add `process.install` to plugin schema, SDK, platform validator, fixtures, and run smoke/runtime capability reporting. 3. Add run lifecycle executor support and tests in `run/runtime`. 4. Add frontend server management contracts, schemas, API methods, page interactions, tests, and styling in `platform_web/`. 5. Verify with platform/run/frontend tests, structure check, strict OpenSpec validation, and browser walkthrough. Rollback is contained to this change before dependent work: remove workflow routes/service methods, lifecycle executor, frontend interactions, and capability enum additions. After dev plugin proof or acceptance suite depends on these routes, rollback must be handled by a new OpenSpec change. ## Open Questions - Whether a future persistence change should add explicit `starting` and `stopping` states or keep active lifecycle status derived from jobs. - Whether restart/update/delete workflows should reuse the same response shape or introduce a richer lifecycle operation resource. - Whether lifecycle action execution should be interpreted by run directly or mediated through a plugin action runtime in the next plugin proof change.